@tanstack/ai 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +0 -25
  2. package/dist/esm/activities/chat/index.d.ts +11 -10
  3. package/dist/esm/activities/chat/index.js +9 -9
  4. package/dist/esm/activities/chat/index.js.map +1 -1
  5. package/dist/esm/activities/chat/tools/schema-converter.d.ts +116 -0
  6. package/dist/esm/activities/chat/tools/schema-converter.js +115 -0
  7. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -0
  8. package/dist/esm/activities/chat/tools/tool-calls.js +23 -34
  9. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
  10. package/dist/esm/activities/chat/tools/tool-definition.d.ts +18 -14
  11. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
  12. package/dist/esm/index.d.ts +2 -2
  13. package/dist/esm/index.js +5 -4
  14. package/dist/esm/stream-to-response.d.ts +28 -21
  15. package/dist/esm/stream-to-response.js +24 -17
  16. package/dist/esm/stream-to-response.js.map +1 -1
  17. package/dist/esm/types.d.ts +44 -25
  18. package/package.json +3 -6
  19. package/src/activities/chat/index.ts +36 -28
  20. package/src/activities/chat/tools/schema-converter.ts +332 -0
  21. package/src/activities/chat/tools/tool-calls.ts +47 -53
  22. package/src/activities/chat/tools/tool-definition.ts +33 -29
  23. package/src/index.ts +5 -2
  24. package/src/stream-to-response.ts +59 -44
  25. package/src/types.ts +47 -26
  26. package/dist/esm/activities/chat/tools/zod-converter.d.ts +0 -69
  27. package/dist/esm/activities/chat/tools/zod-converter.js +0 -99
  28. package/dist/esm/activities/chat/tools/zod-converter.js.map +0 -1
  29. package/src/activities/chat/tools/zod-converter.ts +0 -235
