feat: AI SDK 5.0 support (#2396)

* feat: AI SDK 5.0 support

* Add changeset
This commit is contained in:
Eric Allam
2025-08-15 17:03:39 +01:00
committed by GitHub
parent a76905e272
commit 3d17ce5559
9 changed files with 211 additions and 122 deletions
+5
View File
@@ -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
+2
View File
@@ -547,6 +547,8 @@ export interface Task<TIdentifier extends string, TInput = void, TOutput = any>
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
+2 -2
View File
@@ -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": {
+49 -19
View File
@@ -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<ToolExecutionOptions, "abortSignal">;
export type ToolCallExecutionOptions = Omit<ToolCallOptions, "abortSignal">;
type ToolResultContent = Array<
| {
@@ -27,25 +29,43 @@ export type ToolOptions<TResult> = {
experimental_toToolResultContent?: (result: TResult) => ToolResultContent;
};
function toolFromTask<TIdentifier extends string, TInput = void, TOutput = unknown>(
task: Task<TIdentifier, TInput, TOutput>,
options?: ToolOptions<TOutput>
): Tool<TInput, TOutput>;
function toolFromTask<
TIdentifier extends string,
TTaskSchema extends TaskSchema | undefined = undefined,
TOutput = unknown,
>(task: TaskWithSchema<TIdentifier, TTaskSchema, TOutput>, options?: ToolOptions<TOutput>) {
if (!task.schema) {
>(
task: TaskWithSchema<TIdentifier, TTaskSchema, TOutput>,
options?: ToolOptions<TOutput>
): Tool<inferSchemaIn<TTaskSchema>, TOutput>;
function toolFromTask<
TIdentifier extends string,
TTaskSchema extends TaskSchema | undefined = undefined,
TInput = void,
TOutput = unknown,
>(
task: TaskWithSchema<TIdentifier, TTaskSchema, TOutput> | Task<TIdentifier, TInput, TOutput>,
options?: ToolOptions<TOutput>
): TTaskSchema extends TaskSchema
? Tool<inferSchemaIn<TTaskSchema>, TOutput>
: Tool<TInput, TOutput> {
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<TTaskSchema>, {
metadata: {
[METADATA_KEY]: serializedOptions,
},
@@ -54,6 +74,10 @@ function toolFromTask<
},
...options,
});
return toolDefinition as TTaskSchema extends TaskSchema
? Tool<inferSchemaIn<TTaskSchema>, TOutput>
: Tool<TInput, TOutput>;
}
function getToolOptionsFromMetadata(): ToolCallExecutionOptions | undefined {
@@ -64,21 +88,27 @@ function getToolOptionsFromMetadata(): ToolCallExecutionOptions | undefined {
return tool as ToolCallExecutionOptions;
}
function convertTaskSchemaToToolParameters<TTaskSchema extends TaskSchema>(
schema: TTaskSchema
): Schema<inferSchemaIn<TTaskSchema>> {
// 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<any, any, any>
): Schema<unknown> {
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."
);
}
+1
View File
@@ -161,6 +161,7 @@ export function createTask<
const task: Task<TIdentifier, TInput, TOutput> = {
id: params.id,
description: params.description,
jsonSchema: params.jsonSchema,
trigger: async (payload, options) => {
return await trigger_internal<RunTypes<TIdentifier, TInput, TOutput>>(
"trigger()",
+70 -62
View File
@@ -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
@@ -1993,8 +1993,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
@@ -2599,17 +2599,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'}
@@ -2643,6 +2653,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'}
@@ -2701,17 +2722,6 @@ packages:
nanoid: 3.3.8
secure-json-parse: 2.7.0
zod: 3.25.76
/@ai-sdk/provider-utils@2.2.3(zod@3.25.76):
resolution: {integrity: sha512-o3fWTzkxzI5Af7U7y794MZkYNEsxbjLam2nxyoUZSScqkacb7vZ3EYHLh21+xCcSSzEC161C7pZAGHtC0hTUMw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.23.8
dependencies:
'@ai-sdk/provider': 1.1.0
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):
@@ -2726,6 +2736,18 @@ packages:
zod: 3.25.76
dev: false
/@ai-sdk/provider-utils@3.0.3(zod@3.25.76):
resolution: {integrity: sha512-kAxIw1nYmFW1g5TvE54ZB3eNtgZna0RnLjPUp1ltz1+t9xkXJIuDT4atrwfau9IbS0BOef38wqrI8CjFfQrxhw==}
engines: {node: '>=18'}
peerDependencies:
zod: ^3.25.76 || ^4
dependencies:
'@ai-sdk/provider': 2.0.0
'@standard-schema/spec': 1.0.0
eventsource-parser: 3.0.3
zod: 3.25.76
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==}
engines: {node: '>=18'}
@@ -2752,6 +2774,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==}
@@ -2760,6 +2783,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'}
@@ -2837,24 +2866,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'}
@@ -2996,6 +3007,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==}
@@ -21677,26 +21689,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'}
@@ -21737,6 +21729,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:
@@ -25816,6 +25820,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'}
+3 -3
View File
@@ -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",
@@ -37,7 +37,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",
@@ -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];
+76 -33
View File
@@ -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<QueryApproval>(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<typeof anthropic> | ReturnType<typeof openai>;
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<string>((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<typeof anthropic> | ReturnType<typeof openai>,
messages: CoreMessage[],
messages: ModelMessage[],
chat_id: string,
fallbackModel?: ReturnType<typeof anthropic> | ReturnType<typeof openai>,
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({