@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.
- package/README.md +0 -25
- package/dist/esm/activities/chat/index.d.ts +11 -10
- package/dist/esm/activities/chat/index.js +9 -9
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.d.ts +116 -0
- package/dist/esm/activities/chat/tools/schema-converter.js +115 -0
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -0
- package/dist/esm/activities/chat/tools/tool-calls.js +23 -34
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +18 -14
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +5 -4
- package/dist/esm/stream-to-response.d.ts +28 -21
- package/dist/esm/stream-to-response.js +24 -17
- package/dist/esm/stream-to-response.js.map +1 -1
- package/dist/esm/types.d.ts +44 -25
- package/package.json +3 -6
- package/src/activities/chat/index.ts +36 -28
- package/src/activities/chat/tools/schema-converter.ts +332 -0
- package/src/activities/chat/tools/tool-calls.ts +47 -53
- package/src/activities/chat/tools/tool-definition.ts +33 -29
- package/src/index.ts +5 -2
- package/src/stream-to-response.ts +59 -44
- package/src/types.ts +47 -26
- package/dist/esm/activities/chat/tools/zod-converter.d.ts +0 -69
- package/dist/esm/activities/chat/tools/zod-converter.js +0 -99
- package/dist/esm/activities/chat/tools/zod-converter.js.map +0 -1
- package/src/activities/chat/tools/zod-converter.ts +0 -235
package/src/index.ts
CHANGED
|
@@ -47,14 +47,17 @@ export {
|
|
|
47
47
|
type InferToolInput,
|
|
48
48
|
type InferToolOutput,
|
|
49
49
|
} from './activities/chat/tools/tool-definition'
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
// Schema conversion (Standard JSON Schema compliant)
|
|
52
|
+
export { convertSchemaToJsonSchema } from './activities/chat/tools/schema-converter'
|
|
51
53
|
|
|
52
54
|
// Stream utilities
|
|
53
55
|
export {
|
|
54
56
|
streamToText,
|
|
55
57
|
toServerSentEventsStream,
|
|
56
|
-
|
|
58
|
+
toServerSentEventsResponse,
|
|
57
59
|
toHttpStream,
|
|
60
|
+
toHttpResponse,
|
|
58
61
|
} from './stream-to-response'
|
|
59
62
|
|
|
60
63
|
// Tool call management
|
|
@@ -45,13 +45,6 @@ export async function streamToText(
|
|
|
45
45
|
* @param stream - AsyncIterable of StreamChunks from chat()
|
|
46
46
|
* @param abortController - Optional AbortController to abort when stream is cancelled
|
|
47
47
|
* @returns ReadableStream in Server-Sent Events format
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* ```typescript
|
|
51
|
-
* const stream = chat({ adapter: openaiText(), model: "gpt-4o", messages: [...] });
|
|
52
|
-
* const readableStream = toServerSentEventsStream(stream);
|
|
53
|
-
* // Use with Response, or any API that accepts ReadableStream
|
|
54
|
-
* ```
|
|
55
48
|
*/
|
|
56
49
|
export function toServerSentEventsStream(
|
|
57
50
|
stream: AsyncIterable<StreamChunk>,
|
|
@@ -109,6 +102,52 @@ export function toServerSentEventsStream(
|
|
|
109
102
|
})
|
|
110
103
|
}
|
|
111
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Convert a StreamChunk async iterable to a Response in Server-Sent Events format
|
|
107
|
+
*
|
|
108
|
+
* This creates a Response that emits chunks in SSE format:
|
|
109
|
+
* - Each chunk is prefixed with "data: "
|
|
110
|
+
* - Each chunk is followed by "\n\n"
|
|
111
|
+
* - Stream ends with "data: [DONE]\n\n"
|
|
112
|
+
*
|
|
113
|
+
* @param stream - AsyncIterable of StreamChunks from chat()
|
|
114
|
+
* @param init - Optional Response initialization options (including `abortController`)
|
|
115
|
+
* @returns Response in Server-Sent Events format
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* const stream = chat({ adapter: openaiText(), model: "gpt-4o", messages: [...] });
|
|
120
|
+
* return toServerSentEventsResponse(stream, { abortController });
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function toServerSentEventsResponse(
|
|
124
|
+
stream: AsyncIterable<StreamChunk>,
|
|
125
|
+
init?: ResponseInit & { abortController?: AbortController },
|
|
126
|
+
): Response {
|
|
127
|
+
const { headers, abortController, ...responseInit } = init ?? {}
|
|
128
|
+
|
|
129
|
+
// Start with default SSE headers
|
|
130
|
+
const mergedHeaders = new Headers({
|
|
131
|
+
'Content-Type': 'text/event-stream',
|
|
132
|
+
'Cache-Control': 'no-cache',
|
|
133
|
+
Connection: 'keep-alive',
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// Override with user headers if provided, handling all HeadersInit forms:
|
|
137
|
+
// Headers instance, string[][], or plain object
|
|
138
|
+
if (headers) {
|
|
139
|
+
const userHeaders = new Headers(headers)
|
|
140
|
+
userHeaders.forEach((value, key) => {
|
|
141
|
+
mergedHeaders.set(key, value)
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return new Response(toServerSentEventsStream(stream, abortController), {
|
|
146
|
+
...responseInit,
|
|
147
|
+
headers: mergedHeaders,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
|
|
112
151
|
/**
|
|
113
152
|
* Convert a StreamChunk async iterable to a ReadableStream in HTTP stream format (newline-delimited JSON)
|
|
114
153
|
*
|
|
@@ -185,53 +224,29 @@ export function toHttpStream(
|
|
|
185
224
|
}
|
|
186
225
|
|
|
187
226
|
/**
|
|
188
|
-
*
|
|
189
|
-
* Includes proper headers for Server-Sent Events
|
|
227
|
+
* Convert a StreamChunk async iterable to a Response in HTTP stream format (newline-delimited JSON)
|
|
190
228
|
*
|
|
191
|
-
*
|
|
229
|
+
* This creates a Response that emits chunks in HTTP stream format:
|
|
230
|
+
* - Each chunk is JSON.stringify'd and followed by "\n"
|
|
231
|
+
* - No SSE formatting (no "data: " prefix)
|
|
232
|
+
*
|
|
233
|
+
* This format is compatible with `fetchHttpStream` connection adapter.
|
|
192
234
|
*
|
|
193
235
|
* @param stream - AsyncIterable of StreamChunks from chat()
|
|
194
|
-
* @param init - Optional Response initialization options
|
|
195
|
-
* @
|
|
196
|
-
* @returns Response object with SSE headers and streaming body
|
|
236
|
+
* @param init - Optional Response initialization options (including `abortController`)
|
|
237
|
+
* @returns Response in HTTP stream format (newline-delimited JSON)
|
|
197
238
|
*
|
|
198
239
|
* @example
|
|
199
240
|
* ```typescript
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* const abortController = new AbortController();
|
|
203
|
-
* const stream = chat({
|
|
204
|
-
* adapter: openaiText(),
|
|
205
|
-
* model: "gpt-4o",
|
|
206
|
-
* messages,
|
|
207
|
-
* options: { abortSignal: abortController.signal }
|
|
208
|
-
* });
|
|
209
|
-
* return toStreamResponse(stream, undefined, abortController);
|
|
210
|
-
* }
|
|
241
|
+
* const stream = chat({ adapter: openaiText(), model: "gpt-4o", messages: [...] });
|
|
242
|
+
* return toHttpResponse(stream, { abortController });
|
|
211
243
|
* ```
|
|
212
244
|
*/
|
|
213
|
-
export function
|
|
245
|
+
export function toHttpResponse(
|
|
214
246
|
stream: AsyncIterable<StreamChunk>,
|
|
215
247
|
init?: ResponseInit & { abortController?: AbortController },
|
|
216
248
|
): Response {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
'`toStreamResponse` is deprecated. Use `toServerSentEventsStream` instead. Example:\n' +
|
|
220
|
-
' const readableStream = toServerSentEventsStream(stream, abortController);\n' +
|
|
221
|
-
' return new Response(readableStream, {\n' +
|
|
222
|
-
" headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' }\n" +
|
|
223
|
-
' });',
|
|
224
|
-
)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const { headers, abortController, ...responseInit } = init ?? {}
|
|
228
|
-
return new Response(toServerSentEventsStream(stream, abortController), {
|
|
229
|
-
...responseInit,
|
|
230
|
-
headers: {
|
|
231
|
-
'Content-Type': 'text/event-stream',
|
|
232
|
-
'Cache-Control': 'no-cache',
|
|
233
|
-
Connection: 'keep-alive',
|
|
234
|
-
...(headers || {}),
|
|
235
|
-
},
|
|
249
|
+
return new Response(toHttpStream(stream, init?.abortController), {
|
|
250
|
+
...init,
|
|
236
251
|
})
|
|
237
252
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StandardJSONSchemaV1 } from '@standard-schema/spec'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Tool call states - track the lifecycle of a tool call
|
|
@@ -20,17 +20,17 @@ export type ToolResultState =
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* JSON Schema type for defining tool input/output schemas as raw JSON Schema objects.
|
|
23
|
-
* This allows tools to be defined without
|
|
23
|
+
* This allows tools to be defined without schema libraries when you have JSON Schema definitions available.
|
|
24
24
|
*/
|
|
25
25
|
export interface JSONSchema {
|
|
26
26
|
type?: string | Array<string>
|
|
27
27
|
properties?: Record<string, JSONSchema>
|
|
28
28
|
items?: JSONSchema | Array<JSONSchema>
|
|
29
29
|
required?: Array<string>
|
|
30
|
-
enum?: Array<
|
|
31
|
-
const?:
|
|
30
|
+
enum?: Array<unknown>
|
|
31
|
+
const?: unknown
|
|
32
32
|
description?: string
|
|
33
|
-
default?:
|
|
33
|
+
default?: unknown
|
|
34
34
|
$ref?: string
|
|
35
35
|
$defs?: Record<string, JSONSchema>
|
|
36
36
|
definitions?: Record<string, JSONSchema>
|
|
@@ -59,21 +59,30 @@ export interface JSONSchema {
|
|
|
59
59
|
minProperties?: number
|
|
60
60
|
maxProperties?: number
|
|
61
61
|
title?: string
|
|
62
|
-
examples?: Array<
|
|
62
|
+
examples?: Array<unknown>
|
|
63
63
|
[key: string]: any // Allow additional properties for extensibility
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
|
-
* Union type for schema input - can be
|
|
67
|
+
* Union type for schema input - can be any Standard JSON Schema compliant schema or a plain JSONSchema object.
|
|
68
|
+
*
|
|
69
|
+
* Standard JSON Schema compliant libraries include:
|
|
70
|
+
* - Zod v4.2+ (natively supports StandardJSONSchemaV1)
|
|
71
|
+
* - ArkType v2.1.28+ (natively supports StandardJSONSchemaV1)
|
|
72
|
+
* - Valibot v1.2+ (via `toStandardJsonSchema()` from `@valibot/to-json-schema`)
|
|
73
|
+
*
|
|
74
|
+
* @see https://standardschema.dev/json-schema
|
|
68
75
|
*/
|
|
69
|
-
|
|
76
|
+
|
|
77
|
+
export type SchemaInput = StandardJSONSchemaV1<any, any> | JSONSchema
|
|
70
78
|
|
|
71
79
|
/**
|
|
72
80
|
* Infer the TypeScript type from a schema.
|
|
73
|
-
* For
|
|
74
|
-
* For JSONSchema, returns `any` since we can't infer types from JSON Schema at compile time.
|
|
81
|
+
* For Standard JSON Schema compliant schemas, extracts the input type.
|
|
82
|
+
* For plain JSONSchema, returns `any` since we can't infer types from JSON Schema at compile time.
|
|
75
83
|
*/
|
|
76
|
-
export type InferSchemaType<T> =
|
|
84
|
+
export type InferSchemaType<T> =
|
|
85
|
+
T extends StandardJSONSchemaV1<infer TInput, unknown> ? TInput : unknown
|
|
77
86
|
|
|
78
87
|
export interface ToolCall {
|
|
79
88
|
id: string
|
|
@@ -309,14 +318,16 @@ export type ConstrainedModelMessage<
|
|
|
309
318
|
* Tools allow the model to interact with external systems, APIs, or perform computations.
|
|
310
319
|
* The model will decide when to call tools based on the user's request and the tool descriptions.
|
|
311
320
|
*
|
|
312
|
-
* Tools can use
|
|
321
|
+
* Tools can use any Standard JSON Schema compliant library (Zod, ArkType, Valibot, etc.)
|
|
322
|
+
* or plain JSON Schema objects for runtime validation and type safety.
|
|
313
323
|
*
|
|
314
324
|
* @see https://platform.openai.com/docs/guides/function-calling
|
|
315
325
|
* @see https://docs.anthropic.com/claude/docs/tool-use
|
|
326
|
+
* @see https://standardschema.dev/json-schema
|
|
316
327
|
*/
|
|
317
328
|
export interface Tool<
|
|
318
|
-
TInput extends SchemaInput =
|
|
319
|
-
TOutput extends SchemaInput =
|
|
329
|
+
TInput extends SchemaInput = SchemaInput,
|
|
330
|
+
TOutput extends SchemaInput = SchemaInput,
|
|
320
331
|
TName extends string = string,
|
|
321
332
|
> {
|
|
322
333
|
/**
|
|
@@ -342,16 +353,16 @@ export interface Tool<
|
|
|
342
353
|
/**
|
|
343
354
|
* Schema describing the tool's input parameters.
|
|
344
355
|
*
|
|
345
|
-
* Can be
|
|
356
|
+
* Can be any Standard JSON Schema compliant schema (Zod, ArkType, Valibot, etc.) or a plain JSON Schema object.
|
|
346
357
|
* Defines the structure and types of arguments the tool accepts.
|
|
347
358
|
* The model will generate arguments matching this schema.
|
|
348
|
-
*
|
|
359
|
+
* Standard JSON Schema compliant schemas are converted to JSON Schema for LLM providers.
|
|
349
360
|
*
|
|
350
|
-
* @see https://
|
|
361
|
+
* @see https://standardschema.dev/json-schema
|
|
351
362
|
* @see https://json-schema.org/
|
|
352
363
|
*
|
|
353
364
|
* @example
|
|
354
|
-
* // Using Zod schema
|
|
365
|
+
* // Using Zod v4+ schema (natively supports Standard JSON Schema)
|
|
355
366
|
* import { z } from 'zod';
|
|
356
367
|
* z.object({
|
|
357
368
|
* location: z.string().describe("City name or coordinates"),
|
|
@@ -359,7 +370,15 @@ export interface Tool<
|
|
|
359
370
|
* })
|
|
360
371
|
*
|
|
361
372
|
* @example
|
|
362
|
-
* // Using JSON Schema
|
|
373
|
+
* // Using ArkType (natively supports Standard JSON Schema)
|
|
374
|
+
* import { type } from 'arktype';
|
|
375
|
+
* type({
|
|
376
|
+
* location: 'string',
|
|
377
|
+
* unit: "'celsius' | 'fahrenheit'"
|
|
378
|
+
* })
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* // Using plain JSON Schema
|
|
363
382
|
* {
|
|
364
383
|
* type: 'object',
|
|
365
384
|
* properties: {
|
|
@@ -374,15 +393,16 @@ export interface Tool<
|
|
|
374
393
|
/**
|
|
375
394
|
* Optional schema for validating tool output.
|
|
376
395
|
*
|
|
377
|
-
* Can be
|
|
378
|
-
* If provided with a
|
|
379
|
-
* being sent back to the model. This catches bugs in tool
|
|
380
|
-
* and ensures consistent output formatting.
|
|
396
|
+
* Can be any Standard JSON Schema compliant schema or a plain JSON Schema object.
|
|
397
|
+
* If provided with a Standard Schema compliant schema, tool results will be validated
|
|
398
|
+
* against this schema before being sent back to the model. This catches bugs in tool
|
|
399
|
+
* implementations and ensures consistent output formatting.
|
|
381
400
|
*
|
|
382
401
|
* Note: This is client-side validation only - not sent to LLM providers.
|
|
383
|
-
* Note: JSON Schema output validation is not performed at runtime.
|
|
402
|
+
* Note: Plain JSON Schema output validation is not performed at runtime.
|
|
384
403
|
*
|
|
385
404
|
* @example
|
|
405
|
+
* // Using Zod
|
|
386
406
|
* z.object({
|
|
387
407
|
* temperature: z.number(),
|
|
388
408
|
* conditions: z.string(),
|
|
@@ -601,12 +621,13 @@ export interface TextOptions<
|
|
|
601
621
|
request?: Request | RequestInit
|
|
602
622
|
|
|
603
623
|
/**
|
|
604
|
-
*
|
|
624
|
+
* Schema for structured output.
|
|
605
625
|
* When provided, the adapter should use the provider's native structured output API
|
|
606
626
|
* to ensure the response conforms to this schema.
|
|
607
627
|
* The schema will be converted to JSON Schema format before being sent to the provider.
|
|
628
|
+
* Supports any Standard JSON Schema compliant library (Zod, ArkType, Valibot, etc.).
|
|
608
629
|
*/
|
|
609
|
-
outputSchema?:
|
|
630
|
+
outputSchema?: SchemaInput
|
|
610
631
|
/**
|
|
611
632
|
* Conversation ID for correlating client and server-side devtools events.
|
|
612
633
|
* When provided, server-side events will be linked to the client conversation in devtools.
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { SchemaInput } from '../../../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Options for schema conversion
|
|
4
|
-
*/
|
|
5
|
-
export interface ConvertSchemaOptions {
|
|
6
|
-
/**
|
|
7
|
-
* When true, transforms the schema to be compatible with OpenAI's structured output requirements:
|
|
8
|
-
* - All properties are added to the `required` array
|
|
9
|
-
* - Optional fields get null added to their type union
|
|
10
|
-
* - additionalProperties is set to false for all objects
|
|
11
|
-
*
|
|
12
|
-
* @default false
|
|
13
|
-
*/
|
|
14
|
-
forStructuredOutput?: boolean;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Converts a schema (Zod or JSONSchema) to JSON Schema format compatible with LLM providers.
|
|
18
|
-
* If the input is already a JSONSchema object, it is returned as-is.
|
|
19
|
-
* If the input is a Zod schema, it is converted to JSON Schema.
|
|
20
|
-
*
|
|
21
|
-
* @param schema - Zod schema or JSONSchema object to convert
|
|
22
|
-
* @param options - Conversion options
|
|
23
|
-
* @returns JSON Schema object that can be sent to LLM providers
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```typescript
|
|
27
|
-
* import { z } from 'zod';
|
|
28
|
-
*
|
|
29
|
-
* // Using Zod schema
|
|
30
|
-
* const zodSchema = z.object({
|
|
31
|
-
* location: z.string().describe('City name'),
|
|
32
|
-
* unit: z.enum(['celsius', 'fahrenheit']).optional()
|
|
33
|
-
* });
|
|
34
|
-
*
|
|
35
|
-
* const jsonSchema = convertZodToJsonSchema(zodSchema);
|
|
36
|
-
* // Returns:
|
|
37
|
-
* // {
|
|
38
|
-
* // type: 'object',
|
|
39
|
-
* // properties: {
|
|
40
|
-
* // location: { type: 'string', description: 'City name' },
|
|
41
|
-
* // unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }
|
|
42
|
-
* // },
|
|
43
|
-
* // required: ['location']
|
|
44
|
-
* // }
|
|
45
|
-
*
|
|
46
|
-
* // For OpenAI structured output (all fields required, optional fields nullable)
|
|
47
|
-
* const structuredSchema = convertZodToJsonSchema(zodSchema, { forStructuredOutput: true });
|
|
48
|
-
* // Returns:
|
|
49
|
-
* // {
|
|
50
|
-
* // type: 'object',
|
|
51
|
-
* // properties: {
|
|
52
|
-
* // location: { type: 'string', description: 'City name' },
|
|
53
|
-
* // unit: { type: ['string', 'null'], enum: ['celsius', 'fahrenheit'] }
|
|
54
|
-
* // },
|
|
55
|
-
* // required: ['location', 'unit'],
|
|
56
|
-
* // additionalProperties: false
|
|
57
|
-
* // }
|
|
58
|
-
*
|
|
59
|
-
* // Using JSONSchema directly (passes through unchanged)
|
|
60
|
-
* const rawSchema = {
|
|
61
|
-
* type: 'object',
|
|
62
|
-
* properties: { location: { type: 'string' } },
|
|
63
|
-
* required: ['location']
|
|
64
|
-
* };
|
|
65
|
-
* const result = convertZodToJsonSchema(rawSchema);
|
|
66
|
-
* // Returns the same object
|
|
67
|
-
* ```
|
|
68
|
-
*/
|
|
69
|
-
export declare function convertZodToJsonSchema(schema: SchemaInput | undefined, options?: ConvertSchemaOptions): Record<string, any> | undefined;
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { toJSONSchema } from "zod";
|
|
2
|
-
function isZodSchema(schema) {
|
|
3
|
-
return typeof schema === "object" && schema !== null && "_zod" in schema && typeof schema._zod === "object";
|
|
4
|
-
}
|
|
5
|
-
function makeStructuredOutputCompatible(schema, originalRequired = []) {
|
|
6
|
-
const result = { ...schema };
|
|
7
|
-
if (result.type === "object" && result.properties) {
|
|
8
|
-
const properties = { ...result.properties };
|
|
9
|
-
const allPropertyNames = Object.keys(properties);
|
|
10
|
-
for (const propName of allPropertyNames) {
|
|
11
|
-
const prop = properties[propName];
|
|
12
|
-
const wasOptional = !originalRequired.includes(propName);
|
|
13
|
-
if (prop.type === "object" && prop.properties) {
|
|
14
|
-
properties[propName] = makeStructuredOutputCompatible(
|
|
15
|
-
prop,
|
|
16
|
-
prop.required || []
|
|
17
|
-
);
|
|
18
|
-
} else if (prop.type === "array" && prop.items) {
|
|
19
|
-
properties[propName] = {
|
|
20
|
-
...prop,
|
|
21
|
-
items: makeStructuredOutputCompatible(
|
|
22
|
-
prop.items,
|
|
23
|
-
prop.items.required || []
|
|
24
|
-
)
|
|
25
|
-
};
|
|
26
|
-
} else if (wasOptional) {
|
|
27
|
-
if (prop.type && !Array.isArray(prop.type)) {
|
|
28
|
-
properties[propName] = {
|
|
29
|
-
...prop,
|
|
30
|
-
type: [prop.type, "null"]
|
|
31
|
-
};
|
|
32
|
-
} else if (Array.isArray(prop.type) && !prop.type.includes("null")) {
|
|
33
|
-
properties[propName] = {
|
|
34
|
-
...prop,
|
|
35
|
-
type: [...prop.type, "null"]
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
result.properties = properties;
|
|
41
|
-
result.required = allPropertyNames;
|
|
42
|
-
result.additionalProperties = false;
|
|
43
|
-
}
|
|
44
|
-
if (result.type === "array" && result.items) {
|
|
45
|
-
result.items = makeStructuredOutputCompatible(
|
|
46
|
-
result.items,
|
|
47
|
-
result.items.required || []
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
function convertZodToJsonSchema(schema, options = {}) {
|
|
53
|
-
if (!schema) return void 0;
|
|
54
|
-
const { forStructuredOutput = false } = options;
|
|
55
|
-
if (!isZodSchema(schema)) {
|
|
56
|
-
if (forStructuredOutput && typeof schema === "object") {
|
|
57
|
-
return makeStructuredOutputCompatible(
|
|
58
|
-
schema,
|
|
59
|
-
schema.required || []
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
return schema;
|
|
63
|
-
}
|
|
64
|
-
const jsonSchema = toJSONSchema(schema, {
|
|
65
|
-
target: "openapi-3.0",
|
|
66
|
-
reused: "ref"
|
|
67
|
-
});
|
|
68
|
-
let result = jsonSchema;
|
|
69
|
-
if (typeof result === "object" && "$schema" in result) {
|
|
70
|
-
const { $schema, ...rest } = result;
|
|
71
|
-
result = rest;
|
|
72
|
-
}
|
|
73
|
-
if (typeof result === "object") {
|
|
74
|
-
const isZodObject = typeof schema === "object" && "def" in schema && schema.def.type === "object";
|
|
75
|
-
if (isZodObject && !result.type) {
|
|
76
|
-
result.type = "object";
|
|
77
|
-
}
|
|
78
|
-
if (Object.keys(result).length === 0) {
|
|
79
|
-
result.type = "object";
|
|
80
|
-
}
|
|
81
|
-
if ("properties" in result && !result.type) {
|
|
82
|
-
result.type = "object";
|
|
83
|
-
}
|
|
84
|
-
if (result.type === "object" && !("properties" in result)) {
|
|
85
|
-
result.properties = {};
|
|
86
|
-
}
|
|
87
|
-
if (result.type === "object" && !("required" in result)) {
|
|
88
|
-
result.required = [];
|
|
89
|
-
}
|
|
90
|
-
if (forStructuredOutput) {
|
|
91
|
-
result = makeStructuredOutputCompatible(result, result.required || []);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
export {
|
|
97
|
-
convertZodToJsonSchema
|
|
98
|
-
};
|
|
99
|
-
//# sourceMappingURL=zod-converter.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"zod-converter.js","sources":["../../../../../src/activities/chat/tools/zod-converter.ts"],"sourcesContent":["import { toJSONSchema } from 'zod'\nimport type { z } from 'zod'\nimport type { SchemaInput } from '../../../types'\n\n/**\n * Check if a value is a Zod schema by looking for Zod-specific internals.\n * Zod schemas have a `_zod` property that contains metadata.\n */\nfunction isZodSchema(schema: unknown): schema is z.ZodType {\n return (\n typeof schema === 'object' &&\n schema !== null &&\n '_zod' in schema &&\n typeof (schema as any)._zod === 'object'\n )\n}\n\n/**\n * Transform a JSON schema to be compatible with OpenAI's structured output requirements.\n * OpenAI requires:\n * - All properties must be in the `required` array\n * - Optional fields should have null added to their type union\n * - additionalProperties must be false for objects\n *\n * @param schema - JSON schema to transform\n * @param originalRequired - Original required array (to know which fields were optional)\n * @returns Transformed schema compatible with OpenAI structured output\n */\nfunction makeStructuredOutputCompatible(\n schema: Record<string, any>,\n originalRequired: Array<string> = [],\n): Record<string, any> {\n const result = { ...schema }\n\n // Handle object types\n if (result.type === 'object' && result.properties) {\n const properties = { ...result.properties }\n const allPropertyNames = Object.keys(properties)\n\n // Transform each property\n for (const propName of allPropertyNames) {\n const prop = properties[propName]\n const wasOptional = !originalRequired.includes(propName)\n\n // Recursively transform nested objects/arrays\n if (prop.type === 'object' && prop.properties) {\n properties[propName] = makeStructuredOutputCompatible(\n prop,\n prop.required || [],\n )\n } else if (prop.type === 'array' && prop.items) {\n properties[propName] = {\n ...prop,\n items: makeStructuredOutputCompatible(\n prop.items,\n prop.items.required || [],\n ),\n }\n } else if (wasOptional) {\n // Make optional fields nullable by adding null to the type\n if (prop.type && !Array.isArray(prop.type)) {\n properties[propName] = {\n ...prop,\n type: [prop.type, 'null'],\n }\n } else if (Array.isArray(prop.type) && !prop.type.includes('null')) {\n properties[propName] = {\n ...prop,\n type: [...prop.type, 'null'],\n }\n }\n }\n }\n\n result.properties = properties\n // ALL properties must be required for OpenAI structured output\n result.required = allPropertyNames\n // additionalProperties must be false\n result.additionalProperties = false\n }\n\n // Handle array types with object items\n if (result.type === 'array' && result.items) {\n result.items = makeStructuredOutputCompatible(\n result.items,\n result.items.required || [],\n )\n }\n\n return result\n}\n\n/**\n * Options for schema conversion\n */\nexport interface ConvertSchemaOptions {\n /**\n * When true, transforms the schema to be compatible with OpenAI's structured output requirements:\n * - All properties are added to the `required` array\n * - Optional fields get null added to their type union\n * - additionalProperties is set to false for all objects\n *\n * @default false\n */\n forStructuredOutput?: boolean\n}\n\n/**\n * Converts a schema (Zod or JSONSchema) to JSON Schema format compatible with LLM providers.\n * If the input is already a JSONSchema object, it is returned as-is.\n * If the input is a Zod schema, it is converted to JSON Schema.\n *\n * @param schema - Zod schema or JSONSchema object to convert\n * @param options - Conversion options\n * @returns JSON Schema object that can be sent to LLM providers\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * // Using Zod schema\n * const zodSchema = z.object({\n * location: z.string().describe('City name'),\n * unit: z.enum(['celsius', 'fahrenheit']).optional()\n * });\n *\n * const jsonSchema = convertZodToJsonSchema(zodSchema);\n * // Returns:\n * // {\n * // type: 'object',\n * // properties: {\n * // location: { type: 'string', description: 'City name' },\n * // unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }\n * // },\n * // required: ['location']\n * // }\n *\n * // For OpenAI structured output (all fields required, optional fields nullable)\n * const structuredSchema = convertZodToJsonSchema(zodSchema, { forStructuredOutput: true });\n * // Returns:\n * // {\n * // type: 'object',\n * // properties: {\n * // location: { type: 'string', description: 'City name' },\n * // unit: { type: ['string', 'null'], enum: ['celsius', 'fahrenheit'] }\n * // },\n * // required: ['location', 'unit'],\n * // additionalProperties: false\n * // }\n *\n * // Using JSONSchema directly (passes through unchanged)\n * const rawSchema = {\n * type: 'object',\n * properties: { location: { type: 'string' } },\n * required: ['location']\n * };\n * const result = convertZodToJsonSchema(rawSchema);\n * // Returns the same object\n * ```\n */\nexport function convertZodToJsonSchema(\n schema: SchemaInput | undefined,\n options: ConvertSchemaOptions = {},\n): Record<string, any> | undefined {\n if (!schema) return undefined\n\n const { forStructuredOutput = false } = options\n\n // If it's not a Zod schema, assume it's already a JSONSchema and pass through\n if (!isZodSchema(schema)) {\n // Still apply structured output transformation if requested\n if (forStructuredOutput && typeof schema === 'object') {\n return makeStructuredOutputCompatible(\n schema,\n (schema as any).required || [],\n )\n }\n return schema\n }\n\n // Use Alcyone Labs fork which is compatible with Zod v4\n const jsonSchema = toJSONSchema(schema, {\n target: 'openapi-3.0',\n reused: 'ref',\n })\n\n // Remove $schema property as it's not needed for LLM providers\n let result = jsonSchema\n if (typeof result === 'object' && '$schema' in result) {\n const { $schema, ...rest } = result\n result = rest\n }\n\n // Ensure object schemas always have type: \"object\"\n // This fixes cases where zod-to-json-schema doesn't set type for empty objects\n if (typeof result === 'object') {\n // Check if the input schema is a ZodObject by inspecting its internal structure\n const isZodObject =\n typeof schema === 'object' &&\n 'def' in schema &&\n schema.def.type === 'object'\n\n // If we know it's a ZodObject but result doesn't have type, set it\n if (isZodObject && !result.type) {\n result.type = 'object'\n }\n\n // If result is completely empty (no keys), it's likely an empty object schema\n if (Object.keys(result).length === 0) {\n result.type = 'object'\n }\n\n // If it has properties (even empty), it should be an object type\n if ('properties' in result && !result.type) {\n result.type = 'object'\n }\n\n // Ensure properties exists for object types (even if empty)\n if (result.type === 'object' && !('properties' in result)) {\n result.properties = {}\n }\n\n // Ensure required exists for object types (even if empty array)\n if (result.type === 'object' && !('required' in result)) {\n result.required = []\n }\n\n // Apply structured output transformation if requested\n if (forStructuredOutput) {\n result = makeStructuredOutputCompatible(result, result.required || [])\n }\n }\n\n return result\n}\n"],"names":[],"mappings":"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"}
|