@tanstack/ai 0.6.3 → 0.8.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 +20 -0
- package/dist/esm/activities/chat/index.js +248 -213
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +66 -0
- package/dist/esm/activities/chat/middleware/compose.js +327 -0
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -0
- package/dist/esm/activities/chat/middleware/index.d.ts +2 -0
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.d.ts +89 -0
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +76 -0
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +1 -0
- package/dist/esm/activities/chat/middleware/types.d.ts +307 -0
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +16 -1
- package/dist/esm/activities/chat/tools/tool-calls.js +148 -64
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/generateImage/index.js +1 -1
- package/dist/esm/activities/generateImage/index.js.map +1 -1
- package/dist/esm/activities/generateSpeech/index.js +1 -1
- package/dist/esm/activities/generateSpeech/index.js.map +1 -1
- package/dist/esm/activities/generateTranscription/index.js +1 -1
- package/dist/esm/activities/generateTranscription/index.js.map +1 -1
- package/dist/esm/activities/generateVideo/index.js +1 -1
- package/dist/esm/activities/generateVideo/index.js.map +1 -1
- package/dist/esm/activities/summarize/index.js +1 -1
- package/dist/esm/activities/summarize/index.js.map +1 -1
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/middlewares/content-guard.d.ts +77 -0
- package/dist/esm/middlewares/content-guard.js +155 -0
- package/dist/esm/middlewares/content-guard.js.map +1 -0
- package/dist/esm/middlewares/index.d.ts +2 -0
- package/dist/esm/middlewares/index.js +7 -0
- package/dist/esm/middlewares/index.js.map +1 -0
- package/dist/esm/middlewares/tool-cache.d.ts +1 -0
- package/dist/esm/realtime/index.d.ts +30 -0
- package/dist/esm/realtime/index.js +8 -0
- package/dist/esm/realtime/index.js.map +1 -0
- package/dist/esm/realtime/types.d.ts +234 -0
- package/package.json +6 -6
- package/src/activities/chat/index.ts +322 -256
- package/src/activities/chat/middleware/compose.ts +392 -0
- package/src/activities/chat/middleware/index.ts +17 -0
- package/src/activities/chat/middleware/tool-cache-middleware.ts +189 -0
- package/src/activities/chat/middleware/types.ts +419 -0
- package/src/activities/chat/tools/tool-calls.ts +225 -87
- package/src/activities/generateImage/index.ts +1 -1
- package/src/activities/generateSpeech/index.ts +1 -1
- package/src/activities/generateTranscription/index.ts +1 -1
- package/src/activities/generateVideo/index.ts +1 -1
- package/src/activities/summarize/index.ts +1 -1
- package/src/index.ts +41 -2
- package/src/middlewares/content-guard.ts +285 -0
- package/src/middlewares/index.ts +13 -0
- package/src/middlewares/tool-cache.ts +6 -0
- package/src/realtime/index.ts +38 -0
- package/src/realtime/types.ts +294 -0
- package/dist/esm/event-client.d.ts +0 -394
- package/dist/esm/event-client.js +0 -13
- package/dist/esm/event-client.js.map +0 -1
- package/src/event-client.ts +0 -497
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { ModelMessage, StreamChunk, Tool, ToolCall } from '../../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Phase of the chat middleware lifecycle.
|
|
4
|
+
* - 'init': Initial config transform before the chat engine starts
|
|
5
|
+
* - 'beforeModel': Before each adapter chatStream call (per agent iteration)
|
|
6
|
+
* - 'modelStream': During model streaming
|
|
7
|
+
* - 'beforeTools': Before tool execution phase
|
|
8
|
+
* - 'afterTools': After tool execution phase
|
|
9
|
+
*/
|
|
10
|
+
export type ChatMiddlewarePhase = 'init' | 'beforeModel' | 'modelStream' | 'beforeTools' | 'afterTools';
|
|
11
|
+
/**
|
|
12
|
+
* Stable context object passed to all middleware hooks.
|
|
13
|
+
* Created once per chat() invocation and shared across all hooks.
|
|
14
|
+
*/
|
|
15
|
+
export interface ChatMiddlewareContext {
|
|
16
|
+
/** Unique identifier for this chat request */
|
|
17
|
+
requestId: string;
|
|
18
|
+
/** Unique identifier for this stream */
|
|
19
|
+
streamId: string;
|
|
20
|
+
/** Conversation identifier, if provided by the caller */
|
|
21
|
+
conversationId?: string;
|
|
22
|
+
/** Current lifecycle phase */
|
|
23
|
+
phase: ChatMiddlewarePhase;
|
|
24
|
+
/** Current agent loop iteration (0-indexed) */
|
|
25
|
+
iteration: number;
|
|
26
|
+
/** Running count of chunks yielded so far */
|
|
27
|
+
chunkIndex: number;
|
|
28
|
+
/** Abort signal from the chat request */
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
/** Abort the chat run with a reason */
|
|
31
|
+
abort: (reason?: string) => void;
|
|
32
|
+
/** Opaque user-provided value from chat() options */
|
|
33
|
+
context: unknown;
|
|
34
|
+
/**
|
|
35
|
+
* Defer a non-blocking side-effect promise.
|
|
36
|
+
* Deferred promises do not block streaming and are awaited
|
|
37
|
+
* after the terminal hook (onFinish/onAbort/onError).
|
|
38
|
+
*/
|
|
39
|
+
defer: (promise: Promise<unknown>) => void;
|
|
40
|
+
/** Provider name (e.g., 'openai', 'anthropic') */
|
|
41
|
+
provider: string;
|
|
42
|
+
/** Model identifier (e.g., 'gpt-4o') */
|
|
43
|
+
model: string;
|
|
44
|
+
/** Source of the chat invocation — always 'server' for server-side chat */
|
|
45
|
+
source: 'client' | 'server';
|
|
46
|
+
/** Whether the chat is streaming */
|
|
47
|
+
streaming: boolean;
|
|
48
|
+
/** System prompts configured for this chat */
|
|
49
|
+
systemPrompts: Array<string>;
|
|
50
|
+
/** Names of configured tools, if any */
|
|
51
|
+
toolNames?: Array<string>;
|
|
52
|
+
/** Flattened generation options (temperature, topP, maxTokens, metadata) */
|
|
53
|
+
options?: Record<string, unknown>;
|
|
54
|
+
/** Provider-specific model options */
|
|
55
|
+
modelOptions?: Record<string, unknown>;
|
|
56
|
+
/** Number of messages at the start of the request */
|
|
57
|
+
messageCount: number;
|
|
58
|
+
/** Whether tools are configured */
|
|
59
|
+
hasTools: boolean;
|
|
60
|
+
/** Current assistant message ID (changes per iteration) */
|
|
61
|
+
currentMessageId: string | null;
|
|
62
|
+
/** Accumulated text content for the current iteration */
|
|
63
|
+
accumulatedContent: string;
|
|
64
|
+
/** Current messages array (read-only view) */
|
|
65
|
+
messages: ReadonlyArray<ModelMessage>;
|
|
66
|
+
/** Generate a unique ID with the given prefix */
|
|
67
|
+
createId: (prefix: string) => string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Chat configuration that middleware can observe or transform.
|
|
71
|
+
* This is a subset of the chat engine's effective configuration
|
|
72
|
+
* that middleware is allowed to modify.
|
|
73
|
+
*/
|
|
74
|
+
export interface ChatMiddlewareConfig {
|
|
75
|
+
messages: Array<ModelMessage>;
|
|
76
|
+
systemPrompts: Array<string>;
|
|
77
|
+
tools: Array<Tool>;
|
|
78
|
+
temperature?: number;
|
|
79
|
+
topP?: number;
|
|
80
|
+
maxTokens?: number;
|
|
81
|
+
metadata?: Record<string, unknown>;
|
|
82
|
+
modelOptions?: Record<string, unknown>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Context provided to tool call hooks (onBeforeToolCall / onAfterToolCall).
|
|
86
|
+
*/
|
|
87
|
+
export interface ToolCallHookContext {
|
|
88
|
+
/** The tool call being executed */
|
|
89
|
+
toolCall: ToolCall;
|
|
90
|
+
/** The resolved tool definition, if found */
|
|
91
|
+
tool: Tool | undefined;
|
|
92
|
+
/** Parsed arguments for the tool call */
|
|
93
|
+
args: unknown;
|
|
94
|
+
/** Name of the tool */
|
|
95
|
+
toolName: string;
|
|
96
|
+
/** ID of the tool call */
|
|
97
|
+
toolCallId: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Decision returned from onBeforeToolCall.
|
|
101
|
+
* - undefined/void: continue with normal execution
|
|
102
|
+
* - { type: 'transformArgs', args }: replace args used for execution
|
|
103
|
+
* - { type: 'skip', result }: skip execution, use provided result
|
|
104
|
+
* - { type: 'abort', reason }: abort the entire chat run
|
|
105
|
+
*/
|
|
106
|
+
export type BeforeToolCallDecision = void | undefined | null | {
|
|
107
|
+
type: 'transformArgs';
|
|
108
|
+
args: unknown;
|
|
109
|
+
} | {
|
|
110
|
+
type: 'skip';
|
|
111
|
+
result: unknown;
|
|
112
|
+
} | {
|
|
113
|
+
type: 'abort';
|
|
114
|
+
reason?: string;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Outcome information provided to onAfterToolCall.
|
|
118
|
+
*/
|
|
119
|
+
export interface AfterToolCallInfo {
|
|
120
|
+
/** The tool call that was executed */
|
|
121
|
+
toolCall: ToolCall;
|
|
122
|
+
/** The resolved tool definition */
|
|
123
|
+
tool: Tool | undefined;
|
|
124
|
+
/** Name of the tool */
|
|
125
|
+
toolName: string;
|
|
126
|
+
/** ID of the tool call */
|
|
127
|
+
toolCallId: string;
|
|
128
|
+
/** Whether the execution succeeded */
|
|
129
|
+
ok: boolean;
|
|
130
|
+
/** Duration of tool execution in milliseconds */
|
|
131
|
+
duration: number;
|
|
132
|
+
/** The result (if ok) or error (if not ok) */
|
|
133
|
+
result?: unknown;
|
|
134
|
+
error?: unknown;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Information passed to onIteration at the start of each agent loop iteration.
|
|
138
|
+
*/
|
|
139
|
+
export interface IterationInfo {
|
|
140
|
+
/** 0-based iteration index */
|
|
141
|
+
iteration: number;
|
|
142
|
+
/** The assistant message ID created for this iteration */
|
|
143
|
+
messageId: string;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Aggregate information passed to onToolPhaseComplete after all tool calls
|
|
147
|
+
* in an iteration have been processed.
|
|
148
|
+
*/
|
|
149
|
+
export interface ToolPhaseCompleteInfo {
|
|
150
|
+
/** Tool calls that were assigned to the assistant message */
|
|
151
|
+
toolCalls: Array<ToolCall>;
|
|
152
|
+
/** Completed tool results */
|
|
153
|
+
results: Array<{
|
|
154
|
+
toolCallId: string;
|
|
155
|
+
toolName: string;
|
|
156
|
+
result: unknown;
|
|
157
|
+
duration?: number;
|
|
158
|
+
}>;
|
|
159
|
+
/** Tools that need user approval */
|
|
160
|
+
needsApproval: Array<{
|
|
161
|
+
toolCallId: string;
|
|
162
|
+
toolName: string;
|
|
163
|
+
input: unknown;
|
|
164
|
+
approvalId: string;
|
|
165
|
+
}>;
|
|
166
|
+
/** Tools that need client-side execution */
|
|
167
|
+
needsClientExecution: Array<{
|
|
168
|
+
toolCallId: string;
|
|
169
|
+
toolName: string;
|
|
170
|
+
input: unknown;
|
|
171
|
+
}>;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Token usage statistics passed to the onUsage hook.
|
|
175
|
+
* Extracted from the RUN_FINISHED chunk when usage data is present.
|
|
176
|
+
*/
|
|
177
|
+
export interface UsageInfo {
|
|
178
|
+
promptTokens: number;
|
|
179
|
+
completionTokens: number;
|
|
180
|
+
totalTokens: number;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Information passed to onFinish.
|
|
184
|
+
*/
|
|
185
|
+
export interface FinishInfo {
|
|
186
|
+
/** The finish reason from the last model response */
|
|
187
|
+
finishReason: string | null;
|
|
188
|
+
/** Total duration of the chat run in milliseconds */
|
|
189
|
+
duration: number;
|
|
190
|
+
/** Final accumulated text content */
|
|
191
|
+
content: string;
|
|
192
|
+
/** Final usage totals, if available */
|
|
193
|
+
usage?: {
|
|
194
|
+
promptTokens: number;
|
|
195
|
+
completionTokens: number;
|
|
196
|
+
totalTokens: number;
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Information passed to onAbort.
|
|
201
|
+
*/
|
|
202
|
+
export interface AbortInfo {
|
|
203
|
+
/** The reason for the abort, if provided */
|
|
204
|
+
reason?: string;
|
|
205
|
+
/** Duration until abort in milliseconds */
|
|
206
|
+
duration: number;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Information passed to onError.
|
|
210
|
+
*/
|
|
211
|
+
export interface ErrorInfo {
|
|
212
|
+
/** The error that caused the failure */
|
|
213
|
+
error: unknown;
|
|
214
|
+
/** Duration until error in milliseconds */
|
|
215
|
+
duration: number;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Chat middleware interface.
|
|
219
|
+
*
|
|
220
|
+
* All hooks are optional. Middleware is composed in array order:
|
|
221
|
+
* - `onConfig`: config piped through middlewares in order (first transform influences later)
|
|
222
|
+
* - `onChunk`: each output chunk is fed into the next middleware in order
|
|
223
|
+
*
|
|
224
|
+
* @example Logging middleware
|
|
225
|
+
* ```ts
|
|
226
|
+
* const loggingMiddleware: ChatMiddleware = {
|
|
227
|
+
* name: 'logging',
|
|
228
|
+
* onStart(ctx) { console.log('Chat started', ctx.requestId) },
|
|
229
|
+
* onChunk(ctx, chunk) { console.log('Chunk:', chunk.type) },
|
|
230
|
+
* onFinish(ctx, info) { console.log('Done:', info.duration, 'ms') },
|
|
231
|
+
* }
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* @example Redaction middleware
|
|
235
|
+
* ```ts
|
|
236
|
+
* const redactionMiddleware: ChatMiddleware = {
|
|
237
|
+
* name: 'redaction',
|
|
238
|
+
* onChunk(ctx, chunk) {
|
|
239
|
+
* if (chunk.type === 'TEXT_MESSAGE_CONTENT') {
|
|
240
|
+
* return { ...chunk, delta: redact(chunk.delta) }
|
|
241
|
+
* }
|
|
242
|
+
* },
|
|
243
|
+
* }
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
export interface ChatMiddleware {
|
|
247
|
+
/** Optional name for debugging and identification */
|
|
248
|
+
name?: string;
|
|
249
|
+
/**
|
|
250
|
+
* Called to observe or transform the chat configuration.
|
|
251
|
+
* Called at init and at the beginning of each agent iteration.
|
|
252
|
+
*
|
|
253
|
+
* Return a partial config to merge with the current config, or void to pass through.
|
|
254
|
+
* Only the fields you return are overwritten — everything else is preserved.
|
|
255
|
+
*/
|
|
256
|
+
onConfig?: (ctx: ChatMiddlewareContext, config: ChatMiddlewareConfig) => void | null | Partial<ChatMiddlewareConfig> | Promise<void | Partial<ChatMiddlewareConfig>>;
|
|
257
|
+
/**
|
|
258
|
+
* Called when the chat run starts (after initial onConfig).
|
|
259
|
+
*/
|
|
260
|
+
onStart?: (ctx: ChatMiddlewareContext) => void | Promise<void>;
|
|
261
|
+
/**
|
|
262
|
+
* Called at the start of each agent loop iteration, after a new assistant message ID
|
|
263
|
+
* is created. Use this to observe iteration boundaries.
|
|
264
|
+
*/
|
|
265
|
+
onIteration?: (ctx: ChatMiddlewareContext, info: IterationInfo) => void | Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Called for every chunk yielded by chat().
|
|
268
|
+
* Can observe, transform, expand, or drop chunks.
|
|
269
|
+
*
|
|
270
|
+
* @returns void (pass through), chunk (replace), chunk[] (expand), null (drop)
|
|
271
|
+
*/
|
|
272
|
+
onChunk?: (ctx: ChatMiddlewareContext, chunk: StreamChunk) => void | StreamChunk | Array<StreamChunk> | null | Promise<void | StreamChunk | Array<StreamChunk> | null>;
|
|
273
|
+
/**
|
|
274
|
+
* Called before a tool is executed.
|
|
275
|
+
* Can observe, transform args, skip execution, or abort the run.
|
|
276
|
+
*/
|
|
277
|
+
onBeforeToolCall?: (ctx: ChatMiddlewareContext, hookCtx: ToolCallHookContext) => BeforeToolCallDecision | Promise<BeforeToolCallDecision>;
|
|
278
|
+
/**
|
|
279
|
+
* Called after a tool execution completes (success or failure).
|
|
280
|
+
*/
|
|
281
|
+
onAfterToolCall?: (ctx: ChatMiddlewareContext, info: AfterToolCallInfo) => void | Promise<void>;
|
|
282
|
+
/**
|
|
283
|
+
* Called after all tool calls in an iteration have been processed.
|
|
284
|
+
* Provides aggregate data about tool execution results, approvals, and client tools.
|
|
285
|
+
*/
|
|
286
|
+
onToolPhaseComplete?: (ctx: ChatMiddlewareContext, info: ToolPhaseCompleteInfo) => void | Promise<void>;
|
|
287
|
+
/**
|
|
288
|
+
* Called when usage data is available from a RUN_FINISHED chunk.
|
|
289
|
+
* Called once per model iteration that reports usage.
|
|
290
|
+
*/
|
|
291
|
+
onUsage?: (ctx: ChatMiddlewareContext, usage: UsageInfo) => void | Promise<void>;
|
|
292
|
+
/**
|
|
293
|
+
* Called when the chat run completes normally.
|
|
294
|
+
* Exactly one of onFinish/onAbort/onError will be called per run.
|
|
295
|
+
*/
|
|
296
|
+
onFinish?: (ctx: ChatMiddlewareContext, info: FinishInfo) => void | Promise<void>;
|
|
297
|
+
/**
|
|
298
|
+
* Called when the chat run is aborted.
|
|
299
|
+
* Exactly one of onFinish/onAbort/onError will be called per run.
|
|
300
|
+
*/
|
|
301
|
+
onAbort?: (ctx: ChatMiddlewareContext, info: AbortInfo) => void | Promise<void>;
|
|
302
|
+
/**
|
|
303
|
+
* Called when the chat run encounters an unhandled error.
|
|
304
|
+
* Exactly one of onFinish/onAbort/onError will be called per run.
|
|
305
|
+
*/
|
|
306
|
+
onError?: (ctx: ChatMiddlewareContext, info: ErrorInfo) => void | Promise<void>;
|
|
307
|
+
}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import { CustomEvent, ModelMessage, RunFinishedEvent, Tool, ToolCall, ToolCallArgsEvent, ToolCallEndEvent, ToolCallStartEvent } from '../../../types.js';
|
|
2
|
+
import { AfterToolCallInfo, BeforeToolCallDecision } from '../middleware/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Optional middleware hooks for tool execution.
|
|
5
|
+
* When provided, these callbacks are invoked before/after each tool execution.
|
|
6
|
+
*/
|
|
7
|
+
export interface ToolExecutionMiddlewareHooks {
|
|
8
|
+
onBeforeToolCall?: (toolCall: ToolCall, tool: Tool | undefined, args: unknown) => Promise<BeforeToolCallDecision>;
|
|
9
|
+
onAfterToolCall?: (info: AfterToolCallInfo) => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when middleware decides to abort the chat run during tool execution.
|
|
13
|
+
*/
|
|
14
|
+
export declare class MiddlewareAbortError extends Error {
|
|
15
|
+
constructor(reason: string);
|
|
16
|
+
}
|
|
2
17
|
/**
|
|
3
18
|
* Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.
|
|
4
19
|
*
|
|
@@ -111,5 +126,5 @@ interface ExecuteToolCallsResult {
|
|
|
111
126
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
112
127
|
* @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
|
|
113
128
|
*/
|
|
114
|
-
export declare function executeToolCalls(toolCalls: Array<ToolCall>, tools: ReadonlyArray<Tool>, approvals?: Map<string, boolean>, clientResults?: Map<string, any>, createCustomEventChunk?: (eventName: string, value: Record<string, any>) => CustomEvent): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void>;
|
|
129
|
+
export declare function executeToolCalls(toolCalls: Array<ToolCall>, tools: ReadonlyArray<Tool>, approvals?: Map<string, boolean>, clientResults?: Map<string, any>, createCustomEventChunk?: (eventName: string, value: Record<string, any>) => CustomEvent, middlewareHooks?: ToolExecutionMiddlewareHooks): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void>;
|
|
115
130
|
export {};
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { isStandardSchema, parseWithStandardSchema } from "./schema-converter.js";
|
|
2
|
+
function safeJsonParse(value) {
|
|
3
|
+
try {
|
|
4
|
+
return JSON.parse(value);
|
|
5
|
+
} catch {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
class MiddlewareAbortError extends Error {
|
|
10
|
+
constructor(reason) {
|
|
11
|
+
super(reason);
|
|
12
|
+
this.name = "MiddlewareAbortError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
2
15
|
class ToolCallManager {
|
|
3
16
|
constructor(tools) {
|
|
4
17
|
this.toolCallsMap = /* @__PURE__ */ new Map();
|
|
@@ -152,7 +165,100 @@ async function* executeWithEventPolling(executionPromise, pendingEvents) {
|
|
|
152
165
|
}
|
|
153
166
|
return state.result;
|
|
154
167
|
}
|
|
155
|
-
async function
|
|
168
|
+
async function applyBeforeToolCallDecision(toolCall, tool, input, toolName, middlewareHooks, results) {
|
|
169
|
+
if (!middlewareHooks.onBeforeToolCall) {
|
|
170
|
+
return { proceed: true, input };
|
|
171
|
+
}
|
|
172
|
+
const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input);
|
|
173
|
+
if (!decision) {
|
|
174
|
+
return { proceed: true, input };
|
|
175
|
+
}
|
|
176
|
+
if (decision.type === "abort") {
|
|
177
|
+
throw new MiddlewareAbortError(decision.reason || "Aborted by middleware");
|
|
178
|
+
}
|
|
179
|
+
if (decision.type === "skip") {
|
|
180
|
+
const skipResult = decision.result;
|
|
181
|
+
results.push({
|
|
182
|
+
toolCallId: toolCall.id,
|
|
183
|
+
toolName,
|
|
184
|
+
result: typeof skipResult === "string" ? safeJsonParse(skipResult) : skipResult || null,
|
|
185
|
+
duration: 0
|
|
186
|
+
});
|
|
187
|
+
if (middlewareHooks.onAfterToolCall) {
|
|
188
|
+
await middlewareHooks.onAfterToolCall({
|
|
189
|
+
toolCall,
|
|
190
|
+
tool,
|
|
191
|
+
toolName,
|
|
192
|
+
toolCallId: toolCall.id,
|
|
193
|
+
ok: true,
|
|
194
|
+
duration: 0,
|
|
195
|
+
result: skipResult
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return { proceed: false };
|
|
199
|
+
}
|
|
200
|
+
return { proceed: true, input: decision.args };
|
|
201
|
+
}
|
|
202
|
+
async function* executeServerTool(toolCall, tool, toolName, input, context, pendingEvents, results, middlewareHooks) {
|
|
203
|
+
const startTime = Date.now();
|
|
204
|
+
try {
|
|
205
|
+
const executionPromise = Promise.resolve(tool.execute(input, context));
|
|
206
|
+
let result = yield* executeWithEventPolling(executionPromise, pendingEvents);
|
|
207
|
+
const duration = Date.now() - startTime;
|
|
208
|
+
while (pendingEvents.length > 0) {
|
|
209
|
+
yield pendingEvents.shift();
|
|
210
|
+
}
|
|
211
|
+
if (tool.outputSchema && isStandardSchema(tool.outputSchema) && result !== void 0 && result !== null) {
|
|
212
|
+
result = parseWithStandardSchema(tool.outputSchema, result);
|
|
213
|
+
}
|
|
214
|
+
const finalResult = typeof result === "string" ? safeJsonParse(result) : result || null;
|
|
215
|
+
results.push({
|
|
216
|
+
toolCallId: toolCall.id,
|
|
217
|
+
toolName,
|
|
218
|
+
result: finalResult,
|
|
219
|
+
duration
|
|
220
|
+
});
|
|
221
|
+
if (middlewareHooks?.onAfterToolCall) {
|
|
222
|
+
await middlewareHooks.onAfterToolCall({
|
|
223
|
+
toolCall,
|
|
224
|
+
tool,
|
|
225
|
+
toolName,
|
|
226
|
+
toolCallId: toolCall.id,
|
|
227
|
+
ok: true,
|
|
228
|
+
duration,
|
|
229
|
+
result: finalResult
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
} catch (error) {
|
|
233
|
+
const duration = Date.now() - startTime;
|
|
234
|
+
while (pendingEvents.length > 0) {
|
|
235
|
+
yield pendingEvents.shift();
|
|
236
|
+
}
|
|
237
|
+
if (error instanceof MiddlewareAbortError) {
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
241
|
+
results.push({
|
|
242
|
+
toolCallId: toolCall.id,
|
|
243
|
+
toolName,
|
|
244
|
+
result: { error: message },
|
|
245
|
+
state: "output-error",
|
|
246
|
+
duration
|
|
247
|
+
});
|
|
248
|
+
if (middlewareHooks?.onAfterToolCall) {
|
|
249
|
+
await middlewareHooks.onAfterToolCall({
|
|
250
|
+
toolCall,
|
|
251
|
+
tool,
|
|
252
|
+
toolName,
|
|
253
|
+
toolCallId: toolCall.id,
|
|
254
|
+
ok: false,
|
|
255
|
+
duration,
|
|
256
|
+
error
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ new Map(), clientResults = /* @__PURE__ */ new Map(), createCustomEventChunk, middlewareHooks) {
|
|
156
262
|
const results = [];
|
|
157
263
|
const needsApproval = [];
|
|
158
264
|
const needsClientExecution = [];
|
|
@@ -220,11 +326,6 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
220
326
|
}
|
|
221
327
|
}
|
|
222
328
|
};
|
|
223
|
-
function* flushEvents() {
|
|
224
|
-
while (pendingEvents.length > 0) {
|
|
225
|
-
yield pendingEvents.shift();
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
329
|
if (!tool.execute) {
|
|
229
330
|
if (tool.needsApproval) {
|
|
230
331
|
const approvalId = `approval_${toolCall.id}`;
|
|
@@ -282,38 +383,28 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
282
383
|
if (approvals.has(approvalId)) {
|
|
283
384
|
const approved = approvals.get(approvalId);
|
|
284
385
|
if (approved) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
tool
|
|
289
|
-
|
|
290
|
-
let result = yield* executeWithEventPolling(
|
|
291
|
-
executionPromise,
|
|
292
|
-
pendingEvents
|
|
293
|
-
);
|
|
294
|
-
const duration = Date.now() - startTime2;
|
|
295
|
-
yield* flushEvents();
|
|
296
|
-
if (tool.outputSchema && isStandardSchema(tool.outputSchema) && result !== void 0 && result !== null) {
|
|
297
|
-
result = parseWithStandardSchema(tool.outputSchema, result);
|
|
298
|
-
}
|
|
299
|
-
results.push({
|
|
300
|
-
toolCallId: toolCall.id,
|
|
301
|
-
toolName,
|
|
302
|
-
result: typeof result === "string" ? JSON.parse(result) : result || null,
|
|
303
|
-
duration
|
|
304
|
-
});
|
|
305
|
-
} catch (error) {
|
|
306
|
-
const duration = Date.now() - startTime2;
|
|
307
|
-
yield* flushEvents();
|
|
308
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
309
|
-
results.push({
|
|
310
|
-
toolCallId: toolCall.id,
|
|
386
|
+
if (middlewareHooks) {
|
|
387
|
+
const decision = await applyBeforeToolCallDecision(
|
|
388
|
+
toolCall,
|
|
389
|
+
tool,
|
|
390
|
+
input,
|
|
311
391
|
toolName,
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
392
|
+
middlewareHooks,
|
|
393
|
+
results
|
|
394
|
+
);
|
|
395
|
+
if (!decision.proceed) continue;
|
|
396
|
+
input = decision.input;
|
|
316
397
|
}
|
|
398
|
+
yield* executeServerTool(
|
|
399
|
+
toolCall,
|
|
400
|
+
tool,
|
|
401
|
+
toolName,
|
|
402
|
+
input,
|
|
403
|
+
context,
|
|
404
|
+
pendingEvents,
|
|
405
|
+
results,
|
|
406
|
+
middlewareHooks
|
|
407
|
+
);
|
|
317
408
|
} else {
|
|
318
409
|
results.push({
|
|
319
410
|
toolCallId: toolCall.id,
|
|
@@ -332,40 +423,33 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
332
423
|
}
|
|
333
424
|
continue;
|
|
334
425
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
pendingEvents
|
|
341
|
-
);
|
|
342
|
-
const duration = Date.now() - startTime;
|
|
343
|
-
yield* flushEvents();
|
|
344
|
-
if (tool.outputSchema && isStandardSchema(tool.outputSchema) && result !== void 0 && result !== null) {
|
|
345
|
-
result = parseWithStandardSchema(tool.outputSchema, result);
|
|
346
|
-
}
|
|
347
|
-
results.push({
|
|
348
|
-
toolCallId: toolCall.id,
|
|
349
|
-
toolName,
|
|
350
|
-
result: typeof result === "string" ? JSON.parse(result) : result || null,
|
|
351
|
-
duration
|
|
352
|
-
});
|
|
353
|
-
} catch (error) {
|
|
354
|
-
const duration = Date.now() - startTime;
|
|
355
|
-
yield* flushEvents();
|
|
356
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
357
|
-
results.push({
|
|
358
|
-
toolCallId: toolCall.id,
|
|
426
|
+
if (middlewareHooks) {
|
|
427
|
+
const decision = await applyBeforeToolCallDecision(
|
|
428
|
+
toolCall,
|
|
429
|
+
tool,
|
|
430
|
+
input,
|
|
359
431
|
toolName,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
432
|
+
middlewareHooks,
|
|
433
|
+
results
|
|
434
|
+
);
|
|
435
|
+
if (!decision.proceed) continue;
|
|
436
|
+
input = decision.input;
|
|
364
437
|
}
|
|
438
|
+
yield* executeServerTool(
|
|
439
|
+
toolCall,
|
|
440
|
+
tool,
|
|
441
|
+
toolName,
|
|
442
|
+
input,
|
|
443
|
+
context,
|
|
444
|
+
pendingEvents,
|
|
445
|
+
results,
|
|
446
|
+
middlewareHooks
|
|
447
|
+
);
|
|
365
448
|
}
|
|
366
449
|
return { results, needsApproval, needsClientExecution };
|
|
367
450
|
}
|
|
368
451
|
export {
|
|
452
|
+
MiddlewareAbortError,
|
|
369
453
|
ToolCallManager,
|
|
370
454
|
executeToolCalls
|
|
371
455
|
};
|