@statelyai/agent 2.0.0-alpha.22 → 2.0.0-alpha.23
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 +53 -25
- package/dist/ai-sdk.d.cts +20 -12
- package/dist/ai-sdk.d.mts +20 -12
- package/dist/ai-sdk.mjs +53 -25
- package/dist/{decision-DxSTqzgZ.mjs → decision-BBZ2jMO1.mjs} +444 -79
- package/dist/{decision-1o45_ZrS.cjs → decision-Cv6SfNWl.cjs} +525 -94
- package/dist/index.cjs +2224 -1032
- package/dist/index.d.cts +413 -275
- package/dist/index.d.mts +413 -275
- package/dist/index.mjs +2163 -983
- package/dist/machines.cjs +30 -2
- package/dist/machines.d.cts +1 -1
- package/dist/machines.d.mts +1 -1
- package/dist/machines.mjs +30 -2
- package/dist/openai.cjs +447 -0
- package/dist/openai.d.cts +177 -0
- package/dist/openai.d.mts +177 -0
- package/dist/openai.mjs +437 -0
- package/dist/otel.cjs +3 -6
- package/dist/otel.d.cts +1 -1
- package/dist/otel.d.mts +1 -1
- package/dist/otel.mjs +3 -6
- package/dist/run-agent-Ck7Sa3Tb.d.mts +968 -0
- package/dist/run-agent-DWqA8KiI.d.cts +968 -0
- package/dist/setup-agent-CeO2fARv.mjs +639 -0
- package/dist/setup-agent-Dl2VELLi.cjs +663 -0
- package/dist/{text-logic-BaxPrcLk.d.cts → text-logic-C7bSnH_B.d.cts} +195 -44
- package/dist/{text-logic-DhfFWzu9.d.mts → text-logic-DJBK3LbG.d.mts} +195 -44
- package/dist/{types-DSdj2tGs.d.cts → types-CjHk5OdH.d.cts} +4 -19
- package/dist/{types-CvWRGFxP.d.mts → types-DWA6X9R-.d.mts} +4 -19
- package/package.json +17 -21
- package/readme.md +32 -17
- package/dist/errors-BQRk9eiZ.d.cts +0 -19
- package/dist/errors-C9rxnWbX.d.mts +0 -19
- package/dist/errors-CeSXQx0v.mjs +0 -23
- package/dist/errors-DUBBzRLP.cjs +0 -28
- package/dist/event-log-store-Bz7HDBkE.d.cts +0 -145
- package/dist/event-log-store-DmIDosD6.mjs +0 -427
- package/dist/event-log-store-a_TKy1gk.cjs +0 -468
- package/dist/event-log-store-hrA1vqtN.d.mts +0 -145
- package/dist/run-agent-0fpEZ1wJ.d.mts +0 -1098
- package/dist/run-agent-Zbkx7XP8.d.cts +0 -1098
- package/dist/setup-agent-DkCTy5Eu.cjs +0 -1707
- package/dist/setup-agent-nSFYrNTP.mjs +0 -1587
- package/dist/sqlite.cjs +0 -129
- package/dist/sqlite.d.cts +0 -57
- package/dist/sqlite.d.mts +0 -57
- package/dist/sqlite.mjs +0 -127
package/dist/openai.mjs
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { S as getAgentOutputMode, U as getJsonSchema, W as getJsonSchemaSync, Y as isStandardSchema, f as AGENT_USAGE_TOKEN_FIELDS, k as parseStructuredEnvelope, o as renderDecisionAttempts, st as AgentTruncatedError, v as buildEnvelopeSchema } from "./decision-BBZ2jMO1.mjs";
|
|
2
|
+
//#region src/openai/index.ts
|
|
3
|
+
/** Thrown for a message part Chat Completions cannot carry — dropping it would
|
|
4
|
+
* silently change what the model sees. */
|
|
5
|
+
function unsupportedPart(role, type) {
|
|
6
|
+
throw new Error(`createOpenAiExecutors: a ${role} message part of type '${type}' has no Chat Completions equivalent. Send text, image, tool-call, or tool-result parts, or convert it before the request.`);
|
|
7
|
+
}
|
|
8
|
+
/** A `DataContent`/`URL` image as the URL string OpenAI's `image_url` part wants:
|
|
9
|
+
* an http(s) or `data:` string and a `URL` pass through, a bare base64 string and
|
|
10
|
+
* raw bytes are wrapped in a data URL. */
|
|
11
|
+
function toImageUrl(image, mediaType) {
|
|
12
|
+
if (image instanceof URL) return image.href;
|
|
13
|
+
const type = mediaType ?? "image/jpeg";
|
|
14
|
+
if (typeof image === "string") return /^(https?:|data:)/.test(image) ? image : `data:${type};base64,${image}`;
|
|
15
|
+
const bytes = image instanceof Uint8Array ? image : new Uint8Array(image);
|
|
16
|
+
const encode = globalThis.btoa;
|
|
17
|
+
if (!encode) throw new Error("createOpenAiExecutors: binary image parts need a global `btoa` to base64-encode. Pass the image as a URL or a base64 string instead.");
|
|
18
|
+
let binary = "";
|
|
19
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
20
|
+
return `data:${type};base64,${encode(binary)}`;
|
|
21
|
+
}
|
|
22
|
+
/** One OpenAI tool message per `ToolResultPart` — the tool role is one message per result. */
|
|
23
|
+
function toToolMessage(part) {
|
|
24
|
+
return {
|
|
25
|
+
role: "tool",
|
|
26
|
+
content: part.output.type === "text" || part.output.type === "error-text" ? part.output.value : JSON.stringify(part.output.value),
|
|
27
|
+
tool_call_id: part.toolCallId
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Maps `AgentTextRequest.messages`/`system`/`prompt` to OpenAI chat messages. */
|
|
31
|
+
function toOpenAiMessages(request) {
|
|
32
|
+
if (request.messages) return request.messages.flatMap((message) => {
|
|
33
|
+
switch (message.role) {
|
|
34
|
+
case "system": return [{
|
|
35
|
+
role: "system",
|
|
36
|
+
content: message.content
|
|
37
|
+
}];
|
|
38
|
+
case "user":
|
|
39
|
+
if (typeof message.content === "string") return [{
|
|
40
|
+
role: "user",
|
|
41
|
+
content: message.content
|
|
42
|
+
}];
|
|
43
|
+
return [{
|
|
44
|
+
role: "user",
|
|
45
|
+
content: message.content.map((part) => {
|
|
46
|
+
switch (part.type) {
|
|
47
|
+
case "text": return {
|
|
48
|
+
type: "text",
|
|
49
|
+
text: part.text
|
|
50
|
+
};
|
|
51
|
+
case "image": return {
|
|
52
|
+
type: "image_url",
|
|
53
|
+
image_url: { url: toImageUrl(part.image, part.mediaType) }
|
|
54
|
+
};
|
|
55
|
+
default: return unsupportedPart("user", part.type);
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}];
|
|
59
|
+
case "assistant": {
|
|
60
|
+
if (typeof message.content === "string") return [{
|
|
61
|
+
role: "assistant",
|
|
62
|
+
content: message.content
|
|
63
|
+
}];
|
|
64
|
+
let text = "";
|
|
65
|
+
const toolCalls = [];
|
|
66
|
+
const trailing = [];
|
|
67
|
+
for (const part of message.content) switch (part.type) {
|
|
68
|
+
case "text":
|
|
69
|
+
text += part.text;
|
|
70
|
+
break;
|
|
71
|
+
case "tool-call":
|
|
72
|
+
toolCalls.push({
|
|
73
|
+
id: part.toolCallId,
|
|
74
|
+
type: "function",
|
|
75
|
+
function: {
|
|
76
|
+
name: part.toolName,
|
|
77
|
+
arguments: JSON.stringify(part.input ?? {})
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
break;
|
|
81
|
+
case "tool-result":
|
|
82
|
+
trailing.push(toToolMessage(part));
|
|
83
|
+
break;
|
|
84
|
+
default: unsupportedPart("assistant", part.type);
|
|
85
|
+
}
|
|
86
|
+
return [{
|
|
87
|
+
role: "assistant",
|
|
88
|
+
content: text,
|
|
89
|
+
...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
|
|
90
|
+
}, ...trailing];
|
|
91
|
+
}
|
|
92
|
+
case "tool": return message.content.map(toToolMessage);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const messages = [];
|
|
96
|
+
if (request.system) messages.push({
|
|
97
|
+
role: "system",
|
|
98
|
+
content: request.system
|
|
99
|
+
});
|
|
100
|
+
messages.push({
|
|
101
|
+
role: "user",
|
|
102
|
+
content: request.prompt ?? ""
|
|
103
|
+
});
|
|
104
|
+
return messages;
|
|
105
|
+
}
|
|
106
|
+
/** Drops keys whose value is `undefined`, so a spread cannot erase what it lands on. */
|
|
107
|
+
function defined(settings) {
|
|
108
|
+
return Object.fromEntries(Object.entries(settings).filter(([, value]) => value !== void 0));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Maps the generation settings a text and a decision request share onto OpenAI
|
|
112
|
+
* Chat Completions parameters, with unset keys omitted.
|
|
113
|
+
*
|
|
114
|
+
* Omitting matters: these are the top layer of a stack — the host's
|
|
115
|
+
* {@link CreateOpenAiExecutorsOptions.settings}, then the request's — and an
|
|
116
|
+
* `undefined` spread over the layer below would erase it.
|
|
117
|
+
*
|
|
118
|
+
* `max_tokens` is deprecated by OpenAI in favor of `max_completion_tokens`, so
|
|
119
|
+
* that's what `maxOutputTokens` targets. Chat Completions has no `top_k`
|
|
120
|
+
* parameter, so `topK` is dropped.
|
|
121
|
+
*/
|
|
122
|
+
function toOpenAiCallSettings(request) {
|
|
123
|
+
return defined({
|
|
124
|
+
temperature: request.temperature,
|
|
125
|
+
max_completion_tokens: request.maxOutputTokens,
|
|
126
|
+
top_p: request.topP,
|
|
127
|
+
seed: request.seed,
|
|
128
|
+
stop: request.stopSequences
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** A tool `description` may be a string or, for an AI SDK v7 tool, a function
|
|
132
|
+
* of the call's context. A raw JSON payload can only carry the static form. */
|
|
133
|
+
function staticDescription(descriptor) {
|
|
134
|
+
if (typeof descriptor === "function") return;
|
|
135
|
+
return typeof descriptor.description === "string" ? descriptor.description : void 0;
|
|
136
|
+
}
|
|
137
|
+
/** One OpenAI function tool per `AgentTools` entry. */
|
|
138
|
+
function toOpenAiTools(tools) {
|
|
139
|
+
return Object.entries(tools).flatMap(([name, descriptor]) => {
|
|
140
|
+
if (!descriptor) return [];
|
|
141
|
+
const inputSchema = typeof descriptor === "function" ? void 0 : descriptor.inputSchema;
|
|
142
|
+
return [{
|
|
143
|
+
type: "function",
|
|
144
|
+
function: {
|
|
145
|
+
name,
|
|
146
|
+
description: staticDescription(descriptor),
|
|
147
|
+
parameters: (isStandardSchema(inputSchema) ? getJsonSchemaSync(inputSchema) : void 0) ?? {}
|
|
148
|
+
}
|
|
149
|
+
}];
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Maps an {@link AgentToolChoice} onto OpenAI's `tool_choice` —
|
|
154
|
+
* `{ type: 'tool', name }` becomes `{ type: 'function', function: { name } }`;
|
|
155
|
+
* `'auto'`/`'none'`/`'required'` are OpenAI's own vocabulary and pass through.
|
|
156
|
+
* `undefined` stays `undefined`, so the key is omitted.
|
|
157
|
+
*/
|
|
158
|
+
function toOpenAiToolChoice(toolChoice) {
|
|
159
|
+
if (toolChoice === void 0) return;
|
|
160
|
+
return typeof toolChoice === "object" ? {
|
|
161
|
+
type: "function",
|
|
162
|
+
function: { name: toolChoice.name }
|
|
163
|
+
} : toolChoice;
|
|
164
|
+
}
|
|
165
|
+
/** One OpenAI function tool per candidate decision event — the "tool-per-event
|
|
166
|
+
* + tool_choice: 'required'" recipe, mirroring `toAiSdkEventTools`. */
|
|
167
|
+
function toOpenAiEventTools(events) {
|
|
168
|
+
return events.map((event) => ({
|
|
169
|
+
type: "function",
|
|
170
|
+
function: {
|
|
171
|
+
name: event.toolName,
|
|
172
|
+
description: `Choose the '${event.type}' move.`,
|
|
173
|
+
parameters: getJsonSchemaSync(event.inputSchema) ?? {}
|
|
174
|
+
}
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Messages for a decision request, with prior failed `attempts` rendered as an
|
|
179
|
+
* appended user message so retries converge. Mirrors the AI SDK adapter's
|
|
180
|
+
* `toDecisionMessages`.
|
|
181
|
+
*/
|
|
182
|
+
function toDecisionMessages(request) {
|
|
183
|
+
const messages = toOpenAiMessages(request);
|
|
184
|
+
for (const attempt of renderDecisionAttempts(request)) messages.push({
|
|
185
|
+
role: "user",
|
|
186
|
+
content: attempt.content
|
|
187
|
+
});
|
|
188
|
+
return messages;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Folds OpenAI's usage payload onto the flat {@link AgentCallUsage} field names
|
|
192
|
+
* core aggregates. OpenAI nests the reasoning count under
|
|
193
|
+
* `completion_tokens_details` and the cache-read count under
|
|
194
|
+
* `prompt_tokens_details`; a field the response omitted stays omitted, so it
|
|
195
|
+
* never contributes a `0` to a run's partial sums.
|
|
196
|
+
*/
|
|
197
|
+
function toAgentCallUsage(usage) {
|
|
198
|
+
if (!usage) return;
|
|
199
|
+
const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens;
|
|
200
|
+
const cachedInputTokens = usage.prompt_tokens_details?.cached_tokens;
|
|
201
|
+
return {
|
|
202
|
+
...usage.prompt_tokens !== void 0 ? { inputTokens: usage.prompt_tokens } : {},
|
|
203
|
+
...usage.completion_tokens !== void 0 ? { outputTokens: usage.completion_tokens } : {},
|
|
204
|
+
...usage.total_tokens !== void 0 ? { totalTokens: usage.total_tokens } : {},
|
|
205
|
+
...reasoningTokens !== void 0 ? { reasoningTokens } : {},
|
|
206
|
+
...cachedInputTokens !== void 0 ? { cachedInputTokens } : {}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Maps an OpenAI `finish_reason` onto the portable {@link AgentFinishReason}.
|
|
211
|
+
* `'function_call'` (the deprecated legacy path) joins `'tool_calls'` under
|
|
212
|
+
* `'tool-calls'`; anything unrecognized, including a missing reason, lands on
|
|
213
|
+
* `'other'`. OpenAI's own string stays on the result's `raw`.
|
|
214
|
+
*/
|
|
215
|
+
function toAgentFinishReason(finishReason) {
|
|
216
|
+
switch (finishReason) {
|
|
217
|
+
case "stop": return "stop";
|
|
218
|
+
case "length": return "length";
|
|
219
|
+
case "tool_calls":
|
|
220
|
+
case "function_call": return "tool-calls";
|
|
221
|
+
case "content_filter": return "content-filter";
|
|
222
|
+
default: return "other";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/** Resolves the host's `settings` option for one request. */
|
|
226
|
+
function callSettings(options, request) {
|
|
227
|
+
const settings = options.settings;
|
|
228
|
+
if (typeof settings === "function") return settings(request) ?? {};
|
|
229
|
+
const entry = settings?.[request.model];
|
|
230
|
+
if (entry === void 0) return {};
|
|
231
|
+
if (typeof entry !== "object" || entry === null) throw new Error(`createOpenAiExecutors: \`settings\` is keyed by model ref, so every value must be an object of OpenAI parameters, but '${request.model}' is a ${typeof entry}. Pass a function if you want one set of settings for every call.`);
|
|
232
|
+
return entry;
|
|
233
|
+
}
|
|
234
|
+
function truncated(request, info, partialOutput, cause) {
|
|
235
|
+
return new AgentTruncatedError(`createOpenAiExecutors: 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.`, {
|
|
236
|
+
requestName: request.name ?? "(unnamed)",
|
|
237
|
+
...info?.requestId !== void 0 ? { requestId: info.requestId } : {},
|
|
238
|
+
...partialOutput !== void 0 ? { partialOutput } : {},
|
|
239
|
+
...cause !== void 0 ? { cause } : {}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
function usageField(usage) {
|
|
243
|
+
return usage ? { usage } : {};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Adds two calls' usage together, field by field. A tool loop makes several
|
|
247
|
+
* OpenAI calls per request, and core folds ONE `usage` per executor result into
|
|
248
|
+
* the run's totals, so the steps' usages have to be summed here or the run
|
|
249
|
+
* undercounts. A field neither call reported stays absent.
|
|
250
|
+
*/
|
|
251
|
+
function addAgentCallUsage(total, next) {
|
|
252
|
+
if (!total || !next) return total ?? next;
|
|
253
|
+
const sum = {};
|
|
254
|
+
for (const field of AGENT_USAGE_TOKEN_FIELDS) if (total[field] !== void 0 || next[field] !== void 0) sum[field] = (total[field] ?? 0) + (next[field] ?? 0);
|
|
255
|
+
return sum;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* The function that runs one of the request's tools host-side, or `undefined`
|
|
259
|
+
* when the tool has no `execute` (a client-side tool: the model's call is
|
|
260
|
+
* handed back to the caller instead of being answered here).
|
|
261
|
+
*/
|
|
262
|
+
function toolExecutor(tools, name) {
|
|
263
|
+
const descriptor = tools[name];
|
|
264
|
+
if (!descriptor) return;
|
|
265
|
+
if (typeof descriptor === "function") return (input) => descriptor(input);
|
|
266
|
+
const execute = descriptor.execute;
|
|
267
|
+
return typeof execute === "function" ? (input) => execute(input) : void 0;
|
|
268
|
+
}
|
|
269
|
+
function toToolContent(output) {
|
|
270
|
+
return typeof output === "string" ? output : JSON.stringify(output ?? null);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Builds the `{ generateText, streamText, decide }` executor set for the raw
|
|
274
|
+
* `openai` package's Chat Completions API. Compare `createAiSdkExecutors` —
|
|
275
|
+
* same shape, different SDK underneath.
|
|
276
|
+
*
|
|
277
|
+
* Structured output uses `response_format: { type: 'json_schema' }` around the
|
|
278
|
+
* uniform `{ result, reasoning? }` envelope, and decisions force a tool call
|
|
279
|
+
* with `tool_choice: 'required'`, one function tool per candidate event.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* const executors = createOpenAiExecutors({
|
|
284
|
+
* client: new OpenAI(),
|
|
285
|
+
* resolveModel: () => 'gpt-5.4-mini',
|
|
286
|
+
* });
|
|
287
|
+
* const result = await runAgent(machine, { input, executors });
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
function createOpenAiExecutors(options) {
|
|
291
|
+
const { client, resolveModel = (modelRef) => modelRef } = options;
|
|
292
|
+
const textParams = (request, messages, extra = {}) => {
|
|
293
|
+
const tools = toOpenAiTools(request.tools);
|
|
294
|
+
return {
|
|
295
|
+
model: resolveModel(request.model),
|
|
296
|
+
messages,
|
|
297
|
+
...callSettings(options, request),
|
|
298
|
+
...toOpenAiCallSettings(request),
|
|
299
|
+
...tools.length > 0 ? { tools } : {},
|
|
300
|
+
...extra
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
const envelopeSchema = async (request) => {
|
|
304
|
+
if (getAgentOutputMode(request.outputSchema) !== "structured") return;
|
|
305
|
+
return getJsonSchema(buildEnvelopeSchema(request.outputSchema, { reasoning: request.includeReasoning }));
|
|
306
|
+
};
|
|
307
|
+
const generateText = async (request, info) => {
|
|
308
|
+
const jsonSchema = await envelopeSchema(request);
|
|
309
|
+
const responseFormat = jsonSchema ? { response_format: {
|
|
310
|
+
type: "json_schema",
|
|
311
|
+
json_schema: {
|
|
312
|
+
name: "output",
|
|
313
|
+
schema: jsonSchema,
|
|
314
|
+
strict: false
|
|
315
|
+
}
|
|
316
|
+
} } : {};
|
|
317
|
+
const maxSteps = typeof request.maxSteps === "number" && Number.isFinite(request.maxSteps) ? Math.max(1, Math.floor(request.maxSteps)) : 1;
|
|
318
|
+
const messages = toOpenAiMessages(request);
|
|
319
|
+
const toolChoice = toOpenAiToolChoice(request.toolChoice);
|
|
320
|
+
let response;
|
|
321
|
+
let usage;
|
|
322
|
+
let step = 0;
|
|
323
|
+
for (;;) {
|
|
324
|
+
step++;
|
|
325
|
+
response = await client.chat.completions.create(textParams(request, messages, {
|
|
326
|
+
...responseFormat,
|
|
327
|
+
...step === 1 && toolChoice !== void 0 ? { tool_choice: toolChoice } : {}
|
|
328
|
+
}), { signal: info?.signal });
|
|
329
|
+
usage = addAgentCallUsage(usage, toAgentCallUsage(response.usage));
|
|
330
|
+
const message = response.choices[0]?.message;
|
|
331
|
+
const toolCalls = message?.tool_calls ?? [];
|
|
332
|
+
if (toolCalls.length === 0 || step >= maxSteps) break;
|
|
333
|
+
const executors = toolCalls.map((call) => call.type === "function" ? toolExecutor(request.tools, call.function.name) : void 0);
|
|
334
|
+
if (executors.some((execute) => !execute)) break;
|
|
335
|
+
messages.push(message);
|
|
336
|
+
for (const [index, call] of toolCalls.entries()) {
|
|
337
|
+
const input = call.type === "function" && call.function.arguments ? JSON.parse(call.function.arguments) : {};
|
|
338
|
+
let content;
|
|
339
|
+
try {
|
|
340
|
+
content = toToolContent(await executors[index](input));
|
|
341
|
+
} catch (error) {
|
|
342
|
+
content = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
343
|
+
}
|
|
344
|
+
messages.push({
|
|
345
|
+
role: "tool",
|
|
346
|
+
tool_call_id: call.id,
|
|
347
|
+
content
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const unwrap = (content) => parseStructuredEnvelope(request, content ? JSON.parse(content) : void 0);
|
|
352
|
+
const choice = response.choices[0];
|
|
353
|
+
const finishReason = toAgentFinishReason(choice?.finish_reason);
|
|
354
|
+
const content = choice?.message.content;
|
|
355
|
+
if (jsonSchema) {
|
|
356
|
+
if (finishReason === "length") throw truncated(request, info, content ?? void 0);
|
|
357
|
+
const parsed = unwrap(content);
|
|
358
|
+
return {
|
|
359
|
+
output: parsed.result,
|
|
360
|
+
...typeof parsed.reasoning === "string" ? { reasoning: parsed.reasoning } : {},
|
|
361
|
+
...usageField(usage),
|
|
362
|
+
finishReason,
|
|
363
|
+
raw: response
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
output: content ?? "",
|
|
368
|
+
...usageField(usage),
|
|
369
|
+
finishReason,
|
|
370
|
+
raw: response
|
|
371
|
+
};
|
|
372
|
+
};
|
|
373
|
+
const streamText = async (request, info) => {
|
|
374
|
+
if (getAgentOutputMode(request.outputSchema) === "structured") throw new Error("createOpenAiExecutors: streamText is text-only — a request declaring a structured output schema must be routed to generateText.");
|
|
375
|
+
if (Object.keys(request.tools ?? {}).length > 0) throw new Error("createOpenAiExecutors: streamText is text-only — a request declaring tools must be routed to generateText.");
|
|
376
|
+
const stream = await client.chat.completions.create({
|
|
377
|
+
model: resolveModel(request.model),
|
|
378
|
+
messages: toOpenAiMessages(request),
|
|
379
|
+
...callSettings(options, request),
|
|
380
|
+
...toOpenAiCallSettings(request),
|
|
381
|
+
stream: true,
|
|
382
|
+
stream_options: { include_usage: true }
|
|
383
|
+
}, { signal: info?.signal });
|
|
384
|
+
let text = "";
|
|
385
|
+
let finishReason = "other";
|
|
386
|
+
let usage;
|
|
387
|
+
let last;
|
|
388
|
+
for await (const chunk of stream) {
|
|
389
|
+
last = chunk;
|
|
390
|
+
const choice = chunk.choices[0];
|
|
391
|
+
const delta = choice?.delta.content;
|
|
392
|
+
if (delta) {
|
|
393
|
+
text += delta;
|
|
394
|
+
info?.onChunk?.(delta);
|
|
395
|
+
}
|
|
396
|
+
if (choice?.finish_reason) finishReason = toAgentFinishReason(choice.finish_reason);
|
|
397
|
+
if (chunk.usage) usage = chunk.usage;
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
output: text,
|
|
401
|
+
...usageField(toAgentCallUsage(usage)),
|
|
402
|
+
finishReason,
|
|
403
|
+
raw: last
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
const decide = async (request, info) => {
|
|
407
|
+
const response = await client.chat.completions.create({
|
|
408
|
+
model: resolveModel(request.model),
|
|
409
|
+
messages: toDecisionMessages(request),
|
|
410
|
+
...callSettings(options, request),
|
|
411
|
+
...toOpenAiCallSettings(request),
|
|
412
|
+
tools: toOpenAiEventTools(request.events),
|
|
413
|
+
tool_choice: "required"
|
|
414
|
+
}, { signal: info?.signal });
|
|
415
|
+
const toolCall = response.choices[0]?.message.tool_calls?.[0];
|
|
416
|
+
if (!toolCall || toolCall.type !== "function") throw new Error("createOpenAiExecutors: decide — model did not call an event tool.");
|
|
417
|
+
const chosenEvent = request.events.find((event) => event.toolName === toolCall.function.name);
|
|
418
|
+
if (!chosenEvent) throw new Error(`createOpenAiExecutors: decide — model called unknown tool '${toolCall.function.name}'.`);
|
|
419
|
+
const args = toolCall.function.arguments ? JSON.parse(toolCall.function.arguments) : {};
|
|
420
|
+
return {
|
|
421
|
+
event: {
|
|
422
|
+
...args && typeof args === "object" ? args : {},
|
|
423
|
+
type: chosenEvent.type
|
|
424
|
+
},
|
|
425
|
+
...usageField(toAgentCallUsage(response.usage)),
|
|
426
|
+
finishReason: toAgentFinishReason(response.choices[0]?.finish_reason),
|
|
427
|
+
raw: response
|
|
428
|
+
};
|
|
429
|
+
};
|
|
430
|
+
return {
|
|
431
|
+
generateText,
|
|
432
|
+
streamText,
|
|
433
|
+
decide
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
//#endregion
|
|
437
|
+
export { addAgentCallUsage, createOpenAiExecutors, toAgentCallUsage, toAgentFinishReason, toDecisionMessages, toOpenAiCallSettings, toOpenAiEventTools, toOpenAiMessages, toOpenAiToolChoice, toOpenAiTools };
|
package/dist/otel.cjs
CHANGED
|
@@ -210,15 +210,12 @@ function createOtelTraceHandler(options) {
|
|
|
210
210
|
});
|
|
211
211
|
});
|
|
212
212
|
break;
|
|
213
|
-
case "machine.transition":
|
|
214
|
-
|
|
213
|
+
case "machine.transition":
|
|
214
|
+
addRunEvent(event, "agent.transition", {
|
|
215
215
|
"agent.event_type": event.event.type,
|
|
216
216
|
"agent.state": toJson(event.snapshot.value) ?? ""
|
|
217
|
-
};
|
|
218
|
-
if (event.eventId !== void 0) attributes["agent.event_id"] = event.eventId;
|
|
219
|
-
addRunEvent(event, "agent.transition", attributes);
|
|
217
|
+
});
|
|
220
218
|
break;
|
|
221
|
-
}
|
|
222
219
|
case "emit":
|
|
223
220
|
addRunEvent(event, "agent.emit", { "agent.event_type": event.event.type });
|
|
224
221
|
break;
|
package/dist/otel.d.cts
CHANGED
package/dist/otel.d.mts
CHANGED
package/dist/otel.mjs
CHANGED
|
@@ -208,15 +208,12 @@ function createOtelTraceHandler(options) {
|
|
|
208
208
|
});
|
|
209
209
|
});
|
|
210
210
|
break;
|
|
211
|
-
case "machine.transition":
|
|
212
|
-
|
|
211
|
+
case "machine.transition":
|
|
212
|
+
addRunEvent(event, "agent.transition", {
|
|
213
213
|
"agent.event_type": event.event.type,
|
|
214
214
|
"agent.state": toJson(event.snapshot.value) ?? ""
|
|
215
|
-
};
|
|
216
|
-
if (event.eventId !== void 0) attributes["agent.event_id"] = event.eventId;
|
|
217
|
-
addRunEvent(event, "agent.transition", attributes);
|
|
215
|
+
});
|
|
218
216
|
break;
|
|
219
|
-
}
|
|
220
217
|
case "emit":
|
|
221
218
|
addRunEvent(event, "agent.emit", { "agent.event_type": event.event.type });
|
|
222
219
|
break;
|