@tanstack/ai 0.23.1 → 0.24.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/dist/esm/activities/chat/index.d.ts +33 -9
- package/dist/esm/activities/chat/index.js +19 -9
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +2 -1
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +14 -14
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +16 -16
- package/dist/esm/activities/chat/runtime-context-types.d.ts +43 -0
- package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +1 -0
- package/dist/esm/activities/chat/stream/processor.js +35 -12
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +15 -5
- package/dist/esm/activities/chat/tools/tool-calls.js +59 -19
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +12 -8
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/error-payload.d.ts +26 -0
- package/dist/esm/activities/error-payload.js +12 -1
- package/dist/esm/activities/error-payload.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +1 -1
- package/dist/esm/adapter-internals.js +3 -2
- package/dist/esm/client.d.ts +1 -1
- package/dist/esm/client.js +3 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +7 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tool-registry.d.ts +7 -7
- package/dist/esm/tool-registry.js +1 -1
- package/dist/esm/tool-registry.js.map +1 -1
- package/dist/esm/types.d.ts +40 -8
- package/dist/esm/utilities/ag-ui-wire.js +1 -1
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.d.ts +8 -3
- package/dist/esm/utilities/chat-params.js +6 -2
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/dist/esm/utilities/tool-result.d.ts +21 -0
- package/dist/esm/utilities/tool-result.js +37 -0
- package/dist/esm/utilities/tool-result.js.map +1 -0
- package/package.json +2 -2
- package/src/activities/chat/index.ts +219 -47
- package/src/activities/chat/messages.ts +2 -1
- package/src/activities/chat/middleware/compose.ts +23 -17
- package/src/activities/chat/middleware/types.ts +16 -16
- package/src/activities/chat/runtime-context-types.ts +68 -0
- package/src/activities/chat/stream/message-updaters.ts +2 -1
- package/src/activities/chat/stream/processor.ts +48 -8
- package/src/activities/chat/tools/tool-calls.ts +138 -43
- package/src/activities/chat/tools/tool-definition.ts +25 -31
- package/src/activities/error-payload.ts +44 -0
- package/src/adapter-internals.ts +4 -1
- package/src/client.ts +5 -1
- package/src/index.ts +7 -0
- package/src/tool-registry.ts +16 -14
- package/src/types.ts +86 -29
- package/src/utilities/ag-ui-wire.ts +4 -1
- package/src/utilities/chat-params.ts +22 -7
- package/src/utilities/tool-result.ts +60 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import type { StandardJSONSchemaV1 } from '@standard-schema/spec'
|
|
2
2
|
import type {
|
|
3
|
-
InferSchemaType,
|
|
4
3
|
JSONSchema,
|
|
5
4
|
SchemaInput,
|
|
6
5
|
Tool,
|
|
7
|
-
|
|
6
|
+
ToolExecuteFunction,
|
|
8
7
|
} from '../../../types'
|
|
9
8
|
|
|
10
9
|
/**
|
|
@@ -14,7 +13,8 @@ export interface ServerTool<
|
|
|
14
13
|
TInput extends SchemaInput = SchemaInput,
|
|
15
14
|
TOutput extends SchemaInput = SchemaInput,
|
|
16
15
|
TName extends string = string,
|
|
17
|
-
|
|
16
|
+
TContext = unknown,
|
|
17
|
+
> extends Tool<TInput, TOutput, TName, TContext> {
|
|
18
18
|
__toolSide: 'server'
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -25,6 +25,7 @@ export interface ClientTool<
|
|
|
25
25
|
TInput extends SchemaInput = SchemaInput,
|
|
26
26
|
TOutput extends SchemaInput = SchemaInput,
|
|
27
27
|
TName extends string = string,
|
|
28
|
+
TContext = unknown,
|
|
28
29
|
> {
|
|
29
30
|
__toolSide: 'client'
|
|
30
31
|
name: TName
|
|
@@ -39,9 +40,7 @@ export interface ClientTool<
|
|
|
39
40
|
needsApproval?: boolean
|
|
40
41
|
lazy?: boolean
|
|
41
42
|
metadata?: Record<string, unknown>
|
|
42
|
-
execute?:
|
|
43
|
-
args: InferSchemaType<TInput>,
|
|
44
|
-
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>
|
|
43
|
+
execute?: ToolExecuteFunction<TInput, TOutput, TContext>
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
/**
|
|
@@ -51,7 +50,8 @@ export interface ToolDefinitionInstance<
|
|
|
51
50
|
TInput extends SchemaInput = SchemaInput,
|
|
52
51
|
TOutput extends SchemaInput = SchemaInput,
|
|
53
52
|
TName extends string = string,
|
|
54
|
-
|
|
53
|
+
TContext = unknown,
|
|
54
|
+
> extends Tool<TInput, TOutput, TName, TContext> {
|
|
55
55
|
__toolSide: 'definition'
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -59,8 +59,12 @@ export interface ToolDefinitionInstance<
|
|
|
59
59
|
* Union type for any kind of client-side tool (client tool or definition)
|
|
60
60
|
*/
|
|
61
61
|
export type AnyClientTool =
|
|
62
|
-
| ClientTool<
|
|
63
|
-
|
|
62
|
+
| (Omit<ClientTool<any, any, string, any>, 'execute'> & {
|
|
63
|
+
execute?: ((args: any, context?: any) => any) | undefined
|
|
64
|
+
})
|
|
65
|
+
| (Omit<ToolDefinitionInstance<any, any, string, any>, 'execute'> & {
|
|
66
|
+
execute?: ((args: any, context?: any) => any) | undefined
|
|
67
|
+
})
|
|
64
68
|
|
|
65
69
|
/**
|
|
66
70
|
* Extract the tool name as a literal type
|
|
@@ -117,21 +121,16 @@ export interface ToolDefinition<
|
|
|
117
121
|
/**
|
|
118
122
|
* Create a server-side tool with execute function
|
|
119
123
|
*/
|
|
120
|
-
server: (
|
|
121
|
-
execute:
|
|
122
|
-
|
|
123
|
-
context?: ToolExecutionContext,
|
|
124
|
-
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
125
|
-
) => ServerTool<TInput, TOutput, TName>
|
|
124
|
+
server: <TContext = unknown>(
|
|
125
|
+
execute: ToolExecuteFunction<TInput, TOutput, TContext>,
|
|
126
|
+
) => ServerTool<TInput, TOutput, TName, TContext>
|
|
126
127
|
|
|
127
128
|
/**
|
|
128
129
|
* Create a client-side tool with optional execute function
|
|
129
130
|
*/
|
|
130
|
-
client: (
|
|
131
|
-
execute?:
|
|
132
|
-
|
|
133
|
-
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
134
|
-
) => ClientTool<TInput, TOutput, TName>
|
|
131
|
+
client: <TContext = unknown>(
|
|
132
|
+
execute?: ToolExecuteFunction<TInput, TOutput, TContext>,
|
|
133
|
+
) => ClientTool<TInput, TOutput, TName, TContext>
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
/**
|
|
@@ -199,12 +198,9 @@ export function toolDefinition<
|
|
|
199
198
|
const definition: ToolDefinition<TInput, TOutput, TName> = {
|
|
200
199
|
__toolSide: 'definition',
|
|
201
200
|
...config,
|
|
202
|
-
server(
|
|
203
|
-
execute:
|
|
204
|
-
|
|
205
|
-
context?: ToolExecutionContext,
|
|
206
|
-
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
207
|
-
): ServerTool<TInput, TOutput, TName> {
|
|
201
|
+
server<TContext = unknown>(
|
|
202
|
+
execute: ToolExecuteFunction<TInput, TOutput, TContext>,
|
|
203
|
+
): ServerTool<TInput, TOutput, TName, TContext> {
|
|
208
204
|
return {
|
|
209
205
|
__toolSide: 'server',
|
|
210
206
|
...config,
|
|
@@ -212,11 +208,9 @@ export function toolDefinition<
|
|
|
212
208
|
}
|
|
213
209
|
},
|
|
214
210
|
|
|
215
|
-
client(
|
|
216
|
-
execute?:
|
|
217
|
-
|
|
218
|
-
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
219
|
-
): ClientTool<TInput, TOutput, TName> {
|
|
211
|
+
client<TContext = unknown>(
|
|
212
|
+
execute?: ToolExecuteFunction<TInput, TOutput, TContext>,
|
|
213
|
+
): ClientTool<TInput, TOutput, TName, TContext> {
|
|
220
214
|
return {
|
|
221
215
|
__toolSide: 'client',
|
|
222
216
|
...config,
|
|
@@ -62,3 +62,47 @@ export function toRunErrorPayload(
|
|
|
62
62
|
}
|
|
63
63
|
return { message: fallbackMessage, code: undefined }
|
|
64
64
|
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Extract the provider's *structured error body* from a thrown value, to attach
|
|
68
|
+
* as the AG-UI `rawEvent` on a RUN_ERROR event. This is the recoverable upstream
|
|
69
|
+
* detail (provider name, the upstream model's error JSON, rate-limit/overload
|
|
70
|
+
* codes, etc.) that `toRunErrorPayload`'s `{ message, code }` deliberately drops.
|
|
71
|
+
*
|
|
72
|
+
* Security boundary: only known provider-response-body fields are forwarded —
|
|
73
|
+
* never the raw SDK exception object, which can carry request metadata such as
|
|
74
|
+
* auth headers or request ids. The recognized sources, in priority order:
|
|
75
|
+
*
|
|
76
|
+
* - `error.rawEvent` — a provider body an adapter attached explicitly (e.g. the
|
|
77
|
+
* OpenRouter mid-stream `chunk.error`).
|
|
78
|
+
* - `error.error` (object) — the parsed provider response body exposed by SDK
|
|
79
|
+
* `APIError` instances (OpenAI/Anthropic `{ type, message, code, param }`,
|
|
80
|
+
* OpenRouter typed errors whose `.error` carries `.metadata`). This is
|
|
81
|
+
* provider-shaped data, distinct from `.headers` / `.request_id`.
|
|
82
|
+
* - `error.metadata` — OpenRouter's `provider_name` + raw upstream body, when
|
|
83
|
+
* surfaced directly on the thrown error.
|
|
84
|
+
*
|
|
85
|
+
* Returns `undefined` when no structured provider body is present, so callers
|
|
86
|
+
* omit the field entirely rather than setting it to `null`:
|
|
87
|
+
*
|
|
88
|
+
* const rawEvent = toRunErrorRawEvent(error)
|
|
89
|
+
* yield { type: EventType.RUN_ERROR, ..., ...(rawEvent !== undefined && { rawEvent }) }
|
|
90
|
+
*/
|
|
91
|
+
export function toRunErrorRawEvent(error: unknown): unknown {
|
|
92
|
+
if (!error || typeof error !== 'object') return undefined
|
|
93
|
+
const e = error as {
|
|
94
|
+
rawEvent?: unknown
|
|
95
|
+
error?: unknown
|
|
96
|
+
metadata?: unknown
|
|
97
|
+
}
|
|
98
|
+
if (e.rawEvent !== undefined && e.rawEvent !== null) return e.rawEvent
|
|
99
|
+
if (
|
|
100
|
+
e.error !== undefined &&
|
|
101
|
+
e.error !== null &&
|
|
102
|
+
typeof e.error === 'object'
|
|
103
|
+
) {
|
|
104
|
+
return e.error
|
|
105
|
+
}
|
|
106
|
+
if (e.metadata !== undefined && e.metadata !== null) return e.metadata
|
|
107
|
+
return undefined
|
|
108
|
+
}
|
package/src/adapter-internals.ts
CHANGED
|
@@ -6,4 +6,7 @@ export type { ResolvedCategories } from './logger/internal-logger'
|
|
|
6
6
|
export { InternalLogger } from './logger/internal-logger'
|
|
7
7
|
export type { Logger } from './logger/types'
|
|
8
8
|
export { resolveDebugOption } from './logger/resolve'
|
|
9
|
-
export {
|
|
9
|
+
export {
|
|
10
|
+
toRunErrorPayload,
|
|
11
|
+
toRunErrorRawEvent,
|
|
12
|
+
} from './activities/error-payload'
|
package/src/client.ts
CHANGED
|
@@ -46,7 +46,11 @@ export {
|
|
|
46
46
|
type ToolDefinitionInstance,
|
|
47
47
|
} from './activities/chat/tools/tool-definition'
|
|
48
48
|
|
|
49
|
-
export {
|
|
49
|
+
export {
|
|
50
|
+
convertSchemaToJsonSchema,
|
|
51
|
+
isStandardSchema,
|
|
52
|
+
parseWithStandardSchema,
|
|
53
|
+
} from './activities/chat/tools/schema-converter'
|
|
50
54
|
|
|
51
55
|
export {
|
|
52
56
|
convertMessagesToModelMessages,
|
package/src/index.ts
CHANGED
|
@@ -55,6 +55,8 @@ export {
|
|
|
55
55
|
// Schema conversion (Standard JSON Schema compliant)
|
|
56
56
|
export {
|
|
57
57
|
convertSchemaToJsonSchema,
|
|
58
|
+
isStandardSchema,
|
|
59
|
+
parseWithStandardSchema,
|
|
58
60
|
StandardSchemaValidationError,
|
|
59
61
|
} from './activities/chat/tools/schema-converter'
|
|
60
62
|
|
|
@@ -187,6 +189,11 @@ export {
|
|
|
187
189
|
// AG-UI wire serialization (used internally by @tanstack/ai-client)
|
|
188
190
|
export { uiMessagesToWire } from './utilities/ag-ui-wire'
|
|
189
191
|
export type { WireMessage } from './utilities/ag-ui-wire'
|
|
192
|
+
export {
|
|
193
|
+
isContentPart,
|
|
194
|
+
isContentPartArray,
|
|
195
|
+
normalizeToolResult,
|
|
196
|
+
} from './utilities/tool-result'
|
|
190
197
|
|
|
191
198
|
// Adapter extension utilities
|
|
192
199
|
export { createModel, extendAdapter } from './extend-adapter'
|
package/src/tool-registry.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AnyTool } from './types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A registry that holds tools and allows dynamic tool management.
|
|
@@ -6,12 +6,12 @@ import type { Tool } from './types'
|
|
|
6
6
|
* The registry can be either mutable (allowing additions/removals during execution)
|
|
7
7
|
* or frozen (static tool list, for backward compatibility with tools arrays).
|
|
8
8
|
*/
|
|
9
|
-
export interface ToolRegistry {
|
|
9
|
+
export interface ToolRegistry<TTool extends AnyTool = AnyTool> {
|
|
10
10
|
/**
|
|
11
11
|
* Get all current tools in the registry.
|
|
12
12
|
* Called each agent loop iteration to get the latest tool list.
|
|
13
13
|
*/
|
|
14
|
-
getTools: () =>
|
|
14
|
+
getTools: () => Array<TTool>
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Add a tool to the registry dynamically.
|
|
@@ -19,7 +19,7 @@ export interface ToolRegistry {
|
|
|
19
19
|
*
|
|
20
20
|
* @param tool - The tool to add
|
|
21
21
|
*/
|
|
22
|
-
add: (tool:
|
|
22
|
+
add: (tool: TTool) => void
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Remove a tool from the registry by name.
|
|
@@ -43,7 +43,7 @@ export interface ToolRegistry {
|
|
|
43
43
|
* @param name - The name of the tool to get
|
|
44
44
|
* @returns The tool if found, undefined otherwise
|
|
45
45
|
*/
|
|
46
|
-
get: (name: string) =>
|
|
46
|
+
get: (name: string) => TTool | undefined
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* Whether this registry is frozen (immutable).
|
|
@@ -75,10 +75,10 @@ export interface ToolRegistry {
|
|
|
75
75
|
* registry.add(newTool) // Immediately available to LLM
|
|
76
76
|
* ```
|
|
77
77
|
*/
|
|
78
|
-
export function createToolRegistry(
|
|
79
|
-
initialTools: Array<
|
|
80
|
-
): ToolRegistry {
|
|
81
|
-
const tools = new Map<string,
|
|
78
|
+
export function createToolRegistry<TTool extends AnyTool = AnyTool>(
|
|
79
|
+
initialTools: Array<TTool> = [],
|
|
80
|
+
): ToolRegistry<TTool> {
|
|
81
|
+
const tools = new Map<string, TTool>()
|
|
82
82
|
|
|
83
83
|
for (const tool of initialTools) {
|
|
84
84
|
tools.set(tool.name, tool)
|
|
@@ -87,7 +87,7 @@ export function createToolRegistry(
|
|
|
87
87
|
return {
|
|
88
88
|
getTools: () => Array.from(tools.values()),
|
|
89
89
|
|
|
90
|
-
add: (tool:
|
|
90
|
+
add: (tool: TTool) => {
|
|
91
91
|
tools.set(tool.name, tool)
|
|
92
92
|
},
|
|
93
93
|
|
|
@@ -116,8 +116,10 @@ export function createToolRegistry(
|
|
|
116
116
|
* @param tools - The static array of tools
|
|
117
117
|
* @returns A frozen ToolRegistry
|
|
118
118
|
*/
|
|
119
|
-
export function createFrozenRegistry
|
|
120
|
-
|
|
119
|
+
export function createFrozenRegistry<TTool extends AnyTool = AnyTool>(
|
|
120
|
+
tools: Array<TTool> = [],
|
|
121
|
+
): ToolRegistry<TTool> {
|
|
122
|
+
const toolMap = new Map<string, TTool>()
|
|
121
123
|
|
|
122
124
|
for (const tool of tools) {
|
|
123
125
|
toolMap.set(tool.name, tool)
|
|
@@ -126,9 +128,9 @@ export function createFrozenRegistry(tools: Array<Tool> = []): ToolRegistry {
|
|
|
126
128
|
const frozenTools = Object.freeze([...tools])
|
|
127
129
|
|
|
128
130
|
return {
|
|
129
|
-
getTools: () => frozenTools,
|
|
131
|
+
getTools: () => [...frozenTools],
|
|
130
132
|
|
|
131
|
-
add: (_tool:
|
|
133
|
+
add: (_tool: TTool) => {
|
|
132
134
|
// No-op for frozen registry
|
|
133
135
|
},
|
|
134
136
|
|
package/src/types.ts
CHANGED
|
@@ -50,6 +50,8 @@ export type ToolResultState =
|
|
|
50
50
|
| 'complete' // Result is complete
|
|
51
51
|
| 'error' // Error occurred
|
|
52
52
|
|
|
53
|
+
export type ToolOutputState = 'output-available' | 'output-error'
|
|
54
|
+
|
|
53
55
|
/**
|
|
54
56
|
* JSON Schema type for defining tool input/output schemas as raw JSON Schema objects.
|
|
55
57
|
* This allows tools to be defined without schema libraries when you have JSON Schema definitions available.
|
|
@@ -355,7 +357,7 @@ export interface ToolCallPart<TMetadata = unknown> {
|
|
|
355
357
|
export interface ToolResultPart {
|
|
356
358
|
type: 'tool-result'
|
|
357
359
|
toolCallId: string
|
|
358
|
-
content: string
|
|
360
|
+
content: string | Array<ContentPart>
|
|
359
361
|
state: ToolResultState
|
|
360
362
|
error?: string // Error message if state is "error"
|
|
361
363
|
}
|
|
@@ -443,33 +445,75 @@ export type ConstrainedModelMessage<
|
|
|
443
445
|
content: ConstrainedContent<TInputModalitiesTypes>
|
|
444
446
|
}
|
|
445
447
|
|
|
448
|
+
type IsUnknown<T> = unknown extends T
|
|
449
|
+
? [T] extends [unknown]
|
|
450
|
+
? true
|
|
451
|
+
: false
|
|
452
|
+
: false
|
|
453
|
+
|
|
454
|
+
type RuntimeContextField<TContext> =
|
|
455
|
+
IsUnknown<TContext> extends true
|
|
456
|
+
? {
|
|
457
|
+
/**
|
|
458
|
+
* Runtime context provided by the caller.
|
|
459
|
+
*
|
|
460
|
+
* This is request-local application state for tool and middleware
|
|
461
|
+
* implementations, not the AG-UI `Context[]` protocol field.
|
|
462
|
+
*/
|
|
463
|
+
context?: TContext
|
|
464
|
+
}
|
|
465
|
+
: {
|
|
466
|
+
/**
|
|
467
|
+
* Runtime context provided by the caller.
|
|
468
|
+
*
|
|
469
|
+
* This is request-local application state for tool and middleware
|
|
470
|
+
* implementations, not the AG-UI `Context[]` protocol field.
|
|
471
|
+
*/
|
|
472
|
+
context: TContext
|
|
473
|
+
}
|
|
474
|
+
|
|
446
475
|
/**
|
|
447
476
|
* Context passed to tool execute functions, providing capabilities like
|
|
448
477
|
* emitting custom events during execution.
|
|
449
478
|
*/
|
|
450
|
-
export
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
479
|
+
export type ToolExecutionContext<TContext = unknown> =
|
|
480
|
+
RuntimeContextField<TContext> & {
|
|
481
|
+
/** The ID of the tool call being executed */
|
|
482
|
+
toolCallId?: string
|
|
483
|
+
/**
|
|
484
|
+
* Emit a custom event during tool execution.
|
|
485
|
+
* Events are streamed to the client in real-time as AG-UI CUSTOM events.
|
|
486
|
+
*
|
|
487
|
+
* @param eventName - Name of the custom event
|
|
488
|
+
* @param value - Event payload value
|
|
489
|
+
*
|
|
490
|
+
* @example
|
|
491
|
+
* ```ts
|
|
492
|
+
* const tool = toolDefinition({ ... }).server(async (args, context) => {
|
|
493
|
+
* context?.emitCustomEvent('progress', { step: 1, total: 3 })
|
|
494
|
+
* // ... do work ...
|
|
495
|
+
* context?.emitCustomEvent('progress', { step: 2, total: 3 })
|
|
496
|
+
* // ... do more work ...
|
|
497
|
+
* return result
|
|
498
|
+
* })
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
501
|
+
emitCustomEvent: (eventName: string, value: Record<string, any>) => void
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export type ToolExecuteFunction<
|
|
505
|
+
TInput extends SchemaInput = SchemaInput,
|
|
506
|
+
TOutput extends SchemaInput = SchemaInput,
|
|
507
|
+
TContext = unknown,
|
|
508
|
+
> = undefined extends TContext
|
|
509
|
+
? (
|
|
510
|
+
args: InferSchemaType<TInput>,
|
|
511
|
+
context?: ToolExecutionContext<TContext>,
|
|
512
|
+
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>
|
|
513
|
+
: (
|
|
514
|
+
args: InferSchemaType<TInput>,
|
|
515
|
+
context: ToolExecutionContext<TContext>,
|
|
516
|
+
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>
|
|
473
517
|
|
|
474
518
|
/**
|
|
475
519
|
* Tool/Function definition for function calling.
|
|
@@ -488,6 +532,7 @@ export interface Tool<
|
|
|
488
532
|
TInput extends SchemaInput = SchemaInput,
|
|
489
533
|
TOutput extends SchemaInput = SchemaInput,
|
|
490
534
|
TName extends string = string,
|
|
535
|
+
TContext = unknown,
|
|
491
536
|
> {
|
|
492
537
|
/**
|
|
493
538
|
* Unique name of the tool (used by the model to call it).
|
|
@@ -587,9 +632,7 @@ export interface Tool<
|
|
|
587
632
|
* return weather; // Can return object or string
|
|
588
633
|
* }
|
|
589
634
|
*/
|
|
590
|
-
execute?:
|
|
591
|
-
| ((args: any, context?: ToolExecutionContext) => Promise<any> | any)
|
|
592
|
-
| undefined
|
|
635
|
+
execute?: ToolExecuteFunction<TInput, TOutput, TContext> | undefined
|
|
593
636
|
|
|
594
637
|
/** If true, tool execution requires user approval before running. Works with both server and client tools. */
|
|
595
638
|
needsApproval?: boolean
|
|
@@ -601,6 +644,10 @@ export interface Tool<
|
|
|
601
644
|
metadata?: Record<string, any> | undefined
|
|
602
645
|
}
|
|
603
646
|
|
|
647
|
+
export type AnyTool = Omit<Tool<any, any, any, any>, 'execute'> & {
|
|
648
|
+
execute?: ((args: any, context?: any) => any) | undefined
|
|
649
|
+
}
|
|
650
|
+
|
|
604
651
|
export interface ToolConfig {
|
|
605
652
|
[key: string]: Tool
|
|
606
653
|
}
|
|
@@ -729,10 +776,16 @@ export type AgentLoopStrategy = (state: AgentLoopState) => boolean
|
|
|
729
776
|
export interface TextOptions<
|
|
730
777
|
TProviderOptionsSuperset extends Record<string, any> = Record<string, any>,
|
|
731
778
|
TProviderOptionsForModel = TProviderOptionsSuperset,
|
|
779
|
+
TContext = unknown,
|
|
732
780
|
> {
|
|
733
781
|
model: string
|
|
734
782
|
messages: Array<ModelMessage>
|
|
735
|
-
tools?: Array<
|
|
783
|
+
tools?: Array<AnyTool> | undefined
|
|
784
|
+
/**
|
|
785
|
+
* Runtime context provided by the caller and passed to middleware and
|
|
786
|
+
* server-side tool implementations.
|
|
787
|
+
*/
|
|
788
|
+
context?: TContext
|
|
736
789
|
/**
|
|
737
790
|
* System prompts to include with the request.
|
|
738
791
|
*
|
|
@@ -1084,7 +1137,9 @@ export interface ToolCallEndEvent extends AGUIToolCallEndEvent {
|
|
|
1084
1137
|
/** Final parsed input arguments (TanStack AI internal) */
|
|
1085
1138
|
input?: unknown
|
|
1086
1139
|
/** Tool execution result (TanStack AI internal) */
|
|
1087
|
-
result?: string
|
|
1140
|
+
result?: string | Array<ContentPart>
|
|
1141
|
+
/** Tool execution output state (TanStack AI internal) */
|
|
1142
|
+
state?: ToolOutputState
|
|
1088
1143
|
}
|
|
1089
1144
|
|
|
1090
1145
|
/**
|
|
@@ -1096,6 +1151,8 @@ export interface ToolCallEndEvent extends AGUIToolCallEndEvent {
|
|
|
1096
1151
|
export interface ToolCallResultEvent extends AGUIToolCallResultEvent {
|
|
1097
1152
|
/** Model identifier for multi-model support */
|
|
1098
1153
|
model?: string
|
|
1154
|
+
/** Tool execution output state (TanStack AI internal) */
|
|
1155
|
+
state?: ToolOutputState
|
|
1099
1156
|
}
|
|
1100
1157
|
|
|
1101
1158
|
/**
|
|
@@ -103,7 +103,10 @@ export function uiMessagesToWire(
|
|
|
103
103
|
role: 'tool',
|
|
104
104
|
id: deriveToolMessageId(part.toolCallId),
|
|
105
105
|
toolCallId: part.toolCallId,
|
|
106
|
-
content:
|
|
106
|
+
content:
|
|
107
|
+
typeof part.content === 'string'
|
|
108
|
+
? part.content
|
|
109
|
+
: JSON.stringify(part.content),
|
|
107
110
|
...(part.error !== undefined && { error: part.error }),
|
|
108
111
|
})
|
|
109
112
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { AGUIError, RunAgentInputSchema } from '@ag-ui/core'
|
|
2
2
|
import type { Context as AGUIContext } from '@ag-ui/core'
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
JSONSchema,
|
|
5
|
+
ModelMessage,
|
|
6
|
+
SchemaInput,
|
|
7
|
+
Tool,
|
|
8
|
+
UIMessage,
|
|
9
|
+
} from '../types'
|
|
4
10
|
|
|
5
11
|
const KNOWN_PART_TYPES = new Set([
|
|
6
12
|
'text',
|
|
@@ -43,7 +49,12 @@ export function chatParamsFromRequestBody(body: unknown): Promise<{
|
|
|
43
49
|
tools: Array<{ name: string; description: string; parameters: JSONSchema }>
|
|
44
50
|
forwardedProps: Record<string, unknown>
|
|
45
51
|
state: unknown
|
|
52
|
+
/**
|
|
53
|
+
* @deprecated Use `aguiContext` instead. This alias will be removed in a
|
|
54
|
+
* future release.
|
|
55
|
+
*/
|
|
46
56
|
context: Array<AGUIContext>
|
|
57
|
+
aguiContext: Array<AGUIContext>
|
|
47
58
|
}> {
|
|
48
59
|
const parseResult = RunAgentInputSchema.safeParse(body)
|
|
49
60
|
if (!parseResult.success) {
|
|
@@ -58,6 +69,7 @@ export function chatParamsFromRequestBody(body: unknown): Promise<{
|
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
const parsed = parseResult.data
|
|
72
|
+
const aguiContext = parsed.context
|
|
61
73
|
|
|
62
74
|
// AG-UI Zod uses `.strip()` so extra fields like `parts` on messages are
|
|
63
75
|
// dropped during parse. We re-attach them from the original body so the
|
|
@@ -89,7 +101,8 @@ export function chatParamsFromRequestBody(body: unknown): Promise<{
|
|
|
89
101
|
}>,
|
|
90
102
|
forwardedProps: (parsed.forwardedProps ?? {}) as Record<string, unknown>,
|
|
91
103
|
state: parsed.state,
|
|
92
|
-
context:
|
|
104
|
+
context: aguiContext,
|
|
105
|
+
aguiContext,
|
|
93
106
|
})
|
|
94
107
|
}
|
|
95
108
|
|
|
@@ -171,16 +184,18 @@ export async function chatParamsFromRequest(
|
|
|
171
184
|
* `chatParamsFromRequest(...)` / `chatParamsFromRequestBody(...)`.
|
|
172
185
|
* @returns A merged array suitable for `chat({ tools })`.
|
|
173
186
|
*/
|
|
174
|
-
export function mergeAgentTools(
|
|
175
|
-
serverTools: ReadonlyArray<Tool
|
|
187
|
+
export function mergeAgentTools<TContext = unknown>(
|
|
188
|
+
serverTools: ReadonlyArray<Tool<SchemaInput, SchemaInput, string, TContext>>,
|
|
176
189
|
clientTools: ReadonlyArray<{
|
|
177
190
|
name: string
|
|
178
191
|
description: string
|
|
179
192
|
parameters: JSONSchema
|
|
180
193
|
}>,
|
|
181
|
-
): Array<Tool
|
|
194
|
+
): Array<Tool<SchemaInput, SchemaInput, string, TContext>> {
|
|
182
195
|
const seen = new Set(serverTools.map((t) => t.name))
|
|
183
|
-
const merged: Array<Tool
|
|
196
|
+
const merged: Array<Tool<SchemaInput, SchemaInput, string, TContext>> = [
|
|
197
|
+
...serverTools,
|
|
198
|
+
]
|
|
184
199
|
for (const ct of clientTools) {
|
|
185
200
|
if (seen.has(ct.name)) {
|
|
186
201
|
// Server wins on name collision.
|
|
@@ -193,7 +208,7 @@ export function mergeAgentTools(
|
|
|
193
208
|
inputSchema: ct.parameters,
|
|
194
209
|
// No `execute` — runtime treats this as a client-side tool and
|
|
195
210
|
// emits ClientToolRequest events.
|
|
196
|
-
} as Tool)
|
|
211
|
+
} as Tool<SchemaInput, SchemaInput, string, TContext>)
|
|
197
212
|
}
|
|
198
213
|
return merged
|
|
199
214
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ContentPart } from '../types'
|
|
2
|
+
|
|
3
|
+
const CONTENT_PART_TYPES = new Set([
|
|
4
|
+
'text',
|
|
5
|
+
'image',
|
|
6
|
+
'audio',
|
|
7
|
+
'video',
|
|
8
|
+
'document',
|
|
9
|
+
])
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Structural check for a single `ContentPart`. A text part must carry a string
|
|
13
|
+
* `content`; every other modality must carry a `source` with `type` of
|
|
14
|
+
* `'url' | 'data'` and a string `value`.
|
|
15
|
+
*/
|
|
16
|
+
export function isContentPart(value: unknown): value is ContentPart {
|
|
17
|
+
if (typeof value !== 'object' || value === null) return false
|
|
18
|
+
const part = value as Record<string, unknown>
|
|
19
|
+
if (typeof part.type !== 'string' || !CONTENT_PART_TYPES.has(part.type)) {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
if (part.type === 'text') {
|
|
23
|
+
return typeof part.content === 'string'
|
|
24
|
+
}
|
|
25
|
+
const source = part.source
|
|
26
|
+
if (typeof source !== 'object' || source === null) return false
|
|
27
|
+
const src = source as Record<string, unknown>
|
|
28
|
+
if (typeof src.value !== 'string') return false
|
|
29
|
+
// `data` sources require a mimeType (matches ContentPartDataSource); `url`
|
|
30
|
+
// sources don't. Requiring it here keeps the runtime guard consistent with
|
|
31
|
+
// the type and avoids emitting `data:undefined;base64,...` downstream.
|
|
32
|
+
if (src.type === 'data') return typeof src.mimeType === 'string'
|
|
33
|
+
return src.type === 'url'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* True iff `value` is a NON-EMPTY array whose every element is a valid
|
|
38
|
+
* `ContentPart`. Empty arrays and mixed arrays return false so they continue
|
|
39
|
+
* to be treated as ordinary (stringified) data — this keeps the auto-detection
|
|
40
|
+
* footgun narrow.
|
|
41
|
+
*/
|
|
42
|
+
export function isContentPartArray(
|
|
43
|
+
value: unknown,
|
|
44
|
+
): value is Array<ContentPart> {
|
|
45
|
+
return Array.isArray(value) && value.length > 0 && value.every(isContentPart)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Normalize a tool's return value for transport:
|
|
50
|
+
* - string → unchanged
|
|
51
|
+
* - ContentPart array → unchanged (multimodal, passed through to the adapter)
|
|
52
|
+
* - anything else → `JSON.stringify`
|
|
53
|
+
*/
|
|
54
|
+
export function normalizeToolResult(
|
|
55
|
+
result: unknown,
|
|
56
|
+
): string | Array<ContentPart> {
|
|
57
|
+
if (typeof result === 'string') return result
|
|
58
|
+
if (isContentPartArray(result)) return result
|
|
59
|
+
return JSON.stringify(result)
|
|
60
|
+
}
|