@tanstack/ai 0.0.3 → 0.2.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 (146) 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 +166 -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/activities/chat/tools/schema-converter.d.ts +116 -0
  22. package/dist/esm/activities/chat/tools/schema-converter.js +115 -0
  23. package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -0
  24. package/dist/esm/{tools → activities/chat/tools}/tool-calls.d.ts +1 -1
  25. package/dist/esm/{tools → activities/chat/tools}/tool-calls.js +23 -30
  26. package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -0
  27. package/dist/esm/{tools → activities/chat/tools}/tool-definition.d.ts +22 -18
  28. package/dist/esm/activities/chat/tools/tool-definition.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 -18
  63. package/dist/esm/event-client.js.map +1 -1
  64. package/dist/esm/index.d.ts +16 -14
  65. package/dist/esm/index.js +29 -20
  66. package/dist/esm/stream-to-response.d.ts +95 -0
  67. package/dist/esm/stream-to-response.js +118 -0
  68. package/dist/esm/stream-to-response.js.map +1 -0
  69. package/dist/esm/types.d.ts +370 -133
  70. package/package.json +7 -6
  71. package/src/activities/chat/adapter.ts +150 -0
  72. package/src/{utilities → activities/chat}/agent-loop-strategies.ts +4 -4
  73. package/src/{core/chat.ts → activities/chat/index.ts} +435 -79
  74. package/src/{message-converters.ts → activities/chat/messages.ts} +10 -13
  75. package/src/{stream → activities/chat/stream}/message-updaters.ts +1 -1
  76. package/src/{stream → activities/chat/stream}/processor.ts +2 -5
  77. package/src/{stream → activities/chat/stream}/types.ts +8 -18
  78. package/src/activities/chat/tools/schema-converter.ts +332 -0
  79. package/src/{tools → activities/chat/tools}/tool-calls.ts +63 -44
  80. package/src/{tools → activities/chat/tools}/tool-definition.ts +51 -38
  81. package/src/activities/generateImage/adapter.ts +104 -0
  82. package/src/activities/generateImage/index.ts +162 -0
  83. package/src/activities/generateSpeech/adapter.ts +87 -0
  84. package/src/activities/generateSpeech/index.ts +122 -0
  85. package/src/activities/generateTranscription/adapter.ts +89 -0
  86. package/src/activities/generateTranscription/index.ts +132 -0
  87. package/src/activities/generateVideo/adapter.ts +116 -0
  88. package/src/activities/generateVideo/index.ts +261 -0
  89. package/src/activities/index.ts +164 -0
  90. package/src/activities/summarize/adapter.ts +107 -0
  91. package/src/activities/summarize/index.ts +287 -0
  92. package/src/event-client.ts +5 -21
  93. package/src/index.ts +60 -15
  94. package/src/stream-to-response.ts +237 -0
  95. package/src/types.ts +429 -284
  96. package/dist/esm/base-adapter.d.ts +0 -36
  97. package/dist/esm/base-adapter.js +0 -12
  98. package/dist/esm/base-adapter.js.map +0 -1
  99. package/dist/esm/core/chat-common-options.d.ts +0 -52
  100. package/dist/esm/core/chat.d.ts +0 -30
  101. package/dist/esm/core/chat.js.map +0 -1
  102. package/dist/esm/core/embedding.d.ts +0 -8
  103. package/dist/esm/core/embedding.js +0 -33
  104. package/dist/esm/core/embedding.js.map +0 -1
  105. package/dist/esm/core/summarize.d.ts +0 -9
  106. package/dist/esm/core/summarize.js +0 -36
  107. package/dist/esm/core/summarize.js.map +0 -1
  108. package/dist/esm/message-converters.js.map +0 -1
  109. package/dist/esm/stream/json-parser.js.map +0 -1
  110. package/dist/esm/stream/message-updaters.js.map +0 -1
  111. package/dist/esm/stream/processor.js.map +0 -1
  112. package/dist/esm/stream/strategies.js.map +0 -1
  113. package/dist/esm/tools/tool-calls.js.map +0 -1
  114. package/dist/esm/tools/tool-definition.js.map +0 -1
  115. package/dist/esm/tools/zod-converter.d.ts +0 -30
  116. package/dist/esm/tools/zod-converter.js +0 -36
  117. package/dist/esm/tools/zod-converter.js.map +0 -1
  118. package/dist/esm/utilities/agent-loop-strategies.js.map +0 -1
  119. package/dist/esm/utilities/chat-options.d.ts +0 -6
  120. package/dist/esm/utilities/chat-options.js +0 -7
  121. package/dist/esm/utilities/chat-options.js.map +0 -1
  122. package/dist/esm/utilities/messages.d.ts +0 -30
  123. package/dist/esm/utilities/messages.js +0 -7
  124. package/dist/esm/utilities/messages.js.map +0 -1
  125. package/dist/esm/utilities/stream-to-response.d.ts +0 -48
  126. package/dist/esm/utilities/stream-to-response.js +0 -62
  127. package/dist/esm/utilities/stream-to-response.js.map +0 -1
  128. package/src/base-adapter.ts +0 -86
  129. package/src/core/chat-common-options.ts +0 -55
  130. package/src/core/embedding.ts +0 -54
  131. package/src/core/summarize.ts +0 -56
  132. package/src/tools/zod-converter.ts +0 -85
  133. package/src/utilities/chat-options.ts +0 -35
  134. package/src/utilities/messages.ts +0 -63
  135. package/src/utilities/stream-to-response.ts +0 -116
  136. /package/dist/esm/{utilities → activities/chat}/agent-loop-strategies.js +0 -0
  137. /package/dist/esm/{stream → activities/chat/stream}/index.d.ts +0 -0
  138. /package/dist/esm/{stream → activities/chat/stream}/json-parser.d.ts +0 -0
  139. /package/dist/esm/{stream → activities/chat/stream}/json-parser.js +0 -0
  140. /package/dist/esm/{stream → activities/chat/stream}/message-updaters.js +0 -0
  141. /package/dist/esm/{stream → activities/chat/stream}/strategies.d.ts +0 -0
  142. /package/dist/esm/{stream → activities/chat/stream}/strategies.js +0 -0
  143. /package/dist/esm/{tools → activities/chat/tools}/tool-definition.js +0 -0
  144. /package/src/{stream → activities/chat/stream}/index.ts +0 -0
  145. /package/src/{stream → activities/chat/stream}/json-parser.ts +0 -0
  146. /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
