@tanstack/ai 0.23.1 → 0.25.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/adapter.d.ts +3 -1
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- 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 +21 -21
- 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/activities/generateAudio/index.js +9 -0
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.js +9 -0
- package/dist/esm/activities/generateSpeech/index.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 +3 -1
- package/dist/esm/index.js +9 -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 +56 -59
- 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/dist/esm/utilities/usage.d.ts +31 -0
- package/dist/esm/utilities/usage.js +11 -0
- package/dist/esm/utilities/usage.js.map +1 -0
- package/package.json +2 -2
- package/src/activities/chat/adapter.ts +3 -0
- 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 +21 -21
- 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/activities/generateAudio/index.ts +10 -0
- package/src/activities/generateSpeech/index.ts +10 -0
- package/src/adapter-internals.ts +4 -1
- package/src/client.ts +5 -1
- package/src/index.ts +10 -0
- package/src/tool-registry.ts +16 -14
- package/src/types.ts +118 -79
- 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
- package/src/utilities/usage.ts +41 -0
|
@@ -10,6 +10,7 @@ import { stripToSpecMiddleware } from '../../strip-to-spec-middleware'
|
|
|
10
10
|
import { streamToText } from '../../stream-to-response.js'
|
|
11
11
|
import { resolveDebugOption } from '../../logger/resolve'
|
|
12
12
|
import { EventType } from '../../types'
|
|
13
|
+
import { normalizeToolResult } from '../../utilities/tool-result'
|
|
13
14
|
import { LazyToolManager } from './tools/lazy-tool-manager'
|
|
14
15
|
import {
|
|
15
16
|
MiddlewareAbortError,
|
|
@@ -32,6 +33,7 @@ import type {
|
|
|
32
33
|
import type { AnyTextAdapter, StructuredOutputOptions } from './adapter'
|
|
33
34
|
import type {
|
|
34
35
|
AgentLoopStrategy,
|
|
36
|
+
AnyTool,
|
|
35
37
|
ConstrainedModelMessage,
|
|
36
38
|
CustomEvent,
|
|
37
39
|
InferSchemaType,
|
|
@@ -44,7 +46,6 @@ import type {
|
|
|
44
46
|
StructuredOutputStream,
|
|
45
47
|
TextMessageContentEvent,
|
|
46
48
|
TextOptions,
|
|
47
|
-
Tool,
|
|
48
49
|
ToolCall,
|
|
49
50
|
ToolCallArgsEvent,
|
|
50
51
|
ToolCallEndEvent,
|
|
@@ -61,6 +62,13 @@ import type { SystemPrompt } from '../../system-prompts'
|
|
|
61
62
|
import type { InternalLogger } from '../../logger/internal-logger'
|
|
62
63
|
import type { DebugOption } from '../../logger/types'
|
|
63
64
|
import type { ProviderTool } from '../../tools/provider-tool'
|
|
65
|
+
import type {
|
|
66
|
+
ContextFromMiddleware,
|
|
67
|
+
ContextFromTool,
|
|
68
|
+
DefinedContext,
|
|
69
|
+
MergeContext,
|
|
70
|
+
UnionToIntersection,
|
|
71
|
+
} from './runtime-context-types'
|
|
64
72
|
|
|
65
73
|
// ===========================
|
|
66
74
|
// Activity Kind
|
|
@@ -69,6 +77,71 @@ import type { ProviderTool } from '../../tools/provider-tool'
|
|
|
69
77
|
/** The adapter kind this activity handles */
|
|
70
78
|
export const kind = 'text' as const
|
|
71
79
|
|
|
80
|
+
type AnyRuntimeTool = AnyTool
|
|
81
|
+
|
|
82
|
+
// The leaf context-inference primitives (KnownContext, MergeContext,
|
|
83
|
+
// UnionToIntersection, DefinedContext, ContextFromTool, ContextFromMiddleware)
|
|
84
|
+
// are shared with the tool execution layer — see ./runtime-context-types.
|
|
85
|
+
type ContextFromConsumer<T> = ContextFromTool<T> | ContextFromMiddleware<T>
|
|
86
|
+
|
|
87
|
+
type RequiredContextFromConsumerUnion<T> = T extends unknown
|
|
88
|
+
? undefined extends ContextFromConsumer<T>
|
|
89
|
+
? never
|
|
90
|
+
: ContextFromConsumer<T>
|
|
91
|
+
: never
|
|
92
|
+
|
|
93
|
+
type ContextFromConsumerUnion<T> = [
|
|
94
|
+
UnionToIntersection<DefinedContext<ContextFromConsumer<T>>>,
|
|
95
|
+
] extends [never]
|
|
96
|
+
? never
|
|
97
|
+
: [RequiredContextFromConsumerUnion<T>] extends [never]
|
|
98
|
+
? UnionToIntersection<DefinedContext<ContextFromConsumer<T>>> | undefined
|
|
99
|
+
: UnionToIntersection<DefinedContext<ContextFromConsumer<T>>>
|
|
100
|
+
|
|
101
|
+
type ContextFromArray<T> = T extends readonly [infer THead, ...infer TTail]
|
|
102
|
+
? MergeContext<ContextFromConsumer<THead>, ContextFromArray<TTail>>
|
|
103
|
+
: T extends ReadonlyArray<infer TItem>
|
|
104
|
+
? ContextFromConsumerUnion<TItem>
|
|
105
|
+
: never
|
|
106
|
+
|
|
107
|
+
type ContextFromInputs<TTools, TMiddleware> = MergeContext<
|
|
108
|
+
ContextFromArray<NonNullable<TTools>>,
|
|
109
|
+
ContextFromArray<NonNullable<TMiddleware>>
|
|
110
|
+
>
|
|
111
|
+
|
|
112
|
+
type InferredContext<TTools, TMiddleware> = [
|
|
113
|
+
ContextFromInputs<TTools, TMiddleware>,
|
|
114
|
+
] extends [never]
|
|
115
|
+
? unknown
|
|
116
|
+
: ContextFromInputs<TTools, TMiddleware>
|
|
117
|
+
|
|
118
|
+
type RequiredContextFromInputs<TTools, TMiddleware> = [
|
|
119
|
+
ContextFromInputs<TTools, TMiddleware>,
|
|
120
|
+
] extends [never]
|
|
121
|
+
? { context?: unknown }
|
|
122
|
+
: undefined extends ContextFromInputs<TTools, TMiddleware>
|
|
123
|
+
? { context?: ContextFromInputs<TTools, TMiddleware> }
|
|
124
|
+
: { context: ContextFromInputs<TTools, TMiddleware> }
|
|
125
|
+
|
|
126
|
+
type TextActivityOptionsWithContext<
|
|
127
|
+
TAdapter extends AnyTextAdapter,
|
|
128
|
+
TSchema extends SchemaInput | undefined,
|
|
129
|
+
TStream extends boolean,
|
|
130
|
+
TTools extends TextActivityOptions<TAdapter, TSchema, TStream, any>['tools'],
|
|
131
|
+
TMiddleware extends TextActivityOptions<
|
|
132
|
+
TAdapter,
|
|
133
|
+
TSchema,
|
|
134
|
+
TStream,
|
|
135
|
+
any
|
|
136
|
+
>['middleware'],
|
|
137
|
+
> = Omit<
|
|
138
|
+
TextActivityOptions<TAdapter, TSchema, TStream, any>,
|
|
139
|
+
'tools' | 'middleware' | 'context'
|
|
140
|
+
> & {
|
|
141
|
+
tools?: TTools
|
|
142
|
+
middleware?: TMiddleware
|
|
143
|
+
} & RequiredContextFromInputs<TTools, TMiddleware>
|
|
144
|
+
|
|
72
145
|
// ===========================
|
|
73
146
|
// Activity Options Type
|
|
74
147
|
// ===========================
|
|
@@ -85,6 +158,7 @@ export interface TextActivityOptions<
|
|
|
85
158
|
TAdapter extends AnyTextAdapter,
|
|
86
159
|
TSchema extends SchemaInput | undefined,
|
|
87
160
|
TStream extends boolean,
|
|
161
|
+
TContext = unknown,
|
|
88
162
|
> {
|
|
89
163
|
/** The text adapter to use (created by a provider function like openaiText('gpt-4o')) */
|
|
90
164
|
adapter: TAdapter
|
|
@@ -130,7 +204,7 @@ export interface TextActivityOptions<
|
|
|
130
204
|
*/
|
|
131
205
|
tools?:
|
|
132
206
|
| Array<
|
|
133
|
-
| (
|
|
207
|
+
| (AnyRuntimeTool & { readonly '~toolKind'?: never })
|
|
134
208
|
| ProviderTool<string, TAdapter['~types']['toolCapabilities'][number]>
|
|
135
209
|
>
|
|
136
210
|
| undefined
|
|
@@ -209,12 +283,11 @@ export interface TextActivityOptions<
|
|
|
209
283
|
* })
|
|
210
284
|
* ```
|
|
211
285
|
*/
|
|
212
|
-
middleware?: Array<ChatMiddleware
|
|
286
|
+
middleware?: Array<ChatMiddleware<TContext>>
|
|
213
287
|
/**
|
|
214
|
-
*
|
|
215
|
-
* Can be used to pass request-scoped data (e.g., user ID, request context).
|
|
288
|
+
* Runtime context value passed to middleware hooks and server tools.
|
|
216
289
|
*/
|
|
217
|
-
context?:
|
|
290
|
+
context?: TContext
|
|
218
291
|
/**
|
|
219
292
|
* Enable debug logging. Pass `true` to enable all categories with the default
|
|
220
293
|
* console logger, `false` to silence everything, or a `DebugConfig` object for
|
|
@@ -245,9 +318,37 @@ export function createChatOptions<
|
|
|
245
318
|
TAdapter extends AnyTextAdapter,
|
|
246
319
|
TSchema extends SchemaInput | undefined = undefined,
|
|
247
320
|
TStream extends boolean = true,
|
|
321
|
+
const TTools extends TextActivityOptions<
|
|
322
|
+
TAdapter,
|
|
323
|
+
TSchema,
|
|
324
|
+
TStream,
|
|
325
|
+
any
|
|
326
|
+
>['tools'] = TextActivityOptions<TAdapter, TSchema, TStream, any>['tools'],
|
|
327
|
+
const TMiddleware extends TextActivityOptions<
|
|
328
|
+
TAdapter,
|
|
329
|
+
TSchema,
|
|
330
|
+
TStream,
|
|
331
|
+
any
|
|
332
|
+
>['middleware'] = TextActivityOptions<
|
|
333
|
+
TAdapter,
|
|
334
|
+
TSchema,
|
|
335
|
+
TStream,
|
|
336
|
+
any
|
|
337
|
+
>['middleware'],
|
|
248
338
|
>(
|
|
249
|
-
options:
|
|
250
|
-
|
|
339
|
+
options: TextActivityOptionsWithContext<
|
|
340
|
+
TAdapter,
|
|
341
|
+
TSchema,
|
|
342
|
+
TStream,
|
|
343
|
+
TTools,
|
|
344
|
+
TMiddleware
|
|
345
|
+
>,
|
|
346
|
+
): TextActivityOptions<
|
|
347
|
+
TAdapter,
|
|
348
|
+
TSchema,
|
|
349
|
+
TStream,
|
|
350
|
+
InferredContext<TTools, TMiddleware>
|
|
351
|
+
> {
|
|
251
352
|
return options
|
|
252
353
|
}
|
|
253
354
|
|
|
@@ -288,13 +389,18 @@ export type TextActivityResult<
|
|
|
288
389
|
|
|
289
390
|
interface TextEngineConfig<
|
|
290
391
|
TAdapter extends AnyTextAdapter,
|
|
291
|
-
|
|
392
|
+
TContext = unknown,
|
|
393
|
+
TParams extends TextOptions<any, any, TContext> = TextOptions<
|
|
394
|
+
any,
|
|
395
|
+
any,
|
|
396
|
+
TContext
|
|
397
|
+
>,
|
|
292
398
|
> {
|
|
293
399
|
adapter: TAdapter
|
|
294
400
|
systemPrompts?: Array<SystemPrompt>
|
|
295
401
|
params: TParams
|
|
296
|
-
middleware?: Array<ChatMiddleware
|
|
297
|
-
context?:
|
|
402
|
+
middleware?: Array<ChatMiddleware<TContext>>
|
|
403
|
+
context?: TContext
|
|
298
404
|
/**
|
|
299
405
|
* If set, after the agent loop finishes the engine runs a
|
|
300
406
|
* structured-output finalization step through the same middleware
|
|
@@ -334,14 +440,19 @@ type CyclePhase = 'processText' | 'executeToolCalls'
|
|
|
334
440
|
|
|
335
441
|
class TextEngine<
|
|
336
442
|
TAdapter extends AnyTextAdapter,
|
|
337
|
-
|
|
443
|
+
TContext = unknown,
|
|
444
|
+
TParams extends TextOptions<any, any, TContext> = TextOptions<
|
|
445
|
+
any,
|
|
446
|
+
any,
|
|
447
|
+
TContext
|
|
448
|
+
>,
|
|
338
449
|
> {
|
|
339
450
|
private readonly adapter: TAdapter
|
|
340
451
|
private params: TParams
|
|
341
452
|
private systemPrompts: Array<SystemPrompt>
|
|
342
|
-
private tools: Array<
|
|
453
|
+
private tools: Array<AnyRuntimeTool>
|
|
343
454
|
private readonly loopStrategy: AgentLoopStrategy
|
|
344
|
-
private toolCallManager: ToolCallManager
|
|
455
|
+
private toolCallManager: ToolCallManager<ReadonlyArray<AnyTool>, TContext>
|
|
345
456
|
private readonly lazyToolManager: LazyToolManager
|
|
346
457
|
private readonly initialMessageCount: number
|
|
347
458
|
private readonly requestId: string
|
|
@@ -376,8 +487,8 @@ class TextEngine<
|
|
|
376
487
|
private readonly parentRunIdOverride?: string
|
|
377
488
|
|
|
378
489
|
// Middleware support
|
|
379
|
-
private readonly middlewareRunner: MiddlewareRunner
|
|
380
|
-
private readonly middlewareCtx: ChatMiddlewareContext
|
|
490
|
+
private readonly middlewareRunner: MiddlewareRunner<TContext>
|
|
491
|
+
private readonly middlewareCtx: ChatMiddlewareContext<TContext>
|
|
381
492
|
private readonly deferredPromises: Array<Promise<unknown>> = []
|
|
382
493
|
private abortReason?: string
|
|
383
494
|
private readonly middlewareAbortController?: AbortController
|
|
@@ -416,7 +527,7 @@ class TextEngine<
|
|
|
416
527
|
}
|
|
417
528
|
|
|
418
529
|
constructor(
|
|
419
|
-
config: TextEngineConfig<TAdapter, TParams>,
|
|
530
|
+
config: TextEngineConfig<TAdapter, TContext, TParams>,
|
|
420
531
|
logger: InternalLogger,
|
|
421
532
|
) {
|
|
422
533
|
this.logger = logger
|
|
@@ -447,7 +558,10 @@ class TextEngine<
|
|
|
447
558
|
this.messages,
|
|
448
559
|
)
|
|
449
560
|
this.tools = this.lazyToolManager.getActiveTools()
|
|
450
|
-
this.toolCallManager = new ToolCallManager
|
|
561
|
+
this.toolCallManager = new ToolCallManager<
|
|
562
|
+
ReadonlyArray<AnyTool>,
|
|
563
|
+
TContext
|
|
564
|
+
>(this.tools)
|
|
451
565
|
this.requestId = this.createId('chat')
|
|
452
566
|
this.streamId = this.createId('stream')
|
|
453
567
|
this.effectiveRequest = config.params.abortController
|
|
@@ -468,10 +582,7 @@ class TextEngine<
|
|
|
468
582
|
// handleStreamChunk processes raw chunks BEFORE middleware, so internal
|
|
469
583
|
// state management sees extended fields (finishReason, delta, toolCallName, etc.).
|
|
470
584
|
// The strip middleware ensures the yielded public stream is AG-UI spec-compliant.
|
|
471
|
-
|
|
472
|
-
// `DevtoolsChatMiddleware` (defined in `@tanstack/ai-event-client` to
|
|
473
|
-
// avoid a circular dep). Cast it to `ChatMiddleware` for the runner.
|
|
474
|
-
const allMiddleware: Array<ChatMiddleware> = [
|
|
585
|
+
const allMiddleware: Array<ChatMiddleware<TContext>> = [
|
|
475
586
|
devtoolsMiddleware(),
|
|
476
587
|
...(config.middleware || []),
|
|
477
588
|
stripToSpecMiddleware(),
|
|
@@ -494,7 +605,7 @@ class TextEngine<
|
|
|
494
605
|
this.abortReason = reason
|
|
495
606
|
this.middlewareAbortController?.abort(reason)
|
|
496
607
|
},
|
|
497
|
-
context: config.context,
|
|
608
|
+
context: config.context as TContext,
|
|
498
609
|
defer: (promise: Promise<unknown>) => {
|
|
499
610
|
this.deferredPromises.push(promise)
|
|
500
611
|
},
|
|
@@ -1125,6 +1236,7 @@ class TextEngine<
|
|
|
1125
1236
|
)
|
|
1126
1237
|
},
|
|
1127
1238
|
},
|
|
1239
|
+
this.middlewareCtx.context,
|
|
1128
1240
|
)
|
|
1129
1241
|
|
|
1130
1242
|
// Consume the async generator, yielding custom events and collecting the return value
|
|
@@ -1285,6 +1397,7 @@ class TextEngine<
|
|
|
1285
1397
|
)
|
|
1286
1398
|
},
|
|
1287
1399
|
},
|
|
1400
|
+
this.middlewareCtx.context,
|
|
1288
1401
|
)
|
|
1289
1402
|
|
|
1290
1403
|
// Consume the async generator, yielding custom events and collecting the return value
|
|
@@ -1349,7 +1462,10 @@ class TextEngine<
|
|
|
1349
1462
|
// Refresh tools if lazy tools were discovered in this batch
|
|
1350
1463
|
if (this.lazyToolManager.hasNewlyDiscoveredTools()) {
|
|
1351
1464
|
this.tools = this.lazyToolManager.getActiveTools()
|
|
1352
|
-
this.toolCallManager = new ToolCallManager
|
|
1465
|
+
this.toolCallManager = new ToolCallManager<
|
|
1466
|
+
ReadonlyArray<AnyTool>,
|
|
1467
|
+
TContext
|
|
1468
|
+
>(this.tools)
|
|
1353
1469
|
this.setToolPhase('continue')
|
|
1354
1470
|
return
|
|
1355
1471
|
}
|
|
@@ -1435,12 +1551,18 @@ class TextEngine<
|
|
|
1435
1551
|
// Check for ModelMessage format (role: 'tool' messages contain tool results)
|
|
1436
1552
|
// This handles results sent back from the client after executing client-side tools
|
|
1437
1553
|
if (message.role === 'tool' && message.toolCallId) {
|
|
1438
|
-
// Parse content back to original output (was stringified by
|
|
1554
|
+
// Parse content back to original output (was stringified by
|
|
1555
|
+
// uiMessageToModelMessages). Multimodal results carry an
|
|
1556
|
+
// Array<ContentPart> directly — pass it through without parsing.
|
|
1439
1557
|
let output: unknown
|
|
1440
|
-
|
|
1441
|
-
output = JSON.parse(message.content as string)
|
|
1442
|
-
} catch {
|
|
1558
|
+
if (Array.isArray(message.content)) {
|
|
1443
1559
|
output = message.content
|
|
1560
|
+
} else {
|
|
1561
|
+
try {
|
|
1562
|
+
output = JSON.parse(message.content as string)
|
|
1563
|
+
} catch {
|
|
1564
|
+
output = message.content
|
|
1565
|
+
}
|
|
1444
1566
|
}
|
|
1445
1567
|
// Skip approval response messages (they have pendingExecution marker)
|
|
1446
1568
|
// These are NOT real client tool results — they are synthetic tool messages
|
|
@@ -1519,7 +1641,15 @@ class TextEngine<
|
|
|
1519
1641
|
const chunks: Array<StreamChunk> = []
|
|
1520
1642
|
|
|
1521
1643
|
for (const result of results) {
|
|
1522
|
-
|
|
1644
|
+
// `content` is the canonical value for the tool `ModelMessage` — it may
|
|
1645
|
+
// be an `Array<ContentPart>` (multimodal) which the adapters convert to
|
|
1646
|
+
// structured provider output on the next iteration. `wireContent` is the
|
|
1647
|
+
// string form emitted on the AG-UI stream events (TOOL_CALL_END.result /
|
|
1648
|
+
// TOOL_CALL_RESULT.content are string-only per the AG-UI spec); the
|
|
1649
|
+
// multimodal array travels via the message itself, not the wire event.
|
|
1650
|
+
const content = normalizeToolResult(result.result)
|
|
1651
|
+
const wireContent =
|
|
1652
|
+
typeof content === 'string' ? content : JSON.stringify(content)
|
|
1523
1653
|
|
|
1524
1654
|
// Emit TOOL_CALL_START + TOOL_CALL_ARGS before TOOL_CALL_END so that
|
|
1525
1655
|
// the client can reconstruct the full tool call during continuations.
|
|
@@ -1551,18 +1681,20 @@ class TextEngine<
|
|
|
1551
1681
|
toolCallId: result.toolCallId,
|
|
1552
1682
|
toolCallName: result.toolName,
|
|
1553
1683
|
toolName: result.toolName,
|
|
1554
|
-
result:
|
|
1684
|
+
result: wireContent,
|
|
1685
|
+
...(result.state !== undefined && { state: result.state }),
|
|
1555
1686
|
} as StreamChunk)
|
|
1556
1687
|
|
|
1557
|
-
// AG-UI spec TOOL_CALL_RESULT event
|
|
1688
|
+
// AG-UI spec TOOL_CALL_RESULT event (content is string-only per spec)
|
|
1558
1689
|
chunks.push({
|
|
1559
1690
|
type: 'TOOL_CALL_RESULT',
|
|
1560
1691
|
timestamp: Date.now(),
|
|
1561
1692
|
model: finishEvent.model,
|
|
1562
1693
|
messageId: this.createId('tool-result'),
|
|
1563
1694
|
toolCallId: result.toolCallId,
|
|
1564
|
-
content,
|
|
1695
|
+
content: wireContent,
|
|
1565
1696
|
role: 'tool',
|
|
1697
|
+
...(result.state !== undefined && { state: result.state }),
|
|
1566
1698
|
} as StreamChunk)
|
|
1567
1699
|
|
|
1568
1700
|
// If a placeholder tool message exists for this toolCallId (created by
|
|
@@ -2387,8 +2519,31 @@ export function chat<
|
|
|
2387
2519
|
TAdapter extends AnyTextAdapter,
|
|
2388
2520
|
TSchema extends SchemaInput | undefined = undefined,
|
|
2389
2521
|
TStream extends boolean = boolean,
|
|
2522
|
+
const TTools extends TextActivityOptions<
|
|
2523
|
+
TAdapter,
|
|
2524
|
+
TSchema,
|
|
2525
|
+
TStream,
|
|
2526
|
+
any
|
|
2527
|
+
>['tools'] = TextActivityOptions<TAdapter, TSchema, TStream, any>['tools'],
|
|
2528
|
+
const TMiddleware extends TextActivityOptions<
|
|
2529
|
+
TAdapter,
|
|
2530
|
+
TSchema,
|
|
2531
|
+
TStream,
|
|
2532
|
+
any
|
|
2533
|
+
>['middleware'] = TextActivityOptions<
|
|
2534
|
+
TAdapter,
|
|
2535
|
+
TSchema,
|
|
2536
|
+
TStream,
|
|
2537
|
+
any
|
|
2538
|
+
>['middleware'],
|
|
2390
2539
|
>(
|
|
2391
|
-
options:
|
|
2540
|
+
options: TextActivityOptionsWithContext<
|
|
2541
|
+
TAdapter,
|
|
2542
|
+
TSchema,
|
|
2543
|
+
TStream,
|
|
2544
|
+
TTools,
|
|
2545
|
+
TMiddleware
|
|
2546
|
+
>,
|
|
2392
2547
|
): TextActivityResult<TSchema, TStream> {
|
|
2393
2548
|
const { outputSchema, stream } = options
|
|
2394
2549
|
|
|
@@ -2431,8 +2586,8 @@ export function chat<
|
|
|
2431
2586
|
/**
|
|
2432
2587
|
* Run streaming text (agentic or one-shot depending on tools)
|
|
2433
2588
|
*/
|
|
2434
|
-
async function* runStreamingText(
|
|
2435
|
-
options: TextActivityOptions<AnyTextAdapter, undefined, true>,
|
|
2589
|
+
async function* runStreamingText<TContext = unknown>(
|
|
2590
|
+
options: TextActivityOptions<AnyTextAdapter, undefined, true, TContext>,
|
|
2436
2591
|
): AsyncIterable<StreamChunk> {
|
|
2437
2592
|
const { adapter, middleware, context, debug, ...textOptions } = options
|
|
2438
2593
|
const model = adapter.model
|
|
@@ -2443,7 +2598,8 @@ async function* runStreamingText(
|
|
|
2443
2598
|
adapter,
|
|
2444
2599
|
params: { ...textOptions, model, logger } as TextOptions<
|
|
2445
2600
|
Record<string, any>,
|
|
2446
|
-
Record<string, any
|
|
2601
|
+
Record<string, any>,
|
|
2602
|
+
TContext
|
|
2447
2603
|
>,
|
|
2448
2604
|
middleware,
|
|
2449
2605
|
context,
|
|
@@ -2460,13 +2616,18 @@ async function* runStreamingText(
|
|
|
2460
2616
|
* Run non-streaming text - collects all content and returns as a string.
|
|
2461
2617
|
* Runs the full agentic loop (if tools are provided) but returns collected text.
|
|
2462
2618
|
*/
|
|
2463
|
-
function runNonStreamingText(
|
|
2464
|
-
options: TextActivityOptions<AnyTextAdapter, undefined, false>,
|
|
2619
|
+
function runNonStreamingText<TContext = unknown>(
|
|
2620
|
+
options: TextActivityOptions<AnyTextAdapter, undefined, false, TContext>,
|
|
2465
2621
|
): Promise<string> {
|
|
2466
2622
|
// Run the streaming text and collect all text using streamToText.
|
|
2467
2623
|
const stream = runStreamingText(
|
|
2468
2624
|
// eslint-disable-next-line no-restricted-syntax -- generic-stream remap: caller is non-streaming (false), but runStreamingText is invoked internally to collect text; concrete `false`→`true` literals don't structurally overlap.
|
|
2469
|
-
options as unknown as TextActivityOptions<
|
|
2625
|
+
options as unknown as TextActivityOptions<
|
|
2626
|
+
AnyTextAdapter,
|
|
2627
|
+
undefined,
|
|
2628
|
+
true,
|
|
2629
|
+
TContext
|
|
2630
|
+
>,
|
|
2470
2631
|
)
|
|
2471
2632
|
|
|
2472
2633
|
return streamToText(stream)
|
|
@@ -2478,8 +2639,11 @@ function runNonStreamingText(
|
|
|
2478
2639
|
* 2. Once complete, call adapter.structuredOutput with the conversation context
|
|
2479
2640
|
* 3. Validate and return the structured result
|
|
2480
2641
|
*/
|
|
2481
|
-
async function runAgenticStructuredOutput<
|
|
2482
|
-
|
|
2642
|
+
async function runAgenticStructuredOutput<
|
|
2643
|
+
TSchema extends SchemaInput,
|
|
2644
|
+
TContext = unknown,
|
|
2645
|
+
>(
|
|
2646
|
+
options: TextActivityOptions<AnyTextAdapter, TSchema, boolean, TContext>,
|
|
2483
2647
|
): Promise<InferSchemaType<TSchema>> {
|
|
2484
2648
|
const { adapter, outputSchema, middleware, context, debug, ...textOptions } =
|
|
2485
2649
|
options
|
|
@@ -2521,7 +2685,8 @@ async function runAgenticStructuredOutput<TSchema extends SchemaInput>(
|
|
|
2521
2685
|
adapter,
|
|
2522
2686
|
params: { ...textOptions, model, logger } as TextOptions<
|
|
2523
2687
|
Record<string, unknown>,
|
|
2524
|
-
Record<string, unknown
|
|
2688
|
+
Record<string, unknown>,
|
|
2689
|
+
TContext
|
|
2525
2690
|
>,
|
|
2526
2691
|
middleware,
|
|
2527
2692
|
context,
|
|
@@ -2711,8 +2876,11 @@ async function* fallbackStructuredOutputStream(
|
|
|
2711
2876
|
* synchronously at call time rather than as a yielded RUN_ERROR mid-stream —
|
|
2712
2877
|
* those are programmer errors, not runtime conditions.
|
|
2713
2878
|
*/
|
|
2714
|
-
function runStreamingStructuredOutput<
|
|
2715
|
-
|
|
2879
|
+
function runStreamingStructuredOutput<
|
|
2880
|
+
TSchema extends SchemaInput,
|
|
2881
|
+
TContext = unknown,
|
|
2882
|
+
>(
|
|
2883
|
+
options: TextActivityOptions<AnyTextAdapter, TSchema, true, TContext>,
|
|
2716
2884
|
): StructuredOutputStream<InferSchemaType<TSchema>> {
|
|
2717
2885
|
const { outputSchema } = options
|
|
2718
2886
|
|
|
@@ -2758,8 +2926,11 @@ type StructuredOutputStreamInternal<T> = AsyncIterable<
|
|
|
2758
2926
|
StreamChunk | StructuredOutputCompleteEvent<T>
|
|
2759
2927
|
>
|
|
2760
2928
|
|
|
2761
|
-
async function* runStreamingStructuredOutputImpl<
|
|
2762
|
-
|
|
2929
|
+
async function* runStreamingStructuredOutputImpl<
|
|
2930
|
+
TSchema extends SchemaInput,
|
|
2931
|
+
TContext = unknown,
|
|
2932
|
+
>(
|
|
2933
|
+
options: TextActivityOptions<AnyTextAdapter, TSchema, true, TContext>,
|
|
2763
2934
|
jsonSchema: NonNullable<ReturnType<typeof convertSchemaToJsonSchema>>,
|
|
2764
2935
|
): StructuredOutputStreamInternal<InferSchemaType<TSchema>> {
|
|
2765
2936
|
const { adapter, outputSchema, middleware, context, debug, ...textOptions } =
|
|
@@ -2784,7 +2955,8 @@ async function* runStreamingStructuredOutputImpl<TSchema extends SchemaInput>(
|
|
|
2784
2955
|
adapter,
|
|
2785
2956
|
params: { ...textOptions, model, logger } as TextOptions<
|
|
2786
2957
|
Record<string, unknown>,
|
|
2787
|
-
Record<string, unknown
|
|
2958
|
+
Record<string, unknown>,
|
|
2959
|
+
TContext
|
|
2788
2960
|
>,
|
|
2789
2961
|
middleware,
|
|
2790
2962
|
context,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeToolResult } from '../../utilities/tool-result'
|
|
1
2
|
import type {
|
|
2
3
|
ContentPart,
|
|
3
4
|
MessagePart,
|
|
@@ -339,7 +340,7 @@ function buildAssistantMessages(uiMessage: UIMessage): Array<ModelMessage> {
|
|
|
339
340
|
if (part.output !== undefined && !emittedToolResultIds.has(part.id)) {
|
|
340
341
|
messageList.push({
|
|
341
342
|
role: 'tool',
|
|
342
|
-
content:
|
|
343
|
+
content: normalizeToolResult(part.output),
|
|
343
344
|
toolCallId: part.id,
|
|
344
345
|
})
|
|
345
346
|
emittedToolResultIds.add(part.id)
|
|
@@ -18,12 +18,12 @@ import type {
|
|
|
18
18
|
} from './types'
|
|
19
19
|
|
|
20
20
|
/** Check if a middleware should be skipped for instrumentation events. */
|
|
21
|
-
function shouldSkipInstrumentation(mw: ChatMiddleware): boolean {
|
|
21
|
+
function shouldSkipInstrumentation(mw: ChatMiddleware<any>): boolean {
|
|
22
22
|
return mw.name === 'devtools' || mw.name === 'strip-to-spec'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/** Build the base context for middleware instrumentation events. */
|
|
26
|
-
function instrumentCtx(ctx: ChatMiddlewareContext) {
|
|
26
|
+
function instrumentCtx(ctx: ChatMiddlewareContext<any>) {
|
|
27
27
|
return {
|
|
28
28
|
requestId: ctx.requestId,
|
|
29
29
|
streamId: ctx.streamId,
|
|
@@ -36,12 +36,12 @@ function instrumentCtx(ctx: ChatMiddlewareContext) {
|
|
|
36
36
|
* Internal middleware runner that manages composed execution of middleware hooks.
|
|
37
37
|
* Created once per chat() invocation.
|
|
38
38
|
*/
|
|
39
|
-
export class MiddlewareRunner {
|
|
40
|
-
private readonly middlewares: ReadonlyArray<ChatMiddleware
|
|
39
|
+
export class MiddlewareRunner<TContext = unknown> {
|
|
40
|
+
private readonly middlewares: ReadonlyArray<ChatMiddleware<TContext>>
|
|
41
41
|
private readonly logger: InternalLogger
|
|
42
42
|
|
|
43
43
|
constructor(
|
|
44
|
-
middlewares: ReadonlyArray<ChatMiddleware
|
|
44
|
+
middlewares: ReadonlyArray<ChatMiddleware<TContext>>,
|
|
45
45
|
logger: InternalLogger,
|
|
46
46
|
) {
|
|
47
47
|
this.middlewares = middlewares
|
|
@@ -58,7 +58,7 @@ export class MiddlewareRunner {
|
|
|
58
58
|
* Partial returns are shallow-merged with the current config.
|
|
59
59
|
*/
|
|
60
60
|
async runOnConfig(
|
|
61
|
-
ctx: ChatMiddlewareContext
|
|
61
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
62
62
|
config: ChatMiddlewareConfig,
|
|
63
63
|
): Promise<ChatMiddlewareConfig> {
|
|
64
64
|
let current = config
|
|
@@ -113,7 +113,7 @@ export class MiddlewareRunner {
|
|
|
113
113
|
* same boundary (which receives a ChatMiddlewareConfig view, no outputSchema).
|
|
114
114
|
*/
|
|
115
115
|
async runOnStructuredOutputConfig(
|
|
116
|
-
ctx: ChatMiddlewareContext
|
|
116
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
117
117
|
config: StructuredOutputMiddlewareConfig,
|
|
118
118
|
): Promise<StructuredOutputMiddlewareConfig> {
|
|
119
119
|
let current = config
|
|
@@ -166,7 +166,7 @@ export class MiddlewareRunner {
|
|
|
166
166
|
/**
|
|
167
167
|
* Call onStart on all middleware in order.
|
|
168
168
|
*/
|
|
169
|
-
async runOnStart(ctx: ChatMiddlewareContext): Promise<void> {
|
|
169
|
+
async runOnStart(ctx: ChatMiddlewareContext<TContext>): Promise<void> {
|
|
170
170
|
for (const mw of this.middlewares) {
|
|
171
171
|
if (mw.onStart) {
|
|
172
172
|
const skip = shouldSkipInstrumentation(mw)
|
|
@@ -200,7 +200,7 @@ export class MiddlewareRunner {
|
|
|
200
200
|
* - null: drop the chunk entirely
|
|
201
201
|
*/
|
|
202
202
|
async runOnChunk(
|
|
203
|
-
ctx: ChatMiddlewareContext
|
|
203
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
204
204
|
chunk: StreamChunk,
|
|
205
205
|
): Promise<Array<StreamChunk>> {
|
|
206
206
|
let chunks: Array<StreamChunk> = [chunk]
|
|
@@ -298,7 +298,7 @@ export class MiddlewareRunner {
|
|
|
298
298
|
* Returns the first non-void decision, or undefined to continue normally.
|
|
299
299
|
*/
|
|
300
300
|
async runOnBeforeToolCall(
|
|
301
|
-
ctx: ChatMiddlewareContext
|
|
301
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
302
302
|
hookCtx: ToolCallHookContext,
|
|
303
303
|
): Promise<BeforeToolCallDecision> {
|
|
304
304
|
for (const mw of this.middlewares) {
|
|
@@ -333,7 +333,7 @@ export class MiddlewareRunner {
|
|
|
333
333
|
* Run onAfterToolCall on all middleware in order.
|
|
334
334
|
*/
|
|
335
335
|
async runOnAfterToolCall(
|
|
336
|
-
ctx: ChatMiddlewareContext
|
|
336
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
337
337
|
info: AfterToolCallInfo,
|
|
338
338
|
): Promise<void> {
|
|
339
339
|
for (const mw of this.middlewares) {
|
|
@@ -363,7 +363,7 @@ export class MiddlewareRunner {
|
|
|
363
363
|
* Run onUsage on all middleware in order.
|
|
364
364
|
*/
|
|
365
365
|
async runOnUsage(
|
|
366
|
-
ctx: ChatMiddlewareContext
|
|
366
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
367
367
|
usage: UsageInfo,
|
|
368
368
|
): Promise<void> {
|
|
369
369
|
for (const mw of this.middlewares) {
|
|
@@ -393,7 +393,7 @@ export class MiddlewareRunner {
|
|
|
393
393
|
* Run onFinish on all middleware in order.
|
|
394
394
|
*/
|
|
395
395
|
async runOnFinish(
|
|
396
|
-
ctx: ChatMiddlewareContext
|
|
396
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
397
397
|
info: FinishInfo,
|
|
398
398
|
): Promise<void> {
|
|
399
399
|
for (const mw of this.middlewares) {
|
|
@@ -422,7 +422,10 @@ export class MiddlewareRunner {
|
|
|
422
422
|
/**
|
|
423
423
|
* Run onAbort on all middleware in order.
|
|
424
424
|
*/
|
|
425
|
-
async runOnAbort(
|
|
425
|
+
async runOnAbort(
|
|
426
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
427
|
+
info: AbortInfo,
|
|
428
|
+
): Promise<void> {
|
|
426
429
|
for (const mw of this.middlewares) {
|
|
427
430
|
if (mw.onAbort) {
|
|
428
431
|
const skip = shouldSkipInstrumentation(mw)
|
|
@@ -449,7 +452,10 @@ export class MiddlewareRunner {
|
|
|
449
452
|
/**
|
|
450
453
|
* Run onError on all middleware in order.
|
|
451
454
|
*/
|
|
452
|
-
async runOnError(
|
|
455
|
+
async runOnError(
|
|
456
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
457
|
+
info: ErrorInfo,
|
|
458
|
+
): Promise<void> {
|
|
453
459
|
for (const mw of this.middlewares) {
|
|
454
460
|
if (mw.onError) {
|
|
455
461
|
const skip = shouldSkipInstrumentation(mw)
|
|
@@ -478,7 +484,7 @@ export class MiddlewareRunner {
|
|
|
478
484
|
* Called at the start of each agent loop iteration.
|
|
479
485
|
*/
|
|
480
486
|
async runOnIteration(
|
|
481
|
-
ctx: ChatMiddlewareContext
|
|
487
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
482
488
|
info: IterationInfo,
|
|
483
489
|
): Promise<void> {
|
|
484
490
|
for (const mw of this.middlewares) {
|
|
@@ -509,7 +515,7 @@ export class MiddlewareRunner {
|
|
|
509
515
|
* Called after all tool calls in an iteration have been processed.
|
|
510
516
|
*/
|
|
511
517
|
async runOnToolPhaseComplete(
|
|
512
|
-
ctx: ChatMiddlewareContext
|
|
518
|
+
ctx: ChatMiddlewareContext<TContext>,
|
|
513
519
|
info: ToolPhaseCompleteInfo,
|
|
514
520
|
): Promise<void> {
|
|
515
521
|
for (const mw of this.middlewares) {
|