@tanstack/ai 0.5.1 → 0.6.2
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 +1 -1
- package/dist/esm/activities/chat/index.js +47 -6
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +2 -2
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.js +7 -4
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +3 -0
- package/dist/esm/activities/chat/stream/processor.js +10 -4
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +5 -3
- package/dist/esm/activities/chat/tools/tool-calls.js +66 -3
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +2 -2
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/types.d.ts +30 -3
- package/package.json +1 -1
- package/src/activities/chat/index.ts +75 -4
- package/src/activities/chat/messages.ts +5 -7
- package/src/activities/chat/stream/message-updaters.ts +6 -4
- package/src/activities/chat/stream/processor.ts +22 -4
- package/src/activities/chat/tools/tool-calls.ts +102 -5
- package/src/activities/chat/tools/tool-definition.ts +3 -0
- package/src/types.ts +31 -3
|
@@ -24,6 +24,7 @@ import type { AnyTextAdapter } from './adapter'
|
|
|
24
24
|
import type {
|
|
25
25
|
AgentLoopStrategy,
|
|
26
26
|
ConstrainedModelMessage,
|
|
27
|
+
CustomEvent,
|
|
27
28
|
InferSchemaType,
|
|
28
29
|
ModelMessage,
|
|
29
30
|
RunFinishedEvent,
|
|
@@ -601,17 +602,30 @@ class TextEngine<
|
|
|
601
602
|
|
|
602
603
|
const { approvals, clientToolResults } = this.collectClientState()
|
|
603
604
|
|
|
604
|
-
const
|
|
605
|
+
const generator = executeToolCalls(
|
|
605
606
|
pendingToolCalls,
|
|
606
607
|
this.tools,
|
|
607
608
|
approvals,
|
|
608
609
|
clientToolResults,
|
|
610
|
+
(eventName, data) => this.createCustomEventChunk(eventName, data),
|
|
609
611
|
)
|
|
610
612
|
|
|
613
|
+
// Consume the async generator, yielding custom events and collecting the return value
|
|
614
|
+
const executionResult = yield* this.drainToolCallGenerator(generator)
|
|
615
|
+
|
|
611
616
|
if (
|
|
612
617
|
executionResult.needsApproval.length > 0 ||
|
|
613
618
|
executionResult.needsClientExecution.length > 0
|
|
614
619
|
) {
|
|
620
|
+
if (executionResult.results.length > 0) {
|
|
621
|
+
for (const chunk of this.emitToolResults(
|
|
622
|
+
executionResult.results,
|
|
623
|
+
finishEvent,
|
|
624
|
+
)) {
|
|
625
|
+
yield chunk
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
615
629
|
for (const chunk of this.emitApprovalRequests(
|
|
616
630
|
executionResult.needsApproval,
|
|
617
631
|
finishEvent,
|
|
@@ -660,17 +674,30 @@ class TextEngine<
|
|
|
660
674
|
|
|
661
675
|
const { approvals, clientToolResults } = this.collectClientState()
|
|
662
676
|
|
|
663
|
-
const
|
|
677
|
+
const generator = executeToolCalls(
|
|
664
678
|
toolCalls,
|
|
665
679
|
this.tools,
|
|
666
680
|
approvals,
|
|
667
681
|
clientToolResults,
|
|
682
|
+
(eventName, data) => this.createCustomEventChunk(eventName, data),
|
|
668
683
|
)
|
|
669
684
|
|
|
685
|
+
// Consume the async generator, yielding custom events and collecting the return value
|
|
686
|
+
const executionResult = yield* this.drainToolCallGenerator(generator)
|
|
687
|
+
|
|
670
688
|
if (
|
|
671
689
|
executionResult.needsApproval.length > 0 ||
|
|
672
690
|
executionResult.needsClientExecution.length > 0
|
|
673
691
|
) {
|
|
692
|
+
if (executionResult.results.length > 0) {
|
|
693
|
+
for (const chunk of this.emitToolResults(
|
|
694
|
+
executionResult.results,
|
|
695
|
+
finishEvent,
|
|
696
|
+
)) {
|
|
697
|
+
yield chunk
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
674
701
|
for (const chunk of this.emitApprovalRequests(
|
|
675
702
|
executionResult.needsApproval,
|
|
676
703
|
finishEvent,
|
|
@@ -833,7 +860,7 @@ class TextEngine<
|
|
|
833
860
|
timestamp: Date.now(),
|
|
834
861
|
model: finishEvent.model,
|
|
835
862
|
name: 'approval-requested',
|
|
836
|
-
|
|
863
|
+
value: {
|
|
837
864
|
toolCallId: approval.toolCallId,
|
|
838
865
|
toolName: approval.toolName,
|
|
839
866
|
input: approval.input,
|
|
@@ -870,7 +897,7 @@ class TextEngine<
|
|
|
870
897
|
timestamp: Date.now(),
|
|
871
898
|
model: finishEvent.model,
|
|
872
899
|
name: 'tool-input-available',
|
|
873
|
-
|
|
900
|
+
value: {
|
|
874
901
|
toolCallId: clientTool.toolCallId,
|
|
875
902
|
toolName: clientTool.toolName,
|
|
876
903
|
input: clientTool.input,
|
|
@@ -1052,6 +1079,50 @@ class TextEngine<
|
|
|
1052
1079
|
}
|
|
1053
1080
|
}
|
|
1054
1081
|
|
|
1082
|
+
/**
|
|
1083
|
+
* Drain an executeToolCalls async generator, yielding any CustomEvent chunks
|
|
1084
|
+
* and returning the final ExecuteToolCallsResult.
|
|
1085
|
+
*/
|
|
1086
|
+
private async *drainToolCallGenerator(
|
|
1087
|
+
generator: AsyncGenerator<
|
|
1088
|
+
CustomEvent,
|
|
1089
|
+
{
|
|
1090
|
+
results: Array<ToolResult>
|
|
1091
|
+
needsApproval: Array<ApprovalRequest>
|
|
1092
|
+
needsClientExecution: Array<ClientToolRequest>
|
|
1093
|
+
},
|
|
1094
|
+
void
|
|
1095
|
+
>,
|
|
1096
|
+
): AsyncGenerator<
|
|
1097
|
+
StreamChunk,
|
|
1098
|
+
{
|
|
1099
|
+
results: Array<ToolResult>
|
|
1100
|
+
needsApproval: Array<ApprovalRequest>
|
|
1101
|
+
needsClientExecution: Array<ClientToolRequest>
|
|
1102
|
+
},
|
|
1103
|
+
void
|
|
1104
|
+
> {
|
|
1105
|
+
let next = await generator.next()
|
|
1106
|
+
while (!next.done) {
|
|
1107
|
+
yield next.value
|
|
1108
|
+
next = await generator.next()
|
|
1109
|
+
}
|
|
1110
|
+
return next.value
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
private createCustomEventChunk(
|
|
1114
|
+
eventName: string,
|
|
1115
|
+
value: Record<string, any>,
|
|
1116
|
+
): CustomEvent {
|
|
1117
|
+
return {
|
|
1118
|
+
type: 'CUSTOM',
|
|
1119
|
+
timestamp: Date.now(),
|
|
1120
|
+
model: this.params.model,
|
|
1121
|
+
name: eventName,
|
|
1122
|
+
value,
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1055
1126
|
private createId(prefix: string): string {
|
|
1056
1127
|
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
1057
1128
|
}
|
|
@@ -243,12 +243,9 @@ function buildAssistantMessages(uiMessage: UIMessage): Array<ModelMessage> {
|
|
|
243
243
|
for (const part of uiMessage.parts) {
|
|
244
244
|
if (part.type !== 'tool-call') continue
|
|
245
245
|
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
!part.approval &&
|
|
250
|
-
!emittedToolResultIds.has(part.id)
|
|
251
|
-
) {
|
|
246
|
+
// Output takes priority — if the tool has already produced a result,
|
|
247
|
+
// emit the concrete output regardless of approval metadata.
|
|
248
|
+
if (part.output !== undefined && !emittedToolResultIds.has(part.id)) {
|
|
252
249
|
messageList.push({
|
|
253
250
|
role: 'tool',
|
|
254
251
|
content: JSON.stringify(part.output),
|
|
@@ -257,8 +254,9 @@ function buildAssistantMessages(uiMessage: UIMessage): Array<ModelMessage> {
|
|
|
257
254
|
emittedToolResultIds.add(part.id)
|
|
258
255
|
}
|
|
259
256
|
|
|
260
|
-
// Approval response
|
|
257
|
+
// Approval response without output — emit approval status for iteration tracking
|
|
261
258
|
if (
|
|
259
|
+
part.output === undefined &&
|
|
262
260
|
part.state === 'approval-responded' &&
|
|
263
261
|
part.approval?.approved !== undefined &&
|
|
264
262
|
!emittedToolResultIds.has(part.id)
|
|
@@ -63,8 +63,7 @@ export function updateToolCallPart(
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
const parts = [...msg.parts]
|
|
66
|
-
|
|
67
|
-
const existingPartIndex = parts.findIndex(
|
|
66
|
+
const existing = parts.find(
|
|
68
67
|
(p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCall.id,
|
|
69
68
|
)
|
|
70
69
|
|
|
@@ -74,11 +73,14 @@ export function updateToolCallPart(
|
|
|
74
73
|
name: toolCall.name,
|
|
75
74
|
arguments: toolCall.arguments,
|
|
76
75
|
state: toolCall.state,
|
|
76
|
+
// Carry forward approval and output from the existing part
|
|
77
|
+
...(existing?.approval && { approval: { ...existing.approval } }),
|
|
78
|
+
...(existing?.output !== undefined && { output: existing.output }),
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
if (
|
|
81
|
+
if (existing) {
|
|
80
82
|
// Update existing tool call
|
|
81
|
-
parts[
|
|
83
|
+
parts[parts.indexOf(existing)] = toolCallPart
|
|
82
84
|
} else {
|
|
83
85
|
// Add new tool call at the end (preserve natural streaming order)
|
|
84
86
|
parts.push(toolCallPart)
|
|
@@ -73,6 +73,13 @@ export interface StreamProcessorEvents {
|
|
|
73
73
|
approvalId: string
|
|
74
74
|
}) => void
|
|
75
75
|
|
|
76
|
+
// Custom events from server-side tools
|
|
77
|
+
onCustomEvent?: (
|
|
78
|
+
eventType: string,
|
|
79
|
+
data: unknown,
|
|
80
|
+
context: { toolCallId?: string },
|
|
81
|
+
) => void
|
|
82
|
+
|
|
76
83
|
// Granular events for UI optimization (character-by-character, state tracking)
|
|
77
84
|
onTextUpdate?: (messageId: string, content: string) => void
|
|
78
85
|
onToolCallStateChange?: (
|
|
@@ -807,8 +814,8 @@ export class StreamProcessor {
|
|
|
807
814
|
chunk: Extract<StreamChunk, { type: 'CUSTOM' }>,
|
|
808
815
|
): void {
|
|
809
816
|
// Handle client tool input availability - trigger client-side execution
|
|
810
|
-
if (chunk.name === 'tool-input-available' && chunk.
|
|
811
|
-
const { toolCallId, toolName, input } = chunk.
|
|
817
|
+
if (chunk.name === 'tool-input-available' && chunk.value) {
|
|
818
|
+
const { toolCallId, toolName, input } = chunk.value as {
|
|
812
819
|
toolCallId: string
|
|
813
820
|
toolName: string
|
|
814
821
|
input: any
|
|
@@ -820,11 +827,12 @@ export class StreamProcessor {
|
|
|
820
827
|
toolName,
|
|
821
828
|
input,
|
|
822
829
|
})
|
|
830
|
+
return
|
|
823
831
|
}
|
|
824
832
|
|
|
825
833
|
// Handle approval requests
|
|
826
|
-
if (chunk.name === 'approval-requested' && chunk.
|
|
827
|
-
const { toolCallId, toolName, input, approval } = chunk.
|
|
834
|
+
if (chunk.name === 'approval-requested' && chunk.value) {
|
|
835
|
+
const { toolCallId, toolName, input, approval } = chunk.value as {
|
|
828
836
|
toolCallId: string
|
|
829
837
|
toolName: string
|
|
830
838
|
input: any
|
|
@@ -849,6 +857,16 @@ export class StreamProcessor {
|
|
|
849
857
|
input,
|
|
850
858
|
approvalId: approval.id,
|
|
851
859
|
})
|
|
860
|
+
return
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Forward non-system custom events to onCustomEvent callback
|
|
864
|
+
if (this.events.onCustomEvent) {
|
|
865
|
+
const toolCallId =
|
|
866
|
+
chunk.value && typeof chunk.value === 'object'
|
|
867
|
+
? (chunk.value as any).toolCallId
|
|
868
|
+
: undefined
|
|
869
|
+
this.events.onCustomEvent(chunk.name, chunk.value, { toolCallId })
|
|
852
870
|
}
|
|
853
871
|
}
|
|
854
872
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isStandardSchema, parseWithStandardSchema } from './schema-converter'
|
|
2
2
|
import type {
|
|
3
|
+
CustomEvent,
|
|
3
4
|
ModelMessage,
|
|
4
5
|
RunFinishedEvent,
|
|
5
6
|
Tool,
|
|
@@ -7,6 +8,7 @@ import type {
|
|
|
7
8
|
ToolCallArgsEvent,
|
|
8
9
|
ToolCallEndEvent,
|
|
9
10
|
ToolCallStartEvent,
|
|
11
|
+
ToolExecutionContext,
|
|
10
12
|
} from '../../../types'
|
|
11
13
|
|
|
12
14
|
/**
|
|
@@ -250,7 +252,46 @@ interface ExecuteToolCallsResult {
|
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
/**
|
|
253
|
-
*
|
|
255
|
+
* Helper that runs a tool execution promise while polling for pending custom events.
|
|
256
|
+
* Yields any custom events that are emitted during execution, then returns the
|
|
257
|
+
* execution result.
|
|
258
|
+
*/
|
|
259
|
+
async function* executeWithEventPolling<T>(
|
|
260
|
+
executionPromise: Promise<T>,
|
|
261
|
+
pendingEvents: Array<CustomEvent>,
|
|
262
|
+
): AsyncGenerator<CustomEvent, T, void> {
|
|
263
|
+
// Use an object to track mutable state across the async boundary
|
|
264
|
+
const state = { done: false, result: undefined as T }
|
|
265
|
+
const executionWithFlag = executionPromise.then((r) => {
|
|
266
|
+
state.done = true
|
|
267
|
+
state.result = r
|
|
268
|
+
return r
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
while (!state.done) {
|
|
272
|
+
// Wait for either the execution to complete or a short timeout
|
|
273
|
+
await Promise.race([
|
|
274
|
+
executionWithFlag,
|
|
275
|
+
new Promise((resolve) => setTimeout(resolve, 10)),
|
|
276
|
+
])
|
|
277
|
+
|
|
278
|
+
// Flush any pending events
|
|
279
|
+
while (pendingEvents.length > 0) {
|
|
280
|
+
yield pendingEvents.shift()!
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Final flush in case events were emitted right at completion
|
|
285
|
+
while (pendingEvents.length > 0) {
|
|
286
|
+
yield pendingEvents.shift()!
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return state.result
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Execute tool calls based on their configuration.
|
|
294
|
+
* Yields CustomEvent chunks during tool execution for real-time progress updates.
|
|
254
295
|
*
|
|
255
296
|
* Handles three cases:
|
|
256
297
|
* 1. Client tools (no execute) - request client to execute
|
|
@@ -261,13 +302,18 @@ interface ExecuteToolCallsResult {
|
|
|
261
302
|
* @param tools - Available tools with their configurations
|
|
262
303
|
* @param approvals - Map of approval decisions (approval.id -> approved boolean)
|
|
263
304
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
305
|
+
* @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
|
|
264
306
|
*/
|
|
265
|
-
export async function executeToolCalls(
|
|
307
|
+
export async function* executeToolCalls(
|
|
266
308
|
toolCalls: Array<ToolCall>,
|
|
267
309
|
tools: ReadonlyArray<Tool>,
|
|
268
310
|
approvals: Map<string, boolean> = new Map(),
|
|
269
311
|
clientResults: Map<string, any> = new Map(),
|
|
270
|
-
|
|
312
|
+
createCustomEventChunk?: (
|
|
313
|
+
eventName: string,
|
|
314
|
+
value: Record<string, any>,
|
|
315
|
+
) => CustomEvent,
|
|
316
|
+
): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void> {
|
|
271
317
|
const results: Array<ToolResult> = []
|
|
272
318
|
const needsApproval: Array<ApprovalRequest> = []
|
|
273
319
|
const needsClientExecution: Array<ClientToolRequest> = []
|
|
@@ -278,6 +324,13 @@ export async function executeToolCalls(
|
|
|
278
324
|
toolMap.set(tool.name, tool)
|
|
279
325
|
}
|
|
280
326
|
|
|
327
|
+
// Batch gating: when any tool in the batch still needs an approval decision,
|
|
328
|
+
// defer all execution so side effects don't happen before the user decides.
|
|
329
|
+
const hasPendingApprovals = toolCalls.some((tc) => {
|
|
330
|
+
const t = toolMap.get(tc.function.name)
|
|
331
|
+
return t?.needsApproval && !approvals.has(`approval_${tc.id}`)
|
|
332
|
+
})
|
|
333
|
+
|
|
281
334
|
for (const toolCall of toolCalls) {
|
|
282
335
|
const tool = toolMap.get(toolCall.function.name)
|
|
283
336
|
const toolName = toolCall.function.name
|
|
@@ -293,6 +346,13 @@ export async function executeToolCalls(
|
|
|
293
346
|
continue
|
|
294
347
|
}
|
|
295
348
|
|
|
349
|
+
// Skip non-pending tools while approvals are outstanding
|
|
350
|
+
if (hasPendingApprovals) {
|
|
351
|
+
if (!tool.needsApproval || approvals.has(`approval_${toolCall.id}`)) {
|
|
352
|
+
continue
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
296
356
|
// Parse arguments, throwing error if invalid JSON
|
|
297
357
|
let input: unknown = {}
|
|
298
358
|
const argsStr = toolCall.function.arguments.trim() || '{}'
|
|
@@ -326,6 +386,29 @@ export async function executeToolCalls(
|
|
|
326
386
|
}
|
|
327
387
|
}
|
|
328
388
|
|
|
389
|
+
// Create a ToolExecutionContext for this tool call with event emission
|
|
390
|
+
const pendingEvents: Array<CustomEvent> = []
|
|
391
|
+
const context: ToolExecutionContext = {
|
|
392
|
+
toolCallId: toolCall.id,
|
|
393
|
+
emitCustomEvent: (eventName: string, value: Record<string, any>) => {
|
|
394
|
+
if (createCustomEventChunk) {
|
|
395
|
+
pendingEvents.push(
|
|
396
|
+
createCustomEventChunk(eventName, {
|
|
397
|
+
...value,
|
|
398
|
+
toolCallId: toolCall.id,
|
|
399
|
+
}),
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Helper to flush any pending events
|
|
406
|
+
function* flushEvents(): Generator<CustomEvent> {
|
|
407
|
+
while (pendingEvents.length > 0) {
|
|
408
|
+
yield pendingEvents.shift()!
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
329
412
|
// CASE 1: Client-side tool (no execute function)
|
|
330
413
|
if (!tool.execute) {
|
|
331
414
|
// Check if tool needs approval
|
|
@@ -402,8 +485,15 @@ export async function executeToolCalls(
|
|
|
402
485
|
// Execute after approval
|
|
403
486
|
const startTime = Date.now()
|
|
404
487
|
try {
|
|
405
|
-
|
|
488
|
+
const executionPromise = Promise.resolve(
|
|
489
|
+
tool.execute(input, context),
|
|
490
|
+
)
|
|
491
|
+
let result = yield* executeWithEventPolling(
|
|
492
|
+
executionPromise,
|
|
493
|
+
pendingEvents,
|
|
494
|
+
)
|
|
406
495
|
const duration = Date.now() - startTime
|
|
496
|
+
yield* flushEvents()
|
|
407
497
|
|
|
408
498
|
// Validate output against outputSchema if provided (for Standard Schema compliant schemas)
|
|
409
499
|
if (
|
|
@@ -426,6 +516,7 @@ export async function executeToolCalls(
|
|
|
426
516
|
})
|
|
427
517
|
} catch (error: unknown) {
|
|
428
518
|
const duration = Date.now() - startTime
|
|
519
|
+
yield* flushEvents()
|
|
429
520
|
const message =
|
|
430
521
|
error instanceof Error ? error.message : 'Unknown error'
|
|
431
522
|
results.push({
|
|
@@ -460,8 +551,13 @@ export async function executeToolCalls(
|
|
|
460
551
|
// CASE 3: Normal server tool - execute immediately
|
|
461
552
|
const startTime = Date.now()
|
|
462
553
|
try {
|
|
463
|
-
|
|
554
|
+
const executionPromise = Promise.resolve(tool.execute(input, context))
|
|
555
|
+
let result = yield* executeWithEventPolling(
|
|
556
|
+
executionPromise,
|
|
557
|
+
pendingEvents,
|
|
558
|
+
)
|
|
464
559
|
const duration = Date.now() - startTime
|
|
560
|
+
yield* flushEvents()
|
|
465
561
|
|
|
466
562
|
// Validate output against outputSchema if provided (for Standard Schema compliant schemas)
|
|
467
563
|
if (
|
|
@@ -482,6 +578,7 @@ export async function executeToolCalls(
|
|
|
482
578
|
})
|
|
483
579
|
} catch (error: unknown) {
|
|
484
580
|
const duration = Date.now() - startTime
|
|
581
|
+
yield* flushEvents()
|
|
485
582
|
const message = error instanceof Error ? error.message : 'Unknown error'
|
|
486
583
|
results.push({
|
|
487
584
|
toolCallId: toolCall.id,
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
JSONSchema,
|
|
5
5
|
SchemaInput,
|
|
6
6
|
Tool,
|
|
7
|
+
ToolExecutionContext,
|
|
7
8
|
} from '../../../types'
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -112,6 +113,7 @@ export interface ToolDefinition<
|
|
|
112
113
|
server: (
|
|
113
114
|
execute: (
|
|
114
115
|
args: InferSchemaType<TInput>,
|
|
116
|
+
context?: ToolExecutionContext,
|
|
115
117
|
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
116
118
|
) => ServerTool<TInput, TOutput, TName>
|
|
117
119
|
|
|
@@ -193,6 +195,7 @@ export function toolDefinition<
|
|
|
193
195
|
server(
|
|
194
196
|
execute: (
|
|
195
197
|
args: InferSchemaType<TInput>,
|
|
198
|
+
context?: ToolExecutionContext,
|
|
196
199
|
) => Promise<InferSchemaType<TOutput>> | InferSchemaType<TOutput>,
|
|
197
200
|
): ServerTool<TInput, TOutput, TName> {
|
|
198
201
|
return {
|
package/src/types.ts
CHANGED
|
@@ -344,6 +344,34 @@ export type ConstrainedModelMessage<
|
|
|
344
344
|
content: ConstrainedContent<TInputModalitiesTypes>
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Context passed to tool execute functions, providing capabilities like
|
|
349
|
+
* emitting custom events during execution.
|
|
350
|
+
*/
|
|
351
|
+
export interface ToolExecutionContext {
|
|
352
|
+
/** The ID of the tool call being executed */
|
|
353
|
+
toolCallId?: string
|
|
354
|
+
/**
|
|
355
|
+
* Emit a custom event during tool execution.
|
|
356
|
+
* Events are streamed to the client in real-time as AG-UI CUSTOM events.
|
|
357
|
+
*
|
|
358
|
+
* @param eventName - Name of the custom event
|
|
359
|
+
* @param value - Event payload value
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* const tool = toolDefinition({ ... }).server(async (args, context) => {
|
|
364
|
+
* context?.emitCustomEvent('progress', { step: 1, total: 3 })
|
|
365
|
+
* // ... do work ...
|
|
366
|
+
* context?.emitCustomEvent('progress', { step: 2, total: 3 })
|
|
367
|
+
* // ... do more work ...
|
|
368
|
+
* return result
|
|
369
|
+
* })
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
emitCustomEvent: (eventName: string, value: Record<string, any>) => void
|
|
373
|
+
}
|
|
374
|
+
|
|
347
375
|
/**
|
|
348
376
|
* Tool/Function definition for function calling.
|
|
349
377
|
*
|
|
@@ -460,7 +488,7 @@ export interface Tool<
|
|
|
460
488
|
* return weather; // Can return object or string
|
|
461
489
|
* }
|
|
462
490
|
*/
|
|
463
|
-
execute?: (args: any) => Promise<any> | any
|
|
491
|
+
execute?: (args: any, context?: ToolExecutionContext) => Promise<any> | any
|
|
464
492
|
|
|
465
493
|
/** If true, tool execution requires user approval before running. Works with both server and client tools. */
|
|
466
494
|
needsApproval?: boolean
|
|
@@ -894,8 +922,8 @@ export interface CustomEvent extends BaseAGUIEvent {
|
|
|
894
922
|
type: 'CUSTOM'
|
|
895
923
|
/** Custom event name */
|
|
896
924
|
name: string
|
|
897
|
-
/** Custom event
|
|
898
|
-
|
|
925
|
+
/** Custom event value */
|
|
926
|
+
value?: unknown
|
|
899
927
|
}
|
|
900
928
|
|
|
901
929
|
/**
|