@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.
- 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 +166 -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/activities/chat/tools/schema-converter.d.ts +116 -0
- package/dist/esm/activities/chat/tools/schema-converter.js +115 -0
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -0
- package/dist/esm/{tools → activities/chat/tools}/tool-calls.d.ts +1 -1
- package/dist/esm/{tools → activities/chat/tools}/tool-calls.js +23 -30
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -0
- package/dist/esm/{tools → activities/chat/tools}/tool-definition.d.ts +22 -18
- package/dist/esm/activities/chat/tools/tool-definition.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 +29 -20
- 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 +370 -133
- package/package.json +7 -6
- 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} +435 -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/activities/chat/tools/schema-converter.ts +332 -0
- package/src/{tools → activities/chat/tools}/tool-calls.ts +63 -44
- package/src/{tools → activities/chat/tools}/tool-definition.ts +51 -38
- 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 +60 -15
- package/src/stream-to-response.ts +237 -0
- package/src/types.ts +429 -284
- 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
|
|
@@ -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 '
|
|
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:
|
|
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
|
|
137
|
-
} catch (validationError:
|
|
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}: ${
|
|
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 (
|
|
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
|
|
151
|
-
} catch (validationError:
|
|
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}: ${
|
|
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:
|
|
174
|
+
} catch (error: unknown) {
|
|
161
175
|
// If tool execution fails, add error message
|
|
162
|
-
|
|
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:
|
|
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
|
|
289
|
-
} catch (validationError:
|
|
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}: ${
|
|
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 (
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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:
|
|
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:
|
|
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 (
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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:
|
|
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:
|
|
482
|
+
result: { error: message },
|
|
464
483
|
state: 'output-error',
|
|
465
484
|
duration,
|
|
466
485
|
})
|