@tanstack/ai 0.35.0 → 0.36.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/package.json +2 -2
- package/skills/ai-core/tool-calling/SKILL.md +26 -1
- package/src/activities/chat/index.ts +8 -0
- package/src/activities/chat/tools/lazy-tool-manager.ts +12 -4
- package/src/activities/chat/tools/lazy-tools.ts +33 -0
- package/src/index.ts +5 -0
- package/src/types.ts +23 -1
- package/dist/esm/activities/chat/adapter.d.ts +0 -163
- package/dist/esm/activities/chat/adapter.js +0 -17
- package/dist/esm/activities/chat/adapter.js.map +0 -1
- package/dist/esm/activities/chat/agent-loop-strategies.d.ts +0 -59
- package/dist/esm/activities/chat/agent-loop-strategies.js +0 -23
- package/dist/esm/activities/chat/agent-loop-strategies.js.map +0 -1
- package/dist/esm/activities/chat/index.d.ts +0 -264
- package/dist/esm/activities/chat/index.js +0 -1723
- package/dist/esm/activities/chat/index.js.map +0 -1
- package/dist/esm/activities/chat/mcp/manager.d.ts +0 -25
- package/dist/esm/activities/chat/mcp/manager.js +0 -71
- package/dist/esm/activities/chat/mcp/manager.js.map +0 -1
- package/dist/esm/activities/chat/mcp/types.d.ts +0 -56
- package/dist/esm/activities/chat/messages.d.ts +0 -78
- package/dist/esm/activities/chat/messages.js +0 -374
- package/dist/esm/activities/chat/messages.js.map +0 -1
- package/dist/esm/activities/chat/middleware/builder.d.ts +0 -46
- package/dist/esm/activities/chat/middleware/builder.js +0 -17
- package/dist/esm/activities/chat/middleware/builder.js.map +0 -1
- package/dist/esm/activities/chat/middleware/capabilities.d.ts +0 -93
- package/dist/esm/activities/chat/middleware/capabilities.js +0 -45
- package/dist/esm/activities/chat/middleware/capabilities.js.map +0 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +0 -87
- package/dist/esm/activities/chat/middleware/compose.js +0 -510
- package/dist/esm/activities/chat/middleware/compose.js.map +0 -1
- package/dist/esm/activities/chat/middleware/define.d.ts +0 -20
- package/dist/esm/activities/chat/middleware/define.js +0 -7
- package/dist/esm/activities/chat/middleware/define.js.map +0 -1
- package/dist/esm/activities/chat/middleware/index.d.ts +0 -10
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.d.ts +0 -89
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js +0 -76
- package/dist/esm/activities/chat/middleware/tool-cache-middleware.js.map +0 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +0 -405
- package/dist/esm/activities/chat/middleware/validate.d.ts +0 -19
- package/dist/esm/activities/chat/middleware/validate.js +0 -30
- package/dist/esm/activities/chat/middleware/validate.js.map +0 -1
- package/dist/esm/activities/chat/runtime-context-types.d.ts +0 -43
- package/dist/esm/activities/chat/stream/index.d.ts +0 -11
- package/dist/esm/activities/chat/stream/json-parser.d.ts +0 -38
- package/dist/esm/activities/chat/stream/json-parser.js +0 -28
- package/dist/esm/activities/chat/stream/json-parser.js.map +0 -1
- package/dist/esm/activities/chat/stream/message-updaters.d.ts +0 -81
- package/dist/esm/activities/chat/stream/message-updaters.js +0 -253
- package/dist/esm/activities/chat/stream/message-updaters.js.map +0 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +0 -439
- package/dist/esm/activities/chat/stream/processor.js +0 -1426
- package/dist/esm/activities/chat/stream/processor.js.map +0 -1
- package/dist/esm/activities/chat/stream/strategies.d.ts +0 -43
- package/dist/esm/activities/chat/stream/strategies.js +0 -54
- package/dist/esm/activities/chat/stream/strategies.js.map +0 -1
- package/dist/esm/activities/chat/stream/types.d.ts +0 -90
- package/dist/esm/activities/chat/tools/lazy-tool-manager.d.ts +0 -81
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js +0 -188
- package/dist/esm/activities/chat/tools/lazy-tool-manager.js.map +0 -1
- package/dist/esm/activities/chat/tools/schema-converter.d.ts +0 -140
- package/dist/esm/activities/chat/tools/schema-converter.js +0 -167
- package/dist/esm/activities/chat/tools/schema-converter.js.map +0 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +0 -140
- package/dist/esm/activities/chat/tools/tool-calls.js +0 -512
- package/dist/esm/activities/chat/tools/tool-calls.js.map +0 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +0 -135
- package/dist/esm/activities/chat/tools/tool-definition.js +0 -25
- package/dist/esm/activities/chat/tools/tool-definition.js.map +0 -1
- package/dist/esm/activities/error-payload.d.ts +0 -30
- package/dist/esm/activities/error-payload.js +0 -54
- package/dist/esm/activities/error-payload.js.map +0 -1
- package/dist/esm/activities/generateAudio/adapter.d.ts +0 -62
- package/dist/esm/activities/generateAudio/adapter.js +0 -16
- package/dist/esm/activities/generateAudio/adapter.js.map +0 -1
- package/dist/esm/activities/generateAudio/index.d.ts +0 -85
- package/dist/esm/activities/generateAudio/index.js +0 -114
- package/dist/esm/activities/generateAudio/index.js.map +0 -1
- package/dist/esm/activities/generateImage/adapter.d.ts +0 -78
- package/dist/esm/activities/generateImage/adapter.js +0 -16
- package/dist/esm/activities/generateImage/adapter.js.map +0 -1
- package/dist/esm/activities/generateImage/index.d.ts +0 -134
- package/dist/esm/activities/generateImage/index.js +0 -121
- package/dist/esm/activities/generateImage/index.js.map +0 -1
- package/dist/esm/activities/generateSpeech/adapter.d.ts +0 -62
- package/dist/esm/activities/generateSpeech/adapter.js +0 -16
- package/dist/esm/activities/generateSpeech/adapter.js.map +0 -1
- package/dist/esm/activities/generateSpeech/index.d.ts +0 -96
- package/dist/esm/activities/generateSpeech/index.js +0 -119
- package/dist/esm/activities/generateSpeech/index.js.map +0 -1
- package/dist/esm/activities/generateTranscription/adapter.d.ts +0 -62
- package/dist/esm/activities/generateTranscription/adapter.js +0 -16
- package/dist/esm/activities/generateTranscription/adapter.js.map +0 -1
- package/dist/esm/activities/generateTranscription/index.d.ts +0 -109
- package/dist/esm/activities/generateTranscription/index.js +0 -109
- package/dist/esm/activities/generateTranscription/index.js.map +0 -1
- package/dist/esm/activities/generateVideo/adapter.d.ts +0 -145
- package/dist/esm/activities/generateVideo/adapter.js +0 -30
- package/dist/esm/activities/generateVideo/adapter.js.map +0 -1
- package/dist/esm/activities/generateVideo/index.d.ts +0 -223
- package/dist/esm/activities/generateVideo/index.js +0 -295
- package/dist/esm/activities/generateVideo/index.js.map +0 -1
- package/dist/esm/activities/generateVideo/snap.d.ts +0 -14
- package/dist/esm/activities/generateVideo/snap.js +0 -54
- package/dist/esm/activities/generateVideo/snap.js.map +0 -1
- package/dist/esm/activities/index.d.ts +0 -27
- package/dist/esm/activities/index.js +0 -43
- package/dist/esm/activities/index.js.map +0 -1
- package/dist/esm/activities/middleware/index.d.ts +0 -2
- package/dist/esm/activities/middleware/run.d.ts +0 -20
- package/dist/esm/activities/middleware/run.js +0 -42
- package/dist/esm/activities/middleware/run.js.map +0 -1
- package/dist/esm/activities/middleware/types.d.ts +0 -118
- package/dist/esm/activities/stream-generation-result.d.ts +0 -15
- package/dist/esm/activities/stream-generation-result.js +0 -49
- package/dist/esm/activities/stream-generation-result.js.map +0 -1
- package/dist/esm/activities/summarize/adapter.d.ts +0 -74
- package/dist/esm/activities/summarize/adapter.js +0 -16
- package/dist/esm/activities/summarize/adapter.js.map +0 -1
- package/dist/esm/activities/summarize/chat-stream-summarize.d.ts +0 -45
- package/dist/esm/activities/summarize/chat-stream-summarize.js +0 -212
- package/dist/esm/activities/summarize/chat-stream-summarize.js.map +0 -1
- package/dist/esm/activities/summarize/index.d.ts +0 -108
- package/dist/esm/activities/summarize/index.js +0 -113
- package/dist/esm/activities/summarize/index.js.map +0 -1
- package/dist/esm/adapter-internals.d.ts +0 -5
- package/dist/esm/adapter-internals.js +0 -10
- package/dist/esm/adapter-internals.js.map +0 -1
- package/dist/esm/client.d.ts +0 -44
- package/dist/esm/client.js +0 -67
- package/dist/esm/client.js.map +0 -1
- package/dist/esm/extend-adapter.d.ts +0 -152
- package/dist/esm/extend-adapter.js +0 -21
- package/dist/esm/extend-adapter.js.map +0 -1
- package/dist/esm/index.d.ts +0 -44
- package/dist/esm/index.js +0 -106
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/logger/console-logger.d.ts +0 -29
- package/dist/esm/logger/console-logger.js +0 -83
- package/dist/esm/logger/console-logger.js.map +0 -1
- package/dist/esm/logger/internal-logger.d.ts +0 -41
- package/dist/esm/logger/internal-logger.js +0 -86
- package/dist/esm/logger/internal-logger.js.map +0 -1
- package/dist/esm/logger/resolve.d.ts +0 -14
- package/dist/esm/logger/resolve.js +0 -54
- package/dist/esm/logger/resolve.js.map +0 -1
- package/dist/esm/logger/types.d.ts +0 -75
- package/dist/esm/middlewares/content-guard.d.ts +0 -77
- package/dist/esm/middlewares/content-guard.js +0 -156
- package/dist/esm/middlewares/content-guard.js.map +0 -1
- package/dist/esm/middlewares/index.d.ts +0 -2
- package/dist/esm/middlewares/index.js +0 -7
- package/dist/esm/middlewares/index.js.map +0 -1
- package/dist/esm/middlewares/otel.d.ts +0 -81
- package/dist/esm/middlewares/otel.js +0 -745
- package/dist/esm/middlewares/otel.js.map +0 -1
- package/dist/esm/middlewares/tool-cache.d.ts +0 -1
- package/dist/esm/middlewares/usage-attributes.d.ts +0 -24
- package/dist/esm/middlewares/usage-attributes.js +0 -43
- package/dist/esm/middlewares/usage-attributes.js.map +0 -1
- package/dist/esm/realtime/index.d.ts +0 -28
- package/dist/esm/realtime/index.js +0 -8
- package/dist/esm/realtime/index.js.map +0 -1
- package/dist/esm/realtime/types.d.ts +0 -282
- package/dist/esm/stream-to-response.d.ts +0 -102
- package/dist/esm/stream-to-response.js +0 -121
- package/dist/esm/stream-to-response.js.map +0 -1
- package/dist/esm/strip-to-spec-middleware.d.ts +0 -18
- package/dist/esm/strip-to-spec-middleware.js +0 -20
- package/dist/esm/strip-to-spec-middleware.js.map +0 -1
- package/dist/esm/system-prompts.d.ts +0 -66
- package/dist/esm/system-prompts.js +0 -23
- package/dist/esm/system-prompts.js.map +0 -1
- package/dist/esm/tool-registry.d.ts +0 -81
- package/dist/esm/tool-registry.js +0 -49
- package/dist/esm/tool-registry.js.map +0 -1
- package/dist/esm/tools/provider-tool.d.ts +0 -30
- package/dist/esm/tools/provider-tool.js +0 -7
- package/dist/esm/tools/provider-tool.js.map +0 -1
- package/dist/esm/types.d.ts +0 -1573
- package/dist/esm/utilities/ag-ui-wire.d.ts +0 -44
- package/dist/esm/utilities/ag-ui-wire.js +0 -107
- package/dist/esm/utilities/ag-ui-wire.js.map +0 -1
- package/dist/esm/utilities/chat-params.d.ts +0 -85
- package/dist/esm/utilities/chat-params.js +0 -100
- package/dist/esm/utilities/chat-params.js.map +0 -1
- package/dist/esm/utilities/errors.d.ts +0 -13
- package/dist/esm/utilities/errors.js +0 -22
- package/dist/esm/utilities/errors.js.map +0 -1
- package/dist/esm/utilities/media-prompt.d.ts +0 -35
- package/dist/esm/utilities/media-prompt.js +0 -43
- package/dist/esm/utilities/media-prompt.js.map +0 -1
- package/dist/esm/utilities/numbers.d.ts +0 -8
- package/dist/esm/utilities/numbers.js +0 -12
- package/dist/esm/utilities/numbers.js.map +0 -1
- package/dist/esm/utilities/sampling-keys.d.ts +0 -20
- package/dist/esm/utilities/sampling-keys.js +0 -20
- package/dist/esm/utilities/sampling-keys.js.map +0 -1
- package/dist/esm/utilities/tool-result.d.ts +0 -21
- package/dist/esm/utilities/tool-result.js +0 -37
- package/dist/esm/utilities/tool-result.js.map +0 -1
- package/dist/esm/utilities/usage.d.ts +0 -31
- package/dist/esm/utilities/usage.js +0 -11
- package/dist/esm/utilities/usage.js.map +0 -1
- package/dist/esm/utils.d.ts +0 -17
- package/dist/esm/utils.js +0 -20
- package/dist/esm/utils.js.map +0 -1
|
@@ -1,1723 +0,0 @@
|
|
|
1
|
-
import { devtoolsMiddleware } from "@tanstack/ai-event-client";
|
|
2
|
-
import { undoNullWidening } from "@tanstack/ai-utils";
|
|
3
|
-
import { stripToSpecMiddleware } from "../../strip-to-spec-middleware.js";
|
|
4
|
-
import { streamToText } from "../../stream-to-response.js";
|
|
5
|
-
import { resolveDebugOption } from "../../logger/resolve.js";
|
|
6
|
-
import { normalizeToolResult } from "../../utilities/tool-result.js";
|
|
7
|
-
import { LazyToolManager } from "./tools/lazy-tool-manager.js";
|
|
8
|
-
import { ToolCallManager, MiddlewareAbortError, executeToolCalls } from "./tools/tool-calls.js";
|
|
9
|
-
import { convertSchemaForStructuredOutput, isStandardSchema, parseWithStandardSchema, convertSchemaToJsonSchema } from "./tools/schema-converter.js";
|
|
10
|
-
import { maxIterations } from "./agent-loop-strategies.js";
|
|
11
|
-
import { convertMessagesToModelMessages, generateMessageId } from "./messages.js";
|
|
12
|
-
import { MiddlewareRunner } from "./middleware/compose.js";
|
|
13
|
-
import { CapabilityRegistry } from "./middleware/capabilities.js";
|
|
14
|
-
import { validateCapabilities } from "./middleware/validate.js";
|
|
15
|
-
import { MCPManager } from "./mcp/manager.js";
|
|
16
|
-
import { EventType } from "@ag-ui/core";
|
|
17
|
-
const kind = "text";
|
|
18
|
-
function createChatOptions(options) {
|
|
19
|
-
return options;
|
|
20
|
-
}
|
|
21
|
-
function combineAbortSignals(a, b) {
|
|
22
|
-
if (!a) return b;
|
|
23
|
-
if (!b) return a;
|
|
24
|
-
if (a.aborted) return a;
|
|
25
|
-
if (b.aborted) return b;
|
|
26
|
-
const controller = new AbortController();
|
|
27
|
-
const onAbort = (source) => () => {
|
|
28
|
-
controller.abort(source.reason);
|
|
29
|
-
};
|
|
30
|
-
a.addEventListener("abort", onAbort(a), { once: true });
|
|
31
|
-
b.addEventListener("abort", onAbort(b), { once: true });
|
|
32
|
-
return controller.signal;
|
|
33
|
-
}
|
|
34
|
-
class TextEngine {
|
|
35
|
-
adapter;
|
|
36
|
-
params;
|
|
37
|
-
systemPrompts;
|
|
38
|
-
tools;
|
|
39
|
-
loopStrategy;
|
|
40
|
-
toolCallManager;
|
|
41
|
-
lazyToolManager;
|
|
42
|
-
initialMessageCount;
|
|
43
|
-
requestId;
|
|
44
|
-
streamId;
|
|
45
|
-
effectiveRequest;
|
|
46
|
-
effectiveSignal;
|
|
47
|
-
messages;
|
|
48
|
-
iterationCount = 0;
|
|
49
|
-
lastFinishReason = null;
|
|
50
|
-
streamStartTime = 0;
|
|
51
|
-
totalChunkCount = 0;
|
|
52
|
-
currentMessageId = null;
|
|
53
|
-
accumulatedContent = "";
|
|
54
|
-
accumulatedThinking = [];
|
|
55
|
-
currentThinkingContent = "";
|
|
56
|
-
currentThinkingSignature = "";
|
|
57
|
-
eventOptions;
|
|
58
|
-
eventToolNames;
|
|
59
|
-
finishedEvent = null;
|
|
60
|
-
earlyTermination = false;
|
|
61
|
-
toolPhase = "continue";
|
|
62
|
-
cyclePhase = "processText";
|
|
63
|
-
// Client state extracted from initial messages (before conversion to ModelMessage)
|
|
64
|
-
initialApprovals;
|
|
65
|
-
initialClientToolResults;
|
|
66
|
-
// AG-UI protocol IDs
|
|
67
|
-
threadId;
|
|
68
|
-
runIdOverride;
|
|
69
|
-
parentRunIdOverride;
|
|
70
|
-
// Middleware support
|
|
71
|
-
middlewareRunner;
|
|
72
|
-
middlewareCtx;
|
|
73
|
-
deferredPromises = [];
|
|
74
|
-
abortReason;
|
|
75
|
-
middlewareAbortController;
|
|
76
|
-
// Combines the caller's signal with middleware abort() so running tools
|
|
77
|
-
// observe both cancellation sources via ctx.abortSignal.
|
|
78
|
-
toolAbortSignal;
|
|
79
|
-
terminalHookCalled = false;
|
|
80
|
-
logger;
|
|
81
|
-
// Structured-output finalization state (populated by runStructuredFinalization)
|
|
82
|
-
structuredOutputResult = null;
|
|
83
|
-
// Native combined mode: tracks whether we've already emitted the synthetic
|
|
84
|
-
// `structured-output.start` event before the schema-constrained final-turn
|
|
85
|
-
// text begins streaming. The event must precede the first
|
|
86
|
-
// TEXT_MESSAGE_START so the client-side StreamProcessor routes the JSON
|
|
87
|
-
// deltas into a StructuredOutputPart instead of a plain TextPart.
|
|
88
|
-
combinedStartEmitted = false;
|
|
89
|
-
// Native combined mode: messageId we want the synthetic
|
|
90
|
-
// `structured-output.start` (and any error emitted before deltas arrive)
|
|
91
|
-
// to carry, so the client matches it to the streaming text deltas.
|
|
92
|
-
combinedStructuredMessageId = null;
|
|
93
|
-
// Holds the validated value when `finalStructuredOutput.validate` is provided
|
|
94
|
-
// and succeeds. Distinct from `structuredOutputResult.data` (the normalized
|
|
95
|
-
// but unvalidated payload from the structured-output.complete chunk).
|
|
96
|
-
validatedStructuredOutput = void 0;
|
|
97
|
-
hasValidatedStructuredOutput = false;
|
|
98
|
-
finalizationError = null;
|
|
99
|
-
finalStructuredOutput;
|
|
100
|
-
constructor(config, logger) {
|
|
101
|
-
this.logger = logger;
|
|
102
|
-
this.adapter = config.adapter;
|
|
103
|
-
this.finalStructuredOutput = config.finalStructuredOutput;
|
|
104
|
-
this.params = config.params;
|
|
105
|
-
this.systemPrompts = config.params.systemPrompts || [];
|
|
106
|
-
this.loopStrategy = config.params.agentLoopStrategy || maxIterations(5);
|
|
107
|
-
this.initialMessageCount = config.params.messages.length;
|
|
108
|
-
const { approvals, clientToolResults } = this.extractClientStateFromOriginalMessages(
|
|
109
|
-
config.params.messages
|
|
110
|
-
);
|
|
111
|
-
this.initialApprovals = approvals;
|
|
112
|
-
this.initialClientToolResults = clientToolResults;
|
|
113
|
-
this.messages = convertMessagesToModelMessages(config.params.messages);
|
|
114
|
-
this.lazyToolManager = new LazyToolManager(
|
|
115
|
-
config.params.tools || [],
|
|
116
|
-
this.messages
|
|
117
|
-
);
|
|
118
|
-
this.tools = this.lazyToolManager.getActiveTools();
|
|
119
|
-
this.toolCallManager = new ToolCallManager(this.tools);
|
|
120
|
-
this.requestId = this.createId("chat");
|
|
121
|
-
this.streamId = this.createId("stream");
|
|
122
|
-
this.effectiveRequest = config.params.abortController ? { signal: config.params.abortController.signal } : void 0;
|
|
123
|
-
this.effectiveSignal = config.params.abortController?.signal;
|
|
124
|
-
this.threadId = config.params.threadId || config.params.conversationId || this.createId("thread");
|
|
125
|
-
this.runIdOverride = config.params.runId;
|
|
126
|
-
this.parentRunIdOverride = config.params.parentRunId;
|
|
127
|
-
const allMiddleware = [
|
|
128
|
-
devtoolsMiddleware(),
|
|
129
|
-
...config.middleware || [],
|
|
130
|
-
stripToSpecMiddleware()
|
|
131
|
-
];
|
|
132
|
-
this.middlewareRunner = new MiddlewareRunner(allMiddleware, logger);
|
|
133
|
-
this.middlewareAbortController = new AbortController();
|
|
134
|
-
this.toolAbortSignal = combineAbortSignals(
|
|
135
|
-
this.effectiveSignal,
|
|
136
|
-
this.middlewareAbortController.signal
|
|
137
|
-
);
|
|
138
|
-
this.middlewareCtx = {
|
|
139
|
-
requestId: this.requestId,
|
|
140
|
-
streamId: this.streamId,
|
|
141
|
-
runId: this.runIdOverride ?? this.requestId,
|
|
142
|
-
threadId: this.threadId,
|
|
143
|
-
// Legacy alias kept on the ctx so middleware that reads
|
|
144
|
-
// `ctx.conversationId` keeps working. Always equals `threadId`.
|
|
145
|
-
conversationId: this.threadId,
|
|
146
|
-
phase: "init",
|
|
147
|
-
iteration: 0,
|
|
148
|
-
chunkIndex: 0,
|
|
149
|
-
signal: this.effectiveSignal,
|
|
150
|
-
abort: (reason) => {
|
|
151
|
-
this.abortReason = reason;
|
|
152
|
-
this.middlewareAbortController?.abort(reason);
|
|
153
|
-
},
|
|
154
|
-
context: config.context,
|
|
155
|
-
defer: (promise) => {
|
|
156
|
-
this.deferredPromises.push(promise);
|
|
157
|
-
},
|
|
158
|
-
// Provider / adapter info
|
|
159
|
-
activity: "chat",
|
|
160
|
-
provider: config.adapter.name,
|
|
161
|
-
model: config.params.model,
|
|
162
|
-
source: "server",
|
|
163
|
-
streaming: true,
|
|
164
|
-
// Config-derived (updated in beforeRun and applyMiddlewareConfig)
|
|
165
|
-
systemPrompts: this.systemPrompts,
|
|
166
|
-
toolNames: void 0,
|
|
167
|
-
options: void 0,
|
|
168
|
-
modelOptions: config.params.modelOptions,
|
|
169
|
-
// Computed
|
|
170
|
-
messageCount: this.initialMessageCount,
|
|
171
|
-
hasTools: this.tools.length > 0,
|
|
172
|
-
// Mutable per-iteration
|
|
173
|
-
currentMessageId: null,
|
|
174
|
-
accumulatedContent: "",
|
|
175
|
-
// References
|
|
176
|
-
messages: this.messages,
|
|
177
|
-
createId: (prefix) => this.createId(prefix),
|
|
178
|
-
// Capability bookkeeping for this request (populated by middleware setup)
|
|
179
|
-
capabilities: new CapabilityRegistry(),
|
|
180
|
-
// Convenience accessors that delegate to a capability handle's own
|
|
181
|
-
// tuple getter/provider, keyed by this context. `getX(ctx)` and
|
|
182
|
-
// `ctx.get(X)` are interchangeable.
|
|
183
|
-
get: (capability) => capability[0](this.middlewareCtx),
|
|
184
|
-
getOptional: (capability) => capability[0](this.middlewareCtx, { optional: true }),
|
|
185
|
-
provide: (capability, value) => capability[1](this.middlewareCtx, value)
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
/** Get the accumulated content after the chat loop completes */
|
|
189
|
-
getAccumulatedContent() {
|
|
190
|
-
return this.accumulatedContent;
|
|
191
|
-
}
|
|
192
|
-
/** Get the final messages array after the chat loop completes */
|
|
193
|
-
getMessages() {
|
|
194
|
-
return this.messages;
|
|
195
|
-
}
|
|
196
|
-
/** Returns the structured-output result if finalization ran successfully. */
|
|
197
|
-
getStructuredOutputResult() {
|
|
198
|
-
return this.structuredOutputResult;
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* Returns the validated structured-output value (the result of running
|
|
202
|
-
* `finalStructuredOutput.validate` against the raw structured-output data)
|
|
203
|
-
* wrapped in a `{ value }` object so callers can distinguish "no validation
|
|
204
|
-
* happened" from "validation produced undefined". Returns `null` when no
|
|
205
|
-
* validator was configured or validation hasn't been performed yet.
|
|
206
|
-
*/
|
|
207
|
-
getValidatedStructuredOutput() {
|
|
208
|
-
return this.hasValidatedStructuredOutput ? { value: this.validatedStructuredOutput } : null;
|
|
209
|
-
}
|
|
210
|
-
/** Returns the recorded finalization error, if any. */
|
|
211
|
-
getFinalizationError() {
|
|
212
|
-
return this.finalizationError;
|
|
213
|
-
}
|
|
214
|
-
async *run() {
|
|
215
|
-
this.beforeRun();
|
|
216
|
-
this.logger.agentLoop("run started", {
|
|
217
|
-
threadId: this.middlewareCtx.threadId
|
|
218
|
-
});
|
|
219
|
-
try {
|
|
220
|
-
await this.middlewareRunner.runSetup(this.middlewareCtx);
|
|
221
|
-
this.middlewareCtx.phase = "init";
|
|
222
|
-
const initialConfig = this.buildMiddlewareConfig();
|
|
223
|
-
const transformedConfig = await this.middlewareRunner.runOnConfig(
|
|
224
|
-
this.middlewareCtx,
|
|
225
|
-
initialConfig
|
|
226
|
-
);
|
|
227
|
-
this.applyMiddlewareConfig(transformedConfig);
|
|
228
|
-
await this.middlewareRunner.runOnStart(this.middlewareCtx);
|
|
229
|
-
const pendingPhase = yield* this.checkForPendingToolCalls();
|
|
230
|
-
if (pendingPhase === "wait") {
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
const skipAgentLoop = !!this.finalStructuredOutput && this.tools.length === 0 && this.finalStructuredOutput.nativeCombined !== true;
|
|
234
|
-
if (!skipAgentLoop) {
|
|
235
|
-
do {
|
|
236
|
-
if (this.earlyTermination || this.isCancelled()) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
this.logger.agentLoop(`iteration=${this.middlewareCtx.iteration}`, {
|
|
240
|
-
iteration: this.middlewareCtx.iteration
|
|
241
|
-
});
|
|
242
|
-
await this.beginCycle();
|
|
243
|
-
if (this.cyclePhase === "processText") {
|
|
244
|
-
this.middlewareCtx.phase = "beforeModel";
|
|
245
|
-
this.middlewareCtx.iteration = this.iterationCount;
|
|
246
|
-
const iterConfig = this.buildMiddlewareConfig();
|
|
247
|
-
const iterTransformedConfig = await this.middlewareRunner.runOnConfig(
|
|
248
|
-
this.middlewareCtx,
|
|
249
|
-
iterConfig
|
|
250
|
-
);
|
|
251
|
-
this.applyMiddlewareConfig(iterTransformedConfig);
|
|
252
|
-
yield* this.streamModelResponse();
|
|
253
|
-
} else {
|
|
254
|
-
yield* this.processToolCalls();
|
|
255
|
-
}
|
|
256
|
-
this.endCycle();
|
|
257
|
-
} while (this.shouldContinue());
|
|
258
|
-
}
|
|
259
|
-
this.logger.agentLoop("run finished", {
|
|
260
|
-
finishReason: this.lastFinishReason
|
|
261
|
-
});
|
|
262
|
-
if (this.finalStructuredOutput && !this.isCancelled() && !this.finalizationError) {
|
|
263
|
-
if (this.finalStructuredOutput.nativeCombined === true) {
|
|
264
|
-
yield* this.harvestCombinedStructuredOutput();
|
|
265
|
-
} else {
|
|
266
|
-
yield* this.runStructuredFinalization();
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
if (!this.terminalHookCalled && this.toolPhase !== "wait" && !this.isCancelled()) {
|
|
270
|
-
if (this.finalizationError) {
|
|
271
|
-
this.terminalHookCalled = true;
|
|
272
|
-
const errForHook = new Error(
|
|
273
|
-
this.finalizationError.message,
|
|
274
|
-
this.finalizationError.cause !== void 0 ? { cause: this.finalizationError.cause } : void 0
|
|
275
|
-
);
|
|
276
|
-
if (this.finalizationError.code !== void 0) {
|
|
277
|
-
Object.defineProperty(errForHook, "code", {
|
|
278
|
-
value: this.finalizationError.code,
|
|
279
|
-
enumerable: true
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
await this.middlewareRunner.runOnError(this.middlewareCtx, {
|
|
283
|
-
error: errForHook,
|
|
284
|
-
duration: Date.now() - this.streamStartTime
|
|
285
|
-
});
|
|
286
|
-
} else {
|
|
287
|
-
this.terminalHookCalled = true;
|
|
288
|
-
await this.middlewareRunner.runOnFinish(this.middlewareCtx, {
|
|
289
|
-
finishReason: this.lastFinishReason,
|
|
290
|
-
duration: Date.now() - this.streamStartTime,
|
|
291
|
-
content: this.accumulatedContent,
|
|
292
|
-
usage: this.finishedEvent?.usage
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
} catch (error) {
|
|
297
|
-
if (!this.terminalHookCalled) {
|
|
298
|
-
this.terminalHookCalled = true;
|
|
299
|
-
if (error instanceof MiddlewareAbortError) {
|
|
300
|
-
this.abortReason = error.message;
|
|
301
|
-
await this.middlewareRunner.runOnAbort(this.middlewareCtx, {
|
|
302
|
-
reason: error.message,
|
|
303
|
-
duration: Date.now() - this.streamStartTime
|
|
304
|
-
});
|
|
305
|
-
} else {
|
|
306
|
-
this.logger.errors("chat run failed", {
|
|
307
|
-
error,
|
|
308
|
-
threadId: this.middlewareCtx.threadId
|
|
309
|
-
});
|
|
310
|
-
await this.middlewareRunner.runOnError(this.middlewareCtx, {
|
|
311
|
-
error,
|
|
312
|
-
duration: Date.now() - this.streamStartTime
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (!(error instanceof MiddlewareAbortError)) {
|
|
317
|
-
throw error;
|
|
318
|
-
}
|
|
319
|
-
} finally {
|
|
320
|
-
if (!this.terminalHookCalled && this.isCancelled()) {
|
|
321
|
-
this.terminalHookCalled = true;
|
|
322
|
-
await this.middlewareRunner.runOnAbort(this.middlewareCtx, {
|
|
323
|
-
reason: this.abortReason,
|
|
324
|
-
duration: Date.now() - this.streamStartTime
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
if (this.deferredPromises.length > 0) {
|
|
328
|
-
await Promise.allSettled(this.deferredPromises);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
beforeRun() {
|
|
333
|
-
this.streamStartTime = Date.now();
|
|
334
|
-
const { tools, metadata } = this.params;
|
|
335
|
-
const options = {};
|
|
336
|
-
if (metadata !== void 0) options.metadata = metadata;
|
|
337
|
-
this.eventOptions = Object.keys(options).length > 0 ? options : void 0;
|
|
338
|
-
this.eventToolNames = tools?.map((t) => t.name);
|
|
339
|
-
this.middlewareCtx.options = this.eventOptions;
|
|
340
|
-
this.middlewareCtx.toolNames = this.eventToolNames;
|
|
341
|
-
}
|
|
342
|
-
async beginCycle() {
|
|
343
|
-
if (this.cyclePhase === "processText") {
|
|
344
|
-
await this.beginIteration();
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
endCycle() {
|
|
348
|
-
if (this.cyclePhase === "processText") {
|
|
349
|
-
this.cyclePhase = "executeToolCalls";
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
this.cyclePhase = "processText";
|
|
353
|
-
this.iterationCount++;
|
|
354
|
-
}
|
|
355
|
-
async beginIteration() {
|
|
356
|
-
this.currentMessageId = this.createId("msg");
|
|
357
|
-
this.accumulatedContent = "";
|
|
358
|
-
this.accumulatedThinking = [];
|
|
359
|
-
this.currentThinkingContent = "";
|
|
360
|
-
this.currentThinkingSignature = "";
|
|
361
|
-
this.finishedEvent = null;
|
|
362
|
-
this.middlewareCtx.currentMessageId = this.currentMessageId;
|
|
363
|
-
this.middlewareCtx.accumulatedContent = "";
|
|
364
|
-
await this.middlewareRunner.runOnIteration(this.middlewareCtx, {
|
|
365
|
-
iteration: this.iterationCount,
|
|
366
|
-
messageId: this.currentMessageId
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
async *streamModelResponse() {
|
|
370
|
-
const { metadata, modelOptions } = this.params;
|
|
371
|
-
const tools = this.tools;
|
|
372
|
-
const toolsWithJsonSchemas = tools.map((tool) => ({
|
|
373
|
-
...tool,
|
|
374
|
-
inputSchema: tool.inputSchema ? convertSchemaToJsonSchema(tool.inputSchema) : void 0,
|
|
375
|
-
outputSchema: tool.outputSchema ? convertSchemaToJsonSchema(tool.outputSchema) : void 0
|
|
376
|
-
}));
|
|
377
|
-
this.middlewareCtx.phase = "modelStream";
|
|
378
|
-
const providerName = this.adapter.provider ?? this.adapter.name;
|
|
379
|
-
this.logger.request(
|
|
380
|
-
`activity=chat provider=${providerName} model=${this.params.model} messages=${this.messages.length} tools=${this.tools.length} stream=true`,
|
|
381
|
-
{
|
|
382
|
-
provider: providerName,
|
|
383
|
-
model: this.params.model,
|
|
384
|
-
messageCount: this.messages.length,
|
|
385
|
-
toolCount: this.tools.length
|
|
386
|
-
}
|
|
387
|
-
);
|
|
388
|
-
const combinedSchema = this.finalStructuredOutput?.nativeCombined === true ? this.finalStructuredOutput.jsonSchema : void 0;
|
|
389
|
-
for await (const chunk of this.adapter.chatStream({
|
|
390
|
-
model: this.params.model,
|
|
391
|
-
messages: this.messages,
|
|
392
|
-
tools: toolsWithJsonSchemas,
|
|
393
|
-
metadata,
|
|
394
|
-
request: this.effectiveRequest,
|
|
395
|
-
modelOptions,
|
|
396
|
-
systemPrompts: this.systemPrompts,
|
|
397
|
-
logger: this.logger,
|
|
398
|
-
threadId: this.threadId,
|
|
399
|
-
runId: this.runIdOverride,
|
|
400
|
-
parentRunId: this.parentRunIdOverride,
|
|
401
|
-
...combinedSchema ? { outputSchema: combinedSchema } : {}
|
|
402
|
-
})) {
|
|
403
|
-
if (this.isCancelled()) {
|
|
404
|
-
break;
|
|
405
|
-
}
|
|
406
|
-
this.totalChunkCount++;
|
|
407
|
-
this.handleStreamChunk(chunk);
|
|
408
|
-
if (this.finalStructuredOutput?.nativeCombined === true && this.finalStructuredOutput.yieldChunks && !this.combinedStartEmitted && chunk.type === EventType.TEXT_MESSAGE_START) {
|
|
409
|
-
this.combinedStartEmitted = true;
|
|
410
|
-
const messageId = typeof chunk.messageId === "string" && chunk.messageId !== "" ? chunk.messageId : generateMessageId();
|
|
411
|
-
this.combinedStructuredMessageId = messageId;
|
|
412
|
-
const synthStart = {
|
|
413
|
-
type: EventType.CUSTOM,
|
|
414
|
-
name: "structured-output.start",
|
|
415
|
-
value: { messageId },
|
|
416
|
-
model: this.params.model,
|
|
417
|
-
timestamp: Date.now(),
|
|
418
|
-
threadId: this.threadId,
|
|
419
|
-
...this.runIdOverride ? { runId: this.runIdOverride } : {}
|
|
420
|
-
};
|
|
421
|
-
const synthOutputs = await this.middlewareRunner.runOnChunk(
|
|
422
|
-
this.middlewareCtx,
|
|
423
|
-
synthStart
|
|
424
|
-
);
|
|
425
|
-
for (const outputChunk of synthOutputs) {
|
|
426
|
-
yield outputChunk;
|
|
427
|
-
this.middlewareCtx.chunkIndex++;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
const outputChunks = await this.middlewareRunner.runOnChunk(
|
|
431
|
-
this.middlewareCtx,
|
|
432
|
-
chunk
|
|
433
|
-
);
|
|
434
|
-
const suppressAgentLifecycle = !!this.finalStructuredOutput && this.finalStructuredOutput.yieldChunks && this.finalStructuredOutput.nativeCombined !== true;
|
|
435
|
-
for (const outputChunk of outputChunks) {
|
|
436
|
-
if (suppressAgentLifecycle && (outputChunk.type === EventType.RUN_STARTED || outputChunk.type === EventType.RUN_FINISHED)) {
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
this.logger.output(`type=${outputChunk.type}`, { chunk: outputChunk });
|
|
440
|
-
yield outputChunk;
|
|
441
|
-
this.middlewareCtx.chunkIndex++;
|
|
442
|
-
}
|
|
443
|
-
if (chunk.type === "RUN_FINISHED" && chunk.usage) {
|
|
444
|
-
await this.middlewareRunner.runOnUsage(this.middlewareCtx, chunk.usage);
|
|
445
|
-
}
|
|
446
|
-
if (this.earlyTermination) {
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
handleStreamChunk(chunk) {
|
|
452
|
-
switch (chunk.type) {
|
|
453
|
-
// AG-UI Events
|
|
454
|
-
case "TEXT_MESSAGE_CONTENT":
|
|
455
|
-
this.handleTextMessageContentEvent(chunk);
|
|
456
|
-
break;
|
|
457
|
-
case "TOOL_CALL_START":
|
|
458
|
-
this.handleToolCallStartEvent(chunk);
|
|
459
|
-
break;
|
|
460
|
-
case "TOOL_CALL_ARGS":
|
|
461
|
-
this.handleToolCallArgsEvent(chunk);
|
|
462
|
-
break;
|
|
463
|
-
case "TOOL_CALL_END":
|
|
464
|
-
this.handleToolCallEndEvent(chunk);
|
|
465
|
-
break;
|
|
466
|
-
case "RUN_FINISHED":
|
|
467
|
-
this.handleRunFinishedEvent(chunk);
|
|
468
|
-
break;
|
|
469
|
-
case "RUN_ERROR":
|
|
470
|
-
this.handleRunErrorEvent(chunk);
|
|
471
|
-
break;
|
|
472
|
-
case "STEP_STARTED":
|
|
473
|
-
this.handleStepStartedEvent();
|
|
474
|
-
break;
|
|
475
|
-
case "STEP_FINISHED":
|
|
476
|
-
this.handleStepFinishedEvent(chunk);
|
|
477
|
-
break;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
// ===========================
|
|
481
|
-
// AG-UI Event Handlers
|
|
482
|
-
// ===========================
|
|
483
|
-
handleTextMessageContentEvent(chunk) {
|
|
484
|
-
if (chunk.content) {
|
|
485
|
-
this.accumulatedContent = chunk.content;
|
|
486
|
-
} else {
|
|
487
|
-
this.accumulatedContent += chunk.delta;
|
|
488
|
-
}
|
|
489
|
-
this.middlewareCtx.accumulatedContent = this.accumulatedContent;
|
|
490
|
-
}
|
|
491
|
-
handleToolCallStartEvent(chunk) {
|
|
492
|
-
this.toolCallManager.addToolCallStartEvent(chunk);
|
|
493
|
-
}
|
|
494
|
-
handleToolCallArgsEvent(chunk) {
|
|
495
|
-
this.toolCallManager.addToolCallArgsEvent(chunk);
|
|
496
|
-
}
|
|
497
|
-
handleToolCallEndEvent(chunk) {
|
|
498
|
-
this.toolCallManager.completeToolCall(chunk);
|
|
499
|
-
}
|
|
500
|
-
handleRunFinishedEvent(chunk) {
|
|
501
|
-
this.finishedEvent = chunk;
|
|
502
|
-
this.lastFinishReason = chunk.finishReason ?? null;
|
|
503
|
-
}
|
|
504
|
-
handleRunErrorEvent(_chunk) {
|
|
505
|
-
this.earlyTermination = true;
|
|
506
|
-
}
|
|
507
|
-
finalizeCurrentThinkingStep() {
|
|
508
|
-
if (this.currentThinkingContent) {
|
|
509
|
-
this.accumulatedThinking.push({
|
|
510
|
-
content: this.currentThinkingContent,
|
|
511
|
-
...this.currentThinkingSignature && {
|
|
512
|
-
signature: this.currentThinkingSignature
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
this.currentThinkingContent = "";
|
|
516
|
-
this.currentThinkingSignature = "";
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
handleStepStartedEvent() {
|
|
520
|
-
this.finalizeCurrentThinkingStep();
|
|
521
|
-
}
|
|
522
|
-
handleStepFinishedEvent(chunk) {
|
|
523
|
-
if (chunk.delta) {
|
|
524
|
-
this.currentThinkingContent += chunk.delta;
|
|
525
|
-
}
|
|
526
|
-
if (chunk.signature) {
|
|
527
|
-
this.currentThinkingSignature = chunk.signature;
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Tools available for execution this turn. The discovery tool is dropped
|
|
532
|
-
* from the advertised set (`this.tools`) once every lazy tool is discovered,
|
|
533
|
-
* but a model may still re-request discovery; this widens execution lookup
|
|
534
|
-
* to include it so such calls don't fail with "Unknown tool". Centralised so
|
|
535
|
-
* both execution sites (`processToolCalls` and `checkForPendingToolCalls`)
|
|
536
|
-
* stay in sync.
|
|
537
|
-
*/
|
|
538
|
-
resolveExecutableTools(toolCalls) {
|
|
539
|
-
return this.lazyToolManager.getExecutableTools(
|
|
540
|
-
this.tools,
|
|
541
|
-
toolCalls.map((tc) => tc.function.name)
|
|
542
|
-
);
|
|
543
|
-
}
|
|
544
|
-
async *checkForPendingToolCalls() {
|
|
545
|
-
const pendingToolCalls = this.getPendingToolCallsFromMessages();
|
|
546
|
-
if (pendingToolCalls.length === 0) {
|
|
547
|
-
return "continue";
|
|
548
|
-
}
|
|
549
|
-
const finishEvent = this.createSyntheticFinishedEvent();
|
|
550
|
-
const undiscoveredLazyResults = [];
|
|
551
|
-
const executablePendingCalls = pendingToolCalls.filter((tc) => {
|
|
552
|
-
if (this.lazyToolManager.isUndiscoveredLazyTool(tc.function.name)) {
|
|
553
|
-
undiscoveredLazyResults.push({
|
|
554
|
-
toolCallId: tc.id,
|
|
555
|
-
toolName: tc.function.name,
|
|
556
|
-
result: {
|
|
557
|
-
error: this.lazyToolManager.getUndiscoveredToolError(
|
|
558
|
-
tc.function.name
|
|
559
|
-
)
|
|
560
|
-
},
|
|
561
|
-
state: "output-error"
|
|
562
|
-
});
|
|
563
|
-
return false;
|
|
564
|
-
}
|
|
565
|
-
return true;
|
|
566
|
-
});
|
|
567
|
-
if (undiscoveredLazyResults.length > 0) {
|
|
568
|
-
for (const chunk of this.buildToolResultChunks(
|
|
569
|
-
undiscoveredLazyResults,
|
|
570
|
-
finishEvent
|
|
571
|
-
)) {
|
|
572
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
if (executablePendingCalls.length === 0) {
|
|
576
|
-
return "continue";
|
|
577
|
-
}
|
|
578
|
-
const { approvals, clientToolResults } = this.collectClientState();
|
|
579
|
-
const generator = executeToolCalls(
|
|
580
|
-
executablePendingCalls,
|
|
581
|
-
this.resolveExecutableTools(executablePendingCalls),
|
|
582
|
-
approvals,
|
|
583
|
-
clientToolResults,
|
|
584
|
-
(eventName, data) => this.createCustomEventChunk(eventName, data),
|
|
585
|
-
{
|
|
586
|
-
onBeforeToolCall: async (toolCall, tool, args) => {
|
|
587
|
-
this.logger.tools(`phase=before name=${toolCall.function.name}`, {
|
|
588
|
-
name: toolCall.function.name,
|
|
589
|
-
args
|
|
590
|
-
});
|
|
591
|
-
const hookCtx = {
|
|
592
|
-
toolCall,
|
|
593
|
-
tool,
|
|
594
|
-
args,
|
|
595
|
-
toolName: toolCall.function.name,
|
|
596
|
-
toolCallId: toolCall.id
|
|
597
|
-
};
|
|
598
|
-
return this.middlewareRunner.runOnBeforeToolCall(
|
|
599
|
-
this.middlewareCtx,
|
|
600
|
-
hookCtx
|
|
601
|
-
);
|
|
602
|
-
},
|
|
603
|
-
onAfterToolCall: async (info) => {
|
|
604
|
-
this.logger.tools(`phase=after name=${info.toolName}`, {
|
|
605
|
-
name: info.toolName,
|
|
606
|
-
result: info.result
|
|
607
|
-
});
|
|
608
|
-
await this.middlewareRunner.runOnAfterToolCall(
|
|
609
|
-
this.middlewareCtx,
|
|
610
|
-
info
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
},
|
|
614
|
-
this.middlewareCtx.context,
|
|
615
|
-
this.toolAbortSignal
|
|
616
|
-
);
|
|
617
|
-
const executionResult = yield* this.drainToolCallGenerator(generator);
|
|
618
|
-
if (this.isMiddlewareAborted()) {
|
|
619
|
-
this.setToolPhase("stop");
|
|
620
|
-
return "stop";
|
|
621
|
-
}
|
|
622
|
-
await this.middlewareRunner.runOnToolPhaseComplete(this.middlewareCtx, {
|
|
623
|
-
toolCalls: pendingToolCalls,
|
|
624
|
-
results: executionResult.results,
|
|
625
|
-
needsApproval: executionResult.needsApproval,
|
|
626
|
-
needsClientExecution: executionResult.needsClientExecution
|
|
627
|
-
});
|
|
628
|
-
const argsMap = /* @__PURE__ */ new Map();
|
|
629
|
-
for (const tc of pendingToolCalls) {
|
|
630
|
-
argsMap.set(tc.id, tc.function.arguments);
|
|
631
|
-
}
|
|
632
|
-
if (executionResult.needsApproval.length > 0 || executionResult.needsClientExecution.length > 0) {
|
|
633
|
-
if (executionResult.results.length > 0) {
|
|
634
|
-
for (const chunk of this.buildToolResultChunks(
|
|
635
|
-
executionResult.results,
|
|
636
|
-
finishEvent,
|
|
637
|
-
argsMap
|
|
638
|
-
)) {
|
|
639
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
for (const chunk of this.buildApprovalChunks(
|
|
643
|
-
executionResult.needsApproval,
|
|
644
|
-
finishEvent
|
|
645
|
-
)) {
|
|
646
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
647
|
-
}
|
|
648
|
-
for (const chunk of this.buildClientToolChunks(
|
|
649
|
-
executionResult.needsClientExecution,
|
|
650
|
-
finishEvent
|
|
651
|
-
)) {
|
|
652
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
653
|
-
}
|
|
654
|
-
this.setToolPhase("wait");
|
|
655
|
-
return "wait";
|
|
656
|
-
}
|
|
657
|
-
const toolResultChunks = this.buildToolResultChunks(
|
|
658
|
-
executionResult.results,
|
|
659
|
-
finishEvent,
|
|
660
|
-
argsMap
|
|
661
|
-
);
|
|
662
|
-
for (const chunk of toolResultChunks) {
|
|
663
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
664
|
-
}
|
|
665
|
-
return "continue";
|
|
666
|
-
}
|
|
667
|
-
async *processToolCalls() {
|
|
668
|
-
if (!this.shouldExecuteToolPhase()) {
|
|
669
|
-
this.setToolPhase("stop");
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
const toolCalls = this.toolCallManager.getToolCalls();
|
|
673
|
-
const finishEvent = this.finishedEvent;
|
|
674
|
-
if (!finishEvent || toolCalls.length === 0) {
|
|
675
|
-
this.setToolPhase("stop");
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
this.addAssistantToolCallMessage(toolCalls);
|
|
679
|
-
const undiscoveredLazyResults = [];
|
|
680
|
-
const executableToolCalls = toolCalls.filter((tc) => {
|
|
681
|
-
if (this.lazyToolManager.isUndiscoveredLazyTool(tc.function.name)) {
|
|
682
|
-
undiscoveredLazyResults.push({
|
|
683
|
-
toolCallId: tc.id,
|
|
684
|
-
toolName: tc.function.name,
|
|
685
|
-
result: {
|
|
686
|
-
error: this.lazyToolManager.getUndiscoveredToolError(
|
|
687
|
-
tc.function.name
|
|
688
|
-
)
|
|
689
|
-
},
|
|
690
|
-
state: "output-error"
|
|
691
|
-
});
|
|
692
|
-
return false;
|
|
693
|
-
}
|
|
694
|
-
return true;
|
|
695
|
-
});
|
|
696
|
-
if (undiscoveredLazyResults.length > 0 && this.finishedEvent) {
|
|
697
|
-
for (const chunk of this.buildToolResultChunks(
|
|
698
|
-
undiscoveredLazyResults,
|
|
699
|
-
this.finishedEvent
|
|
700
|
-
)) {
|
|
701
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
if (executableToolCalls.length === 0) {
|
|
705
|
-
this.toolCallManager.clear();
|
|
706
|
-
this.setToolPhase("continue");
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
this.middlewareCtx.phase = "beforeTools";
|
|
710
|
-
const { approvals, clientToolResults } = this.collectClientState();
|
|
711
|
-
const generator = executeToolCalls(
|
|
712
|
-
executableToolCalls,
|
|
713
|
-
this.resolveExecutableTools(executableToolCalls),
|
|
714
|
-
approvals,
|
|
715
|
-
clientToolResults,
|
|
716
|
-
(eventName, data) => this.createCustomEventChunk(eventName, data),
|
|
717
|
-
{
|
|
718
|
-
onBeforeToolCall: async (toolCall, tool, args) => {
|
|
719
|
-
this.logger.tools(`phase=before name=${toolCall.function.name}`, {
|
|
720
|
-
name: toolCall.function.name,
|
|
721
|
-
args
|
|
722
|
-
});
|
|
723
|
-
const hookCtx = {
|
|
724
|
-
toolCall,
|
|
725
|
-
tool,
|
|
726
|
-
args,
|
|
727
|
-
toolName: toolCall.function.name,
|
|
728
|
-
toolCallId: toolCall.id
|
|
729
|
-
};
|
|
730
|
-
return this.middlewareRunner.runOnBeforeToolCall(
|
|
731
|
-
this.middlewareCtx,
|
|
732
|
-
hookCtx
|
|
733
|
-
);
|
|
734
|
-
},
|
|
735
|
-
onAfterToolCall: async (info) => {
|
|
736
|
-
this.logger.tools(`phase=after name=${info.toolName}`, {
|
|
737
|
-
name: info.toolName,
|
|
738
|
-
result: info.result
|
|
739
|
-
});
|
|
740
|
-
await this.middlewareRunner.runOnAfterToolCall(
|
|
741
|
-
this.middlewareCtx,
|
|
742
|
-
info
|
|
743
|
-
);
|
|
744
|
-
}
|
|
745
|
-
},
|
|
746
|
-
this.middlewareCtx.context,
|
|
747
|
-
this.toolAbortSignal
|
|
748
|
-
);
|
|
749
|
-
const executionResult = yield* this.drainToolCallGenerator(generator);
|
|
750
|
-
this.middlewareCtx.phase = "afterTools";
|
|
751
|
-
if (this.isMiddlewareAborted()) {
|
|
752
|
-
this.setToolPhase("stop");
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
await this.middlewareRunner.runOnToolPhaseComplete(this.middlewareCtx, {
|
|
756
|
-
toolCalls,
|
|
757
|
-
results: executionResult.results,
|
|
758
|
-
needsApproval: executionResult.needsApproval,
|
|
759
|
-
needsClientExecution: executionResult.needsClientExecution
|
|
760
|
-
});
|
|
761
|
-
if (executionResult.needsApproval.length > 0 || executionResult.needsClientExecution.length > 0) {
|
|
762
|
-
if (executionResult.results.length > 0) {
|
|
763
|
-
for (const chunk of this.buildToolResultChunks(
|
|
764
|
-
executionResult.results,
|
|
765
|
-
finishEvent
|
|
766
|
-
)) {
|
|
767
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
for (const chunk of this.buildApprovalChunks(
|
|
771
|
-
executionResult.needsApproval,
|
|
772
|
-
finishEvent
|
|
773
|
-
)) {
|
|
774
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
775
|
-
}
|
|
776
|
-
for (const chunk of this.buildClientToolChunks(
|
|
777
|
-
executionResult.needsClientExecution,
|
|
778
|
-
finishEvent
|
|
779
|
-
)) {
|
|
780
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
781
|
-
}
|
|
782
|
-
this.setToolPhase("wait");
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
const toolResultChunks = this.buildToolResultChunks(
|
|
786
|
-
executionResult.results,
|
|
787
|
-
finishEvent
|
|
788
|
-
);
|
|
789
|
-
for (const chunk of toolResultChunks) {
|
|
790
|
-
yield* this.pipeThroughMiddleware(chunk);
|
|
791
|
-
}
|
|
792
|
-
if (this.lazyToolManager.hasNewlyDiscoveredTools()) {
|
|
793
|
-
this.tools = this.lazyToolManager.getActiveTools();
|
|
794
|
-
this.toolCallManager = new ToolCallManager(this.tools);
|
|
795
|
-
this.setToolPhase("continue");
|
|
796
|
-
return;
|
|
797
|
-
}
|
|
798
|
-
this.toolCallManager.clear();
|
|
799
|
-
this.setToolPhase("continue");
|
|
800
|
-
}
|
|
801
|
-
shouldExecuteToolPhase() {
|
|
802
|
-
return this.finishedEvent?.finishReason === "tool_calls" && this.tools.length > 0 && this.toolCallManager.hasToolCalls();
|
|
803
|
-
}
|
|
804
|
-
addAssistantToolCallMessage(toolCalls) {
|
|
805
|
-
this.finalizeCurrentThinkingStep();
|
|
806
|
-
this.messages = [
|
|
807
|
-
...this.messages,
|
|
808
|
-
{
|
|
809
|
-
role: "assistant",
|
|
810
|
-
content: this.accumulatedContent || null,
|
|
811
|
-
toolCalls,
|
|
812
|
-
...this.accumulatedThinking.length > 0 && {
|
|
813
|
-
thinking: this.accumulatedThinking
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
];
|
|
817
|
-
}
|
|
818
|
-
/**
|
|
819
|
-
* Extract client state (approvals and client tool results) from original messages.
|
|
820
|
-
* This is called in the constructor BEFORE converting to ModelMessage format,
|
|
821
|
-
* because the parts array (which contains approval state) is lost during conversion.
|
|
822
|
-
*/
|
|
823
|
-
extractClientStateFromOriginalMessages(originalMessages) {
|
|
824
|
-
const approvals = /* @__PURE__ */ new Map();
|
|
825
|
-
const clientToolResults = /* @__PURE__ */ new Map();
|
|
826
|
-
for (const message of originalMessages) {
|
|
827
|
-
if (message.role === "assistant" && message.parts) {
|
|
828
|
-
for (const part of message.parts) {
|
|
829
|
-
if (part.type === "tool-call") {
|
|
830
|
-
if (part.output !== void 0 && !part.approval) {
|
|
831
|
-
clientToolResults.set(part.id, part.output);
|
|
832
|
-
}
|
|
833
|
-
if (part.approval?.id && part.approval?.approved !== void 0 && part.state === "approval-responded") {
|
|
834
|
-
approvals.set(part.approval.id, part.approval.approved);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
return { approvals, clientToolResults };
|
|
841
|
-
}
|
|
842
|
-
collectClientState() {
|
|
843
|
-
const approvals = new Map(this.initialApprovals);
|
|
844
|
-
const clientToolResults = new Map(this.initialClientToolResults);
|
|
845
|
-
for (const message of this.messages) {
|
|
846
|
-
if (message.role === "tool" && message.toolCallId) {
|
|
847
|
-
let output;
|
|
848
|
-
if (Array.isArray(message.content)) {
|
|
849
|
-
output = message.content;
|
|
850
|
-
} else {
|
|
851
|
-
try {
|
|
852
|
-
output = JSON.parse(message.content);
|
|
853
|
-
} catch {
|
|
854
|
-
output = message.content;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
if (output && typeof output === "object" && output.pendingExecution === true) {
|
|
858
|
-
continue;
|
|
859
|
-
}
|
|
860
|
-
clientToolResults.set(message.toolCallId, output);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
return { approvals, clientToolResults };
|
|
864
|
-
}
|
|
865
|
-
buildApprovalChunks(approvals, finishEvent) {
|
|
866
|
-
const chunks = [];
|
|
867
|
-
for (const approval of approvals) {
|
|
868
|
-
chunks.push({
|
|
869
|
-
type: "CUSTOM",
|
|
870
|
-
timestamp: Date.now(),
|
|
871
|
-
model: finishEvent.model,
|
|
872
|
-
name: "approval-requested",
|
|
873
|
-
value: {
|
|
874
|
-
toolCallId: approval.toolCallId,
|
|
875
|
-
toolName: approval.toolName,
|
|
876
|
-
input: approval.input,
|
|
877
|
-
approval: {
|
|
878
|
-
id: approval.approvalId,
|
|
879
|
-
needsApproval: true
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
return chunks;
|
|
885
|
-
}
|
|
886
|
-
buildClientToolChunks(clientRequests, finishEvent) {
|
|
887
|
-
const chunks = [];
|
|
888
|
-
for (const clientTool of clientRequests) {
|
|
889
|
-
chunks.push({
|
|
890
|
-
type: "CUSTOM",
|
|
891
|
-
timestamp: Date.now(),
|
|
892
|
-
model: finishEvent.model,
|
|
893
|
-
name: "tool-input-available",
|
|
894
|
-
value: {
|
|
895
|
-
toolCallId: clientTool.toolCallId,
|
|
896
|
-
toolName: clientTool.toolName,
|
|
897
|
-
input: clientTool.input
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
return chunks;
|
|
902
|
-
}
|
|
903
|
-
buildToolResultChunks(results, finishEvent, argsMap) {
|
|
904
|
-
const chunks = [];
|
|
905
|
-
for (const result of results) {
|
|
906
|
-
const content = normalizeToolResult(result.result);
|
|
907
|
-
const wireContent = typeof content === "string" ? content : JSON.stringify(content);
|
|
908
|
-
if (argsMap) {
|
|
909
|
-
chunks.push({
|
|
910
|
-
type: "TOOL_CALL_START",
|
|
911
|
-
timestamp: Date.now(),
|
|
912
|
-
model: finishEvent.model,
|
|
913
|
-
toolCallId: result.toolCallId,
|
|
914
|
-
toolCallName: result.toolName,
|
|
915
|
-
toolName: result.toolName
|
|
916
|
-
});
|
|
917
|
-
const args = argsMap.get(result.toolCallId) ?? "{}";
|
|
918
|
-
chunks.push({
|
|
919
|
-
type: "TOOL_CALL_ARGS",
|
|
920
|
-
timestamp: Date.now(),
|
|
921
|
-
model: finishEvent.model,
|
|
922
|
-
toolCallId: result.toolCallId,
|
|
923
|
-
delta: args,
|
|
924
|
-
args
|
|
925
|
-
});
|
|
926
|
-
chunks.push({
|
|
927
|
-
type: "TOOL_CALL_END",
|
|
928
|
-
timestamp: Date.now(),
|
|
929
|
-
model: finishEvent.model,
|
|
930
|
-
toolCallId: result.toolCallId,
|
|
931
|
-
toolCallName: result.toolName,
|
|
932
|
-
toolName: result.toolName,
|
|
933
|
-
result: wireContent,
|
|
934
|
-
...result.state !== void 0 && { state: result.state }
|
|
935
|
-
});
|
|
936
|
-
}
|
|
937
|
-
chunks.push({
|
|
938
|
-
type: "TOOL_CALL_RESULT",
|
|
939
|
-
timestamp: Date.now(),
|
|
940
|
-
model: finishEvent.model,
|
|
941
|
-
messageId: this.createId("tool-result"),
|
|
942
|
-
toolCallId: result.toolCallId,
|
|
943
|
-
content: wireContent,
|
|
944
|
-
role: "tool",
|
|
945
|
-
...result.state !== void 0 && { state: result.state }
|
|
946
|
-
});
|
|
947
|
-
const placeholderIdx = this.messages.findIndex((m) => {
|
|
948
|
-
if (m.role !== "tool" || m.toolCallId !== result.toolCallId) {
|
|
949
|
-
return false;
|
|
950
|
-
}
|
|
951
|
-
if (typeof m.content !== "string") return false;
|
|
952
|
-
try {
|
|
953
|
-
return JSON.parse(m.content)?.pendingExecution === true;
|
|
954
|
-
} catch {
|
|
955
|
-
return false;
|
|
956
|
-
}
|
|
957
|
-
});
|
|
958
|
-
const newToolMessage = {
|
|
959
|
-
role: "tool",
|
|
960
|
-
content,
|
|
961
|
-
toolCallId: result.toolCallId
|
|
962
|
-
};
|
|
963
|
-
if (placeholderIdx >= 0) {
|
|
964
|
-
this.messages = [
|
|
965
|
-
...this.messages.slice(0, placeholderIdx),
|
|
966
|
-
newToolMessage,
|
|
967
|
-
...this.messages.slice(placeholderIdx + 1)
|
|
968
|
-
];
|
|
969
|
-
} else {
|
|
970
|
-
this.messages = [...this.messages, newToolMessage];
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
return chunks;
|
|
974
|
-
}
|
|
975
|
-
getPendingToolCallsFromMessages() {
|
|
976
|
-
const completedToolIds = /* @__PURE__ */ new Set();
|
|
977
|
-
for (const message of this.messages) {
|
|
978
|
-
if (message.role === "tool" && message.toolCallId) {
|
|
979
|
-
let hasPendingExecution = false;
|
|
980
|
-
if (typeof message.content === "string") {
|
|
981
|
-
try {
|
|
982
|
-
const parsed = JSON.parse(message.content);
|
|
983
|
-
if (parsed.pendingExecution === true) {
|
|
984
|
-
hasPendingExecution = true;
|
|
985
|
-
}
|
|
986
|
-
} catch {
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
if (!hasPendingExecution) {
|
|
990
|
-
completedToolIds.add(message.toolCallId);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
const pending = [];
|
|
995
|
-
for (const message of this.messages) {
|
|
996
|
-
if (message.role === "assistant" && message.toolCalls) {
|
|
997
|
-
for (const toolCall of message.toolCalls) {
|
|
998
|
-
if (!completedToolIds.has(toolCall.id)) {
|
|
999
|
-
pending.push(toolCall);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
return pending;
|
|
1005
|
-
}
|
|
1006
|
-
createSyntheticFinishedEvent() {
|
|
1007
|
-
return {
|
|
1008
|
-
type: "RUN_FINISHED",
|
|
1009
|
-
runId: this.createId("pending"),
|
|
1010
|
-
threadId: this.threadId,
|
|
1011
|
-
model: this.params.model,
|
|
1012
|
-
timestamp: Date.now(),
|
|
1013
|
-
finishReason: "tool_calls"
|
|
1014
|
-
};
|
|
1015
|
-
}
|
|
1016
|
-
shouldContinue() {
|
|
1017
|
-
if (this.cyclePhase === "executeToolCalls") {
|
|
1018
|
-
return true;
|
|
1019
|
-
}
|
|
1020
|
-
return this.loopStrategy({
|
|
1021
|
-
iterationCount: this.iterationCount,
|
|
1022
|
-
messages: this.messages,
|
|
1023
|
-
finishReason: this.lastFinishReason
|
|
1024
|
-
}) && this.toolPhase === "continue";
|
|
1025
|
-
}
|
|
1026
|
-
isAborted() {
|
|
1027
|
-
return !!this.effectiveSignal?.aborted;
|
|
1028
|
-
}
|
|
1029
|
-
isMiddlewareAborted() {
|
|
1030
|
-
return !!this.middlewareAbortController?.signal.aborted;
|
|
1031
|
-
}
|
|
1032
|
-
isCancelled() {
|
|
1033
|
-
return this.isAborted() || this.isMiddlewareAborted();
|
|
1034
|
-
}
|
|
1035
|
-
/**
|
|
1036
|
-
* Run the final structured-output adapter call through the middleware
|
|
1037
|
-
* pipeline. Yields chunks to the caller only when
|
|
1038
|
-
* `this.finalStructuredOutput.yieldChunks` is true; otherwise consumes
|
|
1039
|
-
* silently while still piping through middleware.
|
|
1040
|
-
*
|
|
1041
|
-
* On success, populates this.structuredOutputResult.
|
|
1042
|
-
* On failure, populates this.finalizationError.
|
|
1043
|
-
*/
|
|
1044
|
-
async *runStructuredFinalization() {
|
|
1045
|
-
if (!this.finalStructuredOutput) {
|
|
1046
|
-
throw new Error(
|
|
1047
|
-
"runStructuredFinalization called without finalStructuredOutput config"
|
|
1048
|
-
);
|
|
1049
|
-
}
|
|
1050
|
-
this.middlewareCtx.phase = "structuredOutput";
|
|
1051
|
-
const baseConfig = this.buildMiddlewareConfig();
|
|
1052
|
-
const { tools: _omitTools, ...baseWithoutTools } = baseConfig;
|
|
1053
|
-
let structuredConfig = {
|
|
1054
|
-
...baseWithoutTools,
|
|
1055
|
-
outputSchema: this.finalStructuredOutput.jsonSchema
|
|
1056
|
-
};
|
|
1057
|
-
structuredConfig = await this.middlewareRunner.runOnStructuredOutputConfig(
|
|
1058
|
-
this.middlewareCtx,
|
|
1059
|
-
structuredConfig
|
|
1060
|
-
);
|
|
1061
|
-
const { outputSchema: pinnedSchema, ...chatConfigSlice } = structuredConfig;
|
|
1062
|
-
const postOnConfig = await this.middlewareRunner.runOnConfig(
|
|
1063
|
-
this.middlewareCtx,
|
|
1064
|
-
{ ...chatConfigSlice, tools: baseConfig.tools }
|
|
1065
|
-
);
|
|
1066
|
-
this.applyMiddlewareConfig(postOnConfig);
|
|
1067
|
-
const structuredCallOptions = {
|
|
1068
|
-
chatOptions: {
|
|
1069
|
-
model: this.params.model,
|
|
1070
|
-
messages: this.messages,
|
|
1071
|
-
metadata: postOnConfig.metadata,
|
|
1072
|
-
modelOptions: postOnConfig.modelOptions,
|
|
1073
|
-
systemPrompts: postOnConfig.systemPrompts,
|
|
1074
|
-
logger: this.logger,
|
|
1075
|
-
threadId: this.threadId,
|
|
1076
|
-
runId: this.runIdOverride,
|
|
1077
|
-
parentRunId: this.parentRunIdOverride,
|
|
1078
|
-
...this.effectiveRequest ? { request: this.effectiveRequest } : {}
|
|
1079
|
-
},
|
|
1080
|
-
outputSchema: pinnedSchema
|
|
1081
|
-
};
|
|
1082
|
-
let fallbackAdapterError = void 0;
|
|
1083
|
-
const providerStream = this.adapter.structuredOutputStream ? this.adapter.structuredOutputStream(structuredCallOptions) : fallbackStructuredOutputStream(
|
|
1084
|
-
this.adapter,
|
|
1085
|
-
structuredCallOptions,
|
|
1086
|
-
(err) => {
|
|
1087
|
-
fallbackAdapterError = err;
|
|
1088
|
-
}
|
|
1089
|
-
);
|
|
1090
|
-
let startEmitted = false;
|
|
1091
|
-
let structuredMessageId = null;
|
|
1092
|
-
const extractMessageId = (c) => {
|
|
1093
|
-
if (c.type === EventType.TEXT_MESSAGE_START || c.type === EventType.TEXT_MESSAGE_CONTENT || c.type === EventType.TEXT_MESSAGE_END) {
|
|
1094
|
-
return typeof c.messageId === "string" && c.messageId !== "" ? c.messageId : null;
|
|
1095
|
-
}
|
|
1096
|
-
return null;
|
|
1097
|
-
};
|
|
1098
|
-
const buildSynthesizedStart = () => {
|
|
1099
|
-
const idForStart = structuredMessageId ?? generateMessageId();
|
|
1100
|
-
structuredMessageId = idForStart;
|
|
1101
|
-
return {
|
|
1102
|
-
type: EventType.CUSTOM,
|
|
1103
|
-
name: "structured-output.start",
|
|
1104
|
-
value: { messageId: idForStart },
|
|
1105
|
-
model: this.params.model,
|
|
1106
|
-
timestamp: Date.now(),
|
|
1107
|
-
threadId: this.threadId,
|
|
1108
|
-
...this.runIdOverride ? { runId: this.runIdOverride } : {}
|
|
1109
|
-
};
|
|
1110
|
-
};
|
|
1111
|
-
const pipeThroughMiddleware = async (synthChunk) => this.middlewareRunner.runOnChunk(this.middlewareCtx, synthChunk);
|
|
1112
|
-
let runErrorYielded = false;
|
|
1113
|
-
for await (const chunk of providerStream) {
|
|
1114
|
-
if (this.isCancelled()) {
|
|
1115
|
-
break;
|
|
1116
|
-
}
|
|
1117
|
-
if (!startEmitted && chunk.type === EventType.CUSTOM && chunk.name === "structured-output.start") {
|
|
1118
|
-
startEmitted = true;
|
|
1119
|
-
}
|
|
1120
|
-
if (!structuredMessageId) {
|
|
1121
|
-
const extracted = extractMessageId(chunk);
|
|
1122
|
-
if (extracted) structuredMessageId = extracted;
|
|
1123
|
-
}
|
|
1124
|
-
if (this.finalStructuredOutput.yieldChunks) {
|
|
1125
|
-
if (!startEmitted && (chunk.type === EventType.TEXT_MESSAGE_START || chunk.type === EventType.TEXT_MESSAGE_CONTENT || chunk.type === EventType.TEXT_MESSAGE_END)) {
|
|
1126
|
-
startEmitted = true;
|
|
1127
|
-
const synthStart = buildSynthesizedStart();
|
|
1128
|
-
const synthOutputs = await pipeThroughMiddleware(synthStart);
|
|
1129
|
-
for (const outputChunk of synthOutputs) {
|
|
1130
|
-
yield outputChunk;
|
|
1131
|
-
this.middlewareCtx.chunkIndex++;
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
if (!startEmitted && chunk.type === EventType.RUN_ERROR) {
|
|
1135
|
-
startEmitted = true;
|
|
1136
|
-
const synthStart = buildSynthesizedStart();
|
|
1137
|
-
const synthOutputs = await pipeThroughMiddleware(synthStart);
|
|
1138
|
-
for (const outputChunk of synthOutputs) {
|
|
1139
|
-
yield outputChunk;
|
|
1140
|
-
this.middlewareCtx.chunkIndex++;
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
let outboundChunk = chunk;
|
|
1145
|
-
if (chunk.type === EventType.CUSTOM && chunk.name === "structured-output.complete") {
|
|
1146
|
-
const parsed = readStructuredOutputCompleteValue(chunk.value);
|
|
1147
|
-
if (parsed) {
|
|
1148
|
-
const object = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed.object) : parsed.object;
|
|
1149
|
-
this.structuredOutputResult = { data: object, rawText: parsed.raw };
|
|
1150
|
-
const value = chunk.value;
|
|
1151
|
-
if (object !== parsed.object && value && typeof value === "object") {
|
|
1152
|
-
outboundChunk = { ...chunk, value: { ...value, object } };
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
if (chunk.type === EventType.RUN_FINISHED && chunk.usage) {
|
|
1157
|
-
await this.middlewareRunner.runOnUsage(this.middlewareCtx, chunk.usage);
|
|
1158
|
-
}
|
|
1159
|
-
if (chunk.type === EventType.RUN_ERROR) {
|
|
1160
|
-
this.finalizationError = {
|
|
1161
|
-
message: chunk.message,
|
|
1162
|
-
...chunk.code ? { code: chunk.code } : {},
|
|
1163
|
-
...fallbackAdapterError !== void 0 ? { cause: fallbackAdapterError } : {}
|
|
1164
|
-
};
|
|
1165
|
-
}
|
|
1166
|
-
const outputChunks = await this.middlewareRunner.runOnChunk(
|
|
1167
|
-
this.middlewareCtx,
|
|
1168
|
-
outboundChunk
|
|
1169
|
-
);
|
|
1170
|
-
if (this.finalStructuredOutput.yieldChunks) {
|
|
1171
|
-
for (const outputChunk of outputChunks) {
|
|
1172
|
-
if (outputChunk.type === EventType.RUN_ERROR) {
|
|
1173
|
-
runErrorYielded = true;
|
|
1174
|
-
}
|
|
1175
|
-
yield outputChunk;
|
|
1176
|
-
this.middlewareCtx.chunkIndex++;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
if (this.finalizationError) {
|
|
1180
|
-
break;
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
if (this.isCancelled()) {
|
|
1184
|
-
return;
|
|
1185
|
-
}
|
|
1186
|
-
if (!this.structuredOutputResult && !this.finalizationError) {
|
|
1187
|
-
this.finalizationError = {
|
|
1188
|
-
message: "missing structured result",
|
|
1189
|
-
code: "structured-output-missing-result"
|
|
1190
|
-
};
|
|
1191
|
-
}
|
|
1192
|
-
if (this.structuredOutputResult && !this.finalizationError && this.finalStructuredOutput.validate) {
|
|
1193
|
-
try {
|
|
1194
|
-
const validated = this.finalStructuredOutput.validate(
|
|
1195
|
-
this.structuredOutputResult.data
|
|
1196
|
-
);
|
|
1197
|
-
this.validatedStructuredOutput = validated;
|
|
1198
|
-
this.hasValidatedStructuredOutput = true;
|
|
1199
|
-
} catch (err) {
|
|
1200
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1201
|
-
this.finalizationError = {
|
|
1202
|
-
message,
|
|
1203
|
-
code: "structured-output-validation-failed",
|
|
1204
|
-
cause: err
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
if (this.finalizationError && this.finalStructuredOutput.yieldChunks && !runErrorYielded) {
|
|
1209
|
-
if (!startEmitted) {
|
|
1210
|
-
const synthStart = buildSynthesizedStart();
|
|
1211
|
-
const startOutputs = await pipeThroughMiddleware(synthStart);
|
|
1212
|
-
for (const outputChunk of startOutputs) {
|
|
1213
|
-
yield outputChunk;
|
|
1214
|
-
this.middlewareCtx.chunkIndex++;
|
|
1215
|
-
}
|
|
1216
|
-
startEmitted = true;
|
|
1217
|
-
}
|
|
1218
|
-
const errChunk = {
|
|
1219
|
-
type: EventType.RUN_ERROR,
|
|
1220
|
-
runId: this.runIdOverride ?? this.requestId,
|
|
1221
|
-
model: this.params.model,
|
|
1222
|
-
timestamp: Date.now(),
|
|
1223
|
-
threadId: this.threadId,
|
|
1224
|
-
message: this.finalizationError.message,
|
|
1225
|
-
...this.finalizationError.code ? { code: this.finalizationError.code } : {},
|
|
1226
|
-
error: {
|
|
1227
|
-
message: this.finalizationError.message,
|
|
1228
|
-
...this.finalizationError.code ? { code: this.finalizationError.code } : {}
|
|
1229
|
-
}
|
|
1230
|
-
};
|
|
1231
|
-
const outputChunks = await this.middlewareRunner.runOnChunk(
|
|
1232
|
-
this.middlewareCtx,
|
|
1233
|
-
errChunk
|
|
1234
|
-
);
|
|
1235
|
-
for (const outputChunk of outputChunks) {
|
|
1236
|
-
yield outputChunk;
|
|
1237
|
-
this.middlewareCtx.chunkIndex++;
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
/**
|
|
1242
|
-
* Native combined mode: harvest the structured output from the agent
|
|
1243
|
-
* loop's accumulated final-turn text (no separate provider call).
|
|
1244
|
-
*
|
|
1245
|
-
* The adapter wired `outputSchema` into the regular `chatStream` request,
|
|
1246
|
-
* so the model's final-turn text is the schema-constrained JSON. We parse
|
|
1247
|
-
* `this.accumulatedContent`, populate `this.structuredOutputResult`, emit
|
|
1248
|
-
* a synthetic `structured-output.complete` (and a `structured-output.start`
|
|
1249
|
-
* if one wasn't emitted earlier — only happens on the streaming path when
|
|
1250
|
-
* the model returned no text at all), and run the validate callback when
|
|
1251
|
-
* present. Failures populate `this.finalizationError` so the engine's
|
|
1252
|
-
* terminal-hook chooser routes to `onError` (per spec §7.3).
|
|
1253
|
-
*
|
|
1254
|
-
* The `'structuredOutput'` middleware phase intentionally does NOT fire on
|
|
1255
|
-
* this path — middleware sees the run through `beforeModel` / `modelStream`
|
|
1256
|
-
* as usual. See PR #605 / issue #605 for the design rationale.
|
|
1257
|
-
*/
|
|
1258
|
-
async *harvestCombinedStructuredOutput() {
|
|
1259
|
-
if (!this.finalStructuredOutput) {
|
|
1260
|
-
throw new Error(
|
|
1261
|
-
"harvestCombinedStructuredOutput called without finalStructuredOutput config"
|
|
1262
|
-
);
|
|
1263
|
-
}
|
|
1264
|
-
const yieldChunks = this.finalStructuredOutput.yieldChunks;
|
|
1265
|
-
const rawText = this.accumulatedContent;
|
|
1266
|
-
if (rawText.length === 0) {
|
|
1267
|
-
this.finalizationError = {
|
|
1268
|
-
message: "missing structured result",
|
|
1269
|
-
code: "structured-output-missing-result"
|
|
1270
|
-
};
|
|
1271
|
-
} else {
|
|
1272
|
-
try {
|
|
1273
|
-
const parsed = JSON.parse(rawText);
|
|
1274
|
-
const data = this.finalStructuredOutput.normalize ? this.finalStructuredOutput.normalize(parsed) : parsed;
|
|
1275
|
-
this.structuredOutputResult = { data, rawText };
|
|
1276
|
-
} catch (err) {
|
|
1277
|
-
const detail = rawText.slice(0, 200) + (rawText.length > 200 ? "..." : "");
|
|
1278
|
-
this.finalizationError = {
|
|
1279
|
-
message: `Failed to parse structured output as JSON. Content: ${detail}`,
|
|
1280
|
-
code: "structured-output-parse-failed",
|
|
1281
|
-
cause: err
|
|
1282
|
-
};
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
if (this.structuredOutputResult && !this.finalizationError && this.finalStructuredOutput.validate) {
|
|
1286
|
-
try {
|
|
1287
|
-
const validated = this.finalStructuredOutput.validate(
|
|
1288
|
-
this.structuredOutputResult.data
|
|
1289
|
-
);
|
|
1290
|
-
this.validatedStructuredOutput = validated;
|
|
1291
|
-
this.hasValidatedStructuredOutput = true;
|
|
1292
|
-
} catch (err) {
|
|
1293
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1294
|
-
this.finalizationError = {
|
|
1295
|
-
message,
|
|
1296
|
-
code: "structured-output-validation-failed",
|
|
1297
|
-
cause: err
|
|
1298
|
-
};
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
if (!yieldChunks) {
|
|
1302
|
-
return;
|
|
1303
|
-
}
|
|
1304
|
-
if (!this.combinedStartEmitted) {
|
|
1305
|
-
this.combinedStartEmitted = true;
|
|
1306
|
-
const messageId = this.combinedStructuredMessageId ?? generateMessageId();
|
|
1307
|
-
this.combinedStructuredMessageId = messageId;
|
|
1308
|
-
const synthStart = {
|
|
1309
|
-
type: EventType.CUSTOM,
|
|
1310
|
-
name: "structured-output.start",
|
|
1311
|
-
value: { messageId },
|
|
1312
|
-
model: this.params.model,
|
|
1313
|
-
timestamp: Date.now(),
|
|
1314
|
-
threadId: this.threadId,
|
|
1315
|
-
...this.runIdOverride ? { runId: this.runIdOverride } : {}
|
|
1316
|
-
};
|
|
1317
|
-
const startOutputs = await this.middlewareRunner.runOnChunk(
|
|
1318
|
-
this.middlewareCtx,
|
|
1319
|
-
synthStart
|
|
1320
|
-
);
|
|
1321
|
-
for (const outputChunk of startOutputs) {
|
|
1322
|
-
yield outputChunk;
|
|
1323
|
-
this.middlewareCtx.chunkIndex++;
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
if (this.structuredOutputResult && !this.finalizationError) {
|
|
1327
|
-
const completeChunk = {
|
|
1328
|
-
type: EventType.CUSTOM,
|
|
1329
|
-
name: "structured-output.complete",
|
|
1330
|
-
value: {
|
|
1331
|
-
object: this.structuredOutputResult.data,
|
|
1332
|
-
raw: this.structuredOutputResult.rawText,
|
|
1333
|
-
...this.combinedStructuredMessageId ? { messageId: this.combinedStructuredMessageId } : {}
|
|
1334
|
-
},
|
|
1335
|
-
model: this.params.model,
|
|
1336
|
-
timestamp: Date.now(),
|
|
1337
|
-
threadId: this.threadId,
|
|
1338
|
-
...this.runIdOverride ? { runId: this.runIdOverride } : {}
|
|
1339
|
-
};
|
|
1340
|
-
const completeOutputs = await this.middlewareRunner.runOnChunk(
|
|
1341
|
-
this.middlewareCtx,
|
|
1342
|
-
completeChunk
|
|
1343
|
-
);
|
|
1344
|
-
for (const outputChunk of completeOutputs) {
|
|
1345
|
-
yield outputChunk;
|
|
1346
|
-
this.middlewareCtx.chunkIndex++;
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
if (this.finalizationError) {
|
|
1350
|
-
const errChunk = {
|
|
1351
|
-
type: EventType.RUN_ERROR,
|
|
1352
|
-
runId: this.runIdOverride ?? this.requestId,
|
|
1353
|
-
model: this.params.model,
|
|
1354
|
-
timestamp: Date.now(),
|
|
1355
|
-
threadId: this.threadId,
|
|
1356
|
-
message: this.finalizationError.message,
|
|
1357
|
-
...this.finalizationError.code ? { code: this.finalizationError.code } : {},
|
|
1358
|
-
error: {
|
|
1359
|
-
message: this.finalizationError.message,
|
|
1360
|
-
...this.finalizationError.code ? { code: this.finalizationError.code } : {}
|
|
1361
|
-
}
|
|
1362
|
-
};
|
|
1363
|
-
const errOutputs = await this.middlewareRunner.runOnChunk(
|
|
1364
|
-
this.middlewareCtx,
|
|
1365
|
-
errChunk
|
|
1366
|
-
);
|
|
1367
|
-
for (const outputChunk of errOutputs) {
|
|
1368
|
-
yield outputChunk;
|
|
1369
|
-
this.middlewareCtx.chunkIndex++;
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
buildMiddlewareConfig() {
|
|
1374
|
-
return {
|
|
1375
|
-
messages: this.messages,
|
|
1376
|
-
systemPrompts: [...this.systemPrompts],
|
|
1377
|
-
tools: [...this.tools],
|
|
1378
|
-
metadata: this.params.metadata,
|
|
1379
|
-
modelOptions: this.params.modelOptions
|
|
1380
|
-
};
|
|
1381
|
-
}
|
|
1382
|
-
applyMiddlewareConfig(config) {
|
|
1383
|
-
this.messages = config.messages;
|
|
1384
|
-
this.systemPrompts = config.systemPrompts;
|
|
1385
|
-
this.tools = config.tools;
|
|
1386
|
-
this.params = {
|
|
1387
|
-
...this.params,
|
|
1388
|
-
metadata: config.metadata,
|
|
1389
|
-
modelOptions: config.modelOptions
|
|
1390
|
-
};
|
|
1391
|
-
this.middlewareCtx.messages = this.messages;
|
|
1392
|
-
this.middlewareCtx.systemPrompts = this.systemPrompts;
|
|
1393
|
-
this.middlewareCtx.hasTools = this.tools.length > 0;
|
|
1394
|
-
this.middlewareCtx.toolNames = this.tools.map((t) => t.name);
|
|
1395
|
-
this.middlewareCtx.modelOptions = config.modelOptions;
|
|
1396
|
-
}
|
|
1397
|
-
setToolPhase(phase) {
|
|
1398
|
-
this.toolPhase = phase;
|
|
1399
|
-
}
|
|
1400
|
-
/**
|
|
1401
|
-
* Pipe a single chunk through the middleware pipeline (strip-to-spec, devtools, etc.)
|
|
1402
|
-
* and yield all resulting output chunks.
|
|
1403
|
-
*/
|
|
1404
|
-
async *pipeThroughMiddleware(chunk) {
|
|
1405
|
-
const outputChunks = await this.middlewareRunner.runOnChunk(
|
|
1406
|
-
this.middlewareCtx,
|
|
1407
|
-
chunk
|
|
1408
|
-
);
|
|
1409
|
-
for (const outputChunk of outputChunks) {
|
|
1410
|
-
yield outputChunk;
|
|
1411
|
-
this.middlewareCtx.chunkIndex++;
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
/**
|
|
1415
|
-
* Drain an executeToolCalls async generator, yielding any CustomEvent chunks
|
|
1416
|
-
* through the middleware pipeline and returning the final ExecuteToolCallsResult.
|
|
1417
|
-
*/
|
|
1418
|
-
async *drainToolCallGenerator(generator) {
|
|
1419
|
-
let next = await generator.next();
|
|
1420
|
-
while (!next.done) {
|
|
1421
|
-
yield* this.pipeThroughMiddleware(next.value);
|
|
1422
|
-
next = await generator.next();
|
|
1423
|
-
}
|
|
1424
|
-
return next.value;
|
|
1425
|
-
}
|
|
1426
|
-
createCustomEventChunk(eventName, value) {
|
|
1427
|
-
return {
|
|
1428
|
-
type: "CUSTOM",
|
|
1429
|
-
timestamp: Date.now(),
|
|
1430
|
-
model: this.params.model,
|
|
1431
|
-
name: eventName,
|
|
1432
|
-
value
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
createId(prefix) {
|
|
1436
|
-
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
function chat(options) {
|
|
1440
|
-
validateCapabilities(options.middleware ?? [], options.adapter);
|
|
1441
|
-
const { outputSchema, stream } = options;
|
|
1442
|
-
if (outputSchema && stream === true) {
|
|
1443
|
-
return runStreamingStructuredOutput({
|
|
1444
|
-
...options,
|
|
1445
|
-
outputSchema,
|
|
1446
|
-
stream
|
|
1447
|
-
});
|
|
1448
|
-
}
|
|
1449
|
-
if (outputSchema) {
|
|
1450
|
-
return runAgenticStructuredOutput({
|
|
1451
|
-
...options,
|
|
1452
|
-
outputSchema
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
if (stream === false) {
|
|
1456
|
-
return runNonStreamingText({
|
|
1457
|
-
...options,
|
|
1458
|
-
outputSchema: void 0,
|
|
1459
|
-
stream
|
|
1460
|
-
});
|
|
1461
|
-
}
|
|
1462
|
-
return runStreamingText({
|
|
1463
|
-
...options,
|
|
1464
|
-
outputSchema: void 0,
|
|
1465
|
-
stream
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
async function* runStreamingText(options) {
|
|
1469
|
-
const { adapter, middleware, context, debug, mcp, ...textOptions } = options;
|
|
1470
|
-
const model = adapter.model;
|
|
1471
|
-
const logger = resolveDebugOption(debug);
|
|
1472
|
-
const mcpManager = MCPManager.from(mcp);
|
|
1473
|
-
const mcpTools = await mcpManager.discover();
|
|
1474
|
-
if (mcpTools.length > 0) {
|
|
1475
|
-
textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
|
|
1476
|
-
}
|
|
1477
|
-
const engine = new TextEngine(
|
|
1478
|
-
{
|
|
1479
|
-
adapter,
|
|
1480
|
-
params: { ...textOptions, model, logger },
|
|
1481
|
-
middleware,
|
|
1482
|
-
context
|
|
1483
|
-
},
|
|
1484
|
-
logger
|
|
1485
|
-
);
|
|
1486
|
-
try {
|
|
1487
|
-
for await (const chunk of engine.run()) {
|
|
1488
|
-
yield chunk;
|
|
1489
|
-
}
|
|
1490
|
-
} finally {
|
|
1491
|
-
await mcpManager.dispose();
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
function runNonStreamingText(options) {
|
|
1495
|
-
const stream = runStreamingText(
|
|
1496
|
-
// eslint-disable-next-line no-restricted-syntax -- generic-stream remap: caller is non-streaming (false), but runStreamingText is invoked internally to collect text; concrete `false`→`true` literals don't structurally overlap.
|
|
1497
|
-
options
|
|
1498
|
-
);
|
|
1499
|
-
return streamToText(stream);
|
|
1500
|
-
}
|
|
1501
|
-
async function runAgenticStructuredOutput(options) {
|
|
1502
|
-
const {
|
|
1503
|
-
adapter,
|
|
1504
|
-
outputSchema,
|
|
1505
|
-
middleware,
|
|
1506
|
-
context,
|
|
1507
|
-
debug,
|
|
1508
|
-
mcp,
|
|
1509
|
-
...textOptions
|
|
1510
|
-
} = options;
|
|
1511
|
-
const model = adapter.model;
|
|
1512
|
-
const logger = resolveDebugOption(debug);
|
|
1513
|
-
if (!outputSchema) {
|
|
1514
|
-
throw new Error("outputSchema is required for structured output");
|
|
1515
|
-
}
|
|
1516
|
-
const { jsonSchema, nullWideningMap } = convertSchemaForStructuredOutput(outputSchema);
|
|
1517
|
-
if (!jsonSchema) {
|
|
1518
|
-
throw new Error("Failed to convert output schema to JSON Schema");
|
|
1519
|
-
}
|
|
1520
|
-
const normalize = (data) => undoNullWidening(data, nullWideningMap);
|
|
1521
|
-
const validate = isStandardSchema(outputSchema) ? (data) => parseWithStandardSchema(outputSchema, data) : void 0;
|
|
1522
|
-
const nativeCombined = adapter.supportsCombinedToolsAndSchema?.(options.modelOptions) === true;
|
|
1523
|
-
const mcpManager = MCPManager.from(mcp);
|
|
1524
|
-
const mcpTools = await mcpManager.discover();
|
|
1525
|
-
if (mcpTools.length > 0) {
|
|
1526
|
-
textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
|
|
1527
|
-
}
|
|
1528
|
-
const engine = new TextEngine(
|
|
1529
|
-
{
|
|
1530
|
-
adapter,
|
|
1531
|
-
params: { ...textOptions, model, logger },
|
|
1532
|
-
middleware,
|
|
1533
|
-
context,
|
|
1534
|
-
finalStructuredOutput: {
|
|
1535
|
-
jsonSchema,
|
|
1536
|
-
yieldChunks: false,
|
|
1537
|
-
normalize,
|
|
1538
|
-
...validate ? { validate } : {},
|
|
1539
|
-
...nativeCombined ? { nativeCombined: true } : {}
|
|
1540
|
-
}
|
|
1541
|
-
},
|
|
1542
|
-
logger
|
|
1543
|
-
);
|
|
1544
|
-
try {
|
|
1545
|
-
for await (const _chunk of engine.run()) {
|
|
1546
|
-
}
|
|
1547
|
-
} finally {
|
|
1548
|
-
await mcpManager.dispose();
|
|
1549
|
-
}
|
|
1550
|
-
const finalizationError = engine.getFinalizationError();
|
|
1551
|
-
if (finalizationError) {
|
|
1552
|
-
const err = new Error(
|
|
1553
|
-
finalizationError.message,
|
|
1554
|
-
finalizationError.cause !== void 0 ? { cause: finalizationError.cause } : void 0
|
|
1555
|
-
);
|
|
1556
|
-
if (finalizationError.code !== void 0) {
|
|
1557
|
-
Object.defineProperty(err, "code", {
|
|
1558
|
-
value: finalizationError.code,
|
|
1559
|
-
enumerable: true
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
throw err;
|
|
1563
|
-
}
|
|
1564
|
-
const validated = engine.getValidatedStructuredOutput();
|
|
1565
|
-
if (validated) {
|
|
1566
|
-
return validated.value;
|
|
1567
|
-
}
|
|
1568
|
-
const result = engine.getStructuredOutputResult();
|
|
1569
|
-
if (!result) {
|
|
1570
|
-
throw new Error("structured output finalization produced no result");
|
|
1571
|
-
}
|
|
1572
|
-
return result.data;
|
|
1573
|
-
}
|
|
1574
|
-
function readStructuredOutputCompleteValue(value) {
|
|
1575
|
-
if (typeof value !== "object" || value === null) return null;
|
|
1576
|
-
if (!("object" in value) || !("raw" in value)) return null;
|
|
1577
|
-
const raw = value.raw;
|
|
1578
|
-
if (typeof raw !== "string") return null;
|
|
1579
|
-
const reasoningField = value.reasoning;
|
|
1580
|
-
const reasoning = typeof reasoningField === "string" ? reasoningField : void 0;
|
|
1581
|
-
return {
|
|
1582
|
-
object: value.object,
|
|
1583
|
-
raw,
|
|
1584
|
-
...reasoning !== void 0 ? { reasoning } : {}
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
async function* fallbackStructuredOutputStream(adapter, options, onAdapterError) {
|
|
1588
|
-
const { chatOptions } = options;
|
|
1589
|
-
const fallbackRand = Math.random().toString(36).slice(2);
|
|
1590
|
-
const runId = chatOptions.runId ?? `fallback-${Date.now()}-${fallbackRand}`;
|
|
1591
|
-
const threadId = chatOptions.threadId ?? `fallback-${Date.now()}-${fallbackRand}`;
|
|
1592
|
-
const messageId = `fallback-${Date.now()}-${fallbackRand}`;
|
|
1593
|
-
const model = chatOptions.model;
|
|
1594
|
-
const timestamp = Date.now();
|
|
1595
|
-
yield {
|
|
1596
|
-
type: EventType.RUN_STARTED,
|
|
1597
|
-
runId,
|
|
1598
|
-
threadId,
|
|
1599
|
-
model,
|
|
1600
|
-
timestamp
|
|
1601
|
-
};
|
|
1602
|
-
let result;
|
|
1603
|
-
try {
|
|
1604
|
-
result = await adapter.structuredOutput(options);
|
|
1605
|
-
} catch (error) {
|
|
1606
|
-
onAdapterError?.(error);
|
|
1607
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1608
|
-
yield {
|
|
1609
|
-
type: EventType.RUN_ERROR,
|
|
1610
|
-
runId,
|
|
1611
|
-
threadId,
|
|
1612
|
-
model,
|
|
1613
|
-
timestamp,
|
|
1614
|
-
message,
|
|
1615
|
-
error: { message }
|
|
1616
|
-
};
|
|
1617
|
-
return;
|
|
1618
|
-
}
|
|
1619
|
-
yield {
|
|
1620
|
-
type: EventType.TEXT_MESSAGE_START,
|
|
1621
|
-
messageId,
|
|
1622
|
-
role: "assistant",
|
|
1623
|
-
model,
|
|
1624
|
-
timestamp
|
|
1625
|
-
};
|
|
1626
|
-
yield {
|
|
1627
|
-
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
1628
|
-
messageId,
|
|
1629
|
-
delta: result.rawText,
|
|
1630
|
-
model,
|
|
1631
|
-
timestamp
|
|
1632
|
-
};
|
|
1633
|
-
yield {
|
|
1634
|
-
type: EventType.TEXT_MESSAGE_END,
|
|
1635
|
-
messageId,
|
|
1636
|
-
model,
|
|
1637
|
-
timestamp
|
|
1638
|
-
};
|
|
1639
|
-
yield {
|
|
1640
|
-
type: EventType.CUSTOM,
|
|
1641
|
-
name: "structured-output.complete",
|
|
1642
|
-
value: { object: result.data, raw: result.rawText },
|
|
1643
|
-
model,
|
|
1644
|
-
timestamp
|
|
1645
|
-
};
|
|
1646
|
-
yield {
|
|
1647
|
-
type: EventType.RUN_FINISHED,
|
|
1648
|
-
runId,
|
|
1649
|
-
threadId,
|
|
1650
|
-
model,
|
|
1651
|
-
timestamp,
|
|
1652
|
-
finishReason: "stop",
|
|
1653
|
-
// Forward adapter-reported token usage so consumers reading
|
|
1654
|
-
// `RUN_FINISHED.usage` (and the engine's `runOnUsage` middleware hook) see
|
|
1655
|
-
// it on the fallback path, mirroring the native streaming path. The
|
|
1656
|
-
// conditional spread avoids emitting `usage: undefined` for adapters that
|
|
1657
|
-
// don't report it. See #758.
|
|
1658
|
-
...result.usage ? { usage: result.usage } : {}
|
|
1659
|
-
};
|
|
1660
|
-
}
|
|
1661
|
-
function runStreamingStructuredOutput(options) {
|
|
1662
|
-
const { outputSchema } = options;
|
|
1663
|
-
if (!outputSchema) {
|
|
1664
|
-
throw new Error("outputSchema is required for streaming structured output");
|
|
1665
|
-
}
|
|
1666
|
-
const { jsonSchema, nullWideningMap } = convertSchemaForStructuredOutput(outputSchema);
|
|
1667
|
-
if (!jsonSchema) {
|
|
1668
|
-
throw new Error("Failed to convert output schema to JSON Schema");
|
|
1669
|
-
}
|
|
1670
|
-
const normalize = (data) => undoNullWidening(data, nullWideningMap);
|
|
1671
|
-
return runStreamingStructuredOutputImpl(
|
|
1672
|
-
options,
|
|
1673
|
-
jsonSchema,
|
|
1674
|
-
normalize
|
|
1675
|
-
);
|
|
1676
|
-
}
|
|
1677
|
-
async function* runStreamingStructuredOutputImpl(options, jsonSchema, normalize) {
|
|
1678
|
-
const {
|
|
1679
|
-
adapter,
|
|
1680
|
-
outputSchema,
|
|
1681
|
-
middleware,
|
|
1682
|
-
context,
|
|
1683
|
-
debug,
|
|
1684
|
-
mcp,
|
|
1685
|
-
...textOptions
|
|
1686
|
-
} = options;
|
|
1687
|
-
const model = adapter.model;
|
|
1688
|
-
const logger = resolveDebugOption(debug);
|
|
1689
|
-
const nativeCombined = adapter.supportsCombinedToolsAndSchema?.(options.modelOptions) === true;
|
|
1690
|
-
const mcpManager = MCPManager.from(mcp);
|
|
1691
|
-
const mcpTools = await mcpManager.discover();
|
|
1692
|
-
if (mcpTools.length > 0) {
|
|
1693
|
-
textOptions.tools = [...textOptions.tools ?? [], ...mcpTools];
|
|
1694
|
-
}
|
|
1695
|
-
const engine = new TextEngine(
|
|
1696
|
-
{
|
|
1697
|
-
adapter,
|
|
1698
|
-
params: { ...textOptions, model, logger },
|
|
1699
|
-
middleware,
|
|
1700
|
-
context,
|
|
1701
|
-
finalStructuredOutput: {
|
|
1702
|
-
jsonSchema,
|
|
1703
|
-
yieldChunks: true,
|
|
1704
|
-
normalize,
|
|
1705
|
-
...nativeCombined ? { nativeCombined: true } : {}
|
|
1706
|
-
}
|
|
1707
|
-
},
|
|
1708
|
-
logger
|
|
1709
|
-
);
|
|
1710
|
-
try {
|
|
1711
|
-
for await (const chunk of engine.run()) {
|
|
1712
|
-
yield chunk;
|
|
1713
|
-
}
|
|
1714
|
-
} finally {
|
|
1715
|
-
await mcpManager.dispose();
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
export {
|
|
1719
|
-
chat,
|
|
1720
|
-
createChatOptions,
|
|
1721
|
-
kind
|
|
1722
|
-
};
|
|
1723
|
-
//# sourceMappingURL=index.js.map
|