@tanstack/ai 0.20.0 → 0.21.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.js +3 -1
- package/dist/esm/activities/chat/adapter.js.map +1 -1
- package/dist/esm/activities/chat/index.d.ts +4 -4
- package/dist/esm/activities/chat/index.js +403 -276
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +1 -1
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +10 -1
- package/dist/esm/activities/chat/middleware/compose.js +57 -0
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/index.d.ts +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +40 -8
- package/dist/esm/activities/chat/stream/processor.d.ts +8 -8
- package/dist/esm/activities/chat/stream/processor.js +29 -21
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/stream/strategies.d.ts +3 -3
- package/dist/esm/activities/chat/stream/strategies.js +4 -4
- package/dist/esm/activities/chat/stream/strategies.js.map +1 -1
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js +5 -0
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +1 -1
- package/dist/esm/activities/chat/tools/schema-converter.d.ts +13 -2
- package/dist/esm/activities/chat/tools/schema-converter.js +47 -37
- package/dist/esm/activities/chat/tools/schema-converter.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +2 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +17 -9
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.js +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/generateAudio/adapter.js +3 -1
- package/dist/esm/activities/generateAudio/adapter.js.map +1 -1
- package/dist/esm/activities/generateAudio/index.d.ts +5 -1
- package/dist/esm/activities/generateAudio/index.js.map +1 -1
- package/dist/esm/activities/generateImage/adapter.js +3 -1
- package/dist/esm/activities/generateImage/adapter.js.map +1 -1
- package/dist/esm/activities/generateImage/index.js +5 -0
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/adapter.js +3 -1
- package/dist/esm/activities/generateSpeech/adapter.js.map +1 -1
- package/dist/esm/activities/generateTranscription/adapter.js +3 -1
- package/dist/esm/activities/generateTranscription/adapter.js.map +1 -1
- package/dist/esm/activities/generateVideo/adapter.js +3 -1
- package/dist/esm/activities/generateVideo/adapter.js.map +1 -1
- package/dist/esm/activities/stream-generation-result.js +6 -2
- package/dist/esm/activities/stream-generation-result.js.map +1 -1
- package/dist/esm/activities/summarize/adapter.js +3 -1
- package/dist/esm/activities/summarize/adapter.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +5 -0
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/extend-adapter.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger/internal-logger.js +2 -0
- package/dist/esm/logger/internal-logger.js.map +1 -1
- package/dist/esm/middlewares/content-guard.js +5 -4
- package/dist/esm/middlewares/content-guard.js.map +1 -1
- package/dist/esm/middlewares/otel.js +25 -18
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/realtime/index.d.ts +1 -1
- package/dist/esm/realtime/index.js.map +1 -1
- package/dist/esm/strip-to-spec-middleware.js.map +1 -1
- package/dist/esm/tools/provider-tool.d.ts +9 -0
- package/dist/esm/tools/provider-tool.js +7 -0
- package/dist/esm/tools/provider-tool.js.map +1 -0
- package/dist/esm/types.d.ts +6 -6
- package/dist/esm/utilities/ag-ui-wire.js +4 -1
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/package.json +2 -2
- package/skills/ai-core/middleware/SKILL.md +124 -18
- package/skills/ai-core/structured-outputs/SKILL.md +13 -0
- package/src/activities/chat/index.ts +685 -394
- package/src/activities/chat/messages.ts +3 -2
- package/src/activities/chat/middleware/compose.ts +65 -5
- package/src/activities/chat/middleware/index.ts +1 -0
- package/src/activities/chat/middleware/types.ts +64 -11
- package/src/activities/chat/stream/processor.ts +21 -21
- package/src/activities/chat/stream/strategies.ts +3 -3
- package/src/activities/chat/tools/schema-converter.ts +98 -58
- package/src/activities/chat/tools/tool-calls.ts +18 -11
- package/src/activities/chat/tools/tool-definition.ts +6 -1
- package/src/activities/generateAudio/index.ts +5 -4
- package/src/activities/generateImage/index.ts +8 -3
- package/src/activities/stream-generation-result.ts +11 -2
- package/src/activities/summarize/chat-stream-summarize.ts +5 -2
- package/src/activities/summarize/index.ts +2 -2
- package/src/extend-adapter.ts +1 -1
- package/src/index.ts +6 -1
- package/src/middlewares/content-guard.ts +12 -4
- package/src/middlewares/otel.ts +32 -24
- package/src/realtime/index.ts +1 -1
- package/src/strip-to-spec-middleware.ts +1 -4
- package/src/tools/provider-tool.ts +14 -0
- package/src/types.ts +12 -8
- package/src/utilities/ag-ui-wire.ts +4 -3
- package/src/utilities/chat-params.ts +2 -2
|
@@ -81,8 +81,8 @@ export class MiddlewareAbortError extends Error {
|
|
|
81
81
|
* ```
|
|
82
82
|
*/
|
|
83
83
|
export class ToolCallManager {
|
|
84
|
-
private toolCallsMap = new Map<number, ToolCall>()
|
|
85
|
-
private tools: ReadonlyArray<Tool>
|
|
84
|
+
private readonly toolCallsMap = new Map<number, ToolCall>()
|
|
85
|
+
private readonly tools: ReadonlyArray<Tool>
|
|
86
86
|
|
|
87
87
|
constructor(tools: ReadonlyArray<Tool>) {
|
|
88
88
|
this.tools = tools
|
|
@@ -320,14 +320,16 @@ async function* executeWithEventPolling<T>(
|
|
|
320
320
|
])
|
|
321
321
|
|
|
322
322
|
// Flush any pending events
|
|
323
|
-
|
|
324
|
-
|
|
323
|
+
let event: CustomEvent | undefined
|
|
324
|
+
while ((event = pendingEvents.shift()) !== undefined) {
|
|
325
|
+
yield event
|
|
325
326
|
}
|
|
326
327
|
}
|
|
327
328
|
|
|
328
329
|
// Final flush in case events were emitted right at completion
|
|
329
|
-
|
|
330
|
-
|
|
330
|
+
let event: CustomEvent | undefined
|
|
331
|
+
while ((event = pendingEvents.shift()) !== undefined) {
|
|
332
|
+
yield event
|
|
331
333
|
}
|
|
332
334
|
|
|
333
335
|
return state.result
|
|
@@ -404,13 +406,17 @@ async function* executeServerTool(
|
|
|
404
406
|
): AsyncGenerator<CustomEvent, void, void> {
|
|
405
407
|
const startTime = Date.now()
|
|
406
408
|
try {
|
|
407
|
-
|
|
409
|
+
if (!tool.execute) {
|
|
410
|
+
throw new Error(`Tool ${toolName} has no execute() implementation`)
|
|
411
|
+
}
|
|
412
|
+
const executionPromise = Promise.resolve(tool.execute(input, context))
|
|
408
413
|
let result = yield* executeWithEventPolling(executionPromise, pendingEvents)
|
|
409
414
|
const duration = Date.now() - startTime
|
|
410
415
|
|
|
411
416
|
// Flush remaining events
|
|
412
|
-
|
|
413
|
-
|
|
417
|
+
let pendingEvent: CustomEvent | undefined
|
|
418
|
+
while ((pendingEvent = pendingEvents.shift()) !== undefined) {
|
|
419
|
+
yield pendingEvent
|
|
414
420
|
}
|
|
415
421
|
|
|
416
422
|
// Validate output against outputSchema if provided
|
|
@@ -448,8 +454,9 @@ async function* executeServerTool(
|
|
|
448
454
|
const duration = Date.now() - startTime
|
|
449
455
|
|
|
450
456
|
// Flush remaining events
|
|
451
|
-
|
|
452
|
-
|
|
457
|
+
let pendingEvent: CustomEvent | undefined
|
|
458
|
+
while ((pendingEvent = pendingEvents.shift()) !== undefined) {
|
|
459
|
+
yield pendingEvent
|
|
453
460
|
}
|
|
454
461
|
|
|
455
462
|
if (error instanceof MiddlewareAbortError) {
|
|
@@ -29,6 +29,11 @@ export interface ClientTool<
|
|
|
29
29
|
__toolSide: 'client'
|
|
30
30
|
name: TName
|
|
31
31
|
description: string
|
|
32
|
+
// Note: `inputSchema` / `outputSchema` stay as bare optionals (not
|
|
33
|
+
// widened to `| undefined`). They participate in inference via
|
|
34
|
+
// `InferToolInput` / `InferToolOutput` — widening with `| undefined`
|
|
35
|
+
// breaks the `infer TInput extends StandardJSONSchemaV1<...>` chain
|
|
36
|
+
// because `undefined` doesn't extend the schema constraint.
|
|
32
37
|
inputSchema?: TInput
|
|
33
38
|
outputSchema?: TOutput
|
|
34
39
|
needsApproval?: boolean
|
|
@@ -215,7 +220,7 @@ export function toolDefinition<
|
|
|
215
220
|
return {
|
|
216
221
|
__toolSide: 'client',
|
|
217
222
|
...config,
|
|
218
|
-
execute,
|
|
223
|
+
...(execute !== undefined && { execute }),
|
|
219
224
|
}
|
|
220
225
|
},
|
|
221
226
|
}
|
|
@@ -27,10 +27,11 @@ export const kind = 'audio' as const
|
|
|
27
27
|
/**
|
|
28
28
|
* Extract provider options from an AudioAdapter via ~types.
|
|
29
29
|
*/
|
|
30
|
-
export type AudioProviderOptions<TAdapter> =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
export type AudioProviderOptions<TAdapter> = TAdapter extends {
|
|
31
|
+
'~types': { providerOptions: infer P extends object }
|
|
32
|
+
}
|
|
33
|
+
? P
|
|
34
|
+
: object
|
|
34
35
|
|
|
35
36
|
// ===========================
|
|
36
37
|
// Activity Options Type
|
|
@@ -210,7 +210,7 @@ async function runGenerateImage<
|
|
|
210
210
|
prompt: rest.prompt,
|
|
211
211
|
numberOfImages: rest.numberOfImages,
|
|
212
212
|
size: rest.size,
|
|
213
|
-
modelOptions: rest.modelOptions
|
|
213
|
+
modelOptions: rest.modelOptions,
|
|
214
214
|
timestamp: startTime,
|
|
215
215
|
})
|
|
216
216
|
|
|
@@ -227,12 +227,17 @@ async function runGenerateImage<
|
|
|
227
227
|
requestId,
|
|
228
228
|
provider: adapter.name,
|
|
229
229
|
model,
|
|
230
|
+
// GeneratedImage is a discriminated `{ url } | { b64Json }` union, but the
|
|
231
|
+
// wire shape on the devtools event is a plain optional pair. Use
|
|
232
|
+
// conditional spreads so the emitted record only sets the field actually
|
|
233
|
+
// present — `exactOptionalPropertyTypes` rejects `field: undefined`
|
|
234
|
+
// against `field?: string` targets.
|
|
230
235
|
images: result.images.map((image) => ({
|
|
231
236
|
url: image.url,
|
|
232
237
|
b64Json: image.b64Json,
|
|
233
238
|
})),
|
|
234
239
|
duration,
|
|
235
|
-
modelOptions: rest.modelOptions
|
|
240
|
+
modelOptions: rest.modelOptions,
|
|
236
241
|
timestamp: Date.now(),
|
|
237
242
|
})
|
|
238
243
|
|
|
@@ -241,7 +246,7 @@ async function runGenerateImage<
|
|
|
241
246
|
requestId,
|
|
242
247
|
model,
|
|
243
248
|
usage: result.usage,
|
|
244
|
-
modelOptions: rest.modelOptions
|
|
249
|
+
modelOptions: rest.modelOptions,
|
|
245
250
|
timestamp: Date.now(),
|
|
246
251
|
})
|
|
247
252
|
}
|
|
@@ -55,12 +55,21 @@ export async function* streamGenerationResult<TResult>(
|
|
|
55
55
|
}
|
|
56
56
|
} catch (error: unknown) {
|
|
57
57
|
const payload = toRunErrorPayload(error, 'Generation failed')
|
|
58
|
+
// `code` is omitted entirely when undefined so the event matches the
|
|
59
|
+
// AG-UI `code?: string` shape under `exactOptionalPropertyTypes`. The
|
|
60
|
+
// deprecated nested `error` form preserves the same conditional
|
|
61
|
+
// structure for backward compatibility.
|
|
62
|
+
const codeFields =
|
|
63
|
+
payload.code !== undefined ? { code: payload.code } : undefined
|
|
58
64
|
yield {
|
|
59
65
|
type: EventType.RUN_ERROR,
|
|
60
66
|
message: payload.message,
|
|
61
|
-
|
|
67
|
+
...codeFields,
|
|
62
68
|
// Deprecated nested form for backward compatibility
|
|
63
|
-
error:
|
|
69
|
+
error: {
|
|
70
|
+
message: payload.message,
|
|
71
|
+
...codeFields,
|
|
72
|
+
},
|
|
64
73
|
timestamp: Date.now(),
|
|
65
74
|
}
|
|
66
75
|
}
|
|
@@ -46,7 +46,7 @@ export class ChatStreamSummarizeAdapter<
|
|
|
46
46
|
> extends BaseSummarizeAdapter<TModel, TProviderOptions> {
|
|
47
47
|
readonly name: string
|
|
48
48
|
|
|
49
|
-
private textAdapter: ChatStreamCapable
|
|
49
|
+
private readonly textAdapter: ChatStreamCapable
|
|
50
50
|
|
|
51
51
|
constructor(
|
|
52
52
|
textAdapter: ChatStreamCapable,
|
|
@@ -124,7 +124,7 @@ export class ChatStreamSummarizeAdapter<
|
|
|
124
124
|
return { id, model, summary, usage }
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
async *summarizeStream(
|
|
127
|
+
override async *summarizeStream(
|
|
128
128
|
options: SummarizationOptions<TProviderOptions>,
|
|
129
129
|
): AsyncIterable<StreamChunk> {
|
|
130
130
|
const systemPrompt = this.buildSummarizationPrompt(options)
|
|
@@ -221,6 +221,9 @@ export class ChatStreamSummarizeAdapter<
|
|
|
221
221
|
case 'concise':
|
|
222
222
|
prompt += 'Provide a very concise summary in 1-2 sentences. '
|
|
223
223
|
break
|
|
224
|
+
case undefined:
|
|
225
|
+
prompt += 'Provide a clear and concise summary. '
|
|
226
|
+
break
|
|
224
227
|
default:
|
|
225
228
|
prompt += 'Provide a clear and concise summary. '
|
|
226
229
|
}
|
|
@@ -159,7 +159,7 @@ export function summarize<
|
|
|
159
159
|
|
|
160
160
|
if (stream) {
|
|
161
161
|
return runStreamingSummarize(
|
|
162
|
-
options as
|
|
162
|
+
options as SummarizeActivityOptions<
|
|
163
163
|
SummarizeAdapter<string, object>,
|
|
164
164
|
true
|
|
165
165
|
>,
|
|
@@ -167,7 +167,7 @@ export function summarize<
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
return runSummarize(
|
|
170
|
-
options as
|
|
170
|
+
options as SummarizeActivityOptions<
|
|
171
171
|
SummarizeAdapter<string, object>,
|
|
172
172
|
false
|
|
173
173
|
>,
|
package/src/extend-adapter.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -53,7 +53,10 @@ export {
|
|
|
53
53
|
} from './activities/chat/tools/tool-definition'
|
|
54
54
|
|
|
55
55
|
// Schema conversion (Standard JSON Schema compliant)
|
|
56
|
-
export {
|
|
56
|
+
export {
|
|
57
|
+
convertSchemaToJsonSchema,
|
|
58
|
+
StandardSchemaValidationError,
|
|
59
|
+
} from './activities/chat/tools/schema-converter'
|
|
57
60
|
|
|
58
61
|
// Stream utilities
|
|
59
62
|
export {
|
|
@@ -69,6 +72,7 @@ export { ToolCallManager } from './activities/chat/tools/tool-calls'
|
|
|
69
72
|
|
|
70
73
|
// Provider tool type
|
|
71
74
|
export type { ProviderTool } from './tools/provider-tool'
|
|
75
|
+
export { brandProviderTool } from './tools/provider-tool'
|
|
72
76
|
|
|
73
77
|
// Agent loop strategies
|
|
74
78
|
export {
|
|
@@ -90,6 +94,7 @@ export type {
|
|
|
90
94
|
ChatMiddlewareContext,
|
|
91
95
|
ChatMiddlewarePhase,
|
|
92
96
|
ChatMiddlewareConfig,
|
|
97
|
+
StructuredOutputMiddlewareConfig,
|
|
93
98
|
ToolCallHookContext,
|
|
94
99
|
BeforeToolCallDecision,
|
|
95
100
|
AfterToolCallInfo,
|
|
@@ -142,11 +142,16 @@ function createDeltaStrategy(
|
|
|
142
142
|
|
|
143
143
|
if (blockOnMatch) return null // drop chunk
|
|
144
144
|
|
|
145
|
+
// Strip out the previous `content` field by destructuring it away — with
|
|
146
|
+
// `exactOptionalPropertyTypes` we can't assign `content: undefined`
|
|
147
|
+
// against `content?: string`. The replacement event carries only the
|
|
148
|
+
// filtered delta.
|
|
149
|
+
const { content: _strippedContent, ...rest } = chunk
|
|
150
|
+
void _strippedContent
|
|
145
151
|
return {
|
|
146
|
-
...
|
|
152
|
+
...rest,
|
|
147
153
|
delta: filtered,
|
|
148
|
-
|
|
149
|
-
} as StreamChunk
|
|
154
|
+
}
|
|
150
155
|
},
|
|
151
156
|
}
|
|
152
157
|
}
|
|
@@ -278,8 +283,11 @@ function createBufferedStrategy(
|
|
|
278
283
|
content: filtered.slice(0, safeFilteredEnd),
|
|
279
284
|
} as StreamChunk
|
|
280
285
|
|
|
286
|
+
// `pending` was empty before this push iff `emitChunk` is now the only
|
|
287
|
+
// entry — return it directly without re-indexing through `pending[0]`.
|
|
288
|
+
const wasEmpty = pending.length === 0
|
|
281
289
|
pending.push(emitChunk)
|
|
282
|
-
return
|
|
290
|
+
return wasEmpty ? emitChunk : pending
|
|
283
291
|
},
|
|
284
292
|
}
|
|
285
293
|
}
|
package/src/middlewares/otel.ts
CHANGED
|
@@ -137,8 +137,11 @@ function serializeContent(content: unknown): string {
|
|
|
137
137
|
case 'document':
|
|
138
138
|
parts.push('[document]')
|
|
139
139
|
break
|
|
140
|
+
case undefined:
|
|
141
|
+
parts.push('[unknown]')
|
|
142
|
+
break
|
|
140
143
|
default:
|
|
141
|
-
parts.push(`[${type
|
|
144
|
+
parts.push(`[${type}]`)
|
|
142
145
|
}
|
|
143
146
|
}
|
|
144
147
|
return parts.join(' ')
|
|
@@ -253,10 +256,7 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
253
256
|
const span = state.currentIterationSpan
|
|
254
257
|
const iteration = state.iterationCount - 1
|
|
255
258
|
safeCall('otel.onSpanEnd', () =>
|
|
256
|
-
onSpanEnd?.(
|
|
257
|
-
{ kind: 'iteration', ctx, iteration } as OtelSpanInfo<'iteration'>,
|
|
258
|
-
span,
|
|
259
|
-
),
|
|
259
|
+
onSpanEnd?.({ kind: 'iteration', ctx, iteration }, span),
|
|
260
260
|
)
|
|
261
261
|
span.end()
|
|
262
262
|
state.currentIterationSpan = null
|
|
@@ -630,9 +630,10 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
630
630
|
|
|
631
631
|
if (!info.ok && info.error !== undefined) {
|
|
632
632
|
toolSpan.recordException(info.error as Exception)
|
|
633
|
+
const msg = errorMessage(info.error)
|
|
633
634
|
toolSpan.setStatus({
|
|
634
635
|
code: SpanStatusCode.ERROR,
|
|
635
|
-
message:
|
|
636
|
+
...(msg !== undefined && { message: msg }),
|
|
636
637
|
})
|
|
637
638
|
}
|
|
638
639
|
|
|
@@ -672,7 +673,7 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
672
673
|
toolName: info.toolName,
|
|
673
674
|
toolCallId: info.toolCallId,
|
|
674
675
|
iteration: state.iterationCount - 1,
|
|
675
|
-
}
|
|
676
|
+
},
|
|
676
677
|
toolSpan,
|
|
677
678
|
),
|
|
678
679
|
)
|
|
@@ -688,13 +689,16 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
688
689
|
|
|
689
690
|
const errType = errorTypeName(info.error)
|
|
690
691
|
const message = errorMessage(info.error)
|
|
692
|
+
const statusMessage =
|
|
693
|
+
message !== undefined ? { message } : ({} as const)
|
|
691
694
|
const exception = info.error as Exception
|
|
692
695
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
+
const iterationSpan = state.currentIterationSpan
|
|
697
|
+
if (iterationSpan) {
|
|
698
|
+
iterationSpan.recordException(exception)
|
|
699
|
+
iterationSpan.setStatus({
|
|
696
700
|
code: SpanStatusCode.ERROR,
|
|
697
|
-
|
|
701
|
+
...statusMessage,
|
|
698
702
|
})
|
|
699
703
|
safeCall('otel.onSpanEnd', () =>
|
|
700
704
|
onSpanEnd?.(
|
|
@@ -702,18 +706,18 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
702
706
|
kind: 'iteration',
|
|
703
707
|
ctx,
|
|
704
708
|
iteration: state.iterationCount - 1,
|
|
705
|
-
}
|
|
706
|
-
|
|
709
|
+
},
|
|
710
|
+
iterationSpan,
|
|
707
711
|
),
|
|
708
712
|
)
|
|
709
|
-
|
|
713
|
+
iterationSpan.end()
|
|
710
714
|
state.currentIterationSpan = null
|
|
711
715
|
}
|
|
712
716
|
|
|
713
717
|
for (const [id, entry] of state.toolSpans) {
|
|
714
718
|
const { span, toolName } = entry
|
|
715
719
|
span.recordException(exception)
|
|
716
|
-
span.setStatus({ code: SpanStatusCode.ERROR,
|
|
720
|
+
span.setStatus({ code: SpanStatusCode.ERROR, ...statusMessage })
|
|
717
721
|
safeCall('otel.onSpanEnd', () =>
|
|
718
722
|
onSpanEnd?.(
|
|
719
723
|
{
|
|
@@ -722,7 +726,7 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
722
726
|
toolCallId: id,
|
|
723
727
|
toolName,
|
|
724
728
|
iteration: state.iterationCount - 1,
|
|
725
|
-
}
|
|
729
|
+
},
|
|
726
730
|
span,
|
|
727
731
|
),
|
|
728
732
|
)
|
|
@@ -731,7 +735,10 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
731
735
|
}
|
|
732
736
|
|
|
733
737
|
state.rootSpan.recordException(exception)
|
|
734
|
-
state.rootSpan.setStatus({
|
|
738
|
+
state.rootSpan.setStatus({
|
|
739
|
+
code: SpanStatusCode.ERROR,
|
|
740
|
+
...statusMessage,
|
|
741
|
+
})
|
|
735
742
|
|
|
736
743
|
if (durationHistogram) {
|
|
737
744
|
durationHistogram.record(info.duration / 1000, {
|
|
@@ -763,19 +770,20 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
763
770
|
span.setStatus({ code: SpanStatusCode.ERROR, message: 'cancelled' })
|
|
764
771
|
}
|
|
765
772
|
|
|
766
|
-
|
|
767
|
-
|
|
773
|
+
const iterationSpan = state.currentIterationSpan
|
|
774
|
+
if (iterationSpan) {
|
|
775
|
+
closeCancelled(iterationSpan)
|
|
768
776
|
safeCall('otel.onSpanEnd', () =>
|
|
769
777
|
onSpanEnd?.(
|
|
770
778
|
{
|
|
771
779
|
kind: 'iteration',
|
|
772
780
|
ctx,
|
|
773
781
|
iteration: state.iterationCount - 1,
|
|
774
|
-
}
|
|
775
|
-
|
|
782
|
+
},
|
|
783
|
+
iterationSpan,
|
|
776
784
|
),
|
|
777
785
|
)
|
|
778
|
-
|
|
786
|
+
iterationSpan.end()
|
|
779
787
|
state.currentIterationSpan = null
|
|
780
788
|
}
|
|
781
789
|
for (const [id, entry] of state.toolSpans) {
|
|
@@ -789,7 +797,7 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
789
797
|
toolCallId: id,
|
|
790
798
|
toolName,
|
|
791
799
|
iteration: state.iterationCount - 1,
|
|
792
|
-
}
|
|
800
|
+
},
|
|
793
801
|
span,
|
|
794
802
|
),
|
|
795
803
|
)
|
|
@@ -834,7 +842,7 @@ export function otelMiddleware(options: OtelMiddlewareOptions): ChatMiddleware {
|
|
|
834
842
|
toolCallId: id,
|
|
835
843
|
toolName,
|
|
836
844
|
iteration: state.iterationCount - 1,
|
|
837
|
-
}
|
|
845
|
+
},
|
|
838
846
|
span,
|
|
839
847
|
),
|
|
840
848
|
)
|
package/src/realtime/index.ts
CHANGED
|
@@ -12,10 +12,7 @@ import type { StreamChunk } from './types'
|
|
|
12
12
|
*/
|
|
13
13
|
export function stripToSpec(chunk: StreamChunk): StreamChunk {
|
|
14
14
|
// Only strip the deprecated nested error object from RUN_ERROR
|
|
15
|
-
if (
|
|
16
|
-
(chunk as StreamChunk & { type: string }).type === 'RUN_ERROR' &&
|
|
17
|
-
'error' in chunk
|
|
18
|
-
) {
|
|
15
|
+
if (chunk.type === 'RUN_ERROR' && 'error' in chunk) {
|
|
19
16
|
const { error: _deprecated, ...rest } = chunk as Record<string, unknown>
|
|
20
17
|
return rest as StreamChunk
|
|
21
18
|
}
|
|
@@ -23,3 +23,17 @@ export interface ProviderTool<
|
|
|
23
23
|
readonly '~provider': TProvider
|
|
24
24
|
readonly '~toolKind': TKind
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Attach the `ProviderTool` phantom brand to a plain `Tool`-shaped object.
|
|
29
|
+
*
|
|
30
|
+
* The brand fields (`'~provider'`, `'~toolKind'`) exist only in the type
|
|
31
|
+
* system and are never assigned at runtime, so this is a single audited
|
|
32
|
+
* type-only assertion. Use it inside adapter `xxxTool()` factories instead
|
|
33
|
+
* of `as unknown as` — the cast collapses to one named site.
|
|
34
|
+
*/
|
|
35
|
+
export function brandProviderTool<T extends ProviderTool<string, string>>(
|
|
36
|
+
tool: Omit<T, '~provider' | '~toolKind'>,
|
|
37
|
+
): T {
|
|
38
|
+
return tool as T
|
|
39
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -586,7 +586,9 @@ export interface Tool<
|
|
|
586
586
|
* return weather; // Can return object or string
|
|
587
587
|
* }
|
|
588
588
|
*/
|
|
589
|
-
execute?:
|
|
589
|
+
execute?:
|
|
590
|
+
| ((args: any, context?: ToolExecutionContext) => Promise<any> | any)
|
|
591
|
+
| undefined
|
|
590
592
|
|
|
591
593
|
/** If true, tool execution requires user approval before running. Works with both server and client tools. */
|
|
592
594
|
needsApproval?: boolean
|
|
@@ -595,7 +597,7 @@ export interface Tool<
|
|
|
595
597
|
lazy?: boolean
|
|
596
598
|
|
|
597
599
|
/** Additional metadata for adapters or custom extensions */
|
|
598
|
-
metadata?: Record<string, any>
|
|
600
|
+
metadata?: Record<string, any> | undefined
|
|
599
601
|
}
|
|
600
602
|
|
|
601
603
|
export interface ToolConfig {
|
|
@@ -729,7 +731,7 @@ export interface TextOptions<
|
|
|
729
731
|
> {
|
|
730
732
|
model: string
|
|
731
733
|
messages: Array<ModelMessage>
|
|
732
|
-
tools?: Array<Tool<any, any, any>>
|
|
734
|
+
tools?: Array<Tool<any, any, any>> | undefined
|
|
733
735
|
/**
|
|
734
736
|
* System prompts to include with the request.
|
|
735
737
|
*
|
|
@@ -791,7 +793,7 @@ export interface TextOptions<
|
|
|
791
793
|
* - Anthropic: `metadata` (Record<string, any>) - includes optional user_id (max 256 chars)
|
|
792
794
|
* - Gemini: Not directly available in TextProviderOptions
|
|
793
795
|
*/
|
|
794
|
-
metadata?: Record<string, any>
|
|
796
|
+
metadata?: Record<string, any> | undefined
|
|
795
797
|
modelOptions?: TProviderOptionsForModel
|
|
796
798
|
request?: Request | RequestInit
|
|
797
799
|
|
|
@@ -938,10 +940,12 @@ export interface RunErrorEvent extends AGUIRunErrorEvent {
|
|
|
938
940
|
* @deprecated Use top-level `message` and `code` fields instead.
|
|
939
941
|
* Kept for backward compatibility.
|
|
940
942
|
*/
|
|
941
|
-
error?:
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
943
|
+
error?:
|
|
944
|
+
| {
|
|
945
|
+
message: string
|
|
946
|
+
code?: string | undefined
|
|
947
|
+
}
|
|
948
|
+
| undefined
|
|
945
949
|
}
|
|
946
950
|
|
|
947
951
|
/**
|
|
@@ -53,6 +53,7 @@ export function uiMessagesToWire(
|
|
|
53
53
|
// Defensive: if parts is missing (ModelMessage-shaped input), pass through as-is.
|
|
54
54
|
// UIMessage always has parts; ModelMessage uses content directly.
|
|
55
55
|
const parts: ReadonlyArray<MessagePart> =
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- runtime input may be ModelMessage-shaped (no `parts`); cast forces the optional-chain fallback below to remain in scope
|
|
56
57
|
(msg.parts as ReadonlyArray<MessagePart> | undefined) ?? []
|
|
57
58
|
|
|
58
59
|
if (msg.role === 'system') {
|
|
@@ -61,7 +62,7 @@ export function uiMessagesToWire(
|
|
|
61
62
|
content:
|
|
62
63
|
parts.length > 0
|
|
63
64
|
? collectText(parts)
|
|
64
|
-
: ((msg as
|
|
65
|
+
: ((msg as { content?: string }).content ?? ''),
|
|
65
66
|
})
|
|
66
67
|
continue
|
|
67
68
|
}
|
|
@@ -72,7 +73,7 @@ export function uiMessagesToWire(
|
|
|
72
73
|
content:
|
|
73
74
|
parts.length > 0
|
|
74
75
|
? collectUserContent(parts)
|
|
75
|
-
: ((msg as
|
|
76
|
+
: ((msg as { content?: string }).content ?? ''),
|
|
76
77
|
})
|
|
77
78
|
continue
|
|
78
79
|
}
|
|
@@ -161,7 +162,7 @@ function collectUserContent(
|
|
|
161
162
|
p.type === 'video' ||
|
|
162
163
|
p.type === 'document'
|
|
163
164
|
) {
|
|
164
|
-
out.push(p
|
|
165
|
+
out.push(p)
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
return out
|
|
@@ -131,7 +131,7 @@ export async function chatParamsFromRequest(
|
|
|
131
131
|
'Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.',
|
|
132
132
|
{ status: 400 },
|
|
133
133
|
)
|
|
134
|
-
;(res as
|
|
134
|
+
;(res as { cause?: unknown }).cause = cause
|
|
135
135
|
throw res
|
|
136
136
|
}
|
|
137
137
|
try {
|
|
@@ -145,7 +145,7 @@ export async function chatParamsFromRequest(
|
|
|
145
145
|
'Invalid AG-UI request body. See docs/migration/ag-ui-compliance.md.',
|
|
146
146
|
{ status: 400 },
|
|
147
147
|
)
|
|
148
|
-
;(res as
|
|
148
|
+
;(res as { cause?: unknown }).cause = cause
|
|
149
149
|
throw res
|
|
150
150
|
}
|
|
151
151
|
}
|