@statelyai/agent 2.0.0-alpha.28 → 2.0.0-alpha.30
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/ai-sdk.cjs +150 -27
- package/dist/ai-sdk.d.cts +59 -36
- package/dist/ai-sdk.d.mts +59 -36
- package/dist/ai-sdk.mjs +148 -24
- package/dist/{decision-RQ0iv0oZ.mjs → decision-BHkvuImD.mjs} +74 -8
- package/dist/{decision-CmM8-HHz.cjs → decision-CIb7ddIv.cjs} +113 -30
- package/dist/{text-logic-f5F5sCdc.cjs → event-log-BZRfBRd7.cjs} +814 -7
- package/dist/{text-logic-CIqHAmBi.mjs → event-log-DJCMHek_.mjs} +680 -4
- package/dist/{event-log-store-BIPOz-di.d.mts → event-log-store-CIIJohjj.d.mts} +90 -5
- package/dist/{event-log-store-2xzFmgr2.d.cts → event-log-store-CLAC9q2Y.d.cts} +90 -5
- package/dist/event-log-store-DZR4IIWe.cjs +95 -0
- package/dist/event-log-store-DsP6GCRh.mjs +84 -0
- package/dist/index.cjs +576 -58
- package/dist/index.d.cts +104 -510
- package/dist/index.d.mts +104 -510
- package/dist/index.mjs +547 -32
- package/dist/log.cjs +77 -20
- package/dist/log.d.cts +5 -5
- package/dist/log.d.mts +5 -5
- package/dist/log.mjs +58 -3
- package/dist/machines.cjs +1 -1
- package/dist/machines.d.cts +15 -15
- package/dist/machines.d.mts +15 -15
- package/dist/machines.mjs +1 -1
- package/dist/model-source-BCKg4qJ1.cjs +44 -0
- package/dist/model-source-BaHwJ1wK.mjs +39 -0
- package/dist/openai.cjs +11 -11
- package/dist/openai.d.cts +28 -26
- package/dist/openai.d.mts +28 -26
- package/dist/openai.mjs +3 -3
- package/dist/otel.cjs +5 -4
- package/dist/otel.d.cts +5 -7
- package/dist/otel.d.mts +5 -7
- package/dist/otel.mjs +5 -3
- package/dist/{run-agent-xNTxpt82.mjs → run-agent-BpHqkaZw.mjs} +181 -44
- package/dist/{run-agent-COXXc0RW.d.mts → run-agent-CAVt5R-r.d.mts} +95 -30
- package/dist/{run-agent-ByO-Bxyd.d.cts → run-agent-CTlMc3Jb.d.cts} +95 -30
- package/dist/{run-agent-Dmf8GV2d.cjs → run-agent-ctLCvNgL.cjs} +230 -88
- package/dist/{setup-agent-BuanDXeh.mjs → setup-agent-7rV_cujV.mjs} +15 -9
- package/dist/setup-agent-CGELn1qZ.d.mts +577 -0
- package/dist/setup-agent-CLPCkXsu.d.cts +577 -0
- package/dist/{setup-agent-DV4cTou-.cjs → setup-agent-DoUk7EdC.cjs} +31 -20
- package/dist/tanstack-ai.cjs +627 -0
- package/dist/tanstack-ai.d.cts +204 -0
- package/dist/tanstack-ai.d.mts +204 -0
- package/dist/tanstack-ai.mjs +618 -0
- package/dist/testing.cjs +204 -985
- package/dist/testing.d.cts +132 -347
- package/dist/testing.d.mts +132 -347
- package/dist/testing.mjs +191 -973
- package/dist/{text-logic-IjfYy9DP.d.cts → text-logic-BRYOPWvY.d.mts} +52 -12
- package/dist/{text-logic-D-czVPYp.d.mts → text-logic-DmciuVUQ.d.cts} +52 -12
- package/dist/{types-BgNbNKnJ.d.mts → types-Dp5_Ru4R.d.cts} +2 -2
- package/dist/{types-hR5f5YXa.d.cts → types-Dp5_Ru4R.d.mts} +2 -2
- package/dist/usage-BkM3z02Y.d.mts +17 -0
- package/dist/usage-Cm_ovz9V.d.cts +17 -0
- package/dist/{utils-_q7F-AZu.d.mts → utils-BnXdWih3.d.mts} +1 -2
- package/dist/{utils-o4YRdHJX.d.cts → utils-ebVpCYoF.d.cts} +1 -2
- package/dist/validate.cjs +239 -8
- package/dist/validate.d.cts +110 -3
- package/dist/validate.d.mts +110 -3
- package/dist/validate.mjs +238 -7
- package/dist/verify-COFWyprJ.mjs +1181 -0
- package/dist/verify-CVlsTZ-V.cjs +1222 -0
- package/dist/verify-Cn86rT8l.d.mts +414 -0
- package/dist/verify-Dzwd4Mvk.d.cts +414 -0
- package/package.json +44 -31
- package/readme.md +40 -28
- package/skills/add-evals/SKILL.md +129 -0
- package/skills/add-traces/SKILL.md +135 -0
- package/skills/generate-machine/SKILL.md +143 -69
- package/skills/graph-engineering/SKILL.md +33 -0
- package/skills/improve-machine/SKILL.md +241 -0
- package/skills/improve-machine/reference.md +101 -0
- package/skills/loop-to-machine/SKILL.md +183 -0
- package/skills/loop-to-machine/policy.md +158 -0
- package/dist/event-log-store-2pAFiWuc.mjs +0 -576
- package/dist/event-log-store-C-COhIXP.cjs +0 -702
- package/dist/usage-BdEjFuIi.mjs +0 -5
- package/dist/usage-efOQPdf_.cjs +0 -10
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_event_log = require("./event-log-BZRfBRd7.cjs");
|
|
3
|
+
const require_decision = require("./decision-CIb7ddIv.cjs");
|
|
4
|
+
const require_model_source = require("./model-source-BCKg4qJ1.cjs");
|
|
5
|
+
let _tanstack_ai = require("@tanstack/ai");
|
|
6
|
+
//#region src/tanstack-ai/index.ts
|
|
7
|
+
/**
|
|
8
|
+
* The TanStack AI adapter: builds the `{ generateText, streamText, decide }`
|
|
9
|
+
* executor set consumed by `createAgentRuntime`/`executeAgentRequest`, mapped
|
|
10
|
+
* onto TanStack AI's `chat()` activity and any text adapter
|
|
11
|
+
* (`openaiText(...)`, `anthropicText(...)`, `geminiText(...)`, …).
|
|
12
|
+
*
|
|
13
|
+
* `@tanstack/ai` is an OPTIONAL peer dependency, imported only here. No
|
|
14
|
+
* provider package (`@tanstack/ai-openai`, …) is a dependency: the host passes
|
|
15
|
+
* its own text adapters. Compare `@statelyai/agent/ai-sdk`, the same contract
|
|
16
|
+
* over the Vercel AI SDK.
|
|
17
|
+
*/
|
|
18
|
+
/** Thrown for a message part TanStack AI's model messages cannot carry —
|
|
19
|
+
* dropping it would silently change what the model sees. */
|
|
20
|
+
function unsupportedPart(role, type) {
|
|
21
|
+
throw new Error(`createTanStackAiExecutors: a ${role} message part of type '${type}' has no TanStack AI equivalent. Send text, image, tool-call, or tool-result parts, or convert it before the request.`);
|
|
22
|
+
}
|
|
23
|
+
/** A `DataContent`/`URL` image as a TanStack AI content source: an http(s)
|
|
24
|
+
* string or `URL` is a `url` source; a `data:` URL, a bare base64 string, and
|
|
25
|
+
* raw bytes are a base64 `data` source. */
|
|
26
|
+
function toImagePart(image, mediaType) {
|
|
27
|
+
const mimeType = mediaType ?? "image/jpeg";
|
|
28
|
+
if (image instanceof URL || typeof image === "string" && /^https?:/.test(image)) return {
|
|
29
|
+
type: "image",
|
|
30
|
+
source: {
|
|
31
|
+
type: "url",
|
|
32
|
+
value: String(image)
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
if (typeof image === "string") {
|
|
36
|
+
const dataUrl = /^data:([^;,]+)?(?:;base64)?,(.*)$/.exec(image);
|
|
37
|
+
return {
|
|
38
|
+
type: "image",
|
|
39
|
+
source: {
|
|
40
|
+
type: "data",
|
|
41
|
+
value: dataUrl ? dataUrl[2] : image,
|
|
42
|
+
mimeType: dataUrl?.[1] ?? mimeType
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const bytes = image instanceof Uint8Array ? image : new Uint8Array(image);
|
|
47
|
+
const encode = globalThis.btoa;
|
|
48
|
+
if (!encode) throw new Error("createTanStackAiExecutors: binary image parts need a global `btoa` to base64-encode. Pass the image as a URL or a base64 string instead.");
|
|
49
|
+
let binary = "";
|
|
50
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
51
|
+
return {
|
|
52
|
+
type: "image",
|
|
53
|
+
source: {
|
|
54
|
+
type: "data",
|
|
55
|
+
value: encode(binary),
|
|
56
|
+
mimeType
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** One tool message per `ToolResultPart` — the tool role is one message per result. */
|
|
61
|
+
function toToolMessage(part) {
|
|
62
|
+
return {
|
|
63
|
+
role: "tool",
|
|
64
|
+
toolCallId: part.toolCallId,
|
|
65
|
+
content: part.output.type === "text" || part.output.type === "error-text" ? part.output.value : JSON.stringify(part.output.value)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Maps `AgentTextRequest.messages`/`system`/`prompt` onto `chat()`'s
|
|
70
|
+
* `systemPrompts` and `messages`. TanStack AI's model messages have no
|
|
71
|
+
* `system` role — a provider adapter may forward one as a user turn — so every
|
|
72
|
+
* system message, and the request's `system`, becomes a system prompt.
|
|
73
|
+
*/
|
|
74
|
+
function toTanStackAiMessages(request) {
|
|
75
|
+
const systemPrompts = request.system ? [request.system] : [];
|
|
76
|
+
if (!request.messages) return {
|
|
77
|
+
systemPrompts,
|
|
78
|
+
messages: [{
|
|
79
|
+
role: "user",
|
|
80
|
+
content: request.prompt ?? ""
|
|
81
|
+
}]
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
systemPrompts,
|
|
85
|
+
messages: request.messages.flatMap((message) => {
|
|
86
|
+
switch (message.role) {
|
|
87
|
+
case "system":
|
|
88
|
+
systemPrompts.push(message.content);
|
|
89
|
+
return [];
|
|
90
|
+
case "user":
|
|
91
|
+
if (typeof message.content === "string") return [{
|
|
92
|
+
role: "user",
|
|
93
|
+
content: message.content
|
|
94
|
+
}];
|
|
95
|
+
return [{
|
|
96
|
+
role: "user",
|
|
97
|
+
content: message.content.map((part) => {
|
|
98
|
+
switch (part.type) {
|
|
99
|
+
case "text": return {
|
|
100
|
+
type: "text",
|
|
101
|
+
content: part.text
|
|
102
|
+
};
|
|
103
|
+
case "image": return toImagePart(part.image, part.mediaType);
|
|
104
|
+
default: return unsupportedPart("user", part.type);
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
}];
|
|
108
|
+
case "assistant": {
|
|
109
|
+
if (typeof message.content === "string") return [{
|
|
110
|
+
role: "assistant",
|
|
111
|
+
content: message.content
|
|
112
|
+
}];
|
|
113
|
+
let text = "";
|
|
114
|
+
const toolCalls = [];
|
|
115
|
+
const trailing = [];
|
|
116
|
+
for (const part of message.content) switch (part.type) {
|
|
117
|
+
case "text":
|
|
118
|
+
text += part.text;
|
|
119
|
+
break;
|
|
120
|
+
case "tool-call":
|
|
121
|
+
toolCalls.push({
|
|
122
|
+
id: part.toolCallId,
|
|
123
|
+
type: "function",
|
|
124
|
+
function: {
|
|
125
|
+
name: part.toolName,
|
|
126
|
+
arguments: JSON.stringify(part.input ?? {})
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
break;
|
|
130
|
+
case "tool-result":
|
|
131
|
+
trailing.push(toToolMessage(part));
|
|
132
|
+
break;
|
|
133
|
+
default: unsupportedPart("assistant", part.type);
|
|
134
|
+
}
|
|
135
|
+
return [{
|
|
136
|
+
role: "assistant",
|
|
137
|
+
content: text || null,
|
|
138
|
+
...toolCalls.length > 0 ? { toolCalls } : {}
|
|
139
|
+
}, ...trailing];
|
|
140
|
+
}
|
|
141
|
+
case "tool": return message.content.map(toToolMessage);
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/** A tool `description` may be a string or, for an AI SDK v7 tool, a function
|
|
147
|
+
* of the call's context. TanStack AI tools carry the static form. */
|
|
148
|
+
function staticDescription(descriptor) {
|
|
149
|
+
if (typeof descriptor === "function") return "";
|
|
150
|
+
return typeof descriptor.description === "string" ? descriptor.description : "";
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* One TanStack AI tool per `AgentTools` entry. A tool with an `execute` (or a
|
|
154
|
+
* bare function) is a server tool `chat()` runs in its loop; a tool without one
|
|
155
|
+
* is a client tool, so the run stops at its call and hands the call back.
|
|
156
|
+
* Input schemas travel as JSON Schema.
|
|
157
|
+
*/
|
|
158
|
+
function toTanStackAiTools(tools) {
|
|
159
|
+
return Object.entries(tools).flatMap(([name, descriptor]) => {
|
|
160
|
+
if (!descriptor) return [];
|
|
161
|
+
const inputSchema = typeof descriptor === "function" ? void 0 : descriptor.inputSchema;
|
|
162
|
+
const execute = typeof descriptor === "function" ? descriptor : descriptor.execute;
|
|
163
|
+
return [{
|
|
164
|
+
name,
|
|
165
|
+
description: staticDescription(descriptor),
|
|
166
|
+
inputSchema: (require_event_log.isStandardSchema(inputSchema) ? require_event_log.getJsonSchemaSync(inputSchema) : void 0) ?? { type: "object" },
|
|
167
|
+
...typeof execute === "function" ? { execute: (input) => execute(input) } : {}
|
|
168
|
+
}];
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/** One client tool per candidate decision event: no `execute`, so the run
|
|
172
|
+
* stops at the model's first call — the "tool-per-event" recipe. */
|
|
173
|
+
function toTanStackAiEventTools(events) {
|
|
174
|
+
return events.map((event) => ({
|
|
175
|
+
name: event.toolName,
|
|
176
|
+
description: `Choose the '${event.type}' move.`,
|
|
177
|
+
inputSchema: require_event_log.getJsonSchemaSync(event.inputSchema) ?? { type: "object" }
|
|
178
|
+
}));
|
|
179
|
+
}
|
|
180
|
+
/** Drops keys whose value is `undefined`, so a spread cannot erase what it lands on. */
|
|
181
|
+
function defined(settings) {
|
|
182
|
+
return Object.fromEntries(Object.entries(settings).filter(([, value]) => value !== void 0));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Maps the request's portable generation settings and tool choice onto the
|
|
186
|
+
* `modelOptions` of the adapter's provider. TanStack AI has no portable
|
|
187
|
+
* sampling or tool-choice options — each provider adapter names its own — so
|
|
188
|
+
* the mapping is per provider (`adapter.name`):
|
|
189
|
+
*
|
|
190
|
+
* - `openai` (Responses API): `temperature`, `top_p`, `max_output_tokens`,
|
|
191
|
+
* `tool_choice`. The API has no `top_k`, `seed`, or `stop`; those are dropped.
|
|
192
|
+
* - `anthropic`: `temperature`, `top_p`, `top_k`, `max_tokens`,
|
|
193
|
+
* `stop_sequences`, `tool_choice`. `seed` is dropped.
|
|
194
|
+
* - `gemini`: `temperature`, `topP`, `topK`, `maxOutputTokens`, `seed`,
|
|
195
|
+
* `stopSequences`, `toolConfig.functionCallingConfig`.
|
|
196
|
+
*
|
|
197
|
+
* Any other provider gets none of them: map them yourself through the
|
|
198
|
+
* `settings` option, which receives the request.
|
|
199
|
+
*/
|
|
200
|
+
function toTanStackAiModelOptions(adapter, request) {
|
|
201
|
+
const { toolChoice } = request;
|
|
202
|
+
switch (adapter.name) {
|
|
203
|
+
case "openai": return defined({
|
|
204
|
+
temperature: request.temperature,
|
|
205
|
+
top_p: request.topP,
|
|
206
|
+
max_output_tokens: request.maxOutputTokens,
|
|
207
|
+
tool_choice: typeof toolChoice === "object" ? {
|
|
208
|
+
type: "function",
|
|
209
|
+
name: toolChoice.name
|
|
210
|
+
} : toolChoice
|
|
211
|
+
});
|
|
212
|
+
case "anthropic": return defined({
|
|
213
|
+
temperature: request.temperature,
|
|
214
|
+
top_p: request.topP,
|
|
215
|
+
top_k: request.topK,
|
|
216
|
+
max_tokens: request.maxOutputTokens,
|
|
217
|
+
stop_sequences: request.stopSequences,
|
|
218
|
+
tool_choice: typeof toolChoice === "object" ? {
|
|
219
|
+
type: "tool",
|
|
220
|
+
name: toolChoice.name
|
|
221
|
+
} : toolChoice === "required" ? { type: "any" } : toolChoice === void 0 ? void 0 : { type: toolChoice }
|
|
222
|
+
});
|
|
223
|
+
case "gemini": return defined({
|
|
224
|
+
temperature: request.temperature,
|
|
225
|
+
topP: request.topP,
|
|
226
|
+
topK: request.topK,
|
|
227
|
+
maxOutputTokens: request.maxOutputTokens,
|
|
228
|
+
seed: request.seed,
|
|
229
|
+
stopSequences: request.stopSequences,
|
|
230
|
+
toolConfig: toolChoice === void 0 ? void 0 : { functionCallingConfig: typeof toolChoice === "object" ? {
|
|
231
|
+
mode: "ANY",
|
|
232
|
+
allowedFunctionNames: [toolChoice.name]
|
|
233
|
+
} : { mode: toolChoice === "required" ? "ANY" : toolChoice.toUpperCase() } }
|
|
234
|
+
});
|
|
235
|
+
default: return {};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Messages for a decision request, with prior failed `attempts` rendered as
|
|
240
|
+
* appended user messages so retries converge. Mirrors the AI SDK adapter's
|
|
241
|
+
* `toDecisionMessages`.
|
|
242
|
+
*/
|
|
243
|
+
function toDecisionMessages(request) {
|
|
244
|
+
const mapped = toTanStackAiMessages(request);
|
|
245
|
+
for (const attempt of require_decision.renderDecisionAttempts(request)) mapped.messages.push({
|
|
246
|
+
role: "user",
|
|
247
|
+
content: attempt.content
|
|
248
|
+
});
|
|
249
|
+
return mapped;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Folds TanStack AI's `TokenUsage` onto the flat {@link AgentCallUsage} field
|
|
253
|
+
* names core aggregates. A field the provider omitted stays omitted, so it
|
|
254
|
+
* never contributes a `0` to a run's partial sums.
|
|
255
|
+
*/
|
|
256
|
+
function toAgentCallUsage(usage) {
|
|
257
|
+
if (!usage) return;
|
|
258
|
+
const reasoningTokens = usage.completionTokensDetails?.reasoningTokens;
|
|
259
|
+
const cachedInputTokens = usage.promptTokensDetails?.cachedTokens;
|
|
260
|
+
return defined({
|
|
261
|
+
inputTokens: usage.promptTokens,
|
|
262
|
+
outputTokens: usage.completionTokens,
|
|
263
|
+
totalTokens: usage.totalTokens,
|
|
264
|
+
reasoningTokens,
|
|
265
|
+
cachedInputTokens
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
/** Adds two model turns' usage together, field by field. A field neither
|
|
269
|
+
* reported stays absent. */
|
|
270
|
+
function addUsage(total, next) {
|
|
271
|
+
if (!total || !next) return total ?? next;
|
|
272
|
+
const sum = {};
|
|
273
|
+
for (const field of require_event_log.AGENT_USAGE_TOKEN_FIELDS) if (total[field] !== void 0 || next[field] !== void 0) sum[field] = (total[field] ?? 0) + (next[field] ?? 0);
|
|
274
|
+
return sum;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Maps a TanStack AI finish reason onto the portable {@link AgentFinishReason}.
|
|
278
|
+
* Anything unrecognized, including a missing reason, lands on `'other'`.
|
|
279
|
+
*/
|
|
280
|
+
function toAgentFinishReason(finishReason) {
|
|
281
|
+
switch (finishReason) {
|
|
282
|
+
case "stop": return "stop";
|
|
283
|
+
case "length": return "length";
|
|
284
|
+
case "tool_calls": return "tool-calls";
|
|
285
|
+
case "content_filter": return "content-filter";
|
|
286
|
+
default: return "other";
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function toToolResultOutput(content) {
|
|
290
|
+
if (typeof content !== "string") return {
|
|
291
|
+
type: "json",
|
|
292
|
+
value: content
|
|
293
|
+
};
|
|
294
|
+
try {
|
|
295
|
+
return {
|
|
296
|
+
type: "json",
|
|
297
|
+
value: JSON.parse(content)
|
|
298
|
+
};
|
|
299
|
+
} catch {
|
|
300
|
+
return {
|
|
301
|
+
type: "text",
|
|
302
|
+
value: content
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/** A `RUN_ERROR` chunk as the `Error` it reports, `code` included. */
|
|
307
|
+
var TanStackAiRunError = class extends Error {
|
|
308
|
+
code;
|
|
309
|
+
constructor(message, code) {
|
|
310
|
+
super(message);
|
|
311
|
+
this.name = "TanStackAiRunError";
|
|
312
|
+
this.code = code;
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* Reads a `chat()` stream to the end. Text arrives as `TEXT_MESSAGE_CONTENT`
|
|
317
|
+
* deltas; every model turn ends in its own `RUN_FINISHED`, so a tool loop's
|
|
318
|
+
* usage is summed and the last finish reason wins. A provider failure arrives
|
|
319
|
+
* as a `RUN_ERROR` chunk rather than a throw, and is thrown here.
|
|
320
|
+
*/
|
|
321
|
+
async function readRun(stream, onDelta) {
|
|
322
|
+
const summary = {
|
|
323
|
+
text: "",
|
|
324
|
+
structured: void 0,
|
|
325
|
+
usage: void 0,
|
|
326
|
+
finishReason: "other",
|
|
327
|
+
toolCalls: [],
|
|
328
|
+
messages: [],
|
|
329
|
+
chunks: []
|
|
330
|
+
};
|
|
331
|
+
const args = /* @__PURE__ */ new Map();
|
|
332
|
+
let assistant;
|
|
333
|
+
let results;
|
|
334
|
+
const flushAssistant = () => {
|
|
335
|
+
if (assistant?.length) summary.messages.push({
|
|
336
|
+
role: "assistant",
|
|
337
|
+
content: assistant
|
|
338
|
+
});
|
|
339
|
+
assistant = void 0;
|
|
340
|
+
};
|
|
341
|
+
const flushResults = () => {
|
|
342
|
+
if (results?.length) summary.messages.push({
|
|
343
|
+
role: "tool",
|
|
344
|
+
content: results
|
|
345
|
+
});
|
|
346
|
+
results = void 0;
|
|
347
|
+
};
|
|
348
|
+
const assistantParts = () => {
|
|
349
|
+
flushResults();
|
|
350
|
+
return assistant ??= [];
|
|
351
|
+
};
|
|
352
|
+
for await (const chunk of stream) {
|
|
353
|
+
summary.chunks.push(chunk);
|
|
354
|
+
switch (chunk.type) {
|
|
355
|
+
case "TEXT_MESSAGE_CONTENT": {
|
|
356
|
+
summary.text += chunk.delta;
|
|
357
|
+
onDelta?.(chunk.delta);
|
|
358
|
+
const parts = assistantParts();
|
|
359
|
+
const last = parts.at(-1);
|
|
360
|
+
if (last?.type === "text") last.text += chunk.delta;
|
|
361
|
+
else parts.push({
|
|
362
|
+
type: "text",
|
|
363
|
+
text: chunk.delta
|
|
364
|
+
});
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
case "TOOL_CALL_START": {
|
|
368
|
+
const part = {
|
|
369
|
+
type: "tool-call",
|
|
370
|
+
toolCallId: chunk.toolCallId,
|
|
371
|
+
toolName: chunk.toolCallName,
|
|
372
|
+
input: {}
|
|
373
|
+
};
|
|
374
|
+
assistantParts().push(part);
|
|
375
|
+
args.set(chunk.toolCallId, {
|
|
376
|
+
part,
|
|
377
|
+
json: ""
|
|
378
|
+
});
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
case "TOOL_CALL_ARGS": {
|
|
382
|
+
const call = args.get(chunk.toolCallId);
|
|
383
|
+
if (call) call.json += chunk.delta;
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
case "TOOL_CALL_RESULT":
|
|
387
|
+
flushAssistant();
|
|
388
|
+
(results ??= []).push({
|
|
389
|
+
type: "tool-result",
|
|
390
|
+
toolCallId: chunk.toolCallId,
|
|
391
|
+
toolName: args.get(chunk.toolCallId)?.part.toolName ?? "",
|
|
392
|
+
output: toToolResultOutput(chunk.content)
|
|
393
|
+
});
|
|
394
|
+
break;
|
|
395
|
+
case "CUSTOM":
|
|
396
|
+
if (chunk.name === "structured-output.complete") summary.structured = chunk.value?.object;
|
|
397
|
+
break;
|
|
398
|
+
case "RUN_FINISHED":
|
|
399
|
+
summary.finishReason = toAgentFinishReason(chunk.finishReason ?? (chunk.metadata?.tanstack)?.finishReason);
|
|
400
|
+
if (chunk.usage && !Array.isArray(chunk.usage)) summary.usage = addUsage(summary.usage, toAgentCallUsage(chunk.usage));
|
|
401
|
+
break;
|
|
402
|
+
case "RUN_ERROR": throw new TanStackAiRunError(chunk.message, chunk.code ?? chunk.error?.code);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
for (const { part, json } of args.values()) part.input = json ? JSON.parse(json) : {};
|
|
406
|
+
flushAssistant();
|
|
407
|
+
flushResults();
|
|
408
|
+
summary.toolCalls = [...args.values()].map(({ part }) => ({
|
|
409
|
+
toolCallId: part.toolCallId,
|
|
410
|
+
toolName: part.toolName,
|
|
411
|
+
input: part.input
|
|
412
|
+
}));
|
|
413
|
+
return summary;
|
|
414
|
+
}
|
|
415
|
+
function isAdapterWithSettings(entry) {
|
|
416
|
+
return "adapter" in entry && !("kind" in entry);
|
|
417
|
+
}
|
|
418
|
+
/** The text adapter inside a map entry, whichever form it takes. */
|
|
419
|
+
function toTextAdapter(entry) {
|
|
420
|
+
return isAdapterWithSettings(entry) ? entry.adapter : entry;
|
|
421
|
+
}
|
|
422
|
+
function truncated(request, info, partialOutput, cause) {
|
|
423
|
+
return new require_event_log.AgentTruncatedError(`createTanStackAiExecutors: request '${request.name ?? "(unnamed)"}' hit the output token limit — its structured output never completed. Raise \`maxOutputTokens\`, shorten the input, or ask for a smaller output schema.`, {
|
|
424
|
+
requestName: request.name ?? "(unnamed)",
|
|
425
|
+
...info?.requestId !== void 0 ? { requestId: info.requestId } : {},
|
|
426
|
+
...partialOutput !== void 0 ? { partialOutput } : {},
|
|
427
|
+
...cause !== void 0 ? { cause } : {}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
function isTruncationError(error) {
|
|
431
|
+
const { code, message } = error ?? {};
|
|
432
|
+
return code === "max_tokens" || code === "incomplete" || message === "max_output_tokens";
|
|
433
|
+
}
|
|
434
|
+
function usageField(usage) {
|
|
435
|
+
return usage ? { usage } : {};
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Runs one `chat()` call to the end. `chat()` takes an `AbortController`, not
|
|
439
|
+
* a signal, so the runtime's signal is bridged to one — and unhooked once the
|
|
440
|
+
* run settles, so a long-lived signal does not collect a listener per request.
|
|
441
|
+
* An aborted `chat()` stream just ends, so the abort is surfaced as the throw
|
|
442
|
+
* it is.
|
|
443
|
+
*/
|
|
444
|
+
async function runChat(options, signal, onDelta) {
|
|
445
|
+
const controller = new AbortController();
|
|
446
|
+
const abort = () => controller.abort(signal?.reason);
|
|
447
|
+
if (signal?.aborted) abort();
|
|
448
|
+
else signal?.addEventListener("abort", abort, { once: true });
|
|
449
|
+
let run;
|
|
450
|
+
try {
|
|
451
|
+
run = await readRun((0, _tanstack_ai.chat)({
|
|
452
|
+
...options,
|
|
453
|
+
abortController: controller
|
|
454
|
+
}), onDelta);
|
|
455
|
+
} finally {
|
|
456
|
+
signal?.removeEventListener("abort", abort);
|
|
457
|
+
}
|
|
458
|
+
if (signal?.aborted) throw signal.reason ?? /* @__PURE__ */ new Error("createTanStackAiExecutors: the request was aborted.");
|
|
459
|
+
return run;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* A request's tool choice belongs to its first model turn. `chat()` reuses
|
|
463
|
+
* `modelOptions` for every turn of its tool loop, so a forced choice left in
|
|
464
|
+
* place would force another tool call after each result, and the request could
|
|
465
|
+
* never answer. From the second turn on, the choice's keys go back to what the
|
|
466
|
+
* host set, or away.
|
|
467
|
+
*/
|
|
468
|
+
function toolChoiceOnFirstTurn(keys, later) {
|
|
469
|
+
return {
|
|
470
|
+
name: "statelyai:tool-choice-first-turn",
|
|
471
|
+
onConfig: (ctx, config) => {
|
|
472
|
+
if (ctx.iteration === 0) return;
|
|
473
|
+
const modelOptions = { ...config.modelOptions };
|
|
474
|
+
for (const key of keys) if (key in later) modelOptions[key] = later[key];
|
|
475
|
+
else delete modelOptions[key];
|
|
476
|
+
return { modelOptions };
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function createTanStackAiExecutors(source) {
|
|
481
|
+
const options = require_model_source.resolveModelSource("createTanStackAiExecutors", source);
|
|
482
|
+
const resolve = (request, portable) => {
|
|
483
|
+
let adapter;
|
|
484
|
+
let entrySettings;
|
|
485
|
+
if (options.resolveModel) adapter = options.resolveModel(request.model);
|
|
486
|
+
else {
|
|
487
|
+
const entry = options.models?.[request.model];
|
|
488
|
+
if (!options.models) throw new Error(`createTanStackAiExecutors: no model resolver configured for '${request.model}'.`);
|
|
489
|
+
if (!entry) throw new Error(`createTanStackAiExecutors: unknown model '${request.model}'.`);
|
|
490
|
+
adapter = toTextAdapter(entry);
|
|
491
|
+
entrySettings = isAdapterWithSettings(entry) ? entry.settings : void 0;
|
|
492
|
+
}
|
|
493
|
+
const host = (typeof options.settings === "function" ? options.settings(request) : options.settings) ?? {};
|
|
494
|
+
const merged = {
|
|
495
|
+
...host,
|
|
496
|
+
...entrySettings
|
|
497
|
+
};
|
|
498
|
+
const base = {
|
|
499
|
+
...host.modelOptions,
|
|
500
|
+
...entrySettings?.modelOptions
|
|
501
|
+
};
|
|
502
|
+
const generation = toTanStackAiModelOptions(adapter, {
|
|
503
|
+
...portable,
|
|
504
|
+
toolChoice: void 0
|
|
505
|
+
});
|
|
506
|
+
const firstTurn = toTanStackAiModelOptions(adapter, portable);
|
|
507
|
+
const choiceKeys = Object.keys(firstTurn).filter((key) => !(key in generation));
|
|
508
|
+
return {
|
|
509
|
+
adapter,
|
|
510
|
+
settings: {
|
|
511
|
+
...merged,
|
|
512
|
+
modelOptions: {
|
|
513
|
+
...base,
|
|
514
|
+
...firstTurn
|
|
515
|
+
},
|
|
516
|
+
...choiceKeys.length > 0 ? { middleware: [...merged.middleware ?? [], toolChoiceOnFirstTurn(choiceKeys, {
|
|
517
|
+
...base,
|
|
518
|
+
...generation
|
|
519
|
+
})] } : {}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
};
|
|
523
|
+
const loopBound = (request) => (0, _tanstack_ai.maxIterations)(typeof request.maxSteps === "number" && Number.isFinite(request.maxSteps) ? Math.max(1, Math.floor(request.maxSteps)) : 1);
|
|
524
|
+
const generateText = async (request, info) => {
|
|
525
|
+
const { adapter, settings } = resolve(request, request);
|
|
526
|
+
const tools = toTanStackAiTools(request.tools);
|
|
527
|
+
const structured = require_event_log.getAgentOutputMode(request.outputSchema) === "structured";
|
|
528
|
+
const outputSchema = structured ? await require_event_log.getJsonSchema(require_event_log.providerOutputSchema(request.outputSchema, { reasoning: request.includeReasoning })) : void 0;
|
|
529
|
+
let run;
|
|
530
|
+
try {
|
|
531
|
+
run = await runChat({
|
|
532
|
+
...settings,
|
|
533
|
+
adapter,
|
|
534
|
+
...toTanStackAiMessages(request),
|
|
535
|
+
...tools.length > 0 ? { tools } : {},
|
|
536
|
+
...outputSchema ? {
|
|
537
|
+
outputSchema,
|
|
538
|
+
stream: true
|
|
539
|
+
} : {},
|
|
540
|
+
agentLoopStrategy: loopBound(request)
|
|
541
|
+
}, info?.signal);
|
|
542
|
+
} catch (error) {
|
|
543
|
+
throw structured && isTruncationError(error) ? truncated(request, info, void 0, error) : error;
|
|
544
|
+
}
|
|
545
|
+
if (outputSchema) {
|
|
546
|
+
if (run.finishReason === "length") throw truncated(request, info, run.structured ?? run.text);
|
|
547
|
+
const parsed = require_event_log.parseProviderOutput(request, run.structured !== void 0 ? run.structured : JSON.parse(run.text));
|
|
548
|
+
return {
|
|
549
|
+
result: parsed.result,
|
|
550
|
+
...typeof parsed.reasoning === "string" ? { reasoning: parsed.reasoning } : {},
|
|
551
|
+
...usageField(run.usage),
|
|
552
|
+
finishReason: run.finishReason,
|
|
553
|
+
toolCalls: run.toolCalls,
|
|
554
|
+
messages: run.messages,
|
|
555
|
+
raw: run.chunks
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
return {
|
|
559
|
+
result: run.text,
|
|
560
|
+
...usageField(run.usage),
|
|
561
|
+
finishReason: run.finishReason,
|
|
562
|
+
toolCalls: run.toolCalls,
|
|
563
|
+
messages: run.messages,
|
|
564
|
+
raw: run.chunks
|
|
565
|
+
};
|
|
566
|
+
};
|
|
567
|
+
const streamText = async (request, info) => {
|
|
568
|
+
if (require_event_log.getAgentOutputMode(request.outputSchema) === "structured") throw new Error("createTanStackAiExecutors: streamText streams text — a request declaring a structured output schema must be routed to generateText.");
|
|
569
|
+
const { adapter, settings } = resolve(request, request);
|
|
570
|
+
const tools = toTanStackAiTools(request.tools ?? {});
|
|
571
|
+
const run = await runChat({
|
|
572
|
+
...settings,
|
|
573
|
+
adapter,
|
|
574
|
+
...toTanStackAiMessages(request),
|
|
575
|
+
...tools.length > 0 ? { tools } : {},
|
|
576
|
+
agentLoopStrategy: loopBound(request)
|
|
577
|
+
}, info?.signal, (delta) => info?.onChunk?.(delta));
|
|
578
|
+
return {
|
|
579
|
+
result: run.text,
|
|
580
|
+
...usageField(run.usage),
|
|
581
|
+
finishReason: run.finishReason,
|
|
582
|
+
messages: run.messages,
|
|
583
|
+
raw: run.chunks
|
|
584
|
+
};
|
|
585
|
+
};
|
|
586
|
+
const decide = async (request, info) => {
|
|
587
|
+
const { adapter, settings } = resolve(request, {
|
|
588
|
+
...request,
|
|
589
|
+
toolChoice: "required"
|
|
590
|
+
});
|
|
591
|
+
const run = await runChat({
|
|
592
|
+
...settings,
|
|
593
|
+
adapter,
|
|
594
|
+
...toDecisionMessages(request),
|
|
595
|
+
tools: toTanStackAiEventTools(request.events),
|
|
596
|
+
agentLoopStrategy: (0, _tanstack_ai.maxIterations)(1)
|
|
597
|
+
}, info?.signal);
|
|
598
|
+
const toolCall = run.toolCalls[0];
|
|
599
|
+
if (!toolCall) throw new Error("createTanStackAiExecutors: decide — model did not call an event tool.");
|
|
600
|
+
const chosenEvent = request.events.find((event) => event.toolName === toolCall.toolName);
|
|
601
|
+
if (!chosenEvent) throw new Error(`createTanStackAiExecutors: decide — model called unknown tool '${toolCall.toolName}'.`);
|
|
602
|
+
return {
|
|
603
|
+
event: {
|
|
604
|
+
...toolCall.input && typeof toolCall.input === "object" ? toolCall.input : {},
|
|
605
|
+
type: chosenEvent.type
|
|
606
|
+
},
|
|
607
|
+
...usageField(run.usage),
|
|
608
|
+
finishReason: run.finishReason,
|
|
609
|
+
raw: run.chunks
|
|
610
|
+
};
|
|
611
|
+
};
|
|
612
|
+
return {
|
|
613
|
+
generateText,
|
|
614
|
+
streamText,
|
|
615
|
+
decide
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
//#endregion
|
|
619
|
+
exports.createTanStackAiExecutors = createTanStackAiExecutors;
|
|
620
|
+
exports.toAgentCallUsage = toAgentCallUsage;
|
|
621
|
+
exports.toAgentFinishReason = toAgentFinishReason;
|
|
622
|
+
exports.toDecisionMessages = toDecisionMessages;
|
|
623
|
+
exports.toTanStackAiEventTools = toTanStackAiEventTools;
|
|
624
|
+
exports.toTanStackAiMessages = toTanStackAiMessages;
|
|
625
|
+
exports.toTanStackAiModelOptions = toTanStackAiModelOptions;
|
|
626
|
+
exports.toTanStackAiTools = toTanStackAiTools;
|
|
627
|
+
exports.toTextAdapter = toTextAdapter;
|