@tanstack/ai 0.0.3 → 0.1.0
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 +26 -0
- package/dist/esm/activities/chat/adapter.d.ts +100 -0
- package/dist/esm/activities/chat/adapter.js +14 -0
- package/dist/esm/activities/chat/adapter.js.map +1 -0
- package/dist/esm/{utilities → activities/chat}/agent-loop-strategies.d.ts +4 -4
- package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -0
- package/dist/esm/activities/chat/index.d.ts +165 -0
- package/dist/esm/{core/chat.js → activities/chat/index.js} +131 -33
- package/dist/esm/activities/chat/index.js.map +1 -0
- package/dist/esm/{message-converters.d.ts → activities/chat/messages.d.ts} +1 -1
- package/dist/esm/{message-converters.js → activities/chat/messages.js} +7 -7
- package/dist/esm/activities/chat/messages.js.map +1 -0
- package/dist/esm/activities/chat/stream/json-parser.js.map +1 -0
- package/dist/esm/{stream → activities/chat/stream}/message-updaters.d.ts +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -0
- package/dist/esm/{stream → activities/chat/stream}/processor.d.ts +1 -1
- package/dist/esm/{stream → activities/chat/stream}/processor.js +1 -1
- package/dist/esm/activities/chat/stream/processor.js.map +1 -0
- package/dist/esm/activities/chat/stream/strategies.js.map +1 -0
- package/dist/esm/{stream → activities/chat/stream}/types.d.ts +2 -9
- package/dist/esm/{tools → activities/chat/tools}/tool-calls.d.ts +1 -1
- package/dist/esm/{tools → activities/chat/tools}/tool-calls.js +9 -5
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -0
- package/dist/esm/{tools → activities/chat/tools}/tool-definition.d.ts +14 -14
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -0
- package/dist/esm/activities/chat/tools/zod-converter.d.ts +69 -0
- package/dist/esm/activities/chat/tools/zod-converter.js +99 -0
- package/dist/esm/activities/chat/tools/zod-converter.js.map +1 -0
- package/dist/esm/activities/generateImage/adapter.d.ts +68 -0
- package/dist/esm/activities/generateImage/adapter.js +14 -0
- package/dist/esm/activities/generateImage/adapter.js.map +1 -0
- package/dist/esm/activities/generateImage/index.d.ts +89 -0
- package/dist/esm/activities/generateImage/index.js +15 -0
- package/dist/esm/activities/generateImage/index.js.map +1 -0
- package/dist/esm/activities/generateSpeech/adapter.d.ts +62 -0
- package/dist/esm/activities/generateSpeech/adapter.js +14 -0
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -0
- package/dist/esm/activities/generateSpeech/index.d.ts +69 -0
- package/dist/esm/activities/generateSpeech/index.js +15 -0
- package/dist/esm/activities/generateSpeech/index.js.map +1 -0
- package/dist/esm/activities/generateTranscription/adapter.d.ts +62 -0
- package/dist/esm/activities/generateTranscription/adapter.js +14 -0
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -0
- package/dist/esm/activities/generateTranscription/index.d.ts +71 -0
- package/dist/esm/activities/generateTranscription/index.js +15 -0
- package/dist/esm/activities/generateTranscription/index.js.map +1 -0
- package/dist/esm/activities/generateVideo/adapter.d.ts +80 -0
- package/dist/esm/activities/generateVideo/adapter.js +14 -0
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -0
- package/dist/esm/activities/generateVideo/index.d.ts +136 -0
- package/dist/esm/activities/generateVideo/index.js +47 -0
- package/dist/esm/activities/generateVideo/index.js.map +1 -0
- package/dist/esm/activities/index.d.ts +22 -0
- package/dist/esm/activities/index.js +34 -0
- package/dist/esm/activities/index.js.map +1 -0
- package/dist/esm/activities/summarize/adapter.d.ts +74 -0
- package/dist/esm/activities/summarize/adapter.js +14 -0
- package/dist/esm/activities/summarize/adapter.js.map +1 -0
- package/dist/esm/activities/summarize/index.d.ts +100 -0
- package/dist/esm/activities/summarize/index.js +90 -0
- package/dist/esm/activities/summarize/index.js.map +1 -0
- package/dist/esm/event-client.d.ts +4 -18
- package/dist/esm/event-client.js.map +1 -1
- package/dist/esm/index.d.ts +16 -14
- package/dist/esm/index.js +28 -19
- package/dist/esm/stream-to-response.d.ts +95 -0
- package/dist/esm/stream-to-response.js +118 -0
- package/dist/esm/stream-to-response.js.map +1 -0
- package/dist/esm/types.d.ts +347 -129
- package/package.json +5 -1
- package/src/activities/chat/adapter.ts +150 -0
- package/src/{utilities → activities/chat}/agent-loop-strategies.ts +4 -4
- package/src/{core/chat.ts → activities/chat/index.ts} +427 -79
- package/src/{message-converters.ts → activities/chat/messages.ts} +10 -13
- package/src/{stream → activities/chat/stream}/message-updaters.ts +1 -1
- package/src/{stream → activities/chat/stream}/processor.ts +2 -5
- package/src/{stream → activities/chat/stream}/types.ts +8 -18
- package/src/{tools → activities/chat/tools}/tool-calls.ts +36 -11
- package/src/{tools → activities/chat/tools}/tool-definition.ts +36 -27
- package/src/activities/chat/tools/zod-converter.ts +235 -0
- package/src/activities/generateImage/adapter.ts +104 -0
- package/src/activities/generateImage/index.ts +162 -0
- package/src/activities/generateSpeech/adapter.ts +87 -0
- package/src/activities/generateSpeech/index.ts +122 -0
- package/src/activities/generateTranscription/adapter.ts +89 -0
- package/src/activities/generateTranscription/index.ts +132 -0
- package/src/activities/generateVideo/adapter.ts +116 -0
- package/src/activities/generateVideo/index.ts +261 -0
- package/src/activities/index.ts +164 -0
- package/src/activities/summarize/adapter.ts +107 -0
- package/src/activities/summarize/index.ts +287 -0
- package/src/event-client.ts +5 -21
- package/src/index.ts +58 -15
- package/src/stream-to-response.ts +237 -0
- package/src/types.ts +404 -280
- package/dist/esm/base-adapter.d.ts +0 -36
- package/dist/esm/base-adapter.js +0 -12
- package/dist/esm/base-adapter.js.map +0 -1
- package/dist/esm/core/chat-common-options.d.ts +0 -52
- package/dist/esm/core/chat.d.ts +0 -30
- package/dist/esm/core/chat.js.map +0 -1
- package/dist/esm/core/embedding.d.ts +0 -8
- package/dist/esm/core/embedding.js +0 -33
- package/dist/esm/core/embedding.js.map +0 -1
- package/dist/esm/core/summarize.d.ts +0 -9
- package/dist/esm/core/summarize.js +0 -36
- package/dist/esm/core/summarize.js.map +0 -1
- package/dist/esm/message-converters.js.map +0 -1
- package/dist/esm/stream/json-parser.js.map +0 -1
- package/dist/esm/stream/message-updaters.js.map +0 -1
- package/dist/esm/stream/processor.js.map +0 -1
- package/dist/esm/stream/strategies.js.map +0 -1
- package/dist/esm/tools/tool-calls.js.map +0 -1
- package/dist/esm/tools/tool-definition.js.map +0 -1
- package/dist/esm/tools/zod-converter.d.ts +0 -30
- package/dist/esm/tools/zod-converter.js +0 -36
- package/dist/esm/tools/zod-converter.js.map +0 -1
- package/dist/esm/utilities/agent-loop-strategies.js.map +0 -1
- package/dist/esm/utilities/chat-options.d.ts +0 -6
- package/dist/esm/utilities/chat-options.js +0 -7
- package/dist/esm/utilities/chat-options.js.map +0 -1
- package/dist/esm/utilities/messages.d.ts +0 -30
- package/dist/esm/utilities/messages.js +0 -7
- package/dist/esm/utilities/messages.js.map +0 -1
- package/dist/esm/utilities/stream-to-response.d.ts +0 -48
- package/dist/esm/utilities/stream-to-response.js +0 -62
- package/dist/esm/utilities/stream-to-response.js.map +0 -1
- package/src/base-adapter.ts +0 -86
- package/src/core/chat-common-options.ts +0 -55
- package/src/core/embedding.ts +0 -54
- package/src/core/summarize.ts +0 -56
- package/src/tools/zod-converter.ts +0 -85
- package/src/utilities/chat-options.ts +0 -35
- package/src/utilities/messages.ts +0 -63
- package/src/utilities/stream-to-response.ts +0 -116
- /package/dist/esm/{utilities → activities/chat}/agent-loop-strategies.js +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/index.d.ts +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/json-parser.d.ts +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/json-parser.js +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/message-updaters.js +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/strategies.d.ts +0 -0
- /package/dist/esm/{stream → activities/chat/stream}/strategies.js +0 -0
- /package/dist/esm/{tools → activities/chat/tools}/tool-definition.js +0 -0
- /package/src/{stream → activities/chat/stream}/index.ts +0 -0
- /package/src/{stream → activities/chat/stream}/json-parser.ts +0 -0
- /package/src/{stream → activities/chat/stream}/strategies.ts +0 -0
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Message Converters
|
|
3
|
-
*
|
|
4
|
-
* Functions for converting between UIMessage and ModelMessage formats.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
1
|
import type {
|
|
8
2
|
ContentPart,
|
|
9
3
|
MessagePart,
|
|
@@ -12,7 +6,10 @@ import type {
|
|
|
12
6
|
ToolCallPart,
|
|
13
7
|
ToolResultPart,
|
|
14
8
|
UIMessage,
|
|
15
|
-
} from '
|
|
9
|
+
} from '../../types'
|
|
10
|
+
// ===========================
|
|
11
|
+
// Message Converters
|
|
12
|
+
// ===========================
|
|
16
13
|
|
|
17
14
|
/**
|
|
18
15
|
* Helper to extract text content from string or ContentPart array
|
|
@@ -65,11 +62,11 @@ export function convertMessagesToModelMessages(
|
|
|
65
62
|
export function uiMessageToModelMessages(
|
|
66
63
|
uiMessage: UIMessage,
|
|
67
64
|
): Array<ModelMessage> {
|
|
68
|
-
const
|
|
65
|
+
const messageList: Array<ModelMessage> = []
|
|
69
66
|
|
|
70
67
|
// Skip system messages - they're handled via systemPrompts, not ModelMessages
|
|
71
68
|
if (uiMessage.role === 'system') {
|
|
72
|
-
return
|
|
69
|
+
return messageList
|
|
73
70
|
}
|
|
74
71
|
|
|
75
72
|
// Separate parts by type
|
|
@@ -112,14 +109,14 @@ export function uiMessageToModelMessages(
|
|
|
112
109
|
|
|
113
110
|
// Create the main message
|
|
114
111
|
if (uiMessage.role !== 'assistant' || content || !toolCalls) {
|
|
115
|
-
|
|
112
|
+
messageList.push({
|
|
116
113
|
role: uiMessage.role,
|
|
117
114
|
content,
|
|
118
115
|
...(toolCalls && toolCalls.length > 0 && { toolCalls }),
|
|
119
116
|
})
|
|
120
117
|
} else if (toolCalls.length > 0) {
|
|
121
118
|
// Assistant message with only tool calls
|
|
122
|
-
|
|
119
|
+
messageList.push({
|
|
123
120
|
role: 'assistant',
|
|
124
121
|
content,
|
|
125
122
|
toolCalls,
|
|
@@ -132,7 +129,7 @@ export function uiMessageToModelMessages(
|
|
|
132
129
|
toolResultPart.state === 'complete' ||
|
|
133
130
|
toolResultPart.state === 'error'
|
|
134
131
|
) {
|
|
135
|
-
|
|
132
|
+
messageList.push({
|
|
136
133
|
role: 'tool',
|
|
137
134
|
content: toolResultPart.content,
|
|
138
135
|
toolCallId: toolResultPart.toolCallId,
|
|
@@ -140,7 +137,7 @@ export function uiMessageToModelMessages(
|
|
|
140
137
|
}
|
|
141
138
|
}
|
|
142
139
|
|
|
143
|
-
return
|
|
140
|
+
return messageList
|
|
144
141
|
}
|
|
145
142
|
|
|
146
143
|
/**
|
|
@@ -13,10 +13,7 @@
|
|
|
13
13
|
* - Recording/replay for testing
|
|
14
14
|
* - Event-driven architecture for UI updates
|
|
15
15
|
*/
|
|
16
|
-
import {
|
|
17
|
-
generateMessageId,
|
|
18
|
-
uiMessageToModelMessages,
|
|
19
|
-
} from '../message-converters'
|
|
16
|
+
import { generateMessageId, uiMessageToModelMessages } from '../messages.js'
|
|
20
17
|
import { defaultJSONParser } from './json-parser'
|
|
21
18
|
import {
|
|
22
19
|
updateTextPart,
|
|
@@ -43,7 +40,7 @@ import type {
|
|
|
43
40
|
ToolCall,
|
|
44
41
|
ToolCallPart,
|
|
45
42
|
UIMessage,
|
|
46
|
-
} from '
|
|
43
|
+
} from '../../../types'
|
|
47
44
|
|
|
48
45
|
/**
|
|
49
46
|
* Events emitted by the StreamProcessor
|
|
@@ -5,25 +5,15 @@
|
|
|
5
5
|
* The canonical chunk format is StreamChunk from @tanstack/ai types.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
StreamChunk,
|
|
10
|
+
ToolCall,
|
|
11
|
+
ToolCallState,
|
|
12
|
+
ToolResultState,
|
|
13
|
+
} from '../../../types'
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*/
|
|
13
|
-
export type ToolCallState =
|
|
14
|
-
| 'awaiting-input' // Received start but no arguments yet
|
|
15
|
-
| 'input-streaming' // Partial arguments received
|
|
16
|
-
| 'input-complete' // All arguments received
|
|
17
|
-
| 'approval-requested' // Waiting for user approval
|
|
18
|
-
| 'approval-responded' // User has approved/denied
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Tool result states - track the lifecycle of a tool result
|
|
22
|
-
*/
|
|
23
|
-
export type ToolResultState =
|
|
24
|
-
| 'streaming' // Placeholder for future streamed output
|
|
25
|
-
| 'complete' // Result is complete
|
|
26
|
-
| 'error' // Error occurred
|
|
15
|
+
// Re-export for backwards compatibility
|
|
16
|
+
export type { ToolCallState, ToolResultState }
|
|
27
17
|
|
|
28
18
|
/**
|
|
29
19
|
* Internal state for a tool call being tracked
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
DoneStreamChunk,
|
|
3
3
|
ModelMessage,
|
|
4
|
+
SchemaInput,
|
|
4
5
|
Tool,
|
|
5
6
|
ToolCall,
|
|
6
7
|
ToolResultStreamChunk,
|
|
7
|
-
} from '
|
|
8
|
+
} from '../../../types'
|
|
9
|
+
import type { z } from 'zod'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if a value is a Zod schema by looking for Zod-specific internals.
|
|
13
|
+
*/
|
|
14
|
+
function isZodSchema(schema: SchemaInput | undefined): schema is z.ZodType {
|
|
15
|
+
if (!schema) return false
|
|
16
|
+
return '_zod' in schema && typeof (schema as any)._zod === 'object'
|
|
17
|
+
}
|
|
8
18
|
|
|
9
19
|
/**
|
|
10
20
|
* Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.
|
|
@@ -130,8 +140,8 @@ export class ToolCallManager {
|
|
|
130
140
|
)
|
|
131
141
|
}
|
|
132
142
|
|
|
133
|
-
// Validate input against inputSchema
|
|
134
|
-
if (tool.inputSchema) {
|
|
143
|
+
// Validate input against inputSchema (only for Zod schemas)
|
|
144
|
+
if (tool.inputSchema && isZodSchema(tool.inputSchema)) {
|
|
135
145
|
try {
|
|
136
146
|
args = tool.inputSchema.parse(args)
|
|
137
147
|
} catch (validationError: any) {
|
|
@@ -144,8 +154,13 @@ export class ToolCallManager {
|
|
|
144
154
|
// Execute the tool
|
|
145
155
|
let result = await tool.execute(args)
|
|
146
156
|
|
|
147
|
-
// Validate output against outputSchema if provided
|
|
148
|
-
if (
|
|
157
|
+
// Validate output against outputSchema if provided (only for Zod schemas)
|
|
158
|
+
if (
|
|
159
|
+
tool.outputSchema &&
|
|
160
|
+
isZodSchema(tool.outputSchema) &&
|
|
161
|
+
result !== undefined &&
|
|
162
|
+
result !== null
|
|
163
|
+
) {
|
|
149
164
|
try {
|
|
150
165
|
result = tool.outputSchema.parse(result)
|
|
151
166
|
} catch (validationError: any) {
|
|
@@ -282,8 +297,8 @@ export async function executeToolCalls(
|
|
|
282
297
|
}
|
|
283
298
|
}
|
|
284
299
|
|
|
285
|
-
// Validate input against inputSchema
|
|
286
|
-
if (tool.inputSchema) {
|
|
300
|
+
// Validate input against inputSchema (only for Zod schemas)
|
|
301
|
+
if (tool.inputSchema && isZodSchema(tool.inputSchema)) {
|
|
287
302
|
try {
|
|
288
303
|
input = tool.inputSchema.parse(input)
|
|
289
304
|
} catch (validationError: any) {
|
|
@@ -378,8 +393,13 @@ export async function executeToolCalls(
|
|
|
378
393
|
let result = await tool.execute(input)
|
|
379
394
|
const duration = Date.now() - startTime
|
|
380
395
|
|
|
381
|
-
// Validate output against outputSchema if provided
|
|
382
|
-
if (
|
|
396
|
+
// Validate output against outputSchema if provided (only for Zod schemas)
|
|
397
|
+
if (
|
|
398
|
+
tool.outputSchema &&
|
|
399
|
+
isZodSchema(tool.outputSchema) &&
|
|
400
|
+
result !== undefined &&
|
|
401
|
+
result !== null
|
|
402
|
+
) {
|
|
383
403
|
const parsed = tool.outputSchema.safeParse(result)
|
|
384
404
|
if (parsed.success) {
|
|
385
405
|
result = parsed.data
|
|
@@ -436,8 +456,13 @@ export async function executeToolCalls(
|
|
|
436
456
|
let result = await tool.execute(input)
|
|
437
457
|
const duration = Date.now() - startTime
|
|
438
458
|
|
|
439
|
-
// Validate output against outputSchema if provided
|
|
440
|
-
if (
|
|
459
|
+
// Validate output against outputSchema if provided (only for Zod schemas)
|
|
460
|
+
if (
|
|
461
|
+
tool.outputSchema &&
|
|
462
|
+
isZodSchema(tool.outputSchema) &&
|
|
463
|
+
result !== undefined &&
|
|
464
|
+
result !== null
|
|
465
|
+
) {
|
|
441
466
|
const parsed = tool.outputSchema.safeParse(result)
|
|
442
467
|
if (parsed.success) {
|
|
443
468
|
result = parsed.data
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type { z } from 'zod'
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
InferSchemaType,
|
|
4
|
+
JSONSchema,
|
|
5
|
+
SchemaInput,
|
|
6
|
+
Tool,
|
|
7
|
+
} from '../../../types'
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
10
|
* Marker type for server-side tools
|
|
6
11
|
*/
|
|
7
12
|
export interface ServerTool<
|
|
8
|
-
TInput extends
|
|
9
|
-
TOutput extends
|
|
13
|
+
TInput extends SchemaInput = z.ZodType,
|
|
14
|
+
TOutput extends SchemaInput = z.ZodType,
|
|
10
15
|
TName extends string = string,
|
|
11
16
|
> extends Tool<TInput, TOutput, TName> {
|
|
12
17
|
__toolSide: 'server'
|
|
@@ -16,8 +21,8 @@ export interface ServerTool<
|
|
|
16
21
|
* Marker type for client-side tools
|
|
17
22
|
*/
|
|
18
23
|
export interface ClientTool<
|
|
19
|
-
TInput extends
|
|
20
|
-
TOutput extends
|
|
24
|
+
TInput extends SchemaInput = z.ZodType,
|
|
25
|
+
TOutput extends SchemaInput = z.ZodType,
|
|
21
26
|
TName extends string = string,
|
|
22
27
|
> {
|
|
23
28
|
__toolSide: 'client'
|
|
@@ -28,16 +33,16 @@ export interface ClientTool<
|
|
|
28
33
|
needsApproval?: boolean
|
|
29
34
|
metadata?: Record<string, any>
|
|
30
35
|
execute?: (
|
|
31
|
-
args:
|
|
32
|
-
) => Promise<
|
|
36
|
+
args: InferSchemaType<TInput>,
|
|
37
|
+
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
/**
|
|
36
41
|
* Tool definition that can be used directly or instantiated for server/client
|
|
37
42
|
*/
|
|
38
43
|
export interface ToolDefinitionInstance<
|
|
39
|
-
TInput extends
|
|
40
|
-
TOutput extends
|
|
44
|
+
TInput extends SchemaInput = z.ZodType,
|
|
45
|
+
TOutput extends SchemaInput = z.ZodType,
|
|
41
46
|
TName extends string = string,
|
|
42
47
|
> extends Tool<TInput, TOutput, TName> {
|
|
43
48
|
__toolSide: 'definition'
|
|
@@ -56,29 +61,33 @@ export type AnyClientTool =
|
|
|
56
61
|
export type InferToolName<T> = T extends { name: infer N } ? N : never
|
|
57
62
|
|
|
58
63
|
/**
|
|
59
|
-
* Extract the input type from a tool (inferred from Zod schema)
|
|
64
|
+
* Extract the input type from a tool (inferred from Zod schema, or `any` for JSONSchema)
|
|
60
65
|
*/
|
|
61
66
|
export type InferToolInput<T> = T extends { inputSchema?: infer TInput }
|
|
62
67
|
? TInput extends z.ZodType
|
|
63
68
|
? z.infer<TInput>
|
|
64
|
-
:
|
|
69
|
+
: TInput extends JSONSchema
|
|
70
|
+
? any
|
|
71
|
+
: any
|
|
65
72
|
: any
|
|
66
73
|
|
|
67
74
|
/**
|
|
68
|
-
* Extract the output type from a tool (inferred from Zod schema)
|
|
75
|
+
* Extract the output type from a tool (inferred from Zod schema, or `any` for JSONSchema)
|
|
69
76
|
*/
|
|
70
77
|
export type InferToolOutput<T> = T extends { outputSchema?: infer TOutput }
|
|
71
78
|
? TOutput extends z.ZodType
|
|
72
79
|
? z.infer<TOutput>
|
|
73
|
-
:
|
|
80
|
+
: TOutput extends JSONSchema
|
|
81
|
+
? any
|
|
82
|
+
: any
|
|
74
83
|
: any
|
|
75
84
|
|
|
76
85
|
/**
|
|
77
86
|
* Tool definition configuration
|
|
78
87
|
*/
|
|
79
88
|
export interface ToolDefinitionConfig<
|
|
80
|
-
TInput extends
|
|
81
|
-
TOutput extends
|
|
89
|
+
TInput extends SchemaInput = z.ZodType,
|
|
90
|
+
TOutput extends SchemaInput = z.ZodType,
|
|
82
91
|
TName extends string = string,
|
|
83
92
|
> {
|
|
84
93
|
name: TName
|
|
@@ -93,8 +102,8 @@ export interface ToolDefinitionConfig<
|
|
|
93
102
|
* Tool definition builder that allows creating server or client tools from a shared definition
|
|
94
103
|
*/
|
|
95
104
|
export interface ToolDefinition<
|
|
96
|
-
TInput extends
|
|
97
|
-
TOutput extends
|
|
105
|
+
TInput extends SchemaInput = z.ZodType,
|
|
106
|
+
TOutput extends SchemaInput = z.ZodType,
|
|
98
107
|
TName extends string = string,
|
|
99
108
|
> extends ToolDefinitionInstance<TInput, TOutput, TName> {
|
|
100
109
|
/**
|
|
@@ -102,8 +111,8 @@ export interface ToolDefinition<
|
|
|
102
111
|
*/
|
|
103
112
|
server: (
|
|
104
113
|
execute: (
|
|
105
|
-
args:
|
|
106
|
-
) => Promise<
|
|
114
|
+
args: InferSchemaType<TInput>,
|
|
115
|
+
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
107
116
|
) => ServerTool<TInput, TOutput, TName>
|
|
108
117
|
|
|
109
118
|
/**
|
|
@@ -111,8 +120,8 @@ export interface ToolDefinition<
|
|
|
111
120
|
*/
|
|
112
121
|
client: (
|
|
113
122
|
execute?: (
|
|
114
|
-
args:
|
|
115
|
-
) => Promise<
|
|
123
|
+
args: InferSchemaType<TInput>,
|
|
124
|
+
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
116
125
|
) => ClientTool<TInput, TOutput, TName>
|
|
117
126
|
}
|
|
118
127
|
|
|
@@ -168,8 +177,8 @@ export interface ToolDefinition<
|
|
|
168
177
|
* ```
|
|
169
178
|
*/
|
|
170
179
|
export function toolDefinition<
|
|
171
|
-
TInput extends
|
|
172
|
-
TOutput extends
|
|
180
|
+
TInput extends SchemaInput = z.ZodAny,
|
|
181
|
+
TOutput extends SchemaInput = z.ZodAny,
|
|
173
182
|
TName extends string = string,
|
|
174
183
|
>(
|
|
175
184
|
config: ToolDefinitionConfig<TInput, TOutput, TName>,
|
|
@@ -179,8 +188,8 @@ export function toolDefinition<
|
|
|
179
188
|
...config,
|
|
180
189
|
server(
|
|
181
190
|
execute: (
|
|
182
|
-
args:
|
|
183
|
-
) => Promise<
|
|
191
|
+
args: InferSchemaType<TInput>,
|
|
192
|
+
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
184
193
|
): ServerTool<TInput, TOutput, TName> {
|
|
185
194
|
return {
|
|
186
195
|
__toolSide: 'server',
|
|
@@ -191,8 +200,8 @@ export function toolDefinition<
|
|
|
191
200
|
|
|
192
201
|
client(
|
|
193
202
|
execute?: (
|
|
194
|
-
args:
|
|
195
|
-
) => Promise<
|
|
203
|
+
args: InferSchemaType<TInput>,
|
|
204
|
+
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
196
205
|
): ClientTool<TInput, TOutput, TName> {
|
|
197
206
|
return {
|
|
198
207
|
__toolSide: 'client',
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { toJSONSchema } from 'zod'
|
|
2
|
+
import type { z } from 'zod'
|
|
3
|
+
import type { SchemaInput } from '../../../types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if a value is a Zod schema by looking for Zod-specific internals.
|
|
7
|
+
* Zod schemas have a `_zod` property that contains metadata.
|
|
8
|
+
*/
|
|
9
|
+
function isZodSchema(schema: unknown): schema is z.ZodType {
|
|
10
|
+
return (
|
|
11
|
+
typeof schema === 'object' &&
|
|
12
|
+
schema !== null &&
|
|
13
|
+
'_zod' in schema &&
|
|
14
|
+
typeof (schema as any)._zod === 'object'
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Transform a JSON schema to be compatible with OpenAI's structured output requirements.
|
|
20
|
+
* OpenAI requires:
|
|
21
|
+
* - All properties must be in the `required` array
|
|
22
|
+
* - Optional fields should have null added to their type union
|
|
23
|
+
* - additionalProperties must be false for objects
|
|
24
|
+
*
|
|
25
|
+
* @param schema - JSON schema to transform
|
|
26
|
+
* @param originalRequired - Original required array (to know which fields were optional)
|
|
27
|
+
* @returns Transformed schema compatible with OpenAI structured output
|
|
28
|
+
*/
|
|
29
|
+
function makeStructuredOutputCompatible(
|
|
30
|
+
schema: Record<string, any>,
|
|
31
|
+
originalRequired: Array<string> = [],
|
|
32
|
+
): Record<string, any> {
|
|
33
|
+
const result = { ...schema }
|
|
34
|
+
|
|
35
|
+
// Handle object types
|
|
36
|
+
if (result.type === 'object' && result.properties) {
|
|
37
|
+
const properties = { ...result.properties }
|
|
38
|
+
const allPropertyNames = Object.keys(properties)
|
|
39
|
+
|
|
40
|
+
// Transform each property
|
|
41
|
+
for (const propName of allPropertyNames) {
|
|
42
|
+
const prop = properties[propName]
|
|
43
|
+
const wasOptional = !originalRequired.includes(propName)
|
|
44
|
+
|
|
45
|
+
// Recursively transform nested objects/arrays
|
|
46
|
+
if (prop.type === 'object' && prop.properties) {
|
|
47
|
+
properties[propName] = makeStructuredOutputCompatible(
|
|
48
|
+
prop,
|
|
49
|
+
prop.required || [],
|
|
50
|
+
)
|
|
51
|
+
} else if (prop.type === 'array' && prop.items) {
|
|
52
|
+
properties[propName] = {
|
|
53
|
+
...prop,
|
|
54
|
+
items: makeStructuredOutputCompatible(
|
|
55
|
+
prop.items,
|
|
56
|
+
prop.items.required || [],
|
|
57
|
+
),
|
|
58
|
+
}
|
|
59
|
+
} else if (wasOptional) {
|
|
60
|
+
// Make optional fields nullable by adding null to the type
|
|
61
|
+
if (prop.type && !Array.isArray(prop.type)) {
|
|
62
|
+
properties[propName] = {
|
|
63
|
+
...prop,
|
|
64
|
+
type: [prop.type, 'null'],
|
|
65
|
+
}
|
|
66
|
+
} else if (Array.isArray(prop.type) && !prop.type.includes('null')) {
|
|
67
|
+
properties[propName] = {
|
|
68
|
+
...prop,
|
|
69
|
+
type: [...prop.type, 'null'],
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
result.properties = properties
|
|
76
|
+
// ALL properties must be required for OpenAI structured output
|
|
77
|
+
result.required = allPropertyNames
|
|
78
|
+
// additionalProperties must be false
|
|
79
|
+
result.additionalProperties = false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Handle array types with object items
|
|
83
|
+
if (result.type === 'array' && result.items) {
|
|
84
|
+
result.items = makeStructuredOutputCompatible(
|
|
85
|
+
result.items,
|
|
86
|
+
result.items.required || [],
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return result
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Options for schema conversion
|
|
95
|
+
*/
|
|
96
|
+
export interface ConvertSchemaOptions {
|
|
97
|
+
/**
|
|
98
|
+
* When true, transforms the schema to be compatible with OpenAI's structured output requirements:
|
|
99
|
+
* - All properties are added to the `required` array
|
|
100
|
+
* - Optional fields get null added to their type union
|
|
101
|
+
* - additionalProperties is set to false for all objects
|
|
102
|
+
*
|
|
103
|
+
* @default false
|
|
104
|
+
*/
|
|
105
|
+
forStructuredOutput?: boolean
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Converts a schema (Zod or JSONSchema) to JSON Schema format compatible with LLM providers.
|
|
110
|
+
* If the input is already a JSONSchema object, it is returned as-is.
|
|
111
|
+
* If the input is a Zod schema, it is converted to JSON Schema.
|
|
112
|
+
*
|
|
113
|
+
* @param schema - Zod schema or JSONSchema object to convert
|
|
114
|
+
* @param options - Conversion options
|
|
115
|
+
* @returns JSON Schema object that can be sent to LLM providers
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* import { z } from 'zod';
|
|
120
|
+
*
|
|
121
|
+
* // Using Zod schema
|
|
122
|
+
* const zodSchema = z.object({
|
|
123
|
+
* location: z.string().describe('City name'),
|
|
124
|
+
* unit: z.enum(['celsius', 'fahrenheit']).optional()
|
|
125
|
+
* });
|
|
126
|
+
*
|
|
127
|
+
* const jsonSchema = convertZodToJsonSchema(zodSchema);
|
|
128
|
+
* // Returns:
|
|
129
|
+
* // {
|
|
130
|
+
* // type: 'object',
|
|
131
|
+
* // properties: {
|
|
132
|
+
* // location: { type: 'string', description: 'City name' },
|
|
133
|
+
* // unit: { type: 'string', enum: ['celsius', 'fahrenheit'] }
|
|
134
|
+
* // },
|
|
135
|
+
* // required: ['location']
|
|
136
|
+
* // }
|
|
137
|
+
*
|
|
138
|
+
* // For OpenAI structured output (all fields required, optional fields nullable)
|
|
139
|
+
* const structuredSchema = convertZodToJsonSchema(zodSchema, { forStructuredOutput: true });
|
|
140
|
+
* // Returns:
|
|
141
|
+
* // {
|
|
142
|
+
* // type: 'object',
|
|
143
|
+
* // properties: {
|
|
144
|
+
* // location: { type: 'string', description: 'City name' },
|
|
145
|
+
* // unit: { type: ['string', 'null'], enum: ['celsius', 'fahrenheit'] }
|
|
146
|
+
* // },
|
|
147
|
+
* // required: ['location', 'unit'],
|
|
148
|
+
* // additionalProperties: false
|
|
149
|
+
* // }
|
|
150
|
+
*
|
|
151
|
+
* // Using JSONSchema directly (passes through unchanged)
|
|
152
|
+
* const rawSchema = {
|
|
153
|
+
* type: 'object',
|
|
154
|
+
* properties: { location: { type: 'string' } },
|
|
155
|
+
* required: ['location']
|
|
156
|
+
* };
|
|
157
|
+
* const result = convertZodToJsonSchema(rawSchema);
|
|
158
|
+
* // Returns the same object
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export function convertZodToJsonSchema(
|
|
162
|
+
schema: SchemaInput | undefined,
|
|
163
|
+
options: ConvertSchemaOptions = {},
|
|
164
|
+
): Record<string, any> | undefined {
|
|
165
|
+
if (!schema) return undefined
|
|
166
|
+
|
|
167
|
+
const { forStructuredOutput = false } = options
|
|
168
|
+
|
|
169
|
+
// If it's not a Zod schema, assume it's already a JSONSchema and pass through
|
|
170
|
+
if (!isZodSchema(schema)) {
|
|
171
|
+
// Still apply structured output transformation if requested
|
|
172
|
+
if (forStructuredOutput && typeof schema === 'object') {
|
|
173
|
+
return makeStructuredOutputCompatible(
|
|
174
|
+
schema,
|
|
175
|
+
(schema as any).required || [],
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
return schema
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Use Alcyone Labs fork which is compatible with Zod v4
|
|
182
|
+
const jsonSchema = toJSONSchema(schema, {
|
|
183
|
+
target: 'openapi-3.0',
|
|
184
|
+
reused: 'ref',
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// Remove $schema property as it's not needed for LLM providers
|
|
188
|
+
let result = jsonSchema
|
|
189
|
+
if (typeof result === 'object' && '$schema' in result) {
|
|
190
|
+
const { $schema, ...rest } = result
|
|
191
|
+
result = rest
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Ensure object schemas always have type: "object"
|
|
195
|
+
// This fixes cases where zod-to-json-schema doesn't set type for empty objects
|
|
196
|
+
if (typeof result === 'object') {
|
|
197
|
+
// Check if the input schema is a ZodObject by inspecting its internal structure
|
|
198
|
+
const isZodObject =
|
|
199
|
+
typeof schema === 'object' &&
|
|
200
|
+
'def' in schema &&
|
|
201
|
+
schema.def.type === 'object'
|
|
202
|
+
|
|
203
|
+
// If we know it's a ZodObject but result doesn't have type, set it
|
|
204
|
+
if (isZodObject && !result.type) {
|
|
205
|
+
result.type = 'object'
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// If result is completely empty (no keys), it's likely an empty object schema
|
|
209
|
+
if (Object.keys(result).length === 0) {
|
|
210
|
+
result.type = 'object'
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// If it has properties (even empty), it should be an object type
|
|
214
|
+
if ('properties' in result && !result.type) {
|
|
215
|
+
result.type = 'object'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Ensure properties exists for object types (even if empty)
|
|
219
|
+
if (result.type === 'object' && !('properties' in result)) {
|
|
220
|
+
result.properties = {}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Ensure required exists for object types (even if empty array)
|
|
224
|
+
if (result.type === 'object' && !('required' in result)) {
|
|
225
|
+
result.required = []
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Apply structured output transformation if requested
|
|
229
|
+
if (forStructuredOutput) {
|
|
230
|
+
result = makeStructuredOutputCompatible(result, result.required || [])
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return result
|
|
235
|
+
}
|