@tanstack/ai 0.26.1 → 0.28.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 +7 -6
- package/dist/esm/activities/chat/index.js +78 -27
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/mcp/manager.d.ts +25 -0
- package/dist/esm/activities/chat/mcp/manager.js +71 -0
- package/dist/esm/activities/chat/mcp/manager.js.map +1 -0
- package/dist/esm/activities/chat/mcp/types.d.ts +56 -0
- package/dist/esm/activities/chat/middleware/types.d.ts +1 -4
- package/dist/esm/activities/chat/stream/message-updaters.js +20 -8
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.js +2 -1
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.js +62 -3
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +1 -1
- package/dist/esm/extend-adapter.d.ts +22 -6
- package/dist/esm/extend-adapter.js.map +1 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger/internal-logger.d.ts +8 -0
- package/dist/esm/logger/internal-logger.js +15 -0
- package/dist/esm/logger/internal-logger.js.map +1 -1
- package/dist/esm/middlewares/otel.js +30 -6
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/types.d.ts +6 -35
- package/dist/esm/utilities/sampling-keys.d.ts +20 -0
- package/dist/esm/utilities/sampling-keys.js +20 -0
- package/dist/esm/utilities/sampling-keys.js.map +1 -0
- package/package.json +2 -2
- package/skills/ai-core/adapter-configuration/SKILL.md +67 -6
- package/skills/ai-core/adapter-configuration/references/anthropic-adapter.md +6 -3
- package/skills/ai-core/adapter-configuration/references/gemini-adapter.md +3 -0
- package/skills/ai-core/adapter-configuration/references/ollama-adapter.md +10 -1
- package/skills/ai-core/adapter-configuration/references/openai-adapter.md +4 -0
- package/skills/ai-core/chat-experience/SKILL.md +95 -7
- package/skills/ai-core/middleware/SKILL.md +11 -0
- package/skills/ai-core/tool-calling/SKILL.md +287 -0
- package/src/activities/chat/index.ts +97 -35
- package/src/activities/chat/mcp/manager.ts +85 -0
- package/src/activities/chat/mcp/types.ts +66 -0
- package/src/activities/chat/middleware/types.ts +1 -4
- package/src/activities/chat/stream/message-updaters.ts +22 -9
- package/src/activities/chat/tools/tool-calls.ts +2 -0
- package/src/activities/summarize/chat-stream-summarize.ts +162 -3
- package/src/extend-adapter.ts +42 -24
- package/src/index.ts +10 -0
- package/src/logger/internal-logger.ts +18 -0
- package/src/middlewares/otel.ts +48 -6
- package/src/types.ts +6 -35
- package/src/utilities/sampling-keys.ts +28 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ServerTool } from '../tools/tool-definition.js';
|
|
2
|
+
import { ChatMCPOptions } from './types.js';
|
|
3
|
+
export declare class MCPDuplicateToolNameError extends Error {
|
|
4
|
+
readonly toolName: string;
|
|
5
|
+
constructor(toolName: string);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Encapsulates MCP tool discovery + connection lifecycle for chat().
|
|
9
|
+
* Built from chat()'s `mcp` option; runners only call `discover()` then
|
|
10
|
+
* `dispose()`. A manager built from `undefined` is an inert no-op
|
|
11
|
+
* (`discover()` → `[]`, `dispose()` → no-op), so runners need no branching.
|
|
12
|
+
*/
|
|
13
|
+
export declare class MCPManager {
|
|
14
|
+
#private;
|
|
15
|
+
static from(options: ChatMCPOptions | undefined): MCPManager;
|
|
16
|
+
private constructor();
|
|
17
|
+
/**
|
|
18
|
+
* Discover + merge tools from all sources. Throws on a fatal discovery error
|
|
19
|
+
* (no `onDiscoveryError`, or it re-threw) or a duplicate tool name; in that
|
|
20
|
+
* case it first closes any connected sources when the policy is 'close'.
|
|
21
|
+
*/
|
|
22
|
+
discover(): Promise<Array<ServerTool>>;
|
|
23
|
+
/** Close sources iff policy is 'close'. Idempotent; never throws. */
|
|
24
|
+
dispose(): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
class MCPDuplicateToolNameError extends Error {
|
|
2
|
+
constructor(toolName) {
|
|
3
|
+
super(
|
|
4
|
+
`Duplicate MCP tool name "${toolName}" in chat({ mcp.clients }). Set a unique \`prefix\` on one of the MCP clients (or use a pool, which auto-prefixes) to disambiguate.`
|
|
5
|
+
);
|
|
6
|
+
this.toolName = toolName;
|
|
7
|
+
this.name = "MCPDuplicateToolNameError";
|
|
8
|
+
}
|
|
9
|
+
toolName;
|
|
10
|
+
}
|
|
11
|
+
class MCPManager {
|
|
12
|
+
static from(options) {
|
|
13
|
+
return new MCPManager(options);
|
|
14
|
+
}
|
|
15
|
+
#sources;
|
|
16
|
+
#shouldClose;
|
|
17
|
+
#lazyTools;
|
|
18
|
+
#onDiscoveryError;
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.#sources = options?.clients ?? [];
|
|
21
|
+
this.#shouldClose = options ? options.connection !== "keep-alive" : false;
|
|
22
|
+
this.#lazyTools = options?.lazyTools ?? false;
|
|
23
|
+
this.#onDiscoveryError = options?.onDiscoveryError;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Discover + merge tools from all sources. Throws on a fatal discovery error
|
|
27
|
+
* (no `onDiscoveryError`, or it re-threw) or a duplicate tool name; in that
|
|
28
|
+
* case it first closes any connected sources when the policy is 'close'.
|
|
29
|
+
*/
|
|
30
|
+
async discover() {
|
|
31
|
+
if (this.#sources.length === 0) return [];
|
|
32
|
+
try {
|
|
33
|
+
const settled = await Promise.allSettled(
|
|
34
|
+
this.#sources.map((s) => s.tools({ lazy: this.#lazyTools }))
|
|
35
|
+
);
|
|
36
|
+
const tools = [];
|
|
37
|
+
const zipped = this.#sources.map(
|
|
38
|
+
(source, i) => [source, settled[i]]
|
|
39
|
+
);
|
|
40
|
+
for (const [source, result] of zipped) {
|
|
41
|
+
if (result === void 0) continue;
|
|
42
|
+
if (result.status === "fulfilled") {
|
|
43
|
+
tools.push(...result.value);
|
|
44
|
+
} else if (this.#onDiscoveryError) {
|
|
45
|
+
await this.#onDiscoveryError(result.reason, source);
|
|
46
|
+
} else {
|
|
47
|
+
throw result.reason;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const seen = /* @__PURE__ */ new Set();
|
|
51
|
+
for (const t of tools) {
|
|
52
|
+
if (seen.has(t.name)) throw new MCPDuplicateToolNameError(t.name);
|
|
53
|
+
seen.add(t.name);
|
|
54
|
+
}
|
|
55
|
+
return tools;
|
|
56
|
+
} catch (err) {
|
|
57
|
+
await this.dispose();
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Close sources iff policy is 'close'. Idempotent; never throws. */
|
|
62
|
+
async dispose() {
|
|
63
|
+
if (!this.#shouldClose || this.#sources.length === 0) return;
|
|
64
|
+
await Promise.allSettled(this.#sources.map((s) => s.close()));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
MCPDuplicateToolNameError,
|
|
69
|
+
MCPManager
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sources":["../../../../../src/activities/chat/mcp/manager.ts"],"sourcesContent":["import type { ServerTool } from '../tools/tool-definition'\nimport type { ChatMCPOptions, MCPToolSource } from './types'\n\nexport class MCPDuplicateToolNameError extends Error {\n constructor(public readonly toolName: string) {\n super(\n `Duplicate MCP tool name \"${toolName}\" in chat({ mcp.clients }). ` +\n `Set a unique \\`prefix\\` on one of the MCP clients (or use a pool, ` +\n `which auto-prefixes) to disambiguate.`,\n )\n this.name = 'MCPDuplicateToolNameError'\n }\n}\n\n/**\n * Encapsulates MCP tool discovery + connection lifecycle for chat().\n * Built from chat()'s `mcp` option; runners only call `discover()` then\n * `dispose()`. A manager built from `undefined` is an inert no-op\n * (`discover()` → `[]`, `dispose()` → no-op), so runners need no branching.\n */\nexport class MCPManager {\n static from(options: ChatMCPOptions | undefined): MCPManager {\n return new MCPManager(options)\n }\n\n readonly #sources: ReadonlyArray<MCPToolSource>\n readonly #shouldClose: boolean\n readonly #lazyTools: boolean\n readonly #onDiscoveryError?: (\n error: unknown,\n source: MCPToolSource,\n ) => void | Promise<void>\n\n private constructor(options: ChatMCPOptions | undefined) {\n this.#sources = options?.clients ?? []\n // default 'close'; only 'keep-alive' disables closing\n this.#shouldClose = options ? options.connection !== 'keep-alive' : false\n this.#lazyTools = options?.lazyTools ?? false\n this.#onDiscoveryError = options?.onDiscoveryError\n }\n\n /**\n * Discover + merge tools from all sources. Throws on a fatal discovery error\n * (no `onDiscoveryError`, or it re-threw) or a duplicate tool name; in that\n * case it first closes any connected sources when the policy is 'close'.\n */\n async discover(): Promise<Array<ServerTool>> {\n if (this.#sources.length === 0) return []\n try {\n const settled = await Promise.allSettled(\n this.#sources.map((s) => s.tools({ lazy: this.#lazyTools })),\n )\n const tools: Array<ServerTool> = []\n const zipped = this.#sources.map(\n (source, i) => [source, settled[i]] as const,\n )\n for (const [source, result] of zipped) {\n if (result === undefined) continue\n if (result.status === 'fulfilled') {\n tools.push(...result.value)\n } else if (this.#onDiscoveryError) {\n // throw/reject inside handler ⇒ propagate (fail-fast); return ⇒ skip\n await this.#onDiscoveryError(result.reason, source)\n } else {\n throw result.reason\n }\n }\n const seen = new Set<string>()\n for (const t of tools) {\n if (seen.has(t.name)) throw new MCPDuplicateToolNameError(t.name)\n seen.add(t.name)\n }\n return tools\n } catch (err) {\n await this.dispose() // cleanup-on-failure (no-op if keep-alive)\n throw err\n }\n }\n\n /** Close sources iff policy is 'close'. Idempotent; never throws. */\n async dispose(): Promise<void> {\n if (!this.#shouldClose || this.#sources.length === 0) return\n await Promise.allSettled(this.#sources.map((s) => s.close()))\n }\n}\n"],"names":[],"mappings":"AAGO,MAAM,kCAAkC,MAAM;AAAA,EACnD,YAA4B,UAAkB;AAC5C;AAAA,MACE,4BAA4B,QAAQ;AAAA,IAAA;AAFZ,SAAA,WAAA;AAM1B,SAAK,OAAO;AAAA,EACd;AAAA,EAP4B;AAQ9B;AAQO,MAAM,WAAW;AAAA,EACtB,OAAO,KAAK,SAAiD;AAC3D,WAAO,IAAI,WAAW,OAAO;AAAA,EAC/B;AAAA,EAES;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAKD,YAAY,SAAqC;AACvD,SAAK,WAAW,SAAS,WAAW,CAAA;AAEpC,SAAK,eAAe,UAAU,QAAQ,eAAe,eAAe;AACpE,SAAK,aAAa,SAAS,aAAa;AACxC,SAAK,oBAAoB,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAuC;AAC3C,QAAI,KAAK,SAAS,WAAW,UAAU,CAAA;AACvC,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ;AAAA,QAC5B,KAAK,SAAS,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,YAAY,CAAC;AAAA,MAAA;AAE7D,YAAM,QAA2B,CAAA;AACjC,YAAM,SAAS,KAAK,SAAS;AAAA,QAC3B,CAAC,QAAQ,MAAM,CAAC,QAAQ,QAAQ,CAAC,CAAC;AAAA,MAAA;AAEpC,iBAAW,CAAC,QAAQ,MAAM,KAAK,QAAQ;AACrC,YAAI,WAAW,OAAW;AAC1B,YAAI,OAAO,WAAW,aAAa;AACjC,gBAAM,KAAK,GAAG,OAAO,KAAK;AAAA,QAC5B,WAAW,KAAK,mBAAmB;AAEjC,gBAAM,KAAK,kBAAkB,OAAO,QAAQ,MAAM;AAAA,QACpD,OAAO;AACL,gBAAM,OAAO;AAAA,QACf;AAAA,MACF;AACA,YAAM,2BAAW,IAAA;AACjB,iBAAW,KAAK,OAAO;AACrB,YAAI,KAAK,IAAI,EAAE,IAAI,EAAG,OAAM,IAAI,0BAA0B,EAAE,IAAI;AAChE,aAAK,IAAI,EAAE,IAAI;AAAA,MACjB;AACA,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,YAAM,KAAK,QAAA;AACX,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,UAAyB;AAC7B,QAAI,CAAC,KAAK,gBAAgB,KAAK,SAAS,WAAW,EAAG;AACtD,UAAM,QAAQ,WAAW,KAAK,SAAS,IAAI,CAAC,MAAM,EAAE,MAAA,CAAO,CAAC;AAAA,EAC9D;AACF;"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ServerTool } from '../tools/tool-definition.js';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal structural shape that `chat({ mcp })` needs from an MCP client.
|
|
4
|
+
*
|
|
5
|
+
* `@tanstack/ai-mcp`'s `MCPClient` and `MCPClients` satisfy this interface by
|
|
6
|
+
* shape — the core `@tanstack/ai` package does NOT import `@tanstack/ai-mcp`
|
|
7
|
+
* (ai-mcp depends on ai, not the reverse).
|
|
8
|
+
*/
|
|
9
|
+
export interface MCPToolSource {
|
|
10
|
+
tools: (options?: {
|
|
11
|
+
lazy?: boolean;
|
|
12
|
+
}) => Promise<Array<ServerTool>>;
|
|
13
|
+
close: () => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Controls what happens to MCP connections when the chat run ends.
|
|
17
|
+
*
|
|
18
|
+
* - `'close'` (default) — `chat()` closes each connection when the run ends
|
|
19
|
+
* (after the agent loop completes and the stream is drained), so tools can
|
|
20
|
+
* still execute throughout the run.
|
|
21
|
+
* - `'keep-alive'` — `chat()` never closes the connections; the caller owns
|
|
22
|
+
* their lifecycle (e.g. keep them warm across requests).
|
|
23
|
+
*/
|
|
24
|
+
export type MCPConnectionPolicy = 'close' | 'keep-alive';
|
|
25
|
+
/**
|
|
26
|
+
* Options controlling MCP tool discovery and lifecycle for a `chat()` call.
|
|
27
|
+
*/
|
|
28
|
+
export interface ChatMCPOptions {
|
|
29
|
+
/**
|
|
30
|
+
* The MCP clients or client pools to discover tools from and manage.
|
|
31
|
+
*/
|
|
32
|
+
clients: Array<MCPToolSource>;
|
|
33
|
+
/**
|
|
34
|
+
* Connection lifecycle policy applied to all clients when the run ends.
|
|
35
|
+
*
|
|
36
|
+
* Defaults to `'close'`.
|
|
37
|
+
*/
|
|
38
|
+
connection?: MCPConnectionPolicy;
|
|
39
|
+
/**
|
|
40
|
+
* When `true`, tool schemas are fetched lazily (forwarded to
|
|
41
|
+
* `tools({ lazy: true })`).
|
|
42
|
+
*
|
|
43
|
+
* Defaults to `false`.
|
|
44
|
+
*/
|
|
45
|
+
lazyTools?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Called when tool discovery fails for a single source.
|
|
48
|
+
*
|
|
49
|
+
* - Throw (or re-throw) from this handler to fail the entire chat call fast.
|
|
50
|
+
* - Return normally to skip that source and continue with remaining clients.
|
|
51
|
+
* - Omit this handler entirely to rethrow the error (fail-fast by default).
|
|
52
|
+
*
|
|
53
|
+
* Async handlers are awaited, so a rejected promise also fails fast.
|
|
54
|
+
*/
|
|
55
|
+
onDiscoveryError?: (error: unknown, source: MCPToolSource) => void | Promise<void>;
|
|
56
|
+
}
|
|
@@ -65,7 +65,7 @@ export interface ChatMiddlewareContext<TContext = unknown> {
|
|
|
65
65
|
systemPrompts: Array<SystemPrompt>;
|
|
66
66
|
/** Names of configured tools, if any */
|
|
67
67
|
toolNames?: Array<string>;
|
|
68
|
-
/** Flattened generation options (
|
|
68
|
+
/** Flattened generation options (metadata) */
|
|
69
69
|
options?: Record<string, unknown> | undefined;
|
|
70
70
|
/** Provider-specific model options */
|
|
71
71
|
modelOptions?: Record<string, unknown> | undefined;
|
|
@@ -91,9 +91,6 @@ export interface ChatMiddlewareConfig {
|
|
|
91
91
|
messages: Array<ModelMessage>;
|
|
92
92
|
systemPrompts: Array<SystemPrompt>;
|
|
93
93
|
tools: Array<Tool>;
|
|
94
|
-
temperature?: number;
|
|
95
|
-
topP?: number;
|
|
96
|
-
maxTokens?: number;
|
|
97
94
|
metadata?: Record<string, unknown> | undefined;
|
|
98
95
|
modelOptions?: Record<string, unknown> | undefined;
|
|
99
96
|
}
|
|
@@ -77,10 +77,14 @@ function updateToolCallApproval(messages, messageId, toolCallId, approvalId) {
|
|
|
77
77
|
(p) => p.type === "tool-call" && p.id === toolCallId
|
|
78
78
|
);
|
|
79
79
|
if (toolCallPart) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
const index = parts.indexOf(toolCallPart);
|
|
81
|
+
parts[index] = {
|
|
82
|
+
...toolCallPart,
|
|
83
|
+
state: "approval-requested",
|
|
84
|
+
approval: {
|
|
85
|
+
id: approvalId,
|
|
86
|
+
needsApproval: true
|
|
87
|
+
}
|
|
84
88
|
};
|
|
85
89
|
}
|
|
86
90
|
return { ...msg, parts };
|
|
@@ -93,8 +97,12 @@ function updateToolCallWithOutput(messages, toolCallId, output, state, errorText
|
|
|
93
97
|
(p) => p.type === "tool-call" && p.id === toolCallId
|
|
94
98
|
);
|
|
95
99
|
if (toolCallPart) {
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
const index = parts.indexOf(toolCallPart);
|
|
101
|
+
parts[index] = {
|
|
102
|
+
...toolCallPart,
|
|
103
|
+
output: errorText ? { error: errorText } : output,
|
|
104
|
+
state: state ?? (errorText ? "input-complete" : "complete")
|
|
105
|
+
};
|
|
98
106
|
}
|
|
99
107
|
return { ...msg, parts };
|
|
100
108
|
});
|
|
@@ -106,8 +114,12 @@ function updateToolCallApprovalResponse(messages, approvalId, approved) {
|
|
|
106
114
|
(p) => p.type === "tool-call" && p.approval?.id === approvalId
|
|
107
115
|
);
|
|
108
116
|
if (toolCallPart && toolCallPart.approval) {
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
const index = parts.indexOf(toolCallPart);
|
|
118
|
+
parts[index] = {
|
|
119
|
+
...toolCallPart,
|
|
120
|
+
approval: { ...toolCallPart.approval, approved },
|
|
121
|
+
state: "approval-responded"
|
|
122
|
+
};
|
|
111
123
|
}
|
|
112
124
|
return { ...msg, parts };
|
|
113
125
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message-updaters.js","sources":["../../../../../src/activities/chat/stream/message-updaters.ts"],"sourcesContent":["/**\n * Message Updaters (Internal)\n *\n * Internal helper functions for updating UIMessage parts.\n * These are used by StreamProcessor to manage the message array.\n */\n\nimport { parsePartialJSON } from './json-parser'\nimport type {\n ContentPart,\n StructuredOutputPart,\n ThinkingPart,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n} from '../../../types'\nimport type { ToolCallState, ToolResultState } from './types'\n\n/**\n * Update or add a text part to a message.\n *\n * If the last part is a text part, update it (continuing the same text segment).\n * Otherwise, create a new text part (starting a new text segment after tool calls).\n */\nexport function updateTextPart(\n messages: Array<UIMessage>,\n messageId: string,\n content: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const lastPart = parts.length > 0 ? parts[parts.length - 1] : null\n\n if (lastPart && lastPart.type === 'text') {\n // Update the last text part (continuing same text segment)\n parts[parts.length - 1] = { type: 'text', content }\n } else {\n // Create new text part (starting new text segment after tool calls/results)\n parts.push({ type: 'text', content })\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool call part to a message.\n */\nexport function updateToolCallPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCall: {\n id: string\n name: string\n arguments: string\n state: ToolCallState\n metadata?: Record<string, unknown>\n },\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existing = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCall.id,\n )\n\n // Carry forward metadata from either the new toolCall or the existing\n // part. Once the adapter has emitted metadata for a tool call (e.g.\n // Gemini's thoughtSignature on TOOL_CALL_START) we must not lose it on\n // subsequent updates that don't re-supply it.\n const metadata = toolCall.metadata ?? existing?.metadata\n\n const toolCallPart: ToolCallPart = {\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n state: toolCall.state,\n // Carry forward approval and output from the existing part\n ...(existing?.approval && { approval: { ...existing.approval } }),\n ...(existing?.output !== undefined && { output: existing.output }),\n ...(metadata !== undefined && { metadata }),\n }\n\n if (existing) {\n // Update existing tool call\n parts[parts.indexOf(existing)] = toolCallPart\n } else {\n // Add new tool call at the end (preserve natural streaming order)\n parts.push(toolCallPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool result part to a message.\n */\nexport function updateToolResultPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n content: string | Array<ContentPart>,\n state: ToolResultState,\n error?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const resultPartIndex = parts.findIndex(\n (p): p is ToolResultPart =>\n p.type === 'tool-result' && p.toolCallId === toolCallId,\n )\n\n const toolResultPart: ToolResultPart = {\n type: 'tool-result',\n toolCallId,\n content,\n state,\n ...(error && { error }),\n }\n\n if (resultPartIndex >= 0) {\n parts[resultPartIndex] = toolResultPart\n } else {\n parts.push(toolResultPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval request metadata.\n */\nexport function updateToolCallApproval(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n approvalId: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.state = 'approval-requested'\n toolCallPart.approval = {\n id: approvalId,\n needsApproval: true,\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part's state (e.g., to \"input-complete\").\n */\nexport function updateToolCallState(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n state: ToolCallState,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.state = state\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with output.\n * Searches all messages to find the tool call by ID.\n */\nexport function updateToolCallWithOutput(\n messages: Array<UIMessage>,\n toolCallId: string,\n output: any,\n state?: ToolCallState,\n errorText?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n toolCallPart.output = errorText ? { error: errorText } : output\n toolCallPart.state = state ?? (errorText ? 'input-complete' : 'complete')\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval response.\n * Searches all messages to find the tool call by approval ID.\n */\nexport function updateToolCallApprovalResponse(\n messages: Array<UIMessage>,\n approvalId: string,\n approved: boolean,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === approvalId,\n )\n\n if (toolCallPart && toolCallPart.approval) {\n toolCallPart.approval.approved = approved\n toolCallPart.state = 'approval-responded'\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Append a delta to the structured-output part on `messageId`, or create one\n * if absent. Progressive parse of the accumulated buffer fills `partial`.\n *\n * Callers must only invoke this while the part is still in flight — the\n * helper unconditionally writes `status: 'streaming'`, so feeding it a delta\n * after a `complete`/`error` terminal would regress the part. In practice the\n * processor gates calls via `structuredMessageIds`, which is dropped on\n * terminal events.\n *\n * If the progressive parse returns null/undefined (the buffer is not yet a\n * parseable JSON prefix), the previously-good `partial` is preserved so the\n * UI doesn't flicker back to empty for a single render.\n */\nexport function appendStructuredOutputDelta(\n messages: Array<UIMessage>,\n messageId: string,\n delta: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n const existing =\n existingIndex >= 0 ? (parts[existingIndex] as StructuredOutputPart) : null\n\n const nextRaw = (existing?.raw ?? '') + delta\n const progressive = parsePartialJSON(nextRaw)\n const nextPartial =\n progressive !== undefined && progressive !== null\n ? progressive\n : existing?.partial\n\n const nextPart: StructuredOutputPart = {\n type: 'structured-output',\n status: 'streaming',\n raw: nextRaw,\n ...(nextPartial !== undefined ? { partial: nextPartial } : {}),\n ...(existing?.reasoning !== undefined\n ? { reasoning: existing.reasoning }\n : {}),\n }\n\n if (existingIndex >= 0) {\n parts[existingIndex] = nextPart\n } else {\n parts.push(nextPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Snap the structured-output part on `messageId` to `complete` with the\n * validated `data`. Picks the freshest available `raw` so the wire\n * round-trip stays internally consistent:\n *\n * 1. Caller-supplied `raw` (the original streamed bytes from the model).\n * 2. The existing part's `raw` (deltas accumulated before this terminal).\n * 3. `JSON.stringify(data)` as a defensive fallback for terminal-only\n * completes that never shipped raw — keeps the part self-consistent\n * so downstream consumers never see a complete part with empty raw.\n */\nexport function completeStructuredOutputPart(\n messages: Array<UIMessage>,\n messageId: string,\n data: unknown,\n raw: string,\n reasoning?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n\n const existingRaw =\n existingIndex >= 0\n ? (parts[existingIndex] as StructuredOutputPart).raw\n : ''\n let resolvedRaw = raw || existingRaw\n if (resolvedRaw === '' && data !== undefined) {\n try {\n resolvedRaw = JSON.stringify(data)\n } catch {\n // Unserializable (circular, BigInt, throwing toJSON). Leave raw\n // empty. Both downstream paths handle this: `ag-ui-wire.ts`\n // `collectText` skips complete parts with empty raw entirely, and\n // `uiMessageToModelMessages` falls back to a defensive\n // `safeJsonStringify(data)` which itself returns `''` for the same\n // unserializable inputs — so the turn is silently dropped from the\n // next request rather than shipping garbage or crashing the stream.\n }\n }\n\n const nextPart: StructuredOutputPart = {\n type: 'structured-output',\n status: 'complete',\n data,\n partial: data,\n raw: resolvedRaw,\n ...(reasoning !== undefined ? { reasoning } : {}),\n }\n\n if (existingIndex >= 0) {\n parts[existingIndex] = nextPart\n } else {\n parts.push(nextPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Mark the structured-output part on `messageId` as errored. If no part\n * exists yet — RUN_ERROR fired after `structured-output.start` but before\n * any delta — create an empty errored placeholder so consumers have\n * something renderable. Existing complete parts are left alone (an error\n * after a successful complete should not retroactively un-complete it).\n */\nexport function errorStructuredOutputPart(\n messages: Array<UIMessage>,\n messageId: string,\n errorMessage: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n\n if (existingIndex < 0) {\n parts.push({\n type: 'structured-output',\n status: 'error',\n raw: '',\n errorMessage,\n })\n return { ...msg, parts }\n }\n\n const existing = parts[existingIndex] as StructuredOutputPart\n if (existing.status === 'complete') {\n return msg\n }\n parts[existingIndex] = {\n ...existing,\n status: 'error',\n errorMessage,\n }\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a thinking part to a message, keyed by stepId.\n * Each distinct stepId produces its own ThinkingPart.\n */\nexport function updateThinkingPart(\n messages: Array<UIMessage>,\n messageId: string,\n stepId: string,\n content: string,\n signature?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const thinkingPartIndex = parts.findIndex(\n (p) => p.type === 'thinking' && p.stepId === stepId,\n )\n\n const thinkingPart: ThinkingPart = {\n type: 'thinking',\n content,\n stepId,\n ...(signature && { signature }),\n }\n\n if (thinkingPartIndex >= 0) {\n // Update existing thinking part for this step\n parts[thinkingPartIndex] = thinkingPart\n } else {\n // Add new thinking part at the end (preserve natural streaming order)\n parts.push(thinkingPart)\n }\n\n return { ...msg, parts }\n })\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"message-updaters.js","sources":["../../../../../src/activities/chat/stream/message-updaters.ts"],"sourcesContent":["/**\n * Message Updaters (Internal)\n *\n * Internal helper functions for updating UIMessage parts.\n * These are used by StreamProcessor to manage the message array.\n */\n\nimport { parsePartialJSON } from './json-parser'\nimport type {\n ContentPart,\n StructuredOutputPart,\n ThinkingPart,\n ToolCallPart,\n ToolResultPart,\n UIMessage,\n} from '../../../types'\nimport type { ToolCallState, ToolResultState } from './types'\n\n/**\n * Update or add a text part to a message.\n *\n * If the last part is a text part, update it (continuing the same text segment).\n * Otherwise, create a new text part (starting a new text segment after tool calls).\n */\nexport function updateTextPart(\n messages: Array<UIMessage>,\n messageId: string,\n content: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const lastPart = parts.length > 0 ? parts[parts.length - 1] : null\n\n if (lastPart && lastPart.type === 'text') {\n // Update the last text part (continuing same text segment)\n parts[parts.length - 1] = { type: 'text', content }\n } else {\n // Create new text part (starting new text segment after tool calls/results)\n parts.push({ type: 'text', content })\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool call part to a message.\n */\nexport function updateToolCallPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCall: {\n id: string\n name: string\n arguments: string\n state: ToolCallState\n metadata?: Record<string, unknown>\n },\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existing = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCall.id,\n )\n\n // Carry forward metadata from either the new toolCall or the existing\n // part. Once the adapter has emitted metadata for a tool call (e.g.\n // Gemini's thoughtSignature on TOOL_CALL_START) we must not lose it on\n // subsequent updates that don't re-supply it.\n const metadata = toolCall.metadata ?? existing?.metadata\n\n const toolCallPart: ToolCallPart = {\n type: 'tool-call',\n id: toolCall.id,\n name: toolCall.name,\n arguments: toolCall.arguments,\n state: toolCall.state,\n // Carry forward approval and output from the existing part\n ...(existing?.approval && { approval: { ...existing.approval } }),\n ...(existing?.output !== undefined && { output: existing.output }),\n ...(metadata !== undefined && { metadata }),\n }\n\n if (existing) {\n // Update existing tool call\n parts[parts.indexOf(existing)] = toolCallPart\n } else {\n // Add new tool call at the end (preserve natural streaming order)\n parts.push(toolCallPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a tool result part to a message.\n */\nexport function updateToolResultPart(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n content: string | Array<ContentPart>,\n state: ToolResultState,\n error?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const resultPartIndex = parts.findIndex(\n (p): p is ToolResultPart =>\n p.type === 'tool-result' && p.toolCallId === toolCallId,\n )\n\n const toolResultPart: ToolResultPart = {\n type: 'tool-result',\n toolCallId,\n content,\n state,\n ...(error && { error }),\n }\n\n if (resultPartIndex >= 0) {\n parts[resultPartIndex] = toolResultPart\n } else {\n parts.push(toolResultPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval request metadata.\n */\nexport function updateToolCallApproval(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n approvalId: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n state: 'approval-requested',\n approval: {\n id: approvalId,\n needsApproval: true,\n },\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part's state (e.g., to \"input-complete\").\n */\nexport function updateToolCallState(\n messages: Array<UIMessage>,\n messageId: string,\n toolCallId: string,\n state: ToolCallState,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = { ...toolCallPart, state }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with output.\n * Searches all messages to find the tool call by ID.\n */\nexport function updateToolCallWithOutput(\n messages: Array<UIMessage>,\n toolCallId: string,\n output: any,\n state?: ToolCallState,\n errorText?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart => p.type === 'tool-call' && p.id === toolCallId,\n )\n\n if (toolCallPart) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n output: errorText ? { error: errorText } : output,\n state: state ?? (errorText ? 'input-complete' : 'complete'),\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Update a tool call part with approval response.\n * Searches all messages to find the tool call by approval ID.\n */\nexport function updateToolCallApprovalResponse(\n messages: Array<UIMessage>,\n approvalId: string,\n approved: boolean,\n): Array<UIMessage> {\n return messages.map((msg) => {\n const parts = [...msg.parts]\n const toolCallPart = parts.find(\n (p): p is ToolCallPart =>\n p.type === 'tool-call' && p.approval?.id === approvalId,\n )\n\n if (toolCallPart && toolCallPart.approval) {\n const index = parts.indexOf(toolCallPart)\n parts[index] = {\n ...toolCallPart,\n approval: { ...toolCallPart.approval, approved },\n state: 'approval-responded',\n }\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Append a delta to the structured-output part on `messageId`, or create one\n * if absent. Progressive parse of the accumulated buffer fills `partial`.\n *\n * Callers must only invoke this while the part is still in flight — the\n * helper unconditionally writes `status: 'streaming'`, so feeding it a delta\n * after a `complete`/`error` terminal would regress the part. In practice the\n * processor gates calls via `structuredMessageIds`, which is dropped on\n * terminal events.\n *\n * If the progressive parse returns null/undefined (the buffer is not yet a\n * parseable JSON prefix), the previously-good `partial` is preserved so the\n * UI doesn't flicker back to empty for a single render.\n */\nexport function appendStructuredOutputDelta(\n messages: Array<UIMessage>,\n messageId: string,\n delta: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n const existing =\n existingIndex >= 0 ? (parts[existingIndex] as StructuredOutputPart) : null\n\n const nextRaw = (existing?.raw ?? '') + delta\n const progressive = parsePartialJSON(nextRaw)\n const nextPartial =\n progressive !== undefined && progressive !== null\n ? progressive\n : existing?.partial\n\n const nextPart: StructuredOutputPart = {\n type: 'structured-output',\n status: 'streaming',\n raw: nextRaw,\n ...(nextPartial !== undefined ? { partial: nextPartial } : {}),\n ...(existing?.reasoning !== undefined\n ? { reasoning: existing.reasoning }\n : {}),\n }\n\n if (existingIndex >= 0) {\n parts[existingIndex] = nextPart\n } else {\n parts.push(nextPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Snap the structured-output part on `messageId` to `complete` with the\n * validated `data`. Picks the freshest available `raw` so the wire\n * round-trip stays internally consistent:\n *\n * 1. Caller-supplied `raw` (the original streamed bytes from the model).\n * 2. The existing part's `raw` (deltas accumulated before this terminal).\n * 3. `JSON.stringify(data)` as a defensive fallback for terminal-only\n * completes that never shipped raw — keeps the part self-consistent\n * so downstream consumers never see a complete part with empty raw.\n */\nexport function completeStructuredOutputPart(\n messages: Array<UIMessage>,\n messageId: string,\n data: unknown,\n raw: string,\n reasoning?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n\n const existingRaw =\n existingIndex >= 0\n ? (parts[existingIndex] as StructuredOutputPart).raw\n : ''\n let resolvedRaw = raw || existingRaw\n if (resolvedRaw === '' && data !== undefined) {\n try {\n resolvedRaw = JSON.stringify(data)\n } catch {\n // Unserializable (circular, BigInt, throwing toJSON). Leave raw\n // empty. Both downstream paths handle this: `ag-ui-wire.ts`\n // `collectText` skips complete parts with empty raw entirely, and\n // `uiMessageToModelMessages` falls back to a defensive\n // `safeJsonStringify(data)` which itself returns `''` for the same\n // unserializable inputs — so the turn is silently dropped from the\n // next request rather than shipping garbage or crashing the stream.\n }\n }\n\n const nextPart: StructuredOutputPart = {\n type: 'structured-output',\n status: 'complete',\n data,\n partial: data,\n raw: resolvedRaw,\n ...(reasoning !== undefined ? { reasoning } : {}),\n }\n\n if (existingIndex >= 0) {\n parts[existingIndex] = nextPart\n } else {\n parts.push(nextPart)\n }\n\n return { ...msg, parts }\n })\n}\n\n/**\n * Mark the structured-output part on `messageId` as errored. If no part\n * exists yet — RUN_ERROR fired after `structured-output.start` but before\n * any delta — create an empty errored placeholder so consumers have\n * something renderable. Existing complete parts are left alone (an error\n * after a successful complete should not retroactively un-complete it).\n */\nexport function errorStructuredOutputPart(\n messages: Array<UIMessage>,\n messageId: string,\n errorMessage: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const existingIndex = parts.findIndex(\n (p): p is StructuredOutputPart => p.type === 'structured-output',\n )\n\n if (existingIndex < 0) {\n parts.push({\n type: 'structured-output',\n status: 'error',\n raw: '',\n errorMessage,\n })\n return { ...msg, parts }\n }\n\n const existing = parts[existingIndex] as StructuredOutputPart\n if (existing.status === 'complete') {\n return msg\n }\n parts[existingIndex] = {\n ...existing,\n status: 'error',\n errorMessage,\n }\n return { ...msg, parts }\n })\n}\n\n/**\n * Update or add a thinking part to a message, keyed by stepId.\n * Each distinct stepId produces its own ThinkingPart.\n */\nexport function updateThinkingPart(\n messages: Array<UIMessage>,\n messageId: string,\n stepId: string,\n content: string,\n signature?: string,\n): Array<UIMessage> {\n return messages.map((msg) => {\n if (msg.id !== messageId) {\n return msg\n }\n\n const parts = [...msg.parts]\n const thinkingPartIndex = parts.findIndex(\n (p) => p.type === 'thinking' && p.stepId === stepId,\n )\n\n const thinkingPart: ThinkingPart = {\n type: 'thinking',\n content,\n stepId,\n ...(signature && { signature }),\n }\n\n if (thinkingPartIndex >= 0) {\n // Update existing thinking part for this step\n parts[thinkingPartIndex] = thinkingPart\n } else {\n // Add new thinking part at the end (preserve natural streaming order)\n parts.push(thinkingPart)\n }\n\n return { ...msg, parts }\n })\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -136,5 +136,5 @@ interface ExecuteToolCallsResult {
|
|
|
136
136
|
* @param clientResults - Map of client-side execution results (toolCallId -> result)
|
|
137
137
|
* @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)
|
|
138
138
|
*/
|
|
139
|
-
export declare function executeToolCalls<TContext = unknown>(toolCalls: Array<ToolCall>, tools: ReadonlyArray<AnyTool>, approvals?: Map<string, boolean>, clientResults?: Map<string, any>, createCustomEventChunk?: (eventName: string, value: Record<string, any>) => CustomEvent, middlewareHooks?: ToolExecutionMiddlewareHooks, userContext?: TContext): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void>;
|
|
139
|
+
export declare function executeToolCalls<TContext = unknown>(toolCalls: Array<ToolCall>, tools: ReadonlyArray<AnyTool>, approvals?: Map<string, boolean>, clientResults?: Map<string, any>, createCustomEventChunk?: (eventName: string, value: Record<string, any>) => CustomEvent, middlewareHooks?: ToolExecutionMiddlewareHooks, userContext?: TContext, abortSignal?: AbortSignal): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void>;
|
|
140
140
|
export {};
|
|
@@ -305,7 +305,7 @@ function buildClientToolResult(toolCallId, toolName, tool, rawResult) {
|
|
|
305
305
|
};
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
|
-
async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ new Map(), clientResults = /* @__PURE__ */ new Map(), createCustomEventChunk, middlewareHooks, userContext) {
|
|
308
|
+
async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ new Map(), clientResults = /* @__PURE__ */ new Map(), createCustomEventChunk, middlewareHooks, userContext, abortSignal) {
|
|
309
309
|
const results = [];
|
|
310
310
|
const needsApproval = [];
|
|
311
311
|
const needsClientExecution = [];
|
|
@@ -364,6 +364,7 @@ async function* executeToolCalls(toolCalls, tools, approvals = /* @__PURE__ */ n
|
|
|
364
364
|
const context = {
|
|
365
365
|
toolCallId: toolCall.id,
|
|
366
366
|
context: userContext,
|
|
367
|
+
abortSignal,
|
|
367
368
|
emitCustomEvent: (eventName, value) => {
|
|
368
369
|
if (createCustomEventChunk) {
|
|
369
370
|
pendingEvents.push(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-calls.js","sources":["../../../../../src/activities/chat/tools/tool-calls.ts"],"sourcesContent":["import { normalizeToolResult } from '../../../utilities/tool-result'\nimport { isStandardSchema, parseWithStandardSchema } from './schema-converter'\nimport type {\n AnyTool,\n ContentPart,\n CustomEvent,\n ModelMessage,\n RunFinishedEvent,\n Tool,\n ToolCall,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolExecutionContext,\n ToolOutputState,\n} from '../../../types'\nimport type {\n AfterToolCallInfo,\n BeforeToolCallDecision,\n} from '../middleware/types'\nimport type {\n ContextFromTool,\n DefinedContext,\n MergeContext,\n UnionToIntersection,\n} from '../runtime-context-types'\n\nfunction safeJsonParse(value: string): unknown {\n try {\n return JSON.parse(value)\n } catch {\n return value\n }\n}\n\n/**\n * Optional middleware hooks for tool execution.\n * When provided, these callbacks are invoked before/after each tool execution.\n */\nexport interface ToolExecutionMiddlewareHooks {\n onBeforeToolCall?: (\n toolCall: ToolCall,\n tool: Tool | undefined,\n args: unknown,\n ) => Promise<BeforeToolCallDecision>\n onAfterToolCall?: (info: AfterToolCallInfo) => Promise<void>\n}\n\n/**\n * Error thrown when middleware decides to abort the chat run during tool execution.\n */\nexport class MiddlewareAbortError extends Error {\n constructor(reason: string) {\n super(reason)\n this.name = 'MiddlewareAbortError'\n }\n}\n\n// The leaf context-inference primitives (ContextFromTool, MergeContext,\n// UnionToIntersection, DefinedContext) are shared with the chat activity\n// options layer — see ../runtime-context-types.\ntype RequiredContextFromToolUnion<T> = T extends unknown\n ? undefined extends ContextFromTool<T>\n ? never\n : ContextFromTool<T>\n : never\n\ntype ContextFromToolUnion<T> = [\n UnionToIntersection<DefinedContext<ContextFromTool<T>>>,\n] extends [never]\n ? unknown\n : [RequiredContextFromToolUnion<T>] extends [never]\n ? UnionToIntersection<DefinedContext<ContextFromTool<T>>> | undefined\n : UnionToIntersection<DefinedContext<ContextFromTool<T>>>\n\ntype ContextFromTools<TTools> = TTools extends readonly [\n infer THead,\n ...infer TTail,\n]\n ? MergeContext<ContextFromTool<THead>, ContextFromTools<TTail>>\n : TTools extends ReadonlyArray<infer TTool>\n ? ContextFromToolUnion<TTool>\n : unknown\n\ntype ExecuteToolsContextArgs<TContext> = undefined extends TContext\n ? [userContext?: TContext]\n : [userContext: TContext]\n\n/**\n * Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.\n *\n * Responsibilities:\n * - Accumulates streaming tool call events (ID, name, arguments)\n * - Validates tool calls (filters out incomplete ones)\n * - Executes tool `execute` functions with parsed arguments\n * - Emits `TOOL_CALL_END` events for client visibility\n * - Returns tool result messages for conversation history\n *\n * This class is used internally by the AI.chat() method to handle the automatic\n * tool execution loop. It can also be used independently for custom tool execution logic.\n *\n * @example\n * ```typescript\n * const manager = new ToolCallManager(tools);\n *\n * // During streaming, accumulate tool calls\n * for await (const chunk of stream) {\n * if (chunk.type === 'TOOL_CALL_START') {\n * manager.addToolCallStartEvent(chunk);\n * } else if (chunk.type === 'TOOL_CALL_ARGS') {\n * manager.addToolCallArgsEvent(chunk);\n * }\n * }\n *\n * // After stream completes, execute tools\n * if (manager.hasToolCalls()) {\n * const toolResults = yield* manager.executeTools(finishEvent);\n * messages = [...messages, ...toolResults];\n * manager.clear();\n * }\n * ```\n */\nexport class ToolCallManager<\n TToolsOrContext = ReadonlyArray<AnyTool>,\n TContext = TToolsOrContext extends ReadonlyArray<AnyTool>\n ? ContextFromTools<TToolsOrContext>\n : TToolsOrContext,\n> {\n private readonly toolCallsMap = new Map<number, ToolCall>()\n private readonly tools: TToolsOrContext extends ReadonlyArray<AnyTool>\n ? TToolsOrContext\n : ReadonlyArray<AnyTool>\n\n constructor(\n tools: TToolsOrContext extends ReadonlyArray<AnyTool>\n ? TToolsOrContext\n : ReadonlyArray<AnyTool>,\n ) {\n this.tools = tools\n }\n\n /**\n * Add a TOOL_CALL_START event to begin tracking a tool call (AG-UI)\n */\n addToolCallStartEvent(event: ToolCallStartEvent): void {\n const index = event.index ?? this.toolCallsMap.size\n const runtimeEvent = event as Partial<ToolCallStartEvent> &\n Pick<ToolCallStartEvent, 'toolName'>\n const name = runtimeEvent.toolCallName ?? runtimeEvent.toolName\n this.toolCallsMap.set(index, {\n id: event.toolCallId,\n type: 'function',\n function: {\n name,\n arguments: '',\n },\n ...(event.metadata !== undefined && { metadata: event.metadata }),\n })\n }\n\n /**\n * Add a TOOL_CALL_ARGS event to accumulate arguments (AG-UI)\n */\n addToolCallArgsEvent(event: ToolCallArgsEvent): void {\n // Find the tool call by ID\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n toolCall.function.arguments += event.delta\n break\n }\n }\n }\n\n /**\n * Complete a tool call with its final input\n * Called when TOOL_CALL_END is received\n */\n completeToolCall(event: ToolCallEndEvent): void {\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n if (event.input !== undefined) {\n // Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)\n const normalized =\n event.input && typeof event.input === 'object' ? event.input : {}\n toolCall.function.arguments = JSON.stringify(normalized)\n }\n break\n }\n }\n }\n\n /**\n * Check if there are any complete tool calls to execute\n */\n hasToolCalls(): boolean {\n return this.getToolCalls().length > 0\n }\n\n /**\n * Get all complete tool calls (filtered for valid ID and name)\n */\n getToolCalls(): Array<ToolCall> {\n return Array.from(this.toolCallsMap.values()).filter(\n (tc) => tc.id && tc.function.name && tc.function.name.trim().length > 0,\n )\n }\n\n /**\n * Execute all tool calls and return tool result messages\n * Yields TOOL_CALL_END events for streaming\n * @param finishEvent - RUN_FINISHED event from the stream\n */\n async *executeTools(\n finishEvent: RunFinishedEvent,\n ...contextArgs: ExecuteToolsContextArgs<TContext>\n ): AsyncGenerator<ToolCallEndEvent, Array<ModelMessage>, void> {\n const toolCallsArray = this.getToolCalls()\n const toolResults: Array<ModelMessage> = []\n const hasRuntimeContext = contextArgs.length > 0\n const userContext = contextArgs[0]\n\n for (const toolCall of toolCallsArray) {\n const tool = this.tools.find((t) => t.name === toolCall.function.name)\n\n let toolResultContent: string | Array<ContentPart>\n let toolResultState: ToolOutputState | undefined\n if (tool?.execute) {\n try {\n // Parse arguments (normalize null/non-object to {} for empty tool_use blocks)\n let args: unknown\n try {\n const argsString = toolCall.function.arguments.trim() || '{}'\n const parsed = JSON.parse(argsString)\n args = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n throw new Error(\n `Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`,\n )\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n args = parseWithStandardSchema(tool.inputSchema, args)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Input validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n // Execute the tool\n const executionContext = {\n toolCallId: toolCall.id,\n context: userContext,\n emitCustomEvent: () => {},\n } as ToolExecutionContext<TContext>\n let result = hasRuntimeContext\n ? await tool.execute(args, executionContext)\n : await tool.execute(args)\n\n // Validate output against outputSchema if provided (for Standard\n // Schema compliant schemas). Unlike the previous implementation we\n // intentionally validate `undefined`/`null` results too, so a tool\n // whose schema forbids them surfaces a validation error instead of\n // silently passing — the schema itself decides whether they're valid.\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n try {\n result = parseWithStandardSchema(tool.outputSchema, result)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Output validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n toolResultContent = normalizeToolResult(result)\n } catch (error: unknown) {\n // If tool execution fails, add error message\n const message =\n error instanceof Error ? error.message : 'Unknown error'\n toolResultContent = `Error executing tool: ${message}`\n toolResultState = 'output-error'\n }\n } else {\n // Tool doesn't have execute function, add placeholder\n toolResultContent = `Tool ${toolCall.function.name} does not have an execute function`\n }\n\n // Emit TOOL_CALL_END event\n yield {\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolCallName: toolCall.function.name,\n toolName: toolCall.function.name,\n model: finishEvent.model,\n timestamp: Date.now(),\n result: toolResultContent,\n ...(toolResultState !== undefined && { state: toolResultState }),\n } as ToolCallEndEvent\n\n // Add tool result message\n toolResults.push({\n role: 'tool',\n content: toolResultContent,\n toolCallId: toolCall.id,\n })\n }\n\n return toolResults\n }\n\n /**\n * Clear the tool calls map for the next iteration\n */\n clear(): void {\n this.toolCallsMap.clear()\n }\n}\n\nexport interface ToolResult {\n toolCallId: string\n toolName: string\n result: any\n state?: 'output-available' | 'output-error'\n /** Duration of tool execution in milliseconds (only for server-executed tools) */\n duration?: number\n}\n\nexport interface ApprovalRequest {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n}\n\nexport interface ClientToolRequest {\n toolCallId: string\n toolName: string\n input: any\n}\n\ninterface ExecuteToolCallsResult {\n /** Tool results ready to send to LLM */\n results: Array<ToolResult>\n /** Tools that need user approval before execution */\n needsApproval: Array<ApprovalRequest>\n /** Tools that need client-side execution */\n needsClientExecution: Array<ClientToolRequest>\n}\n\n/**\n * Helper that runs a tool execution promise while polling for pending custom events.\n * Yields any custom events that are emitted during execution, then returns the\n * execution result.\n */\nasync function* executeWithEventPolling<T>(\n executionPromise: Promise<T>,\n pendingEvents: Array<CustomEvent>,\n): AsyncGenerator<CustomEvent, T, void> {\n // Use an object to track mutable state across the async boundary\n const state = { done: false, result: undefined as T }\n const executionWithFlag = executionPromise.then((r) => {\n state.done = true\n state.result = r\n return r\n })\n\n while (!state.done) {\n // Wait for either the execution to complete or a short timeout\n await Promise.race([\n executionWithFlag,\n new Promise((resolve) => setTimeout(resolve, 10)),\n ])\n\n // Flush any pending events\n let event: CustomEvent | undefined\n while ((event = pendingEvents.shift()) !== undefined) {\n yield event\n }\n }\n\n // Final flush in case events were emitted right at completion\n let event: CustomEvent | undefined\n while ((event = pendingEvents.shift()) !== undefined) {\n yield event\n }\n\n return state.result\n}\n\n/**\n * Apply a middleware onBeforeToolCall decision.\n * Returns the (possibly transformed) input if execution should proceed,\n * or undefined if the tool call was skipped (result already pushed).\n * Throws MiddlewareAbortError if the decision is 'abort'.\n */\nasync function applyBeforeToolCallDecision(\n toolCall: ToolCall,\n tool: Tool,\n input: unknown,\n toolName: string,\n middlewareHooks: ToolExecutionMiddlewareHooks,\n results: Array<ToolResult>,\n): Promise<{ proceed: true; input: unknown } | { proceed: false }> {\n if (!middlewareHooks.onBeforeToolCall) {\n return { proceed: true, input }\n }\n\n const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input)\n if (!decision) {\n return { proceed: true, input }\n }\n\n if (decision.type === 'abort') {\n throw new MiddlewareAbortError(decision.reason || 'Aborted by middleware')\n }\n\n if (decision.type === 'skip') {\n const skipResult = decision.result\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n typeof skipResult === 'string'\n ? safeJsonParse(skipResult)\n : (skipResult ?? null),\n duration: 0,\n })\n if (middlewareHooks.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration: 0,\n result: skipResult,\n })\n }\n return { proceed: false }\n }\n\n return { proceed: true, input: decision.args }\n}\n\n/**\n * Execute a server-side tool with event polling, output validation, and middleware hooks.\n * Yields CustomEvent chunks during execution and pushes the result to the results array.\n */\nasync function* executeServerTool<TContext = unknown>(\n toolCall: ToolCall,\n tool: AnyTool,\n toolName: string,\n input: unknown,\n context: ToolExecutionContext<TContext>,\n pendingEvents: Array<CustomEvent>,\n results: Array<ToolResult>,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n): AsyncGenerator<CustomEvent, void, void> {\n const startTime = Date.now()\n try {\n if (!tool.execute) {\n throw new Error(`Tool ${toolName} has no execute() implementation`)\n }\n const executionPromise = Promise.resolve(tool.execute(input, context))\n let result = yield* executeWithEventPolling(executionPromise, pendingEvents)\n const duration = Date.now() - startTime\n\n // Flush remaining events\n let pendingEvent: CustomEvent | undefined\n while ((pendingEvent = pendingEvents.shift()) !== undefined) {\n yield pendingEvent\n }\n\n // Validate output against outputSchema if provided. Validates\n // `undefined`/`null` too — the schema decides whether they're valid.\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n result = parseWithStandardSchema(tool.outputSchema, result)\n }\n\n const finalResult =\n typeof result === 'string' ? safeJsonParse(result) : (result ?? null)\n\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: finalResult,\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration,\n result: finalResult,\n })\n }\n } catch (error: unknown) {\n const duration = Date.now() - startTime\n\n // Flush remaining events\n let pendingEvent: CustomEvent | undefined\n while ((pendingEvent = pendingEvents.shift()) !== undefined) {\n yield pendingEvent\n }\n\n if (error instanceof MiddlewareAbortError) {\n throw error\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: message },\n state: 'output-error',\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: false,\n duration,\n error,\n })\n }\n }\n}\n\nfunction buildClientToolResult(\n toolCallId: string,\n toolName: string,\n tool: AnyTool,\n rawResult: unknown,\n): ToolResult {\n try {\n let result = rawResult\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n result = parseWithStandardSchema(tool.outputSchema, result)\n }\n\n return {\n toolCallId,\n toolName,\n result:\n typeof result === 'string' ? safeJsonParse(result) : (result ?? null),\n }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Validation failed'\n return {\n toolCallId,\n toolName,\n result: { error: message },\n state: 'output-error',\n }\n }\n}\n\n/**\n * Execute tool calls based on their configuration.\n * Yields CustomEvent chunks during tool execution for real-time progress updates.\n *\n * Handles three cases:\n * 1. Client tools (no execute) - request client to execute\n * 2. Server tools with approval - check approval before executing\n * 3. Normal server tools - execute immediately\n *\n * @param toolCalls - Tool calls from the LLM\n * @param tools - Available tools with their configurations\n * @param approvals - Map of approval decisions (approval.id -> approved boolean)\n * @param clientResults - Map of client-side execution results (toolCallId -> result)\n * @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)\n */\nexport async function* executeToolCalls<TContext = unknown>(\n toolCalls: Array<ToolCall>,\n tools: ReadonlyArray<AnyTool>,\n approvals: Map<string, boolean> = new Map(),\n clientResults: Map<string, any> = new Map(),\n createCustomEventChunk?: (\n eventName: string,\n value: Record<string, any>,\n ) => CustomEvent,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n userContext?: TContext,\n): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void> {\n const results: Array<ToolResult> = []\n const needsApproval: Array<ApprovalRequest> = []\n const needsClientExecution: Array<ClientToolRequest> = []\n\n // Create tool lookup map\n const toolMap = new Map<string, AnyTool>()\n for (const tool of tools) {\n toolMap.set(tool.name, tool)\n }\n\n // Batch gating: when any tool in the batch still needs an approval decision,\n // defer all execution so side effects don't happen before the user decides.\n const hasPendingApprovals = toolCalls.some((tc) => {\n const t = toolMap.get(tc.function.name)\n return t?.needsApproval && !approvals.has(`approval_${tc.id}`)\n })\n\n for (const toolCall of toolCalls) {\n const tool = toolMap.get(toolCall.function.name)\n const toolName = toolCall.function.name\n\n if (!tool) {\n // Unknown tool - return error\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: `Unknown tool: ${toolName}` },\n state: 'output-error',\n })\n continue\n }\n\n // Skip non-pending tools while approvals are outstanding\n if (hasPendingApprovals) {\n if (!tool.needsApproval || approvals.has(`approval_${toolCall.id}`)) {\n continue\n }\n }\n\n // Parse arguments, throwing error if invalid JSON\n let input: unknown = {}\n const argsStr = toolCall.function.arguments.trim() || '{}'\n if (argsStr) {\n try {\n const parsed = JSON.parse(argsStr)\n // Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)\n input = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n // If parsing fails, throw error to fail fast\n throw new Error(`Failed to parse tool arguments as JSON: ${argsStr}`)\n }\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n input = parseWithStandardSchema(tool.inputSchema, input)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: {\n error: `Input validation failed for tool ${tool.name}: ${message}`,\n },\n state: 'output-error',\n })\n continue\n }\n }\n\n // Create a ToolExecutionContext for this tool call with event emission\n const pendingEvents: Array<CustomEvent> = []\n const context = {\n toolCallId: toolCall.id,\n context: userContext,\n emitCustomEvent: (eventName: string, value: Record<string, any>) => {\n if (createCustomEventChunk) {\n pendingEvents.push(\n createCustomEventChunk(eventName, {\n ...value,\n toolCallId: toolCall.id,\n }),\n )\n }\n },\n } as ToolExecutionContext<TContext>\n\n // CASE 1: Client-side tool (no execute function)\n if (!tool.execute) {\n // Check if tool needs approval\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n\n // Check if approval decision exists\n if (approvals.has(approvalId)) {\n const approved = approvals.get(approvalId)\n\n if (approved) {\n // Approved - check if client has executed\n if (clientResults.has(toolCall.id)) {\n results.push(\n buildClientToolResult(\n toolCall.id,\n toolName,\n tool,\n clientResults.get(toolCall.id),\n ),\n )\n } else {\n // Approved but not executed yet - request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'User declined tool execution' },\n state: 'output-error',\n })\n }\n } else {\n // Need approval first\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName: toolCall.function.name,\n input,\n approvalId,\n })\n }\n } else {\n // No approval needed - check if client has executed\n if (clientResults.has(toolCall.id)) {\n results.push(\n buildClientToolResult(\n toolCall.id,\n toolName,\n tool,\n clientResults.get(toolCall.id),\n ),\n )\n } else {\n // Request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n }\n continue\n }\n\n // CASE 2: Server tool with approval required\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n\n // Check if approval decision exists\n if (approvals.has(approvalId)) {\n const approved = approvals.get(approvalId)\n\n if (approved) {\n // Apply middleware before-hook for approved tools\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n )\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'User declined tool execution' },\n state: 'output-error',\n })\n }\n } else {\n // Need approval\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n approvalId,\n })\n }\n continue\n }\n\n // CASE 3: Normal server tool - execute immediately\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n )\n }\n\n return { results, needsApproval, needsClientExecution }\n}\n"],"names":["event"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-calls.js","sources":["../../../../../src/activities/chat/tools/tool-calls.ts"],"sourcesContent":["import { normalizeToolResult } from '../../../utilities/tool-result'\nimport { isStandardSchema, parseWithStandardSchema } from './schema-converter'\nimport type {\n AnyTool,\n ContentPart,\n CustomEvent,\n ModelMessage,\n RunFinishedEvent,\n Tool,\n ToolCall,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallStartEvent,\n ToolExecutionContext,\n ToolOutputState,\n} from '../../../types'\nimport type {\n AfterToolCallInfo,\n BeforeToolCallDecision,\n} from '../middleware/types'\nimport type {\n ContextFromTool,\n DefinedContext,\n MergeContext,\n UnionToIntersection,\n} from '../runtime-context-types'\n\nfunction safeJsonParse(value: string): unknown {\n try {\n return JSON.parse(value)\n } catch {\n return value\n }\n}\n\n/**\n * Optional middleware hooks for tool execution.\n * When provided, these callbacks are invoked before/after each tool execution.\n */\nexport interface ToolExecutionMiddlewareHooks {\n onBeforeToolCall?: (\n toolCall: ToolCall,\n tool: Tool | undefined,\n args: unknown,\n ) => Promise<BeforeToolCallDecision>\n onAfterToolCall?: (info: AfterToolCallInfo) => Promise<void>\n}\n\n/**\n * Error thrown when middleware decides to abort the chat run during tool execution.\n */\nexport class MiddlewareAbortError extends Error {\n constructor(reason: string) {\n super(reason)\n this.name = 'MiddlewareAbortError'\n }\n}\n\n// The leaf context-inference primitives (ContextFromTool, MergeContext,\n// UnionToIntersection, DefinedContext) are shared with the chat activity\n// options layer — see ../runtime-context-types.\ntype RequiredContextFromToolUnion<T> = T extends unknown\n ? undefined extends ContextFromTool<T>\n ? never\n : ContextFromTool<T>\n : never\n\ntype ContextFromToolUnion<T> = [\n UnionToIntersection<DefinedContext<ContextFromTool<T>>>,\n] extends [never]\n ? unknown\n : [RequiredContextFromToolUnion<T>] extends [never]\n ? UnionToIntersection<DefinedContext<ContextFromTool<T>>> | undefined\n : UnionToIntersection<DefinedContext<ContextFromTool<T>>>\n\ntype ContextFromTools<TTools> = TTools extends readonly [\n infer THead,\n ...infer TTail,\n]\n ? MergeContext<ContextFromTool<THead>, ContextFromTools<TTail>>\n : TTools extends ReadonlyArray<infer TTool>\n ? ContextFromToolUnion<TTool>\n : unknown\n\ntype ExecuteToolsContextArgs<TContext> = undefined extends TContext\n ? [userContext?: TContext]\n : [userContext: TContext]\n\n/**\n * Manages tool call accumulation and execution for the chat() method's automatic tool execution loop.\n *\n * Responsibilities:\n * - Accumulates streaming tool call events (ID, name, arguments)\n * - Validates tool calls (filters out incomplete ones)\n * - Executes tool `execute` functions with parsed arguments\n * - Emits `TOOL_CALL_END` events for client visibility\n * - Returns tool result messages for conversation history\n *\n * This class is used internally by the AI.chat() method to handle the automatic\n * tool execution loop. It can also be used independently for custom tool execution logic.\n *\n * @example\n * ```typescript\n * const manager = new ToolCallManager(tools);\n *\n * // During streaming, accumulate tool calls\n * for await (const chunk of stream) {\n * if (chunk.type === 'TOOL_CALL_START') {\n * manager.addToolCallStartEvent(chunk);\n * } else if (chunk.type === 'TOOL_CALL_ARGS') {\n * manager.addToolCallArgsEvent(chunk);\n * }\n * }\n *\n * // After stream completes, execute tools\n * if (manager.hasToolCalls()) {\n * const toolResults = yield* manager.executeTools(finishEvent);\n * messages = [...messages, ...toolResults];\n * manager.clear();\n * }\n * ```\n */\nexport class ToolCallManager<\n TToolsOrContext = ReadonlyArray<AnyTool>,\n TContext = TToolsOrContext extends ReadonlyArray<AnyTool>\n ? ContextFromTools<TToolsOrContext>\n : TToolsOrContext,\n> {\n private readonly toolCallsMap = new Map<number, ToolCall>()\n private readonly tools: TToolsOrContext extends ReadonlyArray<AnyTool>\n ? TToolsOrContext\n : ReadonlyArray<AnyTool>\n\n constructor(\n tools: TToolsOrContext extends ReadonlyArray<AnyTool>\n ? TToolsOrContext\n : ReadonlyArray<AnyTool>,\n ) {\n this.tools = tools\n }\n\n /**\n * Add a TOOL_CALL_START event to begin tracking a tool call (AG-UI)\n */\n addToolCallStartEvent(event: ToolCallStartEvent): void {\n const index = event.index ?? this.toolCallsMap.size\n const runtimeEvent = event as Partial<ToolCallStartEvent> &\n Pick<ToolCallStartEvent, 'toolName'>\n const name = runtimeEvent.toolCallName ?? runtimeEvent.toolName\n this.toolCallsMap.set(index, {\n id: event.toolCallId,\n type: 'function',\n function: {\n name,\n arguments: '',\n },\n ...(event.metadata !== undefined && { metadata: event.metadata }),\n })\n }\n\n /**\n * Add a TOOL_CALL_ARGS event to accumulate arguments (AG-UI)\n */\n addToolCallArgsEvent(event: ToolCallArgsEvent): void {\n // Find the tool call by ID\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n toolCall.function.arguments += event.delta\n break\n }\n }\n }\n\n /**\n * Complete a tool call with its final input\n * Called when TOOL_CALL_END is received\n */\n completeToolCall(event: ToolCallEndEvent): void {\n for (const [, toolCall] of this.toolCallsMap.entries()) {\n if (toolCall.id === event.toolCallId) {\n if (event.input !== undefined) {\n // Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)\n const normalized =\n event.input && typeof event.input === 'object' ? event.input : {}\n toolCall.function.arguments = JSON.stringify(normalized)\n }\n break\n }\n }\n }\n\n /**\n * Check if there are any complete tool calls to execute\n */\n hasToolCalls(): boolean {\n return this.getToolCalls().length > 0\n }\n\n /**\n * Get all complete tool calls (filtered for valid ID and name)\n */\n getToolCalls(): Array<ToolCall> {\n return Array.from(this.toolCallsMap.values()).filter(\n (tc) => tc.id && tc.function.name && tc.function.name.trim().length > 0,\n )\n }\n\n /**\n * Execute all tool calls and return tool result messages\n * Yields TOOL_CALL_END events for streaming\n * @param finishEvent - RUN_FINISHED event from the stream\n */\n async *executeTools(\n finishEvent: RunFinishedEvent,\n ...contextArgs: ExecuteToolsContextArgs<TContext>\n ): AsyncGenerator<ToolCallEndEvent, Array<ModelMessage>, void> {\n const toolCallsArray = this.getToolCalls()\n const toolResults: Array<ModelMessage> = []\n const hasRuntimeContext = contextArgs.length > 0\n const userContext = contextArgs[0]\n\n for (const toolCall of toolCallsArray) {\n const tool = this.tools.find((t) => t.name === toolCall.function.name)\n\n let toolResultContent: string | Array<ContentPart>\n let toolResultState: ToolOutputState | undefined\n if (tool?.execute) {\n try {\n // Parse arguments (normalize null/non-object to {} for empty tool_use blocks)\n let args: unknown\n try {\n const argsString = toolCall.function.arguments.trim() || '{}'\n const parsed = JSON.parse(argsString)\n args = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n throw new Error(\n `Failed to parse tool arguments as JSON: ${toolCall.function.arguments}`,\n )\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n args = parseWithStandardSchema(tool.inputSchema, args)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Input validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n // Execute the tool\n const executionContext = {\n toolCallId: toolCall.id,\n context: userContext,\n emitCustomEvent: () => {},\n } as ToolExecutionContext<TContext>\n let result = hasRuntimeContext\n ? await tool.execute(args, executionContext)\n : await tool.execute(args)\n\n // Validate output against outputSchema if provided (for Standard\n // Schema compliant schemas). Unlike the previous implementation we\n // intentionally validate `undefined`/`null` results too, so a tool\n // whose schema forbids them surfaces a validation error instead of\n // silently passing — the schema itself decides whether they're valid.\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n try {\n result = parseWithStandardSchema(tool.outputSchema, result)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n throw new Error(\n `Output validation failed for tool ${tool.name}: ${message}`,\n )\n }\n }\n\n toolResultContent = normalizeToolResult(result)\n } catch (error: unknown) {\n // If tool execution fails, add error message\n const message =\n error instanceof Error ? error.message : 'Unknown error'\n toolResultContent = `Error executing tool: ${message}`\n toolResultState = 'output-error'\n }\n } else {\n // Tool doesn't have execute function, add placeholder\n toolResultContent = `Tool ${toolCall.function.name} does not have an execute function`\n }\n\n // Emit TOOL_CALL_END event\n yield {\n type: 'TOOL_CALL_END',\n toolCallId: toolCall.id,\n toolCallName: toolCall.function.name,\n toolName: toolCall.function.name,\n model: finishEvent.model,\n timestamp: Date.now(),\n result: toolResultContent,\n ...(toolResultState !== undefined && { state: toolResultState }),\n } as ToolCallEndEvent\n\n // Add tool result message\n toolResults.push({\n role: 'tool',\n content: toolResultContent,\n toolCallId: toolCall.id,\n })\n }\n\n return toolResults\n }\n\n /**\n * Clear the tool calls map for the next iteration\n */\n clear(): void {\n this.toolCallsMap.clear()\n }\n}\n\nexport interface ToolResult {\n toolCallId: string\n toolName: string\n result: any\n state?: 'output-available' | 'output-error'\n /** Duration of tool execution in milliseconds (only for server-executed tools) */\n duration?: number\n}\n\nexport interface ApprovalRequest {\n toolCallId: string\n toolName: string\n input: any\n approvalId: string\n}\n\nexport interface ClientToolRequest {\n toolCallId: string\n toolName: string\n input: any\n}\n\ninterface ExecuteToolCallsResult {\n /** Tool results ready to send to LLM */\n results: Array<ToolResult>\n /** Tools that need user approval before execution */\n needsApproval: Array<ApprovalRequest>\n /** Tools that need client-side execution */\n needsClientExecution: Array<ClientToolRequest>\n}\n\n/**\n * Helper that runs a tool execution promise while polling for pending custom events.\n * Yields any custom events that are emitted during execution, then returns the\n * execution result.\n */\nasync function* executeWithEventPolling<T>(\n executionPromise: Promise<T>,\n pendingEvents: Array<CustomEvent>,\n): AsyncGenerator<CustomEvent, T, void> {\n // Use an object to track mutable state across the async boundary\n const state = { done: false, result: undefined as T }\n const executionWithFlag = executionPromise.then((r) => {\n state.done = true\n state.result = r\n return r\n })\n\n while (!state.done) {\n // Wait for either the execution to complete or a short timeout\n await Promise.race([\n executionWithFlag,\n new Promise((resolve) => setTimeout(resolve, 10)),\n ])\n\n // Flush any pending events\n let event: CustomEvent | undefined\n while ((event = pendingEvents.shift()) !== undefined) {\n yield event\n }\n }\n\n // Final flush in case events were emitted right at completion\n let event: CustomEvent | undefined\n while ((event = pendingEvents.shift()) !== undefined) {\n yield event\n }\n\n return state.result\n}\n\n/**\n * Apply a middleware onBeforeToolCall decision.\n * Returns the (possibly transformed) input if execution should proceed,\n * or undefined if the tool call was skipped (result already pushed).\n * Throws MiddlewareAbortError if the decision is 'abort'.\n */\nasync function applyBeforeToolCallDecision(\n toolCall: ToolCall,\n tool: Tool,\n input: unknown,\n toolName: string,\n middlewareHooks: ToolExecutionMiddlewareHooks,\n results: Array<ToolResult>,\n): Promise<{ proceed: true; input: unknown } | { proceed: false }> {\n if (!middlewareHooks.onBeforeToolCall) {\n return { proceed: true, input }\n }\n\n const decision = await middlewareHooks.onBeforeToolCall(toolCall, tool, input)\n if (!decision) {\n return { proceed: true, input }\n }\n\n if (decision.type === 'abort') {\n throw new MiddlewareAbortError(decision.reason || 'Aborted by middleware')\n }\n\n if (decision.type === 'skip') {\n const skipResult = decision.result\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result:\n typeof skipResult === 'string'\n ? safeJsonParse(skipResult)\n : (skipResult ?? null),\n duration: 0,\n })\n if (middlewareHooks.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration: 0,\n result: skipResult,\n })\n }\n return { proceed: false }\n }\n\n return { proceed: true, input: decision.args }\n}\n\n/**\n * Execute a server-side tool with event polling, output validation, and middleware hooks.\n * Yields CustomEvent chunks during execution and pushes the result to the results array.\n */\nasync function* executeServerTool<TContext = unknown>(\n toolCall: ToolCall,\n tool: AnyTool,\n toolName: string,\n input: unknown,\n context: ToolExecutionContext<TContext>,\n pendingEvents: Array<CustomEvent>,\n results: Array<ToolResult>,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n): AsyncGenerator<CustomEvent, void, void> {\n const startTime = Date.now()\n try {\n if (!tool.execute) {\n throw new Error(`Tool ${toolName} has no execute() implementation`)\n }\n const executionPromise = Promise.resolve(tool.execute(input, context))\n let result = yield* executeWithEventPolling(executionPromise, pendingEvents)\n const duration = Date.now() - startTime\n\n // Flush remaining events\n let pendingEvent: CustomEvent | undefined\n while ((pendingEvent = pendingEvents.shift()) !== undefined) {\n yield pendingEvent\n }\n\n // Validate output against outputSchema if provided. Validates\n // `undefined`/`null` too — the schema decides whether they're valid.\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n result = parseWithStandardSchema(tool.outputSchema, result)\n }\n\n const finalResult =\n typeof result === 'string' ? safeJsonParse(result) : (result ?? null)\n\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: finalResult,\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: true,\n duration,\n result: finalResult,\n })\n }\n } catch (error: unknown) {\n const duration = Date.now() - startTime\n\n // Flush remaining events\n let pendingEvent: CustomEvent | undefined\n while ((pendingEvent = pendingEvents.shift()) !== undefined) {\n yield pendingEvent\n }\n\n if (error instanceof MiddlewareAbortError) {\n throw error\n }\n\n const message = error instanceof Error ? error.message : 'Unknown error'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: message },\n state: 'output-error',\n duration,\n })\n\n if (middlewareHooks?.onAfterToolCall) {\n await middlewareHooks.onAfterToolCall({\n toolCall,\n tool,\n toolName,\n toolCallId: toolCall.id,\n ok: false,\n duration,\n error,\n })\n }\n }\n}\n\nfunction buildClientToolResult(\n toolCallId: string,\n toolName: string,\n tool: AnyTool,\n rawResult: unknown,\n): ToolResult {\n try {\n let result = rawResult\n if (tool.outputSchema && isStandardSchema(tool.outputSchema)) {\n result = parseWithStandardSchema(tool.outputSchema, result)\n }\n\n return {\n toolCallId,\n toolName,\n result:\n typeof result === 'string' ? safeJsonParse(result) : (result ?? null),\n }\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : 'Validation failed'\n return {\n toolCallId,\n toolName,\n result: { error: message },\n state: 'output-error',\n }\n }\n}\n\n/**\n * Execute tool calls based on their configuration.\n * Yields CustomEvent chunks during tool execution for real-time progress updates.\n *\n * Handles three cases:\n * 1. Client tools (no execute) - request client to execute\n * 2. Server tools with approval - check approval before executing\n * 3. Normal server tools - execute immediately\n *\n * @param toolCalls - Tool calls from the LLM\n * @param tools - Available tools with their configurations\n * @param approvals - Map of approval decisions (approval.id -> approved boolean)\n * @param clientResults - Map of client-side execution results (toolCallId -> result)\n * @param createCustomEventChunk - Factory to create CustomEvent chunks (optional)\n */\nexport async function* executeToolCalls<TContext = unknown>(\n toolCalls: Array<ToolCall>,\n tools: ReadonlyArray<AnyTool>,\n approvals: Map<string, boolean> = new Map(),\n clientResults: Map<string, any> = new Map(),\n createCustomEventChunk?: (\n eventName: string,\n value: Record<string, any>,\n ) => CustomEvent,\n middlewareHooks?: ToolExecutionMiddlewareHooks,\n userContext?: TContext,\n abortSignal?: AbortSignal,\n): AsyncGenerator<CustomEvent, ExecuteToolCallsResult, void> {\n const results: Array<ToolResult> = []\n const needsApproval: Array<ApprovalRequest> = []\n const needsClientExecution: Array<ClientToolRequest> = []\n\n // Create tool lookup map\n const toolMap = new Map<string, AnyTool>()\n for (const tool of tools) {\n toolMap.set(tool.name, tool)\n }\n\n // Batch gating: when any tool in the batch still needs an approval decision,\n // defer all execution so side effects don't happen before the user decides.\n const hasPendingApprovals = toolCalls.some((tc) => {\n const t = toolMap.get(tc.function.name)\n return t?.needsApproval && !approvals.has(`approval_${tc.id}`)\n })\n\n for (const toolCall of toolCalls) {\n const tool = toolMap.get(toolCall.function.name)\n const toolName = toolCall.function.name\n\n if (!tool) {\n // Unknown tool - return error\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: `Unknown tool: ${toolName}` },\n state: 'output-error',\n })\n continue\n }\n\n // Skip non-pending tools while approvals are outstanding\n if (hasPendingApprovals) {\n if (!tool.needsApproval || approvals.has(`approval_${toolCall.id}`)) {\n continue\n }\n }\n\n // Parse arguments, throwing error if invalid JSON\n let input: unknown = {}\n const argsStr = toolCall.function.arguments.trim() || '{}'\n if (argsStr) {\n try {\n const parsed = JSON.parse(argsStr)\n // Normalize null/non-object to {} (e.g. Anthropic empty tool_use blocks)\n input = parsed && typeof parsed === 'object' ? parsed : {}\n } catch (parseError) {\n // If parsing fails, throw error to fail fast\n throw new Error(`Failed to parse tool arguments as JSON: ${argsStr}`)\n }\n }\n\n // Validate input against inputSchema (for Standard Schema compliant schemas)\n if (tool.inputSchema && isStandardSchema(tool.inputSchema)) {\n try {\n input = parseWithStandardSchema(tool.inputSchema, input)\n } catch (validationError: unknown) {\n const message =\n validationError instanceof Error\n ? validationError.message\n : 'Validation failed'\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: {\n error: `Input validation failed for tool ${tool.name}: ${message}`,\n },\n state: 'output-error',\n })\n continue\n }\n }\n\n // Create a ToolExecutionContext for this tool call with event emission\n const pendingEvents: Array<CustomEvent> = []\n const context = {\n toolCallId: toolCall.id,\n context: userContext,\n abortSignal,\n emitCustomEvent: (eventName: string, value: Record<string, any>) => {\n if (createCustomEventChunk) {\n pendingEvents.push(\n createCustomEventChunk(eventName, {\n ...value,\n toolCallId: toolCall.id,\n }),\n )\n }\n },\n } as ToolExecutionContext<TContext>\n\n // CASE 1: Client-side tool (no execute function)\n if (!tool.execute) {\n // Check if tool needs approval\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n\n // Check if approval decision exists\n if (approvals.has(approvalId)) {\n const approved = approvals.get(approvalId)\n\n if (approved) {\n // Approved - check if client has executed\n if (clientResults.has(toolCall.id)) {\n results.push(\n buildClientToolResult(\n toolCall.id,\n toolName,\n tool,\n clientResults.get(toolCall.id),\n ),\n )\n } else {\n // Approved but not executed yet - request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'User declined tool execution' },\n state: 'output-error',\n })\n }\n } else {\n // Need approval first\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName: toolCall.function.name,\n input,\n approvalId,\n })\n }\n } else {\n // No approval needed - check if client has executed\n if (clientResults.has(toolCall.id)) {\n results.push(\n buildClientToolResult(\n toolCall.id,\n toolName,\n tool,\n clientResults.get(toolCall.id),\n ),\n )\n } else {\n // Request client execution\n needsClientExecution.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n })\n }\n }\n continue\n }\n\n // CASE 2: Server tool with approval required\n if (tool.needsApproval) {\n const approvalId = `approval_${toolCall.id}`\n\n // Check if approval decision exists\n if (approvals.has(approvalId)) {\n const approved = approvals.get(approvalId)\n\n if (approved) {\n // Apply middleware before-hook for approved tools\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n )\n } else {\n // User declined\n results.push({\n toolCallId: toolCall.id,\n toolName,\n result: { error: 'User declined tool execution' },\n state: 'output-error',\n })\n }\n } else {\n // Need approval\n needsApproval.push({\n toolCallId: toolCall.id,\n toolName,\n input,\n approvalId,\n })\n }\n continue\n }\n\n // CASE 3: Normal server tool - execute immediately\n if (middlewareHooks) {\n const decision = await applyBeforeToolCallDecision(\n toolCall,\n tool,\n input,\n toolName,\n middlewareHooks,\n results,\n )\n if (!decision.proceed) continue\n input = decision.input\n }\n\n yield* executeServerTool(\n toolCall,\n tool,\n toolName,\n input,\n context,\n pendingEvents,\n results,\n middlewareHooks,\n )\n }\n\n return { results, needsApproval, needsClientExecution }\n}\n"],"names":["event"],"mappings":"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"}
|
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
import { EventType } from "@ag-ui/core";
|
|
2
2
|
import { toRunErrorPayload } from "../error-payload.js";
|
|
3
|
+
import { MAX_TOKENS_KEYS } from "../../utilities/sampling-keys.js";
|
|
3
4
|
import { BaseSummarizeAdapter } from "./adapter.js";
|
|
5
|
+
const MAX_TOKENS_KEY_BY_ADAPTER = {
|
|
6
|
+
openai: "max_output_tokens",
|
|
7
|
+
anthropic: "max_tokens",
|
|
8
|
+
grok: "max_tokens",
|
|
9
|
+
groq: "max_completion_tokens",
|
|
10
|
+
gemini: "maxOutputTokens",
|
|
11
|
+
openrouter: "maxCompletionTokens"
|
|
12
|
+
};
|
|
13
|
+
const KNOWN_MAX_TOKENS_KEYS = MAX_TOKENS_KEYS;
|
|
14
|
+
function isKnownMaxTokensAdapter(adapterName) {
|
|
15
|
+
return adapterName === "ollama" || MAX_TOKENS_KEY_BY_ADAPTER[adapterName] !== void 0;
|
|
16
|
+
}
|
|
17
|
+
function applyDefaultTemperature(adapterName, temperature, modelOptions) {
|
|
18
|
+
const merged = { ...modelOptions };
|
|
19
|
+
if (adapterName === "ollama") {
|
|
20
|
+
const existing = merged.options && typeof merged.options === "object" ? merged.options : void 0;
|
|
21
|
+
if (existing && "temperature" in existing) return merged;
|
|
22
|
+
merged.options = { temperature, ...existing };
|
|
23
|
+
return merged;
|
|
24
|
+
}
|
|
25
|
+
if ("temperature" in merged) return merged;
|
|
26
|
+
merged.temperature = temperature;
|
|
27
|
+
return merged;
|
|
28
|
+
}
|
|
29
|
+
function applyMaxLength(adapterName, maxLength, modelOptions) {
|
|
30
|
+
const merged = { ...modelOptions };
|
|
31
|
+
if (adapterName === "ollama") {
|
|
32
|
+
const callerSetFlatLimit = KNOWN_MAX_TOKENS_KEYS.some(
|
|
33
|
+
(k) => typeof merged[k] === "number"
|
|
34
|
+
);
|
|
35
|
+
const existing = merged.options && typeof merged.options === "object" ? merged.options : void 0;
|
|
36
|
+
if (callerSetFlatLimit || existing && typeof existing.num_predict === "number") {
|
|
37
|
+
return merged;
|
|
38
|
+
}
|
|
39
|
+
merged.options = { num_predict: maxLength, ...existing };
|
|
40
|
+
return merged;
|
|
41
|
+
}
|
|
42
|
+
const key = MAX_TOKENS_KEY_BY_ADAPTER[adapterName];
|
|
43
|
+
if (key === void 0) return merged;
|
|
44
|
+
const callerSetLimit = KNOWN_MAX_TOKENS_KEYS.some(
|
|
45
|
+
(k) => typeof merged[k] === "number"
|
|
46
|
+
);
|
|
47
|
+
if (callerSetLimit) return merged;
|
|
48
|
+
merged[key] = maxLength;
|
|
49
|
+
return merged;
|
|
50
|
+
}
|
|
4
51
|
class ChatStreamSummarizeAdapter extends BaseSummarizeAdapter {
|
|
5
52
|
name;
|
|
6
53
|
textAdapter;
|
|
@@ -110,13 +157,25 @@ class ChatStreamSummarizeAdapter extends BaseSummarizeAdapter {
|
|
|
110
157
|
* still reach the wire layer.
|
|
111
158
|
*/
|
|
112
159
|
buildTextOptions(options, systemPrompt) {
|
|
160
|
+
let working = {
|
|
161
|
+
...options.modelOptions
|
|
162
|
+
};
|
|
163
|
+
working = applyDefaultTemperature(this.name, 0.3, working);
|
|
164
|
+
if (options.maxLength !== void 0) {
|
|
165
|
+
if (!isKnownMaxTokensAdapter(this.name)) {
|
|
166
|
+
options.logger.warn(
|
|
167
|
+
`summarize: maxLength=${options.maxLength} could not be mapped to a provider token key for adapter name "${this.name}" — it was dropped from modelOptions (the prompt still asks the model to stay under it). Construct ChatStreamSummarizeAdapter with a recognised provider name to forward the cap.`,
|
|
168
|
+
{ provider: this.name }
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
working = applyMaxLength(this.name, options.maxLength, working);
|
|
172
|
+
}
|
|
173
|
+
const modelOptions = working;
|
|
113
174
|
return {
|
|
114
175
|
model: options.model,
|
|
115
176
|
messages: [{ role: "user", content: options.text }],
|
|
116
177
|
systemPrompts: [systemPrompt],
|
|
117
|
-
|
|
118
|
-
temperature: 0.3,
|
|
119
|
-
modelOptions: options.modelOptions,
|
|
178
|
+
modelOptions,
|
|
120
179
|
logger: options.logger
|
|
121
180
|
};
|
|
122
181
|
}
|