@@ -0,0 +1,332 @@
1
+ /* eslint-disable @typescript-eslint/no-unnecessary-condition */
2
+
3
+ import type {
4
+ StandardJSONSchemaV1,
5
+ StandardSchemaV1,
6
+ } from '@standard-schema/spec'
7
+ import type { JSONSchema, SchemaInput } from '../../../types'
8
+
9
+ /**
10
+ * Check if a value is a Standard JSON Schema compliant schema.
11
+ * Standard JSON Schema compliant libraries (Zod v4+, ArkType, Valibot with toStandardJsonSchema, etc.)
12
+ * implement the '~standard' property with jsonSchema converter methods.
13
+ */
14
+ export function isStandardJSONSchema(
15
+ schema: unknown,
16
+ ): schema is StandardJSONSchemaV1 {
17
+ return (
18
+ typeof schema === 'object' &&
19
+ schema !== null &&
20
+ '~standard' in schema &&
21
+ typeof (schema as StandardJSONSchemaV1)['~standard'] === 'object' &&
22
+ (schema as StandardJSONSchemaV1)['~standard'].version === 1 &&
23
+ typeof (schema as StandardJSONSchemaV1)['~standard'].jsonSchema ===
24
+ 'object' &&
25
+ typeof (schema as StandardJSONSchemaV1)['~standard'].jsonSchema.input ===
26
+ 'function'
27
+ )
28
+ }
29
+
30
+ /**
31
+ * Check if a value is a Standard Schema compliant schema (for validation).
32
+ * Standard Schema compliant libraries implement the '~standard' property with a validate function.
33
+ */
34
+ export function isStandardSchema(schema: unknown): schema is StandardSchemaV1 {
35
+ return (
36
+ typeof schema === 'object' &&
37
+ schema !== null &&
38
+ '~standard' in schema &&
39
+ typeof schema['~standard'] === 'object' &&
40
+ schema !== null &&
41
+ schema['~standard'] !== null &&
42
+ 'version' in schema['~standard'] &&
43
+ schema['~standard'].version === 1 &&
44
+ 'validate' in schema['~standard'] &&
45
+ typeof schema['~standard'].validate === 'function'
46
+ )
47
+ }
48
+
49
+ /**
50
+ * Transform a JSON schema to be compatible with OpenAI's structured output requirements.
51
+ * OpenAI requires:
52
+ * - All properties must be in the `required` array
53
+ * - Optional fields should have null added to their type union
54
+ * - additionalProperties must be false for objects
55
+ *
56
+ * @param schema - JSON schema to transform
57
+ * @param originalRequired - Original required array (to know which fields were optional)
58
+ * @returns Transformed schema compatible with OpenAI structured output
59
+ */
60
+ function makeStructuredOutputCompatible(
61
+ schema: Record<string, any>,
62
+ originalRequired: Array<string> = [],
63
+ ): Record<string, any> {
64
+ const result = { ...schema }
65
+
66
+ // Handle object types
67
+ if (result.type === 'object' && result.properties) {
68
+ const properties = { ...result.properties }
69
+ const allPropertyNames = Object.keys(properties)
70
+
71
+ // Transform each property
72
+ for (const propName of allPropertyNames) {
73
+ const prop = properties[propName]
74
+ const wasOptional = !originalRequired.includes(propName)
75
+
76
+ // Recursively transform nested objects/arrays
77
+ if (prop.type === 'object' && prop.properties) {
78
+ properties[propName] = makeStructuredOutputCompatible(
79
+ prop,
80
+ prop.required || [],
81
+ )
82
+ } else if (prop.type === 'array' && prop.items) {
83
+ properties[propName] = {
84
+ ...prop,
85
+ items: makeStructuredOutputCompatible(
86
+ prop.items,
87
+ prop.items.required || [],
88
+ ),
89
+ }
90
+ } else if (wasOptional) {
91
+ // Make optional fields nullable by adding null to the type
92
+ if (prop.type && !Array.isArray(prop.type)) {
93
+ properties[propName] = {
94
+ ...prop,
95
+ type: [prop.type, 'null'],
96
+ }
97
+ } else if (Array.isArray(prop.type) && !prop.type.includes('null')) {
98
+ properties[propName] = {
99
+ ...prop,
100
+ type: [...prop.type, 'null'],
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ result.properties = properties
107
+ // ALL properties must be required for OpenAI structured output
108
+ result.required = allPropertyNames
109
+ // additionalProperties must be false
110
+ result.additionalProperties = false
111
+ }
112
+
113
+ // Handle array types with object items
114
+ if (result.type === 'array' && result.items) {
115
+ result.items = makeStructuredOutputCompatible(
116
+ result.items,
117
+ result.items.required || [],
118
+ )
119
+ }
120
+
121
+ return result
122
+ }
123
+
124
+ /**
125
+ * Options for schema conversion
126
+ */
127
+ export interface ConvertSchemaOptions {
128
+ /**
129
+ * When true, transforms the schema to be compatible with OpenAI's structured output requirements:
130
+ * - All properties are added to the `required` array
131
+ * - Optional fields get null added to their type union
132
+ * - additionalProperties is set to false for all objects
133
+ *
134
+ * @default false
135
+ */
136
+ forStructuredOutput?: boolean
137
+ }
138
+
139
+ /**
140
+ * Converts a Standard JSON Schema compliant schema or plain JSONSchema to JSON Schema format
141
+ * compatible with LLM providers.
142
+ *
143
+ * Supports any schema library that implements the Standard JSON Schema spec (v1):
144
+ * - Zod v4+ (natively supports StandardJSONSchemaV1)
145
+ * - ArkType (natively supports StandardJSONSchemaV1)
146
+ * - Valibot (via `toStandardJsonSchema()` from `@valibot/to-json-schema`)
147
+ *
148
+ * If the input is already a plain JSONSchema object, it is returned as-is.
149
+ *
150
+ * @param schema - Standard JSON Schema compliant schema or plain JSONSchema object to convert
151
+ * @param options - Conversion options
152
+ * @returns JSON Schema object that can be sent to LLM providers
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * // Using Zod v4+ (natively supports Standard JSON Schema)
157
+ * import * as z from 'zod';
158
+ *
159
+ * const zodSchema = z.object({
160
+ * location: z.string().describe('City name'),
161
+ * unit: z.enum(['celsius', 'fahrenheit']).optional()
162
+ * });
163
+ *
164
+ * const jsonSchema = convertSchemaToJsonSchema(zodSchema);
165
+ *
166
+ * @example
167
+ * // Using ArkType (natively supports Standard JSON Schema)
168
+ * import { type } from 'arktype';
169
+ *
170
+ * const arkSchema = type({
171
+ * location: 'string',
172
+ * unit: "'celsius' | 'fahrenheit'"
173
+ * });
174
+ *
175
+ * const jsonSchema = convertSchemaToJsonSchema(arkSchema);
176
+ *
177
+ * @example
178
+ * // Using Valibot (via toStandardJsonSchema)
179
+ * import * as v from 'valibot';
180
+ * import { toStandardJsonSchema } from '@valibot/to-json-schema';
181
+ *
182
+ * const valibotSchema = toStandardJsonSchema(v.object({
183
+ * location: v.string(),
184
+ * unit: v.optional(v.picklist(['celsius', 'fahrenheit']))
185
+ * }));
186
+ *
187
+ * const jsonSchema = convertSchemaToJsonSchema(valibotSchema);
188
+ *
189
+ * @example
190
+ * // Using JSONSchema directly (passes through unchanged)
191
+ * const rawSchema = {
192
+ * type: 'object',
193
+ * properties: { location: { type: 'string' } },
194
+ * required: ['location']
195
+ * };
196
+ * const result = convertSchemaToJsonSchema(rawSchema);
197
+ * ```
198
+ */
199
+ export function convertSchemaToJsonSchema(
200
+ schema: SchemaInput | undefined,
201
+ options: ConvertSchemaOptions = {},
202
+ ): JSONSchema | undefined {
203
+ if (!schema) return undefined
204
+
205
+ const { forStructuredOutput = false } = options
206
+
207
+ // If it's a Standard JSON Schema compliant schema, use the standard interface
208
+ if (isStandardJSONSchema(schema)) {
209
+ const jsonSchema = schema['~standard'].jsonSchema.input({
210
+ target: 'draft-07',
211
+ })
212
+
213
+ let result = jsonSchema
214
+
215
+ if (typeof result === 'object' && '$schema' in result) {
216
+ // Remove $schema property as it's not needed for LLM providers
217
+ const { $schema, ...rest } = result
218
+ result = rest
219
+ }
220
+
221
+ // Ensure object schemas always have type: "object"
222
+
223
+ if (typeof result === 'object') {
224
+ // If it has properties (even empty), it should be an object type
225
+ if ('properties' in result && !result.type) {
226
+ result.type = 'object'
227
+ }
228
+
229
+ // Ensure properties exists for object types (even if empty)
230
+ if (result.type === 'object' && !('properties' in result)) {
231
+ result.properties = {}
232
+ }
233
+
234
+ // Ensure required exists for object types (even if empty array)
235
+ if (result.type === 'object' && !('required' in result)) {
236
+ result.required = []
237
+ }
238
+
239
+ // Apply structured output transformation if requested
240
+ if (forStructuredOutput) {
241
+ result = makeStructuredOutputCompatible(
242
+ result,
243
+ (result.required as Array<string>) || [],
244
+ )
245
+ }
246
+ }
247
+
248
+ return result as JSONSchema
249
+ }
250
+
251
+ // If it's not a Standard JSON Schema, assume it's already a JSONSchema and pass through
252
+ // Still apply structured output transformation if requested
253
+
254
+ if (forStructuredOutput && typeof schema === 'object') {
255
+ return makeStructuredOutputCompatible(
256
+ schema as Record<string, any>,
257
+ ((schema as JSONSchema).required as Array<string>) || [],
258
+ ) as JSONSchema
259
+ }
260
+
261
+ return schema as JSONSchema
262
+ }
263
+
264
+ /**
265
+ * Validates data against a Standard Schema compliant schema.
266
+ *
267
+ * @param schema - Standard Schema compliant schema
268
+ * @param data - Data to validate
269
+ * @returns Validation result with success status, data or issues
270
+ */
271
+ export async function validateWithStandardSchema<T>(
272
+ schema: unknown,
273
+ data: unknown,
274
+ ): Promise<
275
+ | { success: true; data: T }
276
+ | { success: false; issues: Array<{ message: string; path?: Array<string> }> }
277
+ > {
278
+ if (!isStandardSchema(schema)) {
279
+ // If it's not a Standard Schema, just return the data as-is
280
+ return { success: true, data: data as T }
281
+ }
282
+
283
+ const result = await schema['~standard'].validate(data)
284
+
285
+ if (!result.issues) {
286
+ return { success: true, data: result.value as T }
287
+ }
288
+
289
+ return {
290
+ success: false,
291
+ issues: result.issues.map((issue) => ({
292
+ message: issue.message || 'Validation failed',
293
+ path: issue.path?.map(String),
294
+ })),
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Synchronously validates data against a Standard Schema compliant schema.
300
+ * Note: Some Standard Schema implementations may only support async validation.
301
+ * In those cases, this function will throw.
302
+ *
303
+ * @param schema - Standard Schema compliant schema
304
+ * @param data - Data to validate
305
+ * @returns Parsed/validated data
306
+ * @throws Error if validation fails or if the schema only supports async validation
307
+ */
308
+ export function parseWithStandardSchema<T>(schema: unknown, data: unknown): T {
309
+ if (!isStandardSchema(schema)) {
310
+ // If it's not a Standard Schema, just return the data as-is
311
+ return data as T
312
+ }
313
+
314
+ const result = schema['~standard'].validate(data)
315
+
316
+ // Handle async result (Promise)
317
+ if (result instanceof Promise) {
318
+ throw new Error(
319
+ 'Schema validation returned a Promise. Use validateWithStandardSchema for async validation.',
320
+ )
321
+ }
322
+ // Standard Schema validation returns { value } for success or { issues } for failure
323
+ if (!result.issues) {
324
+ return result.value as T
325
+ }
326
+
327
+ // invalid validation, throw error with all issues
328
+ const errorMessages = result.issues
329
+ .map((issue) => issue.message || 'Validation failed')
330
+ .join(', ')
331
+ throw new Error(`Validation failed: ${errorMessages}`)
332
+ }
@@ -1,10 +1,11 @@
1
+ import { isStandardSchema, parseWithStandardSchema } from './schema-converter'
1
2
  import type {
2
3
  DoneStreamChunk,
3
4
  ModelMessage,
4
5
  Tool,
5
6
  ToolCall,
6
7
  ToolResultStreamChunk,
7
- } from '../types'
8
+ } from '../../../types'
8
9
 
9
10
  /**
10
11
  * Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.
@@ -121,7 +122,7 @@ export class ToolCallManager {
121
122
  if (tool?.execute) {
122
123
  try {
123
124
  // Parse arguments
124
- let args: any
125
+ let args: unknown
125
126
  try {
126
127
  args = JSON.parse(toolCall.function.arguments)
127
128
  } catch (parseError) {
@@ -130,13 +131,17 @@ export class ToolCallManager {
130
131
  )
131
132
  }
132
133
 
133
- // Validate input against inputSchema
134
- if (tool.inputSchema) {
134
+ // Validate input against inputSchema (for Standard Schema compliant schemas)
135
+ if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {
135
136
  try {
136
- args = tool.inputSchema.parse(args)
137
- } catch (validationError: any) {
137
+ args = parseWithStandardSchema(tool.inputSchema, args)
138
+ } catch (validationError: unknown) {
139
+ const message =
140
+ validationError instanceof Error
141
+ ? validationError.message
142
+ : 'Validation failed'
138
143
  throw new Error(
139
- `Input validation failed for tool ${tool.name}: ${validationError.message}`,
144
+ `Input validation failed for tool ${tool.name}: ${message}`,
140
145
  )
141
146
  }
142
147
  }
@@ -144,22 +149,33 @@ export class ToolCallManager {
144
149
  // Execute the tool
145
150
  let result = await tool.execute(args)
146
151
 
147
- // Validate output against outputSchema if provided
148
- if (tool.outputSchema && result !== undefined && result !== null) {
152
+ // Validate output against outputSchema if provided (for Standard Schema compliant schemas)
153
+ if (
154
+ tool.outputSchema &&
155
+ isStandardSchema(tool.outputSchema) &&
156
+ result !== undefined &&
157
+ result !== null
158
+ ) {
149
159
  try {
150
- result = tool.outputSchema.parse(result)
151
- } catch (validationError: any) {
160
+ result = parseWithStandardSchema(tool.outputSchema, result)
161
+ } catch (validationError: unknown) {
162
+ const message =
163
+ validationError instanceof Error
164
+ ? validationError.message
165
+ : 'Validation failed'
152
166
  throw new Error(
153
- `Output validation failed for tool ${tool.name}: ${validationError.message}`,
167
+ `Output validation failed for tool ${tool.name}: ${message}`,
154
168
  )
155
169
  }
156
170
  }
157
171
 
158
172
  toolResultContent =
159
173
  typeof result === 'string' ? result : JSON.stringify(result)
160
- } catch (error: any) {
174
+ } catch (error: unknown) {
161
175
  // If tool execution fails, add error message
162
- toolResultContent = `Error executing tool: ${error.message}`
176
+ const message =
177
+ error instanceof Error ? error.message : 'Unknown error'
178
+ toolResultContent = `Error executing tool: ${message}`
163
179
  }
164
180
  } else {
165
181
  // Tool doesn't have execute function, add placeholder
@@ -271,7 +287,7 @@ export async function executeToolCalls(
271
287
  }
272
288
 
273
289
  // Parse arguments, throwing error if invalid JSON
274
- let input: any = {}
290
+ let input: unknown = {}
275
291
  const argsStr = toolCall.function.arguments.trim() || '{}'
276
292
  if (argsStr) {
277
293
  try {
@@ -282,16 +298,20 @@ export async function executeToolCalls(
282
298
  }
283
299
  }
284
300
 
285
- // Validate input against inputSchema
286
- if (tool.inputSchema) {
301
+ // Validate input against inputSchema (for Standard Schema compliant schemas)
302
+ if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {
287
303
  try {
288
- input = tool.inputSchema.parse(input)
289
- } catch (validationError: any) {
304
+ input = parseWithStandardSchema(tool.inputSchema, input)
305
+ } catch (validationError: unknown) {
306
+ const message =
307
+ validationError instanceof Error
308
+ ? validationError.message
309
+ : 'Validation failed'
290
310
  results.push({
291
311
  toolCallId: toolCall.id,
292
312
  toolName,
293
313
  result: {
294
- error: `Input validation failed for tool ${tool.name}: ${validationError.message}`,
314
+ error: `Input validation failed for tool ${tool.name}: ${message}`,
295
315
  },
296
316
  state: 'output-error',
297
317
  })
@@ -378,16 +398,14 @@ export async function executeToolCalls(
378
398
  let result = await tool.execute(input)
379
399
  const duration = Date.now() - startTime
380
400
 
381
- // Validate output against outputSchema if provided
382
- if (tool.outputSchema && result !== undefined && result !== null) {
383
- const parsed = tool.outputSchema.safeParse(result)
384
- if (parsed.success) {
385
- result = parsed.data
386
- } else {
387
- throw new Error(
388
- `Output validation failed for tool ${tool.name}: ${parsed.error.message}`,
389
- )
390
- }
401
+ // Validate output against outputSchema if provided (for Standard Schema compliant schemas)
402
+ if (
403
+ tool.outputSchema &&
404
+ isStandardSchema(tool.outputSchema) &&
405
+ result !== undefined &&
406
+ result !== null
407
+ ) {
408
+ result = parseWithStandardSchema(tool.outputSchema, result)
391
409
  }
392
410
 
393
411
  results.push({
@@ -399,12 +417,14 @@ export async function executeToolCalls(
399
417
  : result || null,
400
418
  duration,
401
419
  })
402
- } catch (error: any) {
420
+ } catch (error: unknown) {
403
421
  const duration = Date.now() - startTime
422
+ const message =
423
+ error instanceof Error ? error.message : 'Unknown error'
404
424
  results.push({
405
425
  toolCallId: toolCall.id,
406
426
  toolName,
407
- result: { error: error.message },
427
+ result: { error: message },
408
428
  state: 'output-error',
409
429
  duration,
410
430
  })
@@ -436,16 +456,14 @@ 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) {
441
- const parsed = tool.outputSchema.safeParse(result)
442
- if (parsed.success) {
443
- result = parsed.data
444
- } else {
445
- throw new Error(
446
- `Output validation failed for tool ${tool.name}: ${parsed.error.message}`,
447
- )
448
- }
459
+ // Validate output against outputSchema if provided (for Standard Schema compliant schemas)
460
+ if (
461
+ tool.outputSchema &&
462
+ isStandardSchema(tool.outputSchema) &&
463
+ result !== undefined &&
464
+ result !== null
465
+ ) {
466
+ result = parseWithStandardSchema(tool.outputSchema, result)
449
467
  }
450
468
 
451
469
  results.push({
@@ -455,12 +473,13 @@ export async function executeToolCalls(
455
473
  typeof result === 'string' ? JSON.parse(result) : result || null,
456
474
  duration,
457
475
  })
458
- } catch (error: any) {
476
+ } catch (error: unknown) {
459
477
  const duration = Date.now() - startTime
478
+ const message = error instanceof Error ? error.message : 'Unknown error'
460
479
  results.push({
461
480
  toolCallId: toolCall.id,
462
481
  toolName,
463
- result: { error: error.message },
482
+ result: { error: message },
464
483
  state: 'output-error',
465
484
  duration,
466
485
  })