@tanstack/ai 0.0.2 → 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.
Files changed (147) hide show
  1. package/README.md +26 -0
  2. package/dist/esm/activities/chat/adapter.d.ts +100 -0
  3. package/dist/esm/activities/chat/adapter.js +14 -0
  4. package/dist/esm/activities/chat/adapter.js.map +1 -0
  5. package/dist/esm/{utilities → activities/chat}/agent-loop-strategies.d.ts +4 -4
  6. package/dist/esm/activities/chat/agent-loop-strategies.js.map +1 -0
  7. package/dist/esm/activities/chat/index.d.ts +165 -0
  8. package/dist/esm/{core/chat.js → activities/chat/index.js} +131 -33
  9. package/dist/esm/activities/chat/index.js.map +1 -0
  10. package/dist/esm/{message-converters.d.ts → activities/chat/messages.d.ts} +1 -1
  11. package/dist/esm/{message-converters.js → activities/chat/messages.js} +7 -7
  12. package/dist/esm/activities/chat/messages.js.map +1 -0
  13. package/dist/esm/activities/chat/stream/json-parser.js.map +1 -0
  14. package/dist/esm/{stream → activities/chat/stream}/message-updaters.d.ts +1 -1
  15. package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -0
  16. package/dist/esm/{stream → activities/chat/stream}/processor.d.ts +1 -1
  17. package/dist/esm/{stream → activities/chat/stream}/processor.js +1 -1
  18. package/dist/esm/activities/chat/stream/processor.js.map +1 -0
  19. package/dist/esm/activities/chat/stream/strategies.js.map +1 -0
  20. package/dist/esm/{stream → activities/chat/stream}/types.d.ts +2 -9
  21. package/dist/esm/{tools → activities/chat/tools}/tool-calls.d.ts +1 -1
  22. package/dist/esm/{tools → activities/chat/tools}/tool-calls.js +9 -5
  23. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -0
  24. package/dist/esm/{tools → activities/chat/tools}/tool-definition.d.ts +14 -14
  25. package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -0
  26. package/dist/esm/activities/chat/tools/zod-converter.d.ts +69 -0
  27. package/dist/esm/activities/chat/tools/zod-converter.js +99 -0
  28. package/dist/esm/activities/chat/tools/zod-converter.js.map +1 -0
  29. package/dist/esm/activities/generateImage/adapter.d.ts +68 -0
  30. package/dist/esm/activities/generateImage/adapter.js +14 -0
  31. package/dist/esm/activities/generateImage/adapter.js.map +1 -0
  32. package/dist/esm/activities/generateImage/index.d.ts +89 -0
  33. package/dist/esm/activities/generateImage/index.js +15 -0
  34. package/dist/esm/activities/generateImage/index.js.map +1 -0
  35. package/dist/esm/activities/generateSpeech/adapter.d.ts +62 -0
  36. package/dist/esm/activities/generateSpeech/adapter.js +14 -0
  37. package/dist/esm/activities/generateSpeech/adapter.js.map +1 -0
  38. package/dist/esm/activities/generateSpeech/index.d.ts +69 -0
  39. package/dist/esm/activities/generateSpeech/index.js +15 -0
  40. package/dist/esm/activities/generateSpeech/index.js.map +1 -0
  41. package/dist/esm/activities/generateTranscription/adapter.d.ts +62 -0
  42. package/dist/esm/activities/generateTranscription/adapter.js +14 -0
  43. package/dist/esm/activities/generateTranscription/adapter.js.map +1 -0
  44. package/dist/esm/activities/generateTranscription/index.d.ts +71 -0
  45. package/dist/esm/activities/generateTranscription/index.js +15 -0
  46. package/dist/esm/activities/generateTranscription/index.js.map +1 -0
  47. package/dist/esm/activities/generateVideo/adapter.d.ts +80 -0
  48. package/dist/esm/activities/generateVideo/adapter.js +14 -0
  49. package/dist/esm/activities/generateVideo/adapter.js.map +1 -0
  50. package/dist/esm/activities/generateVideo/index.d.ts +136 -0
  51. package/dist/esm/activities/generateVideo/index.js +47 -0
  52. package/dist/esm/activities/generateVideo/index.js.map +1 -0
  53. package/dist/esm/activities/index.d.ts +22 -0
  54. package/dist/esm/activities/index.js +34 -0
  55. package/dist/esm/activities/index.js.map +1 -0
  56. package/dist/esm/activities/summarize/adapter.d.ts +74 -0
  57. package/dist/esm/activities/summarize/adapter.js +14 -0
  58. package/dist/esm/activities/summarize/adapter.js.map +1 -0
  59. package/dist/esm/activities/summarize/index.d.ts +100 -0
  60. package/dist/esm/activities/summarize/index.js +90 -0
  61. package/dist/esm/activities/summarize/index.js.map +1 -0
  62. package/dist/esm/event-client.d.ts +4 -45
  63. package/dist/esm/event-client.js +0 -49
  64. package/dist/esm/event-client.js.map +1 -1
  65. package/dist/esm/index.d.ts +16 -14
  66. package/dist/esm/index.js +28 -19
  67. package/dist/esm/stream-to-response.d.ts +95 -0
  68. package/dist/esm/stream-to-response.js +118 -0
  69. package/dist/esm/stream-to-response.js.map +1 -0
  70. package/dist/esm/types.d.ts +347 -129
  71. package/package.json +6 -2
  72. package/src/activities/chat/adapter.ts +150 -0
  73. package/src/{utilities → activities/chat}/agent-loop-strategies.ts +4 -4
  74. package/src/{core/chat.ts → activities/chat/index.ts} +427 -79
  75. package/src/{message-converters.ts → activities/chat/messages.ts} +10 -13
  76. package/src/{stream → activities/chat/stream}/message-updaters.ts +1 -1
  77. package/src/{stream → activities/chat/stream}/processor.ts +2 -5
  78. package/src/{stream → activities/chat/stream}/types.ts +8 -18
  79. package/src/{tools → activities/chat/tools}/tool-calls.ts +36 -11
  80. package/src/{tools → activities/chat/tools}/tool-definition.ts +36 -27
  81. package/src/activities/chat/tools/zod-converter.ts +235 -0
  82. package/src/activities/generateImage/adapter.ts +104 -0
  83. package/src/activities/generateImage/index.ts +162 -0
  84. package/src/activities/generateSpeech/adapter.ts +87 -0
  85. package/src/activities/generateSpeech/index.ts +122 -0
  86. package/src/activities/generateTranscription/adapter.ts +89 -0
  87. package/src/activities/generateTranscription/index.ts +132 -0
  88. package/src/activities/generateVideo/adapter.ts +116 -0
  89. package/src/activities/generateVideo/index.ts +261 -0
  90. package/src/activities/index.ts +164 -0
  91. package/src/activities/summarize/adapter.ts +107 -0
  92. package/src/activities/summarize/index.ts +287 -0
  93. package/src/event-client.ts +5 -101
  94. package/src/index.ts +58 -15
  95. package/src/stream-to-response.ts +237 -0
  96. package/src/types.ts +404 -280
  97. package/dist/esm/base-adapter.d.ts +0 -36
  98. package/dist/esm/base-adapter.js +0 -12
  99. package/dist/esm/base-adapter.js.map +0 -1
  100. package/dist/esm/core/chat-common-options.d.ts +0 -52
  101. package/dist/esm/core/chat.d.ts +0 -30
  102. package/dist/esm/core/chat.js.map +0 -1
  103. package/dist/esm/core/embedding.d.ts +0 -8
  104. package/dist/esm/core/embedding.js +0 -33
  105. package/dist/esm/core/embedding.js.map +0 -1
  106. package/dist/esm/core/summarize.d.ts +0 -9
  107. package/dist/esm/core/summarize.js +0 -36
  108. package/dist/esm/core/summarize.js.map +0 -1
  109. package/dist/esm/message-converters.js.map +0 -1
  110. package/dist/esm/stream/json-parser.js.map +0 -1
  111. package/dist/esm/stream/message-updaters.js.map +0 -1
  112. package/dist/esm/stream/processor.js.map +0 -1
  113. package/dist/esm/stream/strategies.js.map +0 -1
  114. package/dist/esm/tools/tool-calls.js.map +0 -1
  115. package/dist/esm/tools/tool-definition.js.map +0 -1
  116. package/dist/esm/tools/zod-converter.d.ts +0 -30
  117. package/dist/esm/tools/zod-converter.js +0 -36
  118. package/dist/esm/tools/zod-converter.js.map +0 -1
  119. package/dist/esm/utilities/agent-loop-strategies.js.map +0 -1
  120. package/dist/esm/utilities/chat-options.d.ts +0 -6
  121. package/dist/esm/utilities/chat-options.js +0 -7
  122. package/dist/esm/utilities/chat-options.js.map +0 -1
  123. package/dist/esm/utilities/messages.d.ts +0 -30
  124. package/dist/esm/utilities/messages.js +0 -7
  125. package/dist/esm/utilities/messages.js.map +0 -1
  126. package/dist/esm/utilities/stream-to-response.d.ts +0 -48
  127. package/dist/esm/utilities/stream-to-response.js +0 -62
  128. package/dist/esm/utilities/stream-to-response.js.map +0 -1
  129. package/src/base-adapter.ts +0 -86
  130. package/src/core/chat-common-options.ts +0 -55
  131. package/src/core/embedding.ts +0 -54
  132. package/src/core/summarize.ts +0 -56
  133. package/src/tools/zod-converter.ts +0 -85
  134. package/src/utilities/chat-options.ts +0 -35
  135. package/src/utilities/messages.ts +0 -63
  136. package/src/utilities/stream-to-response.ts +0 -116
  137. /package/dist/esm/{utilities → activities/chat}/agent-loop-strategies.js +0 -0
  138. /package/dist/esm/{stream → activities/chat/stream}/index.d.ts +0 -0
  139. /package/dist/esm/{stream → activities/chat/stream}/json-parser.d.ts +0 -0
  140. /package/dist/esm/{stream → activities/chat/stream}/json-parser.js +0 -0
  141. /package/dist/esm/{stream → activities/chat/stream}/message-updaters.js +0 -0
  142. /package/dist/esm/{stream → activities/chat/stream}/strategies.d.ts +0 -0
  143. /package/dist/esm/{stream → activities/chat/stream}/strategies.js +0 -0
  144. /package/dist/esm/{tools → activities/chat/tools}/tool-definition.js +0 -0
  145. /package/src/{stream → activities/chat/stream}/index.ts +0 -0
  146. /package/src/{stream → activities/chat/stream}/json-parser.ts +0 -0
  147. /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 './types'
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 messages: Array<ModelMessage> = []
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 messages
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
- messages.push({
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
- messages.push({
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
- messages.push({
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 messages
140
+ return messageList
144
141
  }
145
142
 
146
143
  /**
@@ -10,7 +10,7 @@ import type {
10
10
  ToolCallPart,
11
11
  ToolResultPart,
12
12
  UIMessage,
13
- } from '../types'
13
+ } from '../../../types'
14
14
  import type { ToolCallState, ToolResultState } from './types'
15
15
 
16
16
  /**
@@ -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 '../types'
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 { StreamChunk, ToolCall } from '../types'
8
+ import type {
9
+ StreamChunk,
10
+ ToolCall,
11
+ ToolCallState,
12
+ ToolResultState,
13
+ } from '../../../types'
9
14
 
10
- /**
11
- * Tool call states - track the lifecycle of a tool call
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 '../types'
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 (tool.outputSchema && result !== undefined && result !== null) {
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 (tool.outputSchema && result !== undefined && result !== null) {
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 (tool.outputSchema && result !== undefined && result !== null) {
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 { Tool } from '../types'
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 z.ZodType = z.ZodType,
9
- TOutput extends z.ZodType = z.ZodType,
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 z.ZodType = z.ZodType,
20
- TOutput extends z.ZodType = z.ZodType,
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: z.infer<TInput>,
32
- ) => Promise<z.infer<TOutput>> | z.infer<TOutput>
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 z.ZodType = z.ZodType,
40
- TOutput extends z.ZodType = z.ZodType,
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
- : any
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
- : any
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 z.ZodType = z.ZodType,
81
- TOutput extends z.ZodType = z.ZodType,
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 z.ZodType = z.ZodType,
97
- TOutput extends z.ZodType = z.ZodType,
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: z.infer<TInput>,
106
- ) => Promise<z.infer<TOutput>> | z.infer<TOutput>,
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: z.infer<TInput>,
115
- ) => Promise<z.infer<TOutput>> | z.infer<TOutput>,
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 z.ZodType = z.ZodAny,
172
- TOutput extends z.ZodType = z.ZodAny,
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: z.infer<TInput>,
183
- ) => Promise<z.infer<TOutput>> | z.infer<TOutput>,
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: z.infer<TInput>,
195
- ) => Promise<z.infer<TOutput>> | z.infer<TOutput>,
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
+ }