@@ -1 +1 @@
1
- {"version":3,"file":"stream-to-response.js","sources":["../../src/stream-to-response.ts"],"sourcesContent":["import type { StreamChunk } from './types'\n\n/**\n * Collect all text content from a StreamChunk async iterable and return as a string.\n *\n * This function consumes the entire stream, accumulating content from 'content' type chunks,\n * and returns the final concatenated text.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @returns Promise<string> - The accumulated text content\n *\n * @example\n * ```typescript\n * const stream = chat({\n * adapter: openaiText(),\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }]\n * });\n * const text = await streamToText(stream);\n * console.log(text); // \"Hello! How can I help you today?\"\n * ```\n */\nexport async function streamToText(\n stream: AsyncIterable<StreamChunk>,\n): Promise<string> {\n let accumulatedContent = ''\n\n for await (const chunk of stream) {\n if (chunk.type === 'content' && chunk.delta) {\n accumulatedContent += chunk.delta\n }\n }\n\n return accumulatedContent\n}\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in Server-Sent Events format\n *\n * This creates a ReadableStream that emits chunks in SSE format:\n * - Each chunk is prefixed with \"data: \"\n * - Each chunk is followed by \"\\n\\n\"\n * - Stream ends with \"data: [DONE]\\n\\n\"\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in Server-Sent Events format\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * const readableStream = toServerSentEventsStream(stream);\n * // Use with Response, or any API that accepts ReadableStream\n * ```\n */\nexport function toServerSentEventsStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as Server-Sent Events format\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n )\n }\n\n // Send completion marker\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'))\n controller.close()\n } catch (error: any) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error chunk\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({\n type: 'error',\n error: {\n message: error.message || 'Unknown error occurred',\n code: error.code,\n },\n })}\\n\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in HTTP stream format (newline-delimited JSON)\n *\n * This creates a ReadableStream that emits chunks as newline-delimited JSON:\n * - Each chunk is JSON.stringify'd and followed by \"\\n\"\n * - No SSE formatting (no \"data: \" prefix)\n *\n * This format is compatible with `fetchHttpStream` connection adapter.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in HTTP stream format (newline-delimited JSON)\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * const readableStream = toHttpStream(stream);\n * // Use with Response for HTTP streaming (not SSE)\n * return new Response(readableStream, {\n * headers: { 'Content-Type': 'application/x-ndjson' }\n * });\n * ```\n */\nexport function toHttpStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as newline-delimited JSON\n controller.enqueue(encoder.encode(`${JSON.stringify(chunk)}\\n`))\n }\n\n controller.close()\n } catch (error: any) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error chunk\n controller.enqueue(\n encoder.encode(\n `${JSON.stringify({\n type: 'error',\n error: {\n message: error.message || 'Unknown error occurred',\n code: error.code,\n },\n })}\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Create a streaming HTTP response from a StreamChunk async iterable\n * Includes proper headers for Server-Sent Events\n *\n * @deprecated Use `toServerSentEventsStream` instead. This function will be removed in a future version.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param init - Optional Response initialization options\n * @param abortController - Optional AbortController to abort when client disconnects\n * @returns Response object with SSE headers and streaming body\n *\n * @example\n * ```typescript\n * export async function POST(request: Request) {\n * const { messages } = await request.json();\n * const abortController = new AbortController();\n * const stream = chat({\n * adapter: openaiText(),\n * model: \"gpt-4o\",\n * messages,\n * options: { abortSignal: abortController.signal }\n * });\n * return toStreamResponse(stream, undefined, abortController);\n * }\n * ```\n */\nexport function toStreamResponse(\n stream: AsyncIterable<StreamChunk>,\n init?: ResponseInit & { abortController?: AbortController },\n): Response {\n if (typeof console !== 'undefined') {\n console.warn(\n '`toStreamResponse` is deprecated. Use `toServerSentEventsStream` instead. Example:\\n' +\n ' const readableStream = toServerSentEventsStream(stream, abortController);\\n' +\n ' return new Response(readableStream, {\\n' +\n \" headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' }\\n\" +\n ' });',\n )\n }\n\n const { headers, abortController, ...responseInit } = init ?? {}\n return new Response(toServerSentEventsStream(stream, abortController), {\n ...responseInit,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n ...(headers || {}),\n },\n })\n}\n"],"names":[],"mappings":"AAsBA,eAAsB,aACpB,QACiB;AACjB,MAAI,qBAAqB;AAEzB,mBAAiB,SAAS,QAAQ;AAChC,QAAI,MAAM,SAAS,aAAa,MAAM,OAAO;AAC3C,4BAAsB,MAAM;AAAA,IAC9B;AAAA,EACF;AAEA,SAAO;AACT;AAqBO,SAAS,yBACd,QACA,iBAC4B;AAC5B,QAAM,UAAU,IAAI,YAAA;AAEpB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,MAAM,YAAY;AACtB,UAAI;AACF,yBAAiB,SAAS,QAAQ;AAEhC,cAAI,iBAAiB,OAAO,SAAS;AACnC;AAAA,UACF;AAGA,qBAAW;AAAA,YACT,QAAQ,OAAO,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,CAAM;AAAA,UAAA;AAAA,QAEvD;AAGA,mBAAW,QAAQ,QAAQ,OAAO,kBAAkB,CAAC;AACrD,mBAAW,MAAA;AAAA,MACb,SAAS,OAAY;AAEnB,YAAI,iBAAiB,OAAO,SAAS;AACnC,qBAAW,MAAA;AACX;AAAA,QACF;AAGA,mBAAW;AAAA,UACT,QAAQ;AAAA,YACN,SAAS,KAAK,UAAU;AAAA,cACtB,MAAM;AAAA,cACN,OAAO;AAAA,gBACL,SAAS,MAAM,WAAW;AAAA,gBAC1B,MAAM,MAAM;AAAA,cAAA;AAAA,YACd,CACD,CAAC;AAAA;AAAA;AAAA,UAAA;AAAA,QACJ;AAEF,mBAAW,MAAA;AAAA,MACb;AAAA,IACF;AAAA,IACA,SAAS;AAGP,UAAI,iBAAiB;AACnB,wBAAgB,MAAA;AAAA,MAClB;AAAA,IACF;AAAA,EAAA,CACD;AACH;AAyBO,SAAS,aACd,QACA,iBAC4B;AAC5B,QAAM,UAAU,IAAI,YAAA;AAEpB,SAAO,IAAI,eAAe;AAAA,IACxB,MAAM,MAAM,YAAY;AACtB,UAAI;AACF,yBAAiB,SAAS,QAAQ;AAEhC,cAAI,iBAAiB,OAAO,SAAS;AACnC;AAAA,UACF;AAGA,qBAAW,QAAQ,QAAQ,OAAO,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,CAAI,CAAC;AAAA,QACjE;AAEA,mBAAW,MAAA;AAAA,MACb,SAAS,OAAY;AAEnB,YAAI,iBAAiB,OAAO,SAAS;AACnC,qBAAW,MAAA;AACX;AAAA,QACF;AAGA,mBAAW;AAAA,UACT,QAAQ;AAAA,YACN,GAAG,KAAK,UAAU;AAAA,cAChB,MAAM;AAAA,cACN,OAAO;AAAA,gBACL,SAAS,MAAM,WAAW;AAAA,gBAC1B,MAAM,MAAM;AAAA,cAAA;AAAA,YACd,CACD,CAAC;AAAA;AAAA,UAAA;AAAA,QACJ;AAEF,mBAAW,MAAA;AAAA,MACb;AAAA,IACF;AAAA,IACA,SAAS;AAGP,UAAI,iBAAiB;AACnB,wBAAgB,MAAA;AAAA,MAClB;AAAA,IACF;AAAA,EAAA,CACD;AACH;AA4BO,SAAS,iBACd,QACA,MACU;AACV,MAAI,OAAO,YAAY,aAAa;AAClC,YAAQ;AAAA,MACN;AAAA,IAAA;AAAA,EAMJ;AAEA,QAAM,EAAE,SAAS,iBAAiB,GAAG,aAAA,IAAiB,QAAQ,CAAA;AAC9D,SAAO,IAAI,SAAS,yBAAyB,QAAQ,eAAe,GAAG;AAAA,IACrE,GAAG;AAAA,IACH,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,YAAY;AAAA,MACZ,GAAI,WAAW,CAAA;AAAA,IAAC;AAAA,EAClB,CACD;AACH;"}
1
+ {"version":3,"file":"stream-to-response.js","sources":["../../src/stream-to-response.ts"],"sourcesContent":["import type { StreamChunk } from './types'\n\n/**\n * Collect all text content from a StreamChunk async iterable and return as a string.\n *\n * This function consumes the entire stream, accumulating content from 'content' type chunks,\n * and returns the final concatenated text.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @returns Promise<string> - The accumulated text content\n *\n * @example\n * ```typescript\n * const stream = chat({\n * adapter: openaiText(),\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }]\n * });\n * const text = await streamToText(stream);\n * console.log(text); // \"Hello! How can I help you today?\"\n * ```\n */\nexport async function streamToText(\n stream: AsyncIterable<StreamChunk>,\n): Promise<string> {\n let accumulatedContent = ''\n\n for await (const chunk of stream) {\n if (chunk.type === 'content' && chunk.delta) {\n accumulatedContent += chunk.delta\n }\n }\n\n return accumulatedContent\n}\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in Server-Sent Events format\n *\n * This creates a ReadableStream that emits chunks in SSE format:\n * - Each chunk is prefixed with \"data: \"\n * - Each chunk is followed by \"\\n\\n\"\n * - Stream ends with \"data: [DONE]\\n\\n\"\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in Server-Sent Events format\n */\nexport function toServerSentEventsStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as Server-Sent Events format\n controller.enqueue(\n encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`),\n )\n }\n\n // Send completion marker\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'))\n controller.close()\n } catch (error: any) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error chunk\n controller.enqueue(\n encoder.encode(\n `data: ${JSON.stringify({\n type: 'error',\n error: {\n message: error.message || 'Unknown error occurred',\n code: error.code,\n },\n })}\\n\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a Response in Server-Sent Events format\n *\n * This creates a Response that emits chunks in SSE format:\n * - Each chunk is prefixed with \"data: \"\n * - Each chunk is followed by \"\\n\\n\"\n * - Stream ends with \"data: [DONE]\\n\\n\"\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param init - Optional Response initialization options (including `abortController`)\n * @returns Response in Server-Sent Events format\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * return toServerSentEventsResponse(stream, { abortController });\n * ```\n */\nexport function toServerSentEventsResponse(\n stream: AsyncIterable<StreamChunk>,\n init?: ResponseInit & { abortController?: AbortController },\n): Response {\n const { headers, abortController, ...responseInit } = init ?? {}\n\n // Start with default SSE headers\n const mergedHeaders = new Headers({\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n })\n\n // Override with user headers if provided, handling all HeadersInit forms:\n // Headers instance, string[][], or plain object\n if (headers) {\n const userHeaders = new Headers(headers)\n userHeaders.forEach((value, key) => {\n mergedHeaders.set(key, value)\n })\n }\n\n return new Response(toServerSentEventsStream(stream, abortController), {\n ...responseInit,\n headers: mergedHeaders,\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a ReadableStream in HTTP stream format (newline-delimited JSON)\n *\n * This creates a ReadableStream that emits chunks as newline-delimited JSON:\n * - Each chunk is JSON.stringify'd and followed by \"\\n\"\n * - No SSE formatting (no \"data: \" prefix)\n *\n * This format is compatible with `fetchHttpStream` connection adapter.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param abortController - Optional AbortController to abort when stream is cancelled\n * @returns ReadableStream in HTTP stream format (newline-delimited JSON)\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * const readableStream = toHttpStream(stream);\n * // Use with Response for HTTP streaming (not SSE)\n * return new Response(readableStream, {\n * headers: { 'Content-Type': 'application/x-ndjson' }\n * });\n * ```\n */\nexport function toHttpStream(\n stream: AsyncIterable<StreamChunk>,\n abortController?: AbortController,\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const chunk of stream) {\n // Check if stream was cancelled/aborted\n if (abortController?.signal.aborted) {\n break\n }\n\n // Send each chunk as newline-delimited JSON\n controller.enqueue(encoder.encode(`${JSON.stringify(chunk)}\\n`))\n }\n\n controller.close()\n } catch (error: any) {\n // Don't send error if aborted\n if (abortController?.signal.aborted) {\n controller.close()\n return\n }\n\n // Send error chunk\n controller.enqueue(\n encoder.encode(\n `${JSON.stringify({\n type: 'error',\n error: {\n message: error.message || 'Unknown error occurred',\n code: error.code,\n },\n })}\\n`,\n ),\n )\n controller.close()\n }\n },\n cancel() {\n // When the ReadableStream is cancelled (e.g., client disconnects),\n // abort the underlying stream\n if (abortController) {\n abortController.abort()\n }\n },\n })\n}\n\n/**\n * Convert a StreamChunk async iterable to a Response in HTTP stream format (newline-delimited JSON)\n *\n * This creates a Response that emits chunks in HTTP stream format:\n * - Each chunk is JSON.stringify'd and followed by \"\\n\"\n * - No SSE formatting (no \"data: \" prefix)\n *\n * This format is compatible with `fetchHttpStream` connection adapter.\n *\n * @param stream - AsyncIterable of StreamChunks from chat()\n * @param init - Optional Response initialization options (including `abortController`)\n * @returns Response in HTTP stream format (newline-delimited JSON)\n *\n * @example\n * ```typescript\n * const stream = chat({ adapter: openaiText(), model: \"gpt-4o\", messages: [...] });\n * return toHttpResponse(stream, { abortController });\n * ```\n */\nexport function toHttpResponse(\n stream: AsyncIterable<StreamChunk>,\n init?: ResponseInit & { abortController?: AbortController },\n): Response {\n return new Response(toHttpStream(stream, init?.abortController), {\n ...init,\n })\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import { StandardJSONSchemaV1 } from '@standard-schema/spec';
2
2
  /**
3
3
  * Tool call states - track the lifecycle of a tool call
4
4
  */
@@ -9,17 +9,17 @@ export type ToolCallState = 'awaiting-input' | 'input-streaming' | 'input-comple
9
9
  export type ToolResultState = 'streaming' | 'complete' | 'error';
10
10
  /**
11
11
  * JSON Schema type for defining tool input/output schemas as raw JSON Schema objects.
12
- * This allows tools to be defined without Zod when you have JSON Schema definitions available.
12
+ * This allows tools to be defined without schema libraries when you have JSON Schema definitions available.
13
13
  */
14
14
  export interface JSONSchema {
15
15
  type?: string | Array<string>;
16
16
  properties?: Record<string, JSONSchema>;
17
17
  items?: JSONSchema | Array<JSONSchema>;
18
18
  required?: Array<string>;
19
- enum?: Array<any>;
20
- const?: any;
19
+ enum?: Array<unknown>;
20
+ const?: unknown;
21
21
  description?: string;
22
- default?: any;
22
+ default?: unknown;
23
23
  $ref?: string;
24
24
  $defs?: Record<string, JSONSchema>;
25
25
  definitions?: Record<string, JSONSchema>;
@@ -48,19 +48,26 @@ export interface JSONSchema {
48
48
  minProperties?: number;
49
49
  maxProperties?: number;
50
50
  title?: string;
51
- examples?: Array<any>;
51
+ examples?: Array<unknown>;
52
52
  [key: string]: any;
53
53
  }
54
54
  /**
55
- * Union type for schema input - can be either a Zod schema or a JSONSchema object.
55
+ * Union type for schema input - can be any Standard JSON Schema compliant schema or a plain JSONSchema object.
56
+ *
57
+ * Standard JSON Schema compliant libraries include:
58
+ * - Zod v4.2+ (natively supports StandardJSONSchemaV1)
59
+ * - ArkType v2.1.28+ (natively supports StandardJSONSchemaV1)
60
+ * - Valibot v1.2+ (via `toStandardJsonSchema()` from `@valibot/to-json-schema`)
61
+ *
62
+ * @see https://standardschema.dev/json-schema
56
63
  */
57
- export type SchemaInput = z.ZodType | JSONSchema;
64
+ export type SchemaInput = StandardJSONSchemaV1<any, any> | JSONSchema;
58
65
  /**
59
66
  * Infer the TypeScript type from a schema.
60
- * For Zod schemas, uses z.infer to get the proper type.
61
- * For JSONSchema, returns `any` since we can't infer types from JSON Schema at compile time.
67
+ * For Standard JSON Schema compliant schemas, extracts the input type.
68
+ * For plain JSONSchema, returns `any` since we can't infer types from JSON Schema at compile time.
62
69
  */
63
- export type InferSchemaType<T> = T extends z.ZodType ? z.infer<T> : any;
70
+ export type InferSchemaType<T> = T extends StandardJSONSchemaV1<infer TInput, unknown> ? TInput : unknown;
64
71
  export interface ToolCall {
65
72
  id: string;
66
73
  type: 'function';
@@ -234,12 +241,14 @@ export type ConstrainedModelMessage<TInputModalitiesTypes extends InputModalitie
234
241
  * Tools allow the model to interact with external systems, APIs, or perform computations.
235
242
  * The model will decide when to call tools based on the user's request and the tool descriptions.
236
243
  *
237
- * Tools can use either Zod schemas or JSON Schema objects for runtime validation and type safety.
244
+ * Tools can use any Standard JSON Schema compliant library (Zod, ArkType, Valibot, etc.)
245
+ * or plain JSON Schema objects for runtime validation and type safety.
238
246
  *
239
247
  * @see https://platform.openai.com/docs/guides/function-calling
240
248
  * @see https://docs.anthropic.com/claude/docs/tool-use
249
+ * @see https://standardschema.dev/json-schema
241
250
  */
242
- export interface Tool<TInput extends SchemaInput = z.ZodType, TOutput extends SchemaInput = z.ZodType, TName extends string = string> {
251
+ export interface Tool<TInput extends SchemaInput = SchemaInput, TOutput extends SchemaInput = SchemaInput, TName extends string = string> {
243
252
  /**
244
253
  * Unique name of the tool (used by the model to call it).
245
254
  *
@@ -261,16 +270,16 @@ export interface Tool<TInput extends SchemaInput = z.ZodType, TOutput extends Sc
261
270
  /**
262
271
  * Schema describing the tool's input parameters.
263
272
  *
264
- * Can be either a Zod schema or a JSON Schema object.
273
+ * Can be any Standard JSON Schema compliant schema (Zod, ArkType, Valibot, etc.) or a plain JSON Schema object.
265
274
  * Defines the structure and types of arguments the tool accepts.
266
275
  * The model will generate arguments matching this schema.
267
- * Zod schemas are converted to JSON Schema for LLM providers.
276
+ * Standard JSON Schema compliant schemas are converted to JSON Schema for LLM providers.
268
277
  *
269
- * @see https://zod.dev/
278
+ * @see https://standardschema.dev/json-schema
270
279
  * @see https://json-schema.org/
271
280
  *
272
281
  * @example
273
- * // Using Zod schema
282
+ * // Using Zod v4+ schema (natively supports Standard JSON Schema)
274
283
  * import { z } from 'zod';
275
284
  * z.object({
276
285
  * location: z.string().describe("City name or coordinates"),
@@ -278,7 +287,15 @@ export interface Tool<TInput extends SchemaInput = z.ZodType, TOutput extends Sc
278
287
  * })
279
288
  *
280
289
  * @example
281
- * // Using JSON Schema
290
+ * // Using ArkType (natively supports Standard JSON Schema)
291
+ * import { type } from 'arktype';
292
+ * type({
293
+ * location: 'string',
294
+ * unit: "'celsius' | 'fahrenheit'"
295
+ * })
296
+ *
297
+ * @example
298
+ * // Using plain JSON Schema
282
299
  * {
283
300
  * type: 'object',
284
301
  * properties: {
@@ -292,15 +309,16 @@ export interface Tool<TInput extends SchemaInput = z.ZodType, TOutput extends Sc
292
309
  /**
293
310
  * Optional schema for validating tool output.
294
311
  *
295
- * Can be either a Zod schema or a JSON Schema object.
296
- * If provided with a Zod schema, tool results will be validated against this schema before
297
- * being sent back to the model. This catches bugs in tool implementations
298
- * and ensures consistent output formatting.
312
+ * Can be any Standard JSON Schema compliant schema or a plain JSON Schema object.
313
+ * If provided with a Standard Schema compliant schema, tool results will be validated
314
+ * against this schema before being sent back to the model. This catches bugs in tool
315
+ * implementations and ensures consistent output formatting.
299
316
  *
300
317
  * Note: This is client-side validation only - not sent to LLM providers.
301
- * Note: JSON Schema output validation is not performed at runtime.
318
+ * Note: Plain JSON Schema output validation is not performed at runtime.
302
319
  *
303
320
  * @example
321
+ * // Using Zod
304
322
  * z.object({
305
323
  * temperature: z.number(),
306
324
  * conditions: z.string(),
@@ -502,12 +520,13 @@ export interface TextOptions<TProviderOptionsSuperset extends Record<string, any
502
520
  modelOptions?: TProviderOptionsForModel;
503
521
  request?: Request | RequestInit;
504
522
  /**
505
- * Zod schema for structured output.
523
+ * Schema for structured output.
506
524
  * When provided, the adapter should use the provider's native structured output API
507
525
  * to ensure the response conforms to this schema.
508
526
  * The schema will be converted to JSON Schema format before being sent to the provider.
527
+ * Supports any Standard JSON Schema compliant library (Zod, ArkType, Valibot, etc.).
509
528
  */
510
- outputSchema?: z.ZodType;
529
+ outputSchema?: SchemaInput;
511
530
  /**
512
531
  * Conversation ID for correlating client and server-side devtools events.
513
532
  * When provided, server-side events will be linked to the client conversation in devtools.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Core TanStack AI library - Open source AI SDK",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -46,13 +46,10 @@
46
46
  "@tanstack/devtools-event-client": "^0.4.0",
47
47
  "partial-json": "^0.1.7"
48
48
  },
49
- "peerDependencies": {
50
- "@alcyone-labs/zod-to-json-schema": "^4.0.0",
51
- "zod": "^3.0.0 || ^4.0.0"
52
- },
53
49
  "devDependencies": {
50
+ "@standard-schema/spec": "^1.1.0",
54
51
  "@vitest/coverage-v8": "4.0.14",
55
- "zod": "^4.1.13"
52
+ "zod": "^4.2.0"
56
53
  },
57
54
  "scripts": {
58
55
  "build": "vite build",
@@ -8,20 +8,25 @@
8
8
  import { aiEventClient } from '../../event-client.js'
9
9
  import { streamToText } from '../../stream-to-response.js'
10
10
  import { ToolCallManager, executeToolCalls } from './tools/tool-calls'
11
- import { convertZodToJsonSchema } from './tools/zod-converter'
11
+ import {
12
+ convertSchemaToJsonSchema,
13
+ isStandardSchema,
14
+ parseWithStandardSchema,
15
+ } from './tools/schema-converter'
12
16
  import { maxIterations as maxIterationsStrategy } from './agent-loop-strategies'
13
17
  import type {
14
18
  ApprovalRequest,
15
19
  ClientToolRequest,
16
20
  ToolResult,
17
21
  } from './tools/tool-calls'
18
- import type { z } from 'zod'
19
22
  import type { AnyTextAdapter } from './adapter'
20
23
  import type {
21
24
  AgentLoopStrategy,
22
25
  ConstrainedModelMessage,
23
26
  DoneStreamChunk,
27
+ InferSchemaType,
24
28
  ModelMessage,
29
+ SchemaInput,
25
30
  StreamChunk,
26
31
  TextOptions,
27
32
  Tool,
@@ -44,12 +49,12 @@ export const kind = 'text' as const
44
49
  * Types are extracted directly from the adapter (which has pre-resolved generics).
45
50
  *
46
51
  * @template TAdapter - The text adapter type (created by a provider function)
47
- * @template TSchema - Optional Zod schema for structured output
52
+ * @template TSchema - Optional Standard Schema for structured output
48
53
  * @template TStream - Whether to stream the output (default: true)
49
54
  */
50
55
  export interface TextActivityOptions<
51
56
  TAdapter extends AnyTextAdapter,
52
- TSchema extends z.ZodType | undefined,
57
+ TSchema extends SchemaInput | undefined,
53
58
  TStream extends boolean,
54
59
  > {
55
60
  /** The text adapter to use (created by a provider function like openaiText('gpt-4o')) */
@@ -82,11 +87,13 @@ export interface TextActivityOptions<
82
87
  /** Unique conversation identifier for tracking */
83
88
  conversationId?: TextOptions['conversationId']
84
89
  /**
85
- * Optional Zod schema for structured output.
90
+ * Optional Standard Schema for structured output.
86
91
  * When provided, the activity will:
87
92
  * 1. Run the full agentic loop (executing tools as needed)
88
93
  * 2. Once complete, return a Promise with the parsed output matching the schema
89
94
  *
95
+ * Supports any Standard Schema compliant library (Zod v4+, ArkType, Valibot, etc.)
96
+ *
90
97
  * @example
91
98
  * ```ts
92
99
  * const result = await chat({
@@ -104,7 +111,7 @@ export interface TextActivityOptions<
104
111
  * When false, returns a Promise<string> with the collected text content.
105
112
  *
106
113
  * Note: If outputSchema is provided, this option is ignored and the result
107
- * is always a Promise<z.infer<TSchema>>.
114
+ * is always a Promise<InferSchemaType<TSchema>>.
108
115
  *
109
116
  * @default true
110
117
  *
@@ -140,7 +147,7 @@ export interface TextActivityOptions<
140
147
  */
141
148
  export function createChatOptions<
142
149
  TAdapter extends AnyTextAdapter,
143
- TSchema extends z.ZodType | undefined = undefined,
150
+ TSchema extends SchemaInput | undefined = undefined,
144
151
  TStream extends boolean = true,
145
152
  >(
146
153
  options: TextActivityOptions<TAdapter, TSchema, TStream>,
@@ -154,15 +161,15 @@ export function createChatOptions<
154
161
 
155
162
  /**
156
163
  * Result type for the text activity.
157
- * - If outputSchema is provided: Promise<z.infer<TSchema>>
164
+ * - If outputSchema is provided: Promise<InferSchemaType<TSchema>>
158
165
  * - If stream is false: Promise<string>
159
166
  * - Otherwise (stream is true, default): AsyncIterable<StreamChunk>
160
167
  */
161
168
  export type TextActivityResult<
162
- TSchema extends z.ZodType | undefined,
169
+ TSchema extends SchemaInput | undefined,
163
170
  TStream extends boolean = true,
164
- > = TSchema extends z.ZodType
165
- ? Promise<z.infer<TSchema>>
171
+ > = TSchema extends SchemaInput
172
+ ? Promise<InferSchemaType<TSchema>>
166
173
  : TStream extends false
167
174
  ? Promise<string>
168
175
  : AsyncIterable<StreamChunk>
@@ -366,14 +373,14 @@ class TextEngine<
366
373
  const { temperature, topP, maxTokens, metadata, modelOptions } = this.params
367
374
  const tools = this.params.tools
368
375
 
369
- // Convert tool schemas from Zod to JSON Schema before passing to adapter
376
+ // Convert tool schemas to JSON Schema before passing to adapter
370
377
  const toolsWithJsonSchemas = tools?.map((tool) => ({
371
378
  ...tool,
372
379
  inputSchema: tool.inputSchema
373
- ? convertZodToJsonSchema(tool.inputSchema)
380
+ ? convertSchemaToJsonSchema(tool.inputSchema)
374
381
  : undefined,
375
382
  outputSchema: tool.outputSchema
376
- ? convertZodToJsonSchema(tool.outputSchema)
383
+ ? convertSchemaToJsonSchema(tool.outputSchema)
377
384
  : undefined,
378
385
  }))
379
386
 
@@ -968,7 +975,7 @@ class TextEngine<
968
975
  */
969
976
  export function chat<
970
977
  TAdapter extends AnyTextAdapter,
971
- TSchema extends z.ZodType | undefined = undefined,
978
+ TSchema extends SchemaInput | undefined = undefined,
972
979
  TStream extends boolean = true,
973
980
  >(
974
981
  options: TextActivityOptions<TAdapter, TSchema, TStream>,
@@ -980,7 +987,7 @@ export function chat<
980
987
  return runAgenticStructuredOutput(
981
988
  options as unknown as TextActivityOptions<
982
989
  AnyTextAdapter,
983
- z.ZodType,
990
+ SchemaInput,
984
991
  boolean
985
992
  >,
986
993
  ) as TextActivityResult<TSchema, TStream>
@@ -1046,9 +1053,9 @@ function runNonStreamingText(
1046
1053
  * 2. Once complete, call adapter.structuredOutput with the conversation context
1047
1054
  * 3. Validate and return the structured result
1048
1055
  */
1049
- async function runAgenticStructuredOutput<TSchema extends z.ZodType>(
1056
+ async function runAgenticStructuredOutput<TSchema extends SchemaInput>(
1050
1057
  options: TextActivityOptions<AnyTextAdapter, TSchema, boolean>,
1051
- ): Promise<z.infer<TSchema>> {
1058
+ ): Promise<InferSchemaType<TSchema>> {
1052
1059
  const { adapter, outputSchema, ...textOptions } = options
1053
1060
  const model = adapter.model
1054
1061
 
@@ -1060,8 +1067,8 @@ async function runAgenticStructuredOutput<TSchema extends z.ZodType>(
1060
1067
  const engine = new TextEngine({
1061
1068
  adapter,
1062
1069
  params: { ...textOptions, model } as TextOptions<
1063
- Record<string, any>,
1064
- Record<string, any>
1070
+ Record<string, unknown>,
1071
+ Record<string, unknown>
1065
1072
  >,
1066
1073
  })
1067
1074
 
@@ -1081,8 +1088,8 @@ async function runAgenticStructuredOutput<TSchema extends z.ZodType>(
1081
1088
  ...structuredTextOptions
1082
1089
  } = textOptions
1083
1090
 
1084
- // Convert the Zod schema to JSON Schema before passing to the adapter
1085
- const jsonSchema = convertZodToJsonSchema(outputSchema)
1091
+ // Convert the schema to JSON Schema before passing to the adapter
1092
+ const jsonSchema = convertSchemaToJsonSchema(outputSchema)
1086
1093
  if (!jsonSchema) {
1087
1094
  throw new Error('Failed to convert output schema to JSON Schema')
1088
1095
  }
@@ -1098,15 +1105,16 @@ async function runAgenticStructuredOutput<TSchema extends z.ZodType>(
1098
1105
  outputSchema: jsonSchema,
1099
1106
  })
1100
1107
 
1101
- // Validate the result against the Zod schema
1102
- const validationResult = outputSchema.safeParse(result.data)
1103
- if (!validationResult.success) {
1104
- throw new Error(
1105
- `Structured output validation failed: ${validationResult.error.message}`,
1108
+ // Validate the result against the schema if it's a Standard Schema
1109
+ if (isStandardSchema(outputSchema)) {
1110
+ return parseWithStandardSchema<InferSchemaType<TSchema>>(
1111
+ outputSchema,
1112
+ result.data,
1106
1113
  )
1107
1114
  }
1108
1115
 
1109
- return validationResult.data
1116
+ // For plain JSON Schema, return the data as-is
1117
+ return result.data as InferSchemaType<TSchema>
1110
1118
  }
1111
1119
 
1112
1120
  // Re-export adapter types