AI agents

Use another framework

Last updated on

Call the Paradoc tools from plain code, or adapt them to a framework that has no adapter

Paradoc has adapters for the Vercel AI SDK, TanStack AI, and Mastra. For any other framework, or to call the tools from your own code, use @paradoc/ai-tools. It is the package every adapter wraps, and it does not depend on an AI framework.

npm install @paradoc/ai-tools

It needs Node.js 22.13 or newer.

Call the tools from your code

Each tool has an execute function, such as executeFill for fill. It takes the same snake_case input as the tool, and the same configuration as the adapters.

import { executeFill, executeGetFillState, executeRender } from "@paradoc/ai-tools"

const config = { defaultRegistryUrl: "https://public.paradoc.dev" }
const source = { source: "registry", artifact_name: "pet-addendum" } as const

const draft = await executeFill(
  {
    ...source,
    data: {
      fields: { petName: "Rex", species: "dog", weight: 30, isVaccinated: true },
      parties: { tenant: { name: "Jane Doe" } },
    },
  },
  config,
)
if (!draft.accepted) throw new Error(draft.error?.message)

const state = await executeGetFillState(
  { ...source, data: draft.data, evaluation_context: draft.evaluation_context },
  config,
)
console.log(state.next) // { kind: "party", key: "landlord", required: true, order: 1 }

const preview = await executeRender(
  { ...source, data: draft.data, evaluation_context: draft.evaluation_context, layer: "markdown" },
  config,
)
console.log(preview.content)

An execute function does not throw when the tool fails. Its result has an error object instead. See Errors.

Write an adapter

toolDefinitions has the name, the description for the model, the Zod input and output schemas, and the execute function of each tool. An adapter turns each one into the tool type of its framework:

import {
  boundModelValue,
  configForExecution,
  operationNames,
  toolDefinitions,
  type ParadocToolsConfig,
} from "@paradoc/ai-tools"
import { z } from "zod"

export function myFrameworkTools(config?: ParadocToolsConfig) {
  return operationNames.map((name) => {
    const definition = toolDefinitions[name]
    return {
      name: definition.name,
      description: definition.description,
      parameters: z.toJSONSchema(definition.input_schema, { io: "input" }),
      async execute(input: unknown, signal?: AbortSignal) {
        // Join the framework's abort signal to the configured ones.
        const output = await definition.execute(input as never, configForExecution(config, signal))
        // Your code keeps the full result. The model gets a bounded copy.
        return { output, forModel: boundModelValue(output, config?.maxOutputBytes) }
      },
    }
  })
}

An adapter must do three things:

  • Describe the input. Give the framework the Zod schema, or the JSON Schema made from it. Make the JSON Schema from the input side (io: "input"), because some fields have defaults.
  • Join the abort signals. configForExecution(config, signal) joins the framework's abort signal to signal and context.signal from the configuration.
  • Bound what the model sees. boundModelValue(output, maxBytes) cuts long content, such as a base64 PDF, to maxOutputBytes (default 16,384) and marks it truncated: true. Return the full result to application code.

The Vercel AI SDK, TanStack AI, and Mastra adapters are complete examples.

Next

On this page