diff --git a/.changeset/famous-clocks-thank.md b/.changeset/famous-clocks-thank.md new file mode 100644 index 0000000000..7294c4e9ea --- /dev/null +++ b/.changeset/famous-clocks-thank.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +feat: Support AI SDK 5.0. `ai.tool` now accepts either a schemaTask or a task with a provided jsonSchema diff --git a/packages/core/src/v3/types/tasks.ts b/packages/core/src/v3/types/tasks.ts index 5a527b9471..67c80d40b4 100644 --- a/packages/core/src/v3/types/tasks.ts +++ b/packages/core/src/v3/types/tasks.ts @@ -547,6 +547,8 @@ export interface Task description?: string; + jsonSchema?: JSONSchema; + /** * Trigger a task with the given payload, and continue without waiting for the result. If you want to wait for the result, use `triggerAndWait`. Returns the id of the triggered task run. * @param payload diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index d06ec1f43e..9c4f4d53e6 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -68,7 +68,7 @@ "@types/slug": "^5.0.3", "@types/uuid": "^9.0.0", "@types/ws": "^8.5.3", - "ai": "^4.2.0", + "ai": "^5.0.0", "encoding": "^0.1.13", "rimraf": "^3.0.2", "tshy": "^3.0.2", @@ -78,7 +78,7 @@ }, "peerDependencies": { "zod": "^3.0.0 || ^4.0.0", - "ai": "^4.2.0" + "ai": "^4.2.0 || ^5.0.0" }, "peerDependenciesMeta": { "ai": { diff --git a/packages/trigger-sdk/src/v3/ai.ts b/packages/trigger-sdk/src/v3/ai.ts index de4f9794f5..59afa2fe21 100644 --- a/packages/trigger-sdk/src/v3/ai.ts +++ b/packages/trigger-sdk/src/v3/ai.ts @@ -1,15 +1,17 @@ import { + AnyTask, isSchemaZodEsque, + Task, type inferSchemaIn, type TaskSchema, type TaskWithSchema, } from "@trigger.dev/core/v3"; -import { jsonSchema, Schema, tool, ToolExecutionOptions, zodSchema } from "ai"; +import { dynamicTool, jsonSchema, JSONSchema7, Schema, Tool, ToolCallOptions, zodSchema } from "ai"; import { metadata } from "./metadata.js"; const METADATA_KEY = "tool.execute.options"; -export type ToolCallExecutionOptions = Omit; +export type ToolCallExecutionOptions = Omit; type ToolResultContent = Array< | { @@ -27,25 +29,43 @@ export type ToolOptions = { experimental_toToolResultContent?: (result: TResult) => ToolResultContent; }; +function toolFromTask( + task: Task, + options?: ToolOptions +): Tool; function toolFromTask< TIdentifier extends string, TTaskSchema extends TaskSchema | undefined = undefined, TOutput = unknown, ->(task: TaskWithSchema, options?: ToolOptions) { - if (!task.schema) { +>( + task: TaskWithSchema, + options?: ToolOptions +): Tool, TOutput>; +function toolFromTask< + TIdentifier extends string, + TTaskSchema extends TaskSchema | undefined = undefined, + TInput = void, + TOutput = unknown, +>( + task: TaskWithSchema | Task, + options?: ToolOptions +): TTaskSchema extends TaskSchema + ? Tool, TOutput> + : Tool { + if (("schema" in task && !task.schema) || ("jsonSchema" in task && !task.jsonSchema)) { throw new Error( - "Cannot convert schemaTask to a tool because the task has no schema. Make sure the schema used in the task is either zod, arktype, or another supported schema." + "Cannot convert this task to to a tool because the task has no schema. Make sure to either use schemaTask or a task with an input jsonSchema." ); } - return tool({ + const toolDefinition = dynamicTool({ description: task.description, - parameters: convertTaskSchemaToToolParameters(task.schema), - execute: async (args, options) => { + inputSchema: convertTaskSchemaToToolParameters(task), + execute: async (input, options) => { const serializedOptions = options ? JSON.parse(JSON.stringify(options)) : undefined; return await task - .triggerAndWait(args, { + .triggerAndWait(input as inferSchemaIn, { metadata: { [METADATA_KEY]: serializedOptions, }, @@ -54,6 +74,10 @@ function toolFromTask< }, ...options, }); + + return toolDefinition as TTaskSchema extends TaskSchema + ? Tool, TOutput> + : Tool; } function getToolOptionsFromMetadata(): ToolCallExecutionOptions | undefined { @@ -64,21 +88,27 @@ function getToolOptionsFromMetadata(): ToolCallExecutionOptions | undefined { return tool as ToolCallExecutionOptions; } -function convertTaskSchemaToToolParameters( - schema: TTaskSchema -): Schema> { - // If TaskSchema is ZodEsque, use ai.zodSchema to convert it to a Schema - if (isSchemaZodEsque(schema)) { - return zodSchema(schema as any); +function convertTaskSchemaToToolParameters( + task: AnyTask | TaskWithSchema +): Schema { + if ("schema" in task) { + // If TaskSchema is ArkTypeEsque, use ai.jsonSchema to convert it to a Schema + if ("toJsonSchema" in task.schema && typeof task.schema.toJsonSchema === "function") { + return jsonSchema((task.schema as any).toJsonSchema()); + } + + // If TaskSchema is ZodEsque, use ai.zodSchema to convert it to a Schema + if (isSchemaZodEsque(task.schema)) { + return zodSchema(task.schema as any); + } } - // If TaskSchema is ArkTypeEsque, use ai.jsonSchema to convert it to a Schema - if ("toJsonSchema" in schema && typeof schema.toJsonSchema === "function") { - return jsonSchema((schema as any).toJsonSchema()); + if ("jsonSchema" in task) { + return jsonSchema(task.jsonSchema as JSONSchema7); } throw new Error( - "Cannot convert schemaTask to a tool. Make sure the schema used in the task is either zod, arktype, or another supported schema." + "Cannot convert task to a tool. Make sure to use a task with a schema or jsonSchema." ); } diff --git a/packages/trigger-sdk/src/v3/shared.ts b/packages/trigger-sdk/src/v3/shared.ts index deb3f4f6f1..11b92c2f43 100644 --- a/packages/trigger-sdk/src/v3/shared.ts +++ b/packages/trigger-sdk/src/v3/shared.ts @@ -161,6 +161,7 @@ export function createTask< const task: Task = { id: params.id, description: params.description, + jsonSchema: params.jsonSchema, trigger: async (payload, options) => { return await trigger_internal>( "trigger()", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15490dbedd..765f650e3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1902,8 +1902,8 @@ importers: specifier: ^8.5.3 version: 8.5.4 ai: - specifier: ^4.2.0 - version: 4.2.5(react@18.3.1)(zod@3.25.76) + specifier: ^5.0.0 + version: 5.0.14(zod@3.25.76) encoding: specifier: ^0.1.13 version: 0.1.13 @@ -1939,11 +1939,11 @@ importers: references/d3-chat: dependencies: '@ai-sdk/anthropic': - specifier: ^1.2.4 - version: 1.2.4(zod@3.25.76) + specifier: 2.0.4 + version: 2.0.4(zod@3.25.76) '@ai-sdk/openai': - specifier: 1.3.3 - version: 1.3.3(zod@3.25.76) + specifier: 2.0.14 + version: 2.0.14(zod@3.25.76) '@e2b/code-interpreter': specifier: ^1.1.0 version: 1.1.0 @@ -1987,8 +1987,8 @@ importers: specifier: ^0.10.0 version: 0.10.0 ai: - specifier: 4.2.5 - version: 4.2.5(react@19.0.0)(zod@3.25.76) + specifier: 5.0.14 + version: 5.0.14(zod@3.25.76) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -2593,17 +2593,27 @@ packages: resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==} dev: false - /@ai-sdk/anthropic@1.2.4(zod@3.25.76): - resolution: {integrity: sha512-dAN6MXvLffeFVAr2gz3RGvOTgX1KL/Yn5q1l4/Dt0TUeDjQgCt4AbbYxZZB2qIAYzQvoyAFPhlw0sB3nNizG/g==} + /@ai-sdk/anthropic@2.0.4(zod@3.25.76): + resolution: {integrity: sha512-ii2bZEUPwBitUiK1dpX+HsOarcDGY71G9TVdSJqbfXSVqa+speJNZ8PA/bjuNMml0NyX8VxNsaMg3SwBUCZspA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: ^3.25.76 || ^4 dependencies: - '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.3(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.3(zod@3.25.76) zod: 3.25.76 dev: false + /@ai-sdk/gateway@1.0.6(zod@3.25.76): + resolution: {integrity: sha512-JuSj1MtTr4vw2VBBth4wlbciQnQIV0o1YV9qGLFA+r85nR5H+cJp3jaYE0nprqfzC9rYG8w9c6XGHB3SDKgcgA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4 + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.3(zod@3.25.76) + zod: 3.25.76 + /@ai-sdk/openai@1.0.1(zod@3.25.76): resolution: {integrity: sha512-snZge8457afWlosVNUn+BG60MrxAPOOm3zmIMxJZih8tneNSiRbTVCbSzAtq/9vsnOHDe5RR83PRl85juOYEnA==} engines: {node: '>=18'} @@ -2637,6 +2647,17 @@ packages: zod: 3.25.76 dev: false + /@ai-sdk/openai@2.0.14(zod@3.25.76): + resolution: {integrity: sha512-u/wi1ixcvcg29wAJySjO803HlXpyCl6mkcOHn+Zn7DA+CtjuQNkKikJ4pZBc7I3Qhi90kA4XnOfKikhBXh4c4Q==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4 + dependencies: + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.3(zod@3.25.76) + zod: 3.25.76 + dev: false + /@ai-sdk/provider-utils@1.0.17(zod@3.25.76): resolution: {integrity: sha512-2VyeTH5DQ6AxqvwdyytKIeiZyYTyJffpufWjE67zM2sXMIHgYl7fivo8m5wVl6Cbf1dFPSGKq//C9s+lz+NHrQ==} engines: {node: '>=18'} @@ -2695,30 +2716,31 @@ packages: nanoid: 3.3.8 secure-json-parse: 2.7.0 zod: 3.25.76 + dev: false - /@ai-sdk/provider-utils@2.2.3(zod@3.25.76): - resolution: {integrity: sha512-o3fWTzkxzI5Af7U7y794MZkYNEsxbjLam2nxyoUZSScqkacb7vZ3EYHLh21+xCcSSzEC161C7pZAGHtC0hTUMw==} + /@ai-sdk/provider-utils@2.2.8(zod@3.25.76): + resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} engines: {node: '>=18'} peerDependencies: zod: ^3.23.8 dependencies: - '@ai-sdk/provider': 1.1.0 + '@ai-sdk/provider': 1.1.3 nanoid: 3.3.8 secure-json-parse: 2.7.0 zod: 3.25.76 dev: false - /@ai-sdk/provider-utils@2.2.8(zod@3.25.76): - resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} + /@ai-sdk/provider-utils@3.0.3(zod@3.25.76): + resolution: {integrity: sha512-kAxIw1nYmFW1g5TvE54ZB3eNtgZna0RnLjPUp1ltz1+t9xkXJIuDT4atrwfau9IbS0BOef38wqrI8CjFfQrxhw==} engines: {node: '>=18'} peerDependencies: - zod: ^3.23.8 + zod: ^3.25.76 || ^4 dependencies: - '@ai-sdk/provider': 1.1.3 - nanoid: 3.3.8 - secure-json-parse: 2.7.0 + '@ai-sdk/provider': 2.0.0 + '@standard-schema/spec': 1.0.0 + eventsource-parser: 3.0.3 zod: 3.25.76 - dev: false + zod-to-json-schema: 3.24.5(zod@3.25.76) /@ai-sdk/provider@0.0.22: resolution: {integrity: sha512-smZ1/2jL/JSKnbhC6ama/PxI2D/psj+YAe0c0qpd5ComQCNFltg72VFf0rpUSFMmFuj1pCCNoBOCrvyl8HTZHQ==} @@ -2746,6 +2768,7 @@ packages: engines: {node: '>=18'} dependencies: json-schema: 0.4.0 + dev: false /@ai-sdk/provider@1.1.3: resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} @@ -2754,6 +2777,12 @@ packages: json-schema: 0.4.0 dev: false + /@ai-sdk/provider@2.0.0: + resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} + engines: {node: '>=18'} + dependencies: + json-schema: 0.4.0 + /@ai-sdk/react@0.0.53(react@19.0.0-rc.0)(zod@3.25.76): resolution: {integrity: sha512-sIsmTFoR/QHvUUkltmHwP4bPjwy2vko6j/Nj8ayxLhEHs04Ug+dwXQyfA7MwgimEE3BcDQpWL8ikVj0m3ZILWQ==} engines: {node: '>=18'} @@ -2831,24 +2860,6 @@ packages: zod: 3.25.76 dev: false - /@ai-sdk/react@1.2.2(react@18.3.1)(zod@3.25.76): - resolution: {integrity: sha512-rxyNTFjUd3IilVOJFuUJV5ytZBYAIyRi50kFS2gNmSEiG4NHMBBm31ddrxI/i86VpY8gzZVp1/igtljnWBihUA==} - engines: {node: '>=18'} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - zod: ^3.23.8 - peerDependenciesMeta: - zod: - optional: true - dependencies: - '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) - '@ai-sdk/ui-utils': 1.2.1(zod@3.25.76) - react: 18.3.1 - swr: 2.2.5(react@18.3.1) - throttleit: 2.1.0 - zod: 3.25.76 - dev: true - /@ai-sdk/react@1.2.2(react@19.0.0)(zod@3.25.76): resolution: {integrity: sha512-rxyNTFjUd3IilVOJFuUJV5ytZBYAIyRi50kFS2gNmSEiG4NHMBBm31ddrxI/i86VpY8gzZVp1/igtljnWBihUA==} engines: {node: '>=18'} @@ -2990,6 +3001,7 @@ packages: '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) zod: 3.25.76 zod-to-json-schema: 3.24.5(zod@3.25.76) + dev: false /@ai-sdk/ui-utils@1.2.11(zod@3.25.76): resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} @@ -21643,26 +21655,6 @@ packages: zod-to-json-schema: 3.24.5(zod@3.25.76) dev: false - /ai@4.2.5(react@18.3.1)(zod@3.25.76): - resolution: {integrity: sha512-URJEslI3cgF/atdTJHtz+Sj0W1JTmiGmD3znw9KensL3qV605odktDim+GTazNJFPR4QaIu1lUio5b8RymvOjA==} - engines: {node: '>=18'} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - zod: ^3.23.8 - peerDependenciesMeta: - react: - optional: true - dependencies: - '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) - '@ai-sdk/react': 1.2.2(react@18.3.1)(zod@3.25.76) - '@ai-sdk/ui-utils': 1.2.1(zod@3.25.76) - '@opentelemetry/api': 1.9.0 - jsondiffpatch: 0.6.0 - react: 18.3.1 - zod: 3.25.76 - dev: true - /ai@4.2.5(react@19.0.0)(zod@3.25.76): resolution: {integrity: sha512-URJEslI3cgF/atdTJHtz+Sj0W1JTmiGmD3znw9KensL3qV605odktDim+GTazNJFPR4QaIu1lUio5b8RymvOjA==} engines: {node: '>=18'} @@ -21703,6 +21695,18 @@ packages: zod: 3.25.76 dev: false + /ai@5.0.14(zod@3.25.76): + resolution: {integrity: sha512-xiujFa879skB7YxGzbeHAxepsr6AEaWcHPXrc5a9MRM6p4WdVAwn6mGwVZkBnhqGfZtXFr4LUnU2ayvcjWp5ig==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4 + dependencies: + '@ai-sdk/gateway': 1.0.6(zod@3.25.76) + '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider-utils': 3.0.3(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + zod: 3.25.76 + /ajv-formats@2.1.1(ajv@8.17.1): resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -25782,6 +25786,10 @@ packages: engines: {node: '>=18.0.0'} dev: false + /eventsource-parser@3.0.3: + resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==} + engines: {node: '>=20.0.0'} + /eventsource@3.0.5: resolution: {integrity: sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==} engines: {node: '>=18.0.0'} diff --git a/references/d3-chat/package.json b/references/d3-chat/package.json index 22e5a9dae9..4ce209ab95 100644 --- a/references/d3-chat/package.json +++ b/references/d3-chat/package.json @@ -19,8 +19,8 @@ "db:migrate:down": "tsx -r dotenv/config src/lib/migrate.ts down" }, "dependencies": { - "@ai-sdk/anthropic": "^1.2.4", - "@ai-sdk/openai": "1.3.3", + "@ai-sdk/anthropic": "2.0.4", + "@ai-sdk/openai": "2.0.14", "@e2b/code-interpreter": "^1.1.0", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.203.0", @@ -35,7 +35,7 @@ "@trigger.dev/sdk": "workspace:*", "@vercel/otel": "^1.13.0", "@vercel/postgres": "^0.10.0", - "ai": "4.2.5", + "ai": "5.0.14", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.486.0", diff --git a/references/d3-chat/src/components/chat-container.tsx b/references/d3-chat/src/components/chat-container.tsx index 1346ff5c02..b40150e5b3 100644 --- a/references/d3-chat/src/components/chat-container.tsx +++ b/references/d3-chat/src/components/chat-container.tsx @@ -61,17 +61,17 @@ function getMessagesFromRun( id: `tool-${part.toolCallId}`, role: "tool", name: part.toolName, - input: part.args, + input: part.input, }; toolCalls.set(part.toolCallId, toolMessage); messages.push(toolMessage); } else if (part.type === "tool-result") { const toolMessage = toolCalls.get(part.toolCallId); if (toolMessage) { - toolMessage.output = part.result; + toolMessage.output = part.output; } } else if (part.type === "text-delta") { - currentAssistantContent += part.textDelta; + currentAssistantContent += part.text; // Find or create the assistant message const lastMessage = messages[messages.length - 1]; diff --git a/references/d3-chat/src/trigger/chat.ts b/references/d3-chat/src/trigger/chat.ts index 9870cbc17d..d0a2f2d8dd 100644 --- a/references/d3-chat/src/trigger/chat.ts +++ b/references/d3-chat/src/trigger/chat.ts @@ -1,15 +1,16 @@ import { anthropic } from "@ai-sdk/anthropic"; import { openai } from "@ai-sdk/openai"; import { ai } from "@trigger.dev/sdk/ai"; -import { logger, metadata, runs, schemaTask, tasks, wait, otel } from "@trigger.dev/sdk/v3"; +import { logger, metadata, runs, schemaTask, tasks, wait, otel, task } from "@trigger.dev/sdk/v3"; import { sql } from "@vercel/postgres"; import { - CoreMessage, - createDataStream, - DataStreamWriter, + ModelMessage, streamText, TextStreamPart, tool, + stepCountIs, + createUIMessageStream, + UIMessageStreamWriter, } from "ai"; import { nanoid } from "nanoid"; import { z } from "zod"; @@ -80,9 +81,57 @@ const queryApprovalTask = schemaTask({ const queryApproval = ai.tool(queryApprovalTask); +// const queryApprovalTask = task({ +// id: "query-approval", +// description: "Get approval for a SQL query from an admin", +// jsonSchema: { +// type: "object", +// properties: { +// userId: { type: "string", description: "The user_id to get approval for" }, +// input: { type: "string", description: "The input to get approval for" }, +// query: { type: "string", description: "The SQL query to execute" }, +// }, +// required: ["userId", "input", "query"], +// additionalProperties: false, +// }, +// run: async ({ userId, input, query }: { userId: string; input: string; query: string }) => { +// logger.info("queryApproval: starting", { projectRef: process.env.TRIGGER_PROJECT_REF }); + +// await callNextjsApp(); + +// const token = await wait.createToken({ +// tags: [`user:${userId}`, "approval"], +// timeout: "5m", // timeout in 5 minutes +// }); + +// await sendSQLApprovalMessage({ +// query, +// userId, +// tokenId: token.id, +// publicAccessToken: token.publicAccessToken, +// input, +// }); + +// const result = await wait.forToken(token); + +// // result.ok === false if the token timed out +// if (!result.ok) { +// logger.debug("queryApproval: token timed out"); + +// return { +// approved: false, +// }; +// } else { +// return result.output; +// } +// }, +// }); + +// const queryApproval = ai.tool(queryApprovalTask); + const executeSql = tool({ description: "Use this tool to execute a SQL query", - parameters: z.object({ + inputSchema: z.object({ query: z.string().describe("The SQL query to execute"), }), execute: async ({ query }) => { @@ -95,7 +144,7 @@ const executeSql = tool({ const generateId = tool({ description: "Use this tool to generate a unique ID for a todo", - parameters: z.object({ + inputSchema: z.object({ prefix: z.string().describe("The prefix for the ID (defaults to 'todo')").default("todo"), }), execute: async ({ prefix }) => { @@ -105,7 +154,7 @@ const generateId = tool({ const getUserTodos = tool({ description: "Use this tool to get all todos for a user", - parameters: z.object({ + inputSchema: z.object({ userId: z.string().describe("The user_id to get todos for"), }), execute: async ({ userId }) => { @@ -117,7 +166,7 @@ const getUserTodos = tool({ const getUserId = tool({ description: "Use this tool to get the user_id for the current user", - parameters: z.object({}), + inputSchema: z.object({}), execute: async () => { const userId = metadata.get("user_id"); @@ -202,7 +251,7 @@ export const todoChat = schemaTask({ model: getModel(), system, prompt, - maxSteps: 10, + stopWhen: stepCountIs(10), tools: { queryApproval, executeSql, @@ -227,7 +276,7 @@ export const todoChat = schemaTask({ for await (const part of stream) { if (part.type === "text-delta") { - textParts.push(part.textDelta); + textParts.push(part.text); } } @@ -309,7 +358,7 @@ export const interruptibleChat = schemaTask({ async function createStreamWithProvider(params: { model: ReturnType | ReturnType; - messages: CoreMessage[]; + messages: ModelMessage[]; message_request_id: string; chat_id: string; userId?: string; @@ -317,14 +366,13 @@ async function createStreamWithProvider(params: { const { model, messages, message_request_id, chat_id, userId } = params; return new Promise((resolve, reject) => { - const dataStreamResponse = createDataStream({ + const dataStreamResponse = createUIMessageStream({ execute: async (dataStream) => { const result = streamText({ model, system: "This is the system prompt, please be nice.", messages, - maxSteps: 20, - toolCallStreaming: true, + stopWhen: stepCountIs(20), onError: (error) => { logger.error("Error in chatStream task (streamText)", { error: error instanceof Error ? error.message : "Unknown error", @@ -336,7 +384,7 @@ async function createStreamWithProvider(params: { onChunk: async (chunk) => { console.log("Chunk:", chunk); }, - onFinish: async ({ response, reasoning }) => { + onFinish: async ({ response, reasoningText }) => { metadata.flush(); logger.info("AI stream finished", { chat_id, @@ -372,9 +420,7 @@ async function createStreamWithProvider(params: { result.consumeStream(); - result.mergeIntoDataStream(dataStream, { - sendReasoning: true, - }); + dataStream.writer.merge(result.toUIMessageStream()); }, onError: (error) => { logger.error("Error in chatStream task (createDataStream)", { @@ -431,7 +477,7 @@ export const chatStream = schemaTask({ // First try with Anthropic return await createStreamWithProvider({ model: anthropic(model), - messages: messages as CoreMessage[], + messages: messages as ModelMessage[], message_request_id, chat_id, userId, @@ -449,7 +495,7 @@ export const chatStream = schemaTask({ // Fallback to OpenAI return await createStreamWithProvider({ model: openai("gpt-4"), - messages: messages as CoreMessage[], + messages: messages as ModelMessage[], message_request_id, chat_id, userId, @@ -534,12 +580,12 @@ export const chatStream2 = schemaTask({ message_request_id, }); - const dataStreamResponse = createDataStream({ - execute: async (dataStream) => { + const dataStreamResponse = createUIMessageStream({ + execute: async ({ writer }) => { streamTextWithModel( - dataStream, + writer, anthropic(model), - messages as CoreMessage[], + messages as ModelMessage[], chat_id, openai("gpt-4"), userId @@ -556,9 +602,9 @@ export const chatStream2 = schemaTask({ }); function streamTextWithModel( - dataStream: DataStreamWriter, + writer: UIMessageStreamWriter, model: ReturnType | ReturnType, - messages: CoreMessage[], + messages: ModelMessage[], chat_id: string, fallbackModel?: ReturnType | ReturnType, userId?: string @@ -567,8 +613,7 @@ function streamTextWithModel( model, system: "This is the system prompt, please be nice.", messages, - maxSteps: 20, - toolCallStreaming: true, + stopWhen: stepCountIs(20), onError: (error) => { logger.error("Error in chatStream task (streamText)", { error: error instanceof Error ? error.message : "Unknown error", @@ -577,13 +622,13 @@ function streamTextWithModel( }); if (fallbackModel) { - streamTextWithModel(dataStream, fallbackModel, messages, chat_id, undefined, userId); + streamTextWithModel(writer, fallbackModel, messages, chat_id, undefined, userId); } }, onChunk: async (chunk) => { console.log("Chunk:", chunk); }, - onFinish: async ({ response, reasoning }) => { + onFinish: async ({ response, reasoningText }) => { metadata.flush(); logger.info("AI stream finished", { chat_id, @@ -619,9 +664,7 @@ function streamTextWithModel( result.consumeStream(); - result.mergeIntoDataStream(dataStream, { - sendReasoning: true, - }); + writer.merge(result.toUIMessageStream()); } export const chatStreamCaller2 = schemaTask({