@ekairos/openai-reactor 1.22.63-beta.development.0 → 1.22.65-beta.development.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/responses.reactor.d.ts +90 -0
- package/dist/responses.reactor.d.ts.map +1 -0
- package/dist/responses.reactor.js +1003 -0
- package/dist/responses.reactor.js.map +1 -0
- package/dist/responses.websocket.d.ts +52 -0
- package/dist/responses.websocket.d.ts.map +1 -0
- package/dist/responses.websocket.js +548 -0
- package/dist/responses.websocket.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,1003 @@
|
|
|
1
|
+
import { OUTPUT_ITEM_TYPE, actionsToActionSpecs, createContextStepStreamChunk, encodeContextStepStreamChunk, resolveContextPartChunkIdentity, } from "@ekairos/events";
|
|
2
|
+
const DEFAULT_PROVIDER = "openai-responses";
|
|
3
|
+
const DEFAULT_BASE_URL = "https://api.openai.com/v1";
|
|
4
|
+
const TOOL_DESCRIPTION_MAX_LENGTH = 1200;
|
|
5
|
+
const SCHEMA_DESCRIPTION_MAX_LENGTH = 240;
|
|
6
|
+
const DROPPED_JSON_SCHEMA_METADATA_KEYS = new Set([
|
|
7
|
+
"$schema",
|
|
8
|
+
"$id",
|
|
9
|
+
"default",
|
|
10
|
+
"examples",
|
|
11
|
+
"readOnly",
|
|
12
|
+
"writeOnly",
|
|
13
|
+
]);
|
|
14
|
+
function asRecord(value) {
|
|
15
|
+
if (!value || typeof value !== "object")
|
|
16
|
+
return {};
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function asString(value) {
|
|
20
|
+
if (typeof value === "string")
|
|
21
|
+
return value;
|
|
22
|
+
if (value === undefined || value === null)
|
|
23
|
+
return "";
|
|
24
|
+
return String(value);
|
|
25
|
+
}
|
|
26
|
+
function readEnvString(name) {
|
|
27
|
+
const key = asString(name).trim();
|
|
28
|
+
if (!key)
|
|
29
|
+
return "";
|
|
30
|
+
return asString(process.env[key]).trim();
|
|
31
|
+
}
|
|
32
|
+
function cleanRecord(value) {
|
|
33
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
34
|
+
}
|
|
35
|
+
function compactDescription(value, maxLength) {
|
|
36
|
+
const text = asString(value).replace(/\s+/g, " ").trim();
|
|
37
|
+
if (!text)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (text.length <= maxLength)
|
|
40
|
+
return text;
|
|
41
|
+
return `${text.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
|
|
42
|
+
}
|
|
43
|
+
function toJsonSafe(value) {
|
|
44
|
+
if (typeof value === "undefined")
|
|
45
|
+
return undefined;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(JSON.stringify(value));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function compactResponsesJsonSchema(value) {
|
|
54
|
+
const safe = toJsonSafe(value);
|
|
55
|
+
if (safe === undefined)
|
|
56
|
+
return undefined;
|
|
57
|
+
return compactResponsesJsonSchemaValue(safe);
|
|
58
|
+
}
|
|
59
|
+
function compactResponsesJsonSchemaValue(value, containerKey) {
|
|
60
|
+
if (Array.isArray(value)) {
|
|
61
|
+
return value.map((entry) => compactResponsesJsonSchemaValue(entry, containerKey));
|
|
62
|
+
}
|
|
63
|
+
if (!value || typeof value !== "object")
|
|
64
|
+
return value;
|
|
65
|
+
const isNamedSchemaMap = containerKey === "properties" || containerKey === "$defs" || containerKey === "definitions";
|
|
66
|
+
const out = {};
|
|
67
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
68
|
+
if (!isNamedSchemaMap && DROPPED_JSON_SCHEMA_METADATA_KEYS.has(key))
|
|
69
|
+
continue;
|
|
70
|
+
if (isNamedSchemaMap) {
|
|
71
|
+
out[key] = compactResponsesJsonSchemaValue(entry, key);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (key === "description") {
|
|
75
|
+
const description = compactDescription(entry, SCHEMA_DESCRIPTION_MAX_LENGTH);
|
|
76
|
+
if (description)
|
|
77
|
+
out[key] = description;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (key === "title") {
|
|
81
|
+
const title = compactDescription(entry, SCHEMA_DESCRIPTION_MAX_LENGTH);
|
|
82
|
+
if (title)
|
|
83
|
+
out[key] = title;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
out[key] = compactResponsesJsonSchemaValue(entry, key);
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
function sanitizeRaw(value) {
|
|
91
|
+
const seen = new WeakSet();
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(JSON.stringify(value, (key, entry) => {
|
|
94
|
+
if (/token|authorization|cookie|secret|api[_-]?key|password/i.test(key)) {
|
|
95
|
+
return "[redacted]";
|
|
96
|
+
}
|
|
97
|
+
if (typeof entry === "string" && entry.length > 20000) {
|
|
98
|
+
return "[truncated-string]";
|
|
99
|
+
}
|
|
100
|
+
if (entry && typeof entry === "object") {
|
|
101
|
+
if (seen.has(entry))
|
|
102
|
+
return "[circular]";
|
|
103
|
+
seen.add(entry);
|
|
104
|
+
}
|
|
105
|
+
return entry;
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function asArray(value) {
|
|
113
|
+
return Array.isArray(value) ? value : [];
|
|
114
|
+
}
|
|
115
|
+
function asStringArray(value) {
|
|
116
|
+
return asArray(value)
|
|
117
|
+
.map((entry) => asString(entry).trim())
|
|
118
|
+
.filter(Boolean);
|
|
119
|
+
}
|
|
120
|
+
function parseJsonObject(value) {
|
|
121
|
+
const trimmed = value.trim();
|
|
122
|
+
if (!trimmed)
|
|
123
|
+
return {};
|
|
124
|
+
try {
|
|
125
|
+
return JSON.parse(trimmed);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return trimmed;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function stringifyToolOutput(value) {
|
|
132
|
+
if (typeof value === "string")
|
|
133
|
+
return value;
|
|
134
|
+
try {
|
|
135
|
+
return JSON.stringify(toJsonSafe(value) ?? value);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return String(value);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function partText(part) {
|
|
142
|
+
const record = asRecord(part);
|
|
143
|
+
const type = asString(record.type);
|
|
144
|
+
if (type === "message") {
|
|
145
|
+
const content = asRecord(record.content);
|
|
146
|
+
const text = asString(content.text);
|
|
147
|
+
const blocks = asArray(content.blocks)
|
|
148
|
+
.map((block) => {
|
|
149
|
+
const blockRecord = asRecord(block);
|
|
150
|
+
if (asString(blockRecord.type) === "text")
|
|
151
|
+
return asString(blockRecord.text);
|
|
152
|
+
if (asString(blockRecord.type) === "json")
|
|
153
|
+
return JSON.stringify(blockRecord.value);
|
|
154
|
+
return "";
|
|
155
|
+
})
|
|
156
|
+
.filter(Boolean)
|
|
157
|
+
.join("\n");
|
|
158
|
+
return [text, blocks].filter(Boolean).join("\n").trim();
|
|
159
|
+
}
|
|
160
|
+
if (type === "text" || type === "input_text") {
|
|
161
|
+
return asString(record.text || record.input_text).trim();
|
|
162
|
+
}
|
|
163
|
+
return asString(record.text).trim();
|
|
164
|
+
}
|
|
165
|
+
function eventText(event) {
|
|
166
|
+
return asArray(event.content?.parts).map(partText).filter(Boolean).join("\n").trim();
|
|
167
|
+
}
|
|
168
|
+
function roleForEvent(event) {
|
|
169
|
+
return event.type === OUTPUT_ITEM_TYPE ? "assistant" : "user";
|
|
170
|
+
}
|
|
171
|
+
function normalizeContentText(text, role) {
|
|
172
|
+
if (role === "assistant") {
|
|
173
|
+
return [{ type: "output_text", text }];
|
|
174
|
+
}
|
|
175
|
+
return [{ type: "input_text", text }];
|
|
176
|
+
}
|
|
177
|
+
function actionPartContent(part) {
|
|
178
|
+
const record = asRecord(part);
|
|
179
|
+
return asRecord(record.content);
|
|
180
|
+
}
|
|
181
|
+
function actionResultRef(part) {
|
|
182
|
+
const content = actionPartContent(part);
|
|
183
|
+
const status = asString(content.status);
|
|
184
|
+
if (status !== "completed" && status !== "failed")
|
|
185
|
+
return "";
|
|
186
|
+
return asString(content.actionCallId);
|
|
187
|
+
}
|
|
188
|
+
function collectActionResultRefs(events) {
|
|
189
|
+
const refs = new Set();
|
|
190
|
+
for (const event of events) {
|
|
191
|
+
for (const part of asArray(event.content?.parts)) {
|
|
192
|
+
const ref = actionResultRef(part);
|
|
193
|
+
if (ref)
|
|
194
|
+
refs.add(ref);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return [...refs];
|
|
198
|
+
}
|
|
199
|
+
function actionPartsToResponsesInput(params) {
|
|
200
|
+
const out = [];
|
|
201
|
+
for (const part of params.parts) {
|
|
202
|
+
const record = asRecord(part);
|
|
203
|
+
const type = asString(record.type);
|
|
204
|
+
if (type === "action") {
|
|
205
|
+
const content = asRecord(record.content);
|
|
206
|
+
const status = asString(content.status);
|
|
207
|
+
const actionCallId = asString(content.actionCallId);
|
|
208
|
+
const actionName = asString(content.actionName);
|
|
209
|
+
if (!actionCallId || !actionName)
|
|
210
|
+
continue;
|
|
211
|
+
if (status === "started" && !params.onlyResultRefs) {
|
|
212
|
+
out.push({
|
|
213
|
+
type: "function_call",
|
|
214
|
+
call_id: actionCallId,
|
|
215
|
+
name: actionName,
|
|
216
|
+
arguments: JSON.stringify(content.input ?? {}),
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (status === "completed" || status === "failed") {
|
|
221
|
+
if (params.onlyResultRefs && !params.onlyResultRefs.has(actionCallId)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
out.push({
|
|
225
|
+
type: "function_call_output",
|
|
226
|
+
call_id: actionCallId,
|
|
227
|
+
output: status === "failed"
|
|
228
|
+
? stringifyToolOutput(asRecord(content.error).message || content.error)
|
|
229
|
+
: stringifyToolOutput(content.output),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (type.startsWith("tool-")) {
|
|
235
|
+
const toolName = type.slice("tool-".length);
|
|
236
|
+
const toolCallId = asString(record.toolCallId);
|
|
237
|
+
const state = asString(record.state);
|
|
238
|
+
if (!toolName || !toolCallId)
|
|
239
|
+
continue;
|
|
240
|
+
if (state === "input-available" && !params.onlyResultRefs) {
|
|
241
|
+
out.push({
|
|
242
|
+
type: "function_call",
|
|
243
|
+
call_id: toolCallId,
|
|
244
|
+
name: toolName,
|
|
245
|
+
arguments: JSON.stringify(record.input ?? {}),
|
|
246
|
+
});
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (state === "output-available" || state === "output-error") {
|
|
250
|
+
if (params.onlyResultRefs && !params.onlyResultRefs.has(toolCallId)) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
out.push({
|
|
254
|
+
type: "function_call_output",
|
|
255
|
+
call_id: toolCallId,
|
|
256
|
+
output: state === "output-error"
|
|
257
|
+
? stringifyToolOutput(record.errorText || "Action execution failed.")
|
|
258
|
+
: stringifyToolOutput(record.output),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
265
|
+
function itemToResponsesInput(event) {
|
|
266
|
+
const role = roleForEvent(event);
|
|
267
|
+
const parts = asArray(event.content?.parts);
|
|
268
|
+
const input = [];
|
|
269
|
+
const text = eventText(event);
|
|
270
|
+
if (text) {
|
|
271
|
+
input.push({
|
|
272
|
+
role,
|
|
273
|
+
content: normalizeContentText(text, role),
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
if (role === "assistant") {
|
|
277
|
+
input.push(...actionPartsToResponsesInput({ parts }));
|
|
278
|
+
}
|
|
279
|
+
return input;
|
|
280
|
+
}
|
|
281
|
+
function buildResponsesInput(params) {
|
|
282
|
+
const previousResponseId = asString(params.previousState?.responseId);
|
|
283
|
+
const seenItemIds = new Set(asStringArray(params.previousState?.seenItemIds));
|
|
284
|
+
const seenActionResultRefs = new Set(asStringArray(params.previousState?.seenActionResultRefs));
|
|
285
|
+
if (params.usePreviousResponseId && previousResponseId && seenItemIds.size > 0) {
|
|
286
|
+
const incremental = [];
|
|
287
|
+
for (const event of params.events) {
|
|
288
|
+
if (!seenItemIds.has(event.id)) {
|
|
289
|
+
incremental.push(...itemToResponsesInput(event));
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (event.type !== OUTPUT_ITEM_TYPE)
|
|
293
|
+
continue;
|
|
294
|
+
const newResultRefs = new Set();
|
|
295
|
+
for (const part of asArray(event.content?.parts)) {
|
|
296
|
+
const ref = actionResultRef(part);
|
|
297
|
+
if (ref && !seenActionResultRefs.has(ref))
|
|
298
|
+
newResultRefs.add(ref);
|
|
299
|
+
}
|
|
300
|
+
if (newResultRefs.size > 0) {
|
|
301
|
+
incremental.push(...actionPartsToResponsesInput({
|
|
302
|
+
parts: asArray(event.content?.parts),
|
|
303
|
+
onlyResultRefs: newResultRefs,
|
|
304
|
+
}));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (incremental.length > 0) {
|
|
308
|
+
return {
|
|
309
|
+
input: incremental,
|
|
310
|
+
previousResponseId,
|
|
311
|
+
usedPreviousResponseId: true,
|
|
312
|
+
replayed: false,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
let input = params.events.flatMap(itemToResponsesInput);
|
|
317
|
+
if (input.length === 0) {
|
|
318
|
+
input = itemToResponsesInput(params.triggerEvent);
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
input,
|
|
322
|
+
usedPreviousResponseId: false,
|
|
323
|
+
replayed: true,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function buildMessagesForModel(input) {
|
|
327
|
+
return input
|
|
328
|
+
.map((item) => {
|
|
329
|
+
const role = asString(item.role);
|
|
330
|
+
if (role === "user" || role === "assistant" || role === "system") {
|
|
331
|
+
const content = asArray(item.content)
|
|
332
|
+
.map((part) => {
|
|
333
|
+
const record = asRecord(part);
|
|
334
|
+
return asString(record.text || record.input_text);
|
|
335
|
+
})
|
|
336
|
+
.filter(Boolean)
|
|
337
|
+
.join("\n");
|
|
338
|
+
if (!content)
|
|
339
|
+
return null;
|
|
340
|
+
return { role, content };
|
|
341
|
+
}
|
|
342
|
+
if (item.type === "function_call_output") {
|
|
343
|
+
return {
|
|
344
|
+
role: "tool",
|
|
345
|
+
content: [
|
|
346
|
+
{
|
|
347
|
+
type: "tool-result",
|
|
348
|
+
toolCallId: asString(item.call_id),
|
|
349
|
+
toolName: "function",
|
|
350
|
+
output: {
|
|
351
|
+
type: "json",
|
|
352
|
+
value: item.output,
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
})
|
|
360
|
+
.filter(Boolean);
|
|
361
|
+
}
|
|
362
|
+
export function resolveOpenAIResponsesWebSocketUrl(config) {
|
|
363
|
+
const raw = asString(config.webSocketUrl || config.url || config.baseUrl || DEFAULT_BASE_URL).trim();
|
|
364
|
+
if (!raw)
|
|
365
|
+
throw new Error("openai_responses_websocket_url_required");
|
|
366
|
+
const url = new URL(raw);
|
|
367
|
+
if (url.protocol === "https:")
|
|
368
|
+
url.protocol = "wss:";
|
|
369
|
+
if (url.protocol === "http:")
|
|
370
|
+
url.protocol = "ws:";
|
|
371
|
+
if (url.protocol !== "wss:") {
|
|
372
|
+
throw new Error("openai_responses_websocket_url_must_use_wss");
|
|
373
|
+
}
|
|
374
|
+
const path = url.pathname.replace(/\/+$/, "");
|
|
375
|
+
if (!path.endsWith("/responses")) {
|
|
376
|
+
url.pathname = `${path || ""}/responses`;
|
|
377
|
+
}
|
|
378
|
+
return url.toString();
|
|
379
|
+
}
|
|
380
|
+
export function resolveOpenAIResponsesHeaders(config) {
|
|
381
|
+
const headers = { ...(config.headers ?? {}) };
|
|
382
|
+
const apiKey = readEnvString(config.headersFromEnv?.apiKeyEnv);
|
|
383
|
+
const bearer = readEnvString(config.headersFromEnv?.authorizationBearerEnv);
|
|
384
|
+
if (apiKey && !headers["api-key"])
|
|
385
|
+
headers["api-key"] = apiKey;
|
|
386
|
+
if (bearer && !headers.Authorization)
|
|
387
|
+
headers.Authorization = `Bearer ${bearer}`;
|
|
388
|
+
return Object.keys(headers).length > 0 ? headers : undefined;
|
|
389
|
+
}
|
|
390
|
+
function buildResponsesTools(actionSpecs, strictJsonSchema) {
|
|
391
|
+
const tools = [];
|
|
392
|
+
for (const [name, spec] of Object.entries(actionSpecs)) {
|
|
393
|
+
const toolName = asString(name).trim();
|
|
394
|
+
if (!toolName)
|
|
395
|
+
continue;
|
|
396
|
+
if (spec.type === "provider-defined") {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
tools.push(cleanRecord({
|
|
400
|
+
type: "function",
|
|
401
|
+
name: toolName,
|
|
402
|
+
description: compactDescription(spec.description, TOOL_DESCRIPTION_MAX_LENGTH),
|
|
403
|
+
parameters: compactResponsesJsonSchema(spec.inputSchema) ?? { type: "object", additionalProperties: true },
|
|
404
|
+
strict: strictJsonSchema,
|
|
405
|
+
}));
|
|
406
|
+
}
|
|
407
|
+
return tools;
|
|
408
|
+
}
|
|
409
|
+
function getNumber(value) {
|
|
410
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
411
|
+
return Number.isFinite(numeric) ? numeric : 0;
|
|
412
|
+
}
|
|
413
|
+
function extractUsageMetrics(usageSource) {
|
|
414
|
+
const usage = asRecord(usageSource);
|
|
415
|
+
const inputDetails = asRecord(usage.input_tokens_details);
|
|
416
|
+
const outputDetails = asRecord(usage.output_tokens_details);
|
|
417
|
+
const promptTokens = getNumber(usage.promptTokens ?? usage.prompt_tokens ?? usage.inputTokens ?? usage.input_tokens);
|
|
418
|
+
const completionTokens = getNumber(usage.completionTokens ?? usage.completion_tokens ?? usage.outputTokens ?? usage.output_tokens);
|
|
419
|
+
const totalTokens = getNumber(usage.totalTokens ?? usage.total_tokens) || promptTokens + completionTokens;
|
|
420
|
+
const promptTokensCached = getNumber(usage.promptTokensCached ?? usage.cached_prompt_tokens ?? inputDetails.cached_tokens);
|
|
421
|
+
return {
|
|
422
|
+
promptTokens,
|
|
423
|
+
promptTokensCached,
|
|
424
|
+
promptTokensUncached: Math.max(0, promptTokens - promptTokensCached),
|
|
425
|
+
completionTokens,
|
|
426
|
+
totalTokens,
|
|
427
|
+
reasoningTokens: getNumber(outputDetails.reasoning_tokens),
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function responseUsage(response) {
|
|
431
|
+
return asRecord(asRecord(response).usage);
|
|
432
|
+
}
|
|
433
|
+
function responseIdFromEvent(event) {
|
|
434
|
+
const record = asRecord(event);
|
|
435
|
+
return asString(asRecord(record.response).id || record.response_id || record.id);
|
|
436
|
+
}
|
|
437
|
+
function modelFromEvent(event) {
|
|
438
|
+
const record = asRecord(event);
|
|
439
|
+
return asString(asRecord(record.response).model || record.model);
|
|
440
|
+
}
|
|
441
|
+
function providerEventType(event) {
|
|
442
|
+
return asString(asRecord(event).type) || "unknown";
|
|
443
|
+
}
|
|
444
|
+
function providerPartIdForEvent(event, state) {
|
|
445
|
+
const record = asRecord(event);
|
|
446
|
+
const item = asRecord(record.item);
|
|
447
|
+
const type = providerEventType(event);
|
|
448
|
+
if (type === "response.function_call_arguments.delta" ||
|
|
449
|
+
type === "response.function_call_arguments.done") {
|
|
450
|
+
const outputIndex = typeof record.output_index === "number" ? record.output_index : undefined;
|
|
451
|
+
return outputIndex === undefined ? asString(record.item_id) : state.callIdByOutputIndex.get(outputIndex);
|
|
452
|
+
}
|
|
453
|
+
if (type.startsWith("response.reasoning_summary_")) {
|
|
454
|
+
const itemId = asString(record.item_id);
|
|
455
|
+
const summaryIndex = getNumber(record.summary_index);
|
|
456
|
+
return itemId ? `${itemId}:${summaryIndex}` : undefined;
|
|
457
|
+
}
|
|
458
|
+
if (item.type === "function_call") {
|
|
459
|
+
return asString(item.call_id || item.id) || undefined;
|
|
460
|
+
}
|
|
461
|
+
return asString(record.item_id || item.id || record.id) || undefined;
|
|
462
|
+
}
|
|
463
|
+
function mapProviderEventType(event, state) {
|
|
464
|
+
const record = asRecord(event);
|
|
465
|
+
const item = asRecord(record.item);
|
|
466
|
+
const type = providerEventType(event);
|
|
467
|
+
if (type === "response.created" || type === "response.in_progress") {
|
|
468
|
+
return "chunk.response_metadata";
|
|
469
|
+
}
|
|
470
|
+
if (type === "response.output_item.added") {
|
|
471
|
+
if (item.type === "message")
|
|
472
|
+
return "chunk.text_start";
|
|
473
|
+
if (item.type === "reasoning")
|
|
474
|
+
return "chunk.reasoning_start";
|
|
475
|
+
if (item.type === "function_call")
|
|
476
|
+
return "chunk.action_started";
|
|
477
|
+
return "chunk.response_metadata";
|
|
478
|
+
}
|
|
479
|
+
if (type === "response.content_part.added") {
|
|
480
|
+
return "chunk.text_start";
|
|
481
|
+
}
|
|
482
|
+
if (type === "response.output_text.delta") {
|
|
483
|
+
return "chunk.text_delta";
|
|
484
|
+
}
|
|
485
|
+
if (type === "response.output_text.done" || type === "response.content_part.done") {
|
|
486
|
+
return "chunk.text_end";
|
|
487
|
+
}
|
|
488
|
+
if (type === "response.output_item.done") {
|
|
489
|
+
if (item.type === "message")
|
|
490
|
+
return "chunk.text_end";
|
|
491
|
+
if (item.type === "reasoning")
|
|
492
|
+
return "chunk.reasoning_end";
|
|
493
|
+
if (item.type === "function_call")
|
|
494
|
+
return "chunk.action_started";
|
|
495
|
+
return "chunk.response_metadata";
|
|
496
|
+
}
|
|
497
|
+
if (type === "response.function_call_arguments.delta") {
|
|
498
|
+
return "chunk.action_input_delta";
|
|
499
|
+
}
|
|
500
|
+
if (type === "response.function_call_arguments.done") {
|
|
501
|
+
return "chunk.action_started";
|
|
502
|
+
}
|
|
503
|
+
if (type === "response.reasoning_summary_part.added") {
|
|
504
|
+
return "chunk.reasoning_start";
|
|
505
|
+
}
|
|
506
|
+
if (type === "response.reasoning_summary_text.delta") {
|
|
507
|
+
return "chunk.reasoning_delta";
|
|
508
|
+
}
|
|
509
|
+
if (type === "response.reasoning_summary_part.done") {
|
|
510
|
+
return "chunk.reasoning_end";
|
|
511
|
+
}
|
|
512
|
+
if (type === "response.completed" || type === "response.incomplete") {
|
|
513
|
+
return "chunk.finish";
|
|
514
|
+
}
|
|
515
|
+
if (type === "response.failed" || type === "response.error" || type === "error") {
|
|
516
|
+
return "chunk.error";
|
|
517
|
+
}
|
|
518
|
+
return state.responseId ? "chunk.response_metadata" : "chunk.unknown";
|
|
519
|
+
}
|
|
520
|
+
function normalizedEventData(event, state) {
|
|
521
|
+
const record = asRecord(event);
|
|
522
|
+
const item = asRecord(record.item);
|
|
523
|
+
const response = asRecord(record.response);
|
|
524
|
+
const type = providerEventType(event);
|
|
525
|
+
const outputIndex = typeof record.output_index === "number" ? record.output_index : undefined;
|
|
526
|
+
const callId = asString(item.call_id) ||
|
|
527
|
+
(outputIndex === undefined ? "" : asString(state.callIdByOutputIndex.get(outputIndex))) ||
|
|
528
|
+
asString(record.call_id);
|
|
529
|
+
return cleanRecord({
|
|
530
|
+
type,
|
|
531
|
+
id: asString(record.id || response.id || item.id) || undefined,
|
|
532
|
+
itemId: asString(record.item_id || item.id) || undefined,
|
|
533
|
+
responseId: asString(response.id || record.response_id || state.responseId) || undefined,
|
|
534
|
+
outputIndex,
|
|
535
|
+
delta: record.delta,
|
|
536
|
+
text: record.text,
|
|
537
|
+
actionName: asString(item.name || record.name) || undefined,
|
|
538
|
+
toolName: asString(item.name || record.name) || undefined,
|
|
539
|
+
toolCallId: callId || undefined,
|
|
540
|
+
input: item.arguments ? parseJsonObject(asString(item.arguments)) : undefined,
|
|
541
|
+
finishReason: asString(asRecord(response.incomplete_details).reason) || undefined,
|
|
542
|
+
usage: response.usage,
|
|
543
|
+
status: asString(response.status || item.status || record.status) || undefined,
|
|
544
|
+
error: record.error,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
function updateStepStateFromEvent(event, state) {
|
|
548
|
+
const record = asRecord(event);
|
|
549
|
+
const item = asRecord(record.item);
|
|
550
|
+
const response = asRecord(record.response);
|
|
551
|
+
const type = providerEventType(event);
|
|
552
|
+
const responseId = asString(response.id || record.response_id);
|
|
553
|
+
if (responseId)
|
|
554
|
+
state.responseId = responseId;
|
|
555
|
+
const model = asString(response.model || record.model);
|
|
556
|
+
if (model)
|
|
557
|
+
state.model = model;
|
|
558
|
+
if (Object.keys(response).length > 0)
|
|
559
|
+
state.response = response;
|
|
560
|
+
if (response.usage)
|
|
561
|
+
state.usage = asRecord(response.usage);
|
|
562
|
+
if (type === "response.output_item.added" && item.type === "function_call") {
|
|
563
|
+
const callId = asString(item.call_id || item.id);
|
|
564
|
+
const outputIndex = typeof record.output_index === "number" ? record.output_index : undefined;
|
|
565
|
+
if (callId) {
|
|
566
|
+
state.functionCallsByCallId.set(callId, {
|
|
567
|
+
itemId: asString(item.id) || undefined,
|
|
568
|
+
callId,
|
|
569
|
+
name: asString(item.name),
|
|
570
|
+
argumentsText: asString(item.arguments),
|
|
571
|
+
outputIndex,
|
|
572
|
+
});
|
|
573
|
+
if (outputIndex !== undefined)
|
|
574
|
+
state.callIdByOutputIndex.set(outputIndex, callId);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (type === "response.function_call_arguments.delta") {
|
|
578
|
+
const outputIndex = typeof record.output_index === "number" ? record.output_index : undefined;
|
|
579
|
+
const callId = outputIndex === undefined ? "" : asString(state.callIdByOutputIndex.get(outputIndex));
|
|
580
|
+
if (callId) {
|
|
581
|
+
const current = state.functionCallsByCallId.get(callId);
|
|
582
|
+
if (current)
|
|
583
|
+
current.argumentsText += asString(record.delta);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if ((type === "response.output_item.done" || type === "response.function_call_arguments.done") &&
|
|
587
|
+
item.type === "function_call") {
|
|
588
|
+
const callId = asString(item.call_id || item.id);
|
|
589
|
+
if (callId) {
|
|
590
|
+
state.functionCallsByCallId.set(callId, {
|
|
591
|
+
itemId: asString(item.id) || undefined,
|
|
592
|
+
callId,
|
|
593
|
+
name: asString(item.name),
|
|
594
|
+
argumentsText: asString(item.arguments),
|
|
595
|
+
outputIndex: typeof record.output_index === "number" ? record.output_index : undefined,
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (type === "response.output_text.delta") {
|
|
600
|
+
const itemId = asString(record.item_id);
|
|
601
|
+
if (itemId) {
|
|
602
|
+
state.assistantTextByItemId.set(itemId, `${state.assistantTextByItemId.get(itemId) ?? ""}${asString(record.delta)}`);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (type === "response.output_text.done") {
|
|
606
|
+
const itemId = asString(record.item_id);
|
|
607
|
+
const text = asString(record.text);
|
|
608
|
+
if (itemId && text)
|
|
609
|
+
state.assistantTextByItemId.set(itemId, text);
|
|
610
|
+
}
|
|
611
|
+
if (type === "response.reasoning_summary_text.delta") {
|
|
612
|
+
const itemId = asString(record.item_id);
|
|
613
|
+
const summaryIndex = getNumber(record.summary_index);
|
|
614
|
+
const partId = itemId ? `${itemId}:${summaryIndex}` : "";
|
|
615
|
+
if (partId) {
|
|
616
|
+
state.reasoningTextByPartId.set(partId, `${state.reasoningTextByPartId.get(partId) ?? ""}${asString(record.delta)}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function mappedChunkFromEvent(params) {
|
|
621
|
+
updateStepStateFromEvent(params.event, params.state);
|
|
622
|
+
const providerPartId = providerPartIdForEvent(params.event, params.state);
|
|
623
|
+
let chunkType = mapProviderEventType(params.event, params.state);
|
|
624
|
+
if (providerPartId && chunkType === "chunk.text_start") {
|
|
625
|
+
if (params.state.textStarted.has(providerPartId)) {
|
|
626
|
+
chunkType = "chunk.response_metadata";
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
params.state.textStarted.add(providerPartId);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (providerPartId && chunkType === "chunk.text_end") {
|
|
633
|
+
if (params.state.textEnded.has(providerPartId)) {
|
|
634
|
+
chunkType = "chunk.response_metadata";
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
params.state.textEnded.add(providerPartId);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (providerPartId && chunkType === "chunk.reasoning_start") {
|
|
641
|
+
if (params.state.reasoningStarted.has(providerPartId)) {
|
|
642
|
+
chunkType = "chunk.response_metadata";
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
params.state.reasoningStarted.add(providerPartId);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (providerPartId && chunkType === "chunk.reasoning_end") {
|
|
649
|
+
if (params.state.reasoningEnded.has(providerPartId)) {
|
|
650
|
+
chunkType = "chunk.response_metadata";
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
params.state.reasoningEnded.add(providerPartId);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
const identity = resolveContextPartChunkIdentity({
|
|
657
|
+
stepId: params.stepId,
|
|
658
|
+
provider: params.provider,
|
|
659
|
+
providerPartId,
|
|
660
|
+
chunkType,
|
|
661
|
+
});
|
|
662
|
+
const actionRef = chunkType.startsWith("chunk.action_")
|
|
663
|
+
? identity?.providerPartId ?? providerPartId
|
|
664
|
+
: undefined;
|
|
665
|
+
return {
|
|
666
|
+
at: new Date().toISOString(),
|
|
667
|
+
sequence: params.sequence,
|
|
668
|
+
chunkType,
|
|
669
|
+
providerChunkType: providerEventType(params.event),
|
|
670
|
+
partId: identity?.partId,
|
|
671
|
+
providerPartId: identity?.providerPartId,
|
|
672
|
+
partType: identity?.partType,
|
|
673
|
+
partSlot: identity?.partSlot,
|
|
674
|
+
actionRef,
|
|
675
|
+
data: toJsonSafe(normalizedEventData(params.event, params.state)),
|
|
676
|
+
raw: params.includeRaw ? sanitizeRaw(params.event) : undefined,
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
function createStepState() {
|
|
680
|
+
return {
|
|
681
|
+
assistantTextByItemId: new Map(),
|
|
682
|
+
reasoningTextByPartId: new Map(),
|
|
683
|
+
functionCallsByCallId: new Map(),
|
|
684
|
+
callIdByOutputIndex: new Map(),
|
|
685
|
+
textStarted: new Set(),
|
|
686
|
+
textEnded: new Set(),
|
|
687
|
+
reasoningStarted: new Set(),
|
|
688
|
+
reasoningEnded: new Set(),
|
|
689
|
+
response: {},
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
function buildAssistantParts(params) {
|
|
693
|
+
const parts = [];
|
|
694
|
+
for (const [itemId, text] of params.state.assistantTextByItemId.entries()) {
|
|
695
|
+
const normalized = text.trim();
|
|
696
|
+
if (!normalized)
|
|
697
|
+
continue;
|
|
698
|
+
parts.push({
|
|
699
|
+
type: "message",
|
|
700
|
+
content: { text: normalized },
|
|
701
|
+
reactorMetadata: cleanRecord({
|
|
702
|
+
reactorKind: params.provider,
|
|
703
|
+
executionId: params.executionId,
|
|
704
|
+
itemId: params.eventId,
|
|
705
|
+
provider: {
|
|
706
|
+
openaiResponses: cleanRecord({
|
|
707
|
+
responseId: params.state.responseId,
|
|
708
|
+
itemId,
|
|
709
|
+
}),
|
|
710
|
+
},
|
|
711
|
+
}),
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
for (const [partId, text] of params.state.reasoningTextByPartId.entries()) {
|
|
715
|
+
const normalized = text.trim();
|
|
716
|
+
if (!normalized)
|
|
717
|
+
continue;
|
|
718
|
+
parts.push({
|
|
719
|
+
type: "reasoning",
|
|
720
|
+
content: { text: normalized, state: "done" },
|
|
721
|
+
reactorMetadata: cleanRecord({
|
|
722
|
+
reactorKind: params.provider,
|
|
723
|
+
executionId: params.executionId,
|
|
724
|
+
itemId: params.eventId,
|
|
725
|
+
provider: {
|
|
726
|
+
openaiResponses: cleanRecord({
|
|
727
|
+
responseId: params.state.responseId,
|
|
728
|
+
itemId: partId,
|
|
729
|
+
}),
|
|
730
|
+
},
|
|
731
|
+
}),
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
for (const call of params.state.functionCallsByCallId.values()) {
|
|
735
|
+
if (!call.callId || !call.name)
|
|
736
|
+
continue;
|
|
737
|
+
parts.push({
|
|
738
|
+
type: "action",
|
|
739
|
+
content: {
|
|
740
|
+
status: "started",
|
|
741
|
+
actionName: call.name,
|
|
742
|
+
actionCallId: call.callId,
|
|
743
|
+
input: parseJsonObject(call.argumentsText),
|
|
744
|
+
},
|
|
745
|
+
reactorMetadata: cleanRecord({
|
|
746
|
+
reactorKind: params.provider,
|
|
747
|
+
executionId: params.executionId,
|
|
748
|
+
itemId: params.eventId,
|
|
749
|
+
actionCallId: call.callId,
|
|
750
|
+
provider: {
|
|
751
|
+
openaiResponses: cleanRecord({
|
|
752
|
+
responseId: params.state.responseId,
|
|
753
|
+
itemId: call.itemId,
|
|
754
|
+
}),
|
|
755
|
+
},
|
|
756
|
+
}),
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
return parts;
|
|
760
|
+
}
|
|
761
|
+
function buildActionRequests(state) {
|
|
762
|
+
return [...state.functionCallsByCallId.values()]
|
|
763
|
+
.filter((call) => call.callId && call.name)
|
|
764
|
+
.map((call) => ({
|
|
765
|
+
actionRef: call.callId,
|
|
766
|
+
actionName: call.name,
|
|
767
|
+
input: parseJsonObject(call.argumentsText),
|
|
768
|
+
}));
|
|
769
|
+
}
|
|
770
|
+
function buildStreamTrace(params) {
|
|
771
|
+
return cleanRecord({
|
|
772
|
+
totalChunks: params.mappedChunks.length,
|
|
773
|
+
chunkTypes: Object.fromEntries(params.chunkTypeCounters.entries()),
|
|
774
|
+
providerChunkTypes: Object.fromEntries(params.providerChunkTypeCounters.entries()),
|
|
775
|
+
chunks: params.includeChunks ? params.mappedChunks : undefined,
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
export async function executeOpenAIResponsesReactionStep(args) {
|
|
779
|
+
"use step";
|
|
780
|
+
const { streamOpenAIResponsesWebSocket } = await import("./responses.websocket.js");
|
|
781
|
+
const provider = asString(args.config.providerName).trim() || DEFAULT_PROVIDER;
|
|
782
|
+
const includeStreamTraceInOutput = args.includeStreamTraceInOutput !== false;
|
|
783
|
+
const includeRawProviderEventsInOutput = Boolean(args.includeRawProviderEventsInOutput);
|
|
784
|
+
const maxPersistedStreamEvents = Math.max(0, Number(args.maxPersistedStreamEvents ?? 300));
|
|
785
|
+
const state = createStepState();
|
|
786
|
+
const contextWriter = args.contextStepStream?.getWriter();
|
|
787
|
+
const workflowWriter = args.writable?.getWriter();
|
|
788
|
+
const mappedChunks = [];
|
|
789
|
+
const persistedChunks = [];
|
|
790
|
+
const chunkTypeCounters = new Map();
|
|
791
|
+
const providerChunkTypeCounters = new Map();
|
|
792
|
+
const startedAtMs = Date.now();
|
|
793
|
+
let sequence = 0;
|
|
794
|
+
const inputBuild = buildResponsesInput({
|
|
795
|
+
events: args.events,
|
|
796
|
+
triggerEvent: args.triggerEvent,
|
|
797
|
+
previousState: args.previousReactorState,
|
|
798
|
+
usePreviousResponseId: args.config.usePreviousResponseId !== false,
|
|
799
|
+
});
|
|
800
|
+
const messagesForModel = buildMessagesForModel(inputBuild.input);
|
|
801
|
+
const requestDefaults = asRecord(args.config.requestDefaults);
|
|
802
|
+
const requestTools = asArray(requestDefaults.tools);
|
|
803
|
+
const actionTools = buildResponsesTools(args.actionSpecs, Boolean(args.config.strictJsonSchema));
|
|
804
|
+
const tools = [...requestTools, ...actionTools];
|
|
805
|
+
const request = cleanRecord({
|
|
806
|
+
...requestDefaults,
|
|
807
|
+
model: args.config.model,
|
|
808
|
+
input: inputBuild.input,
|
|
809
|
+
instructions: requestDefaults.instructions === undefined
|
|
810
|
+
? asString(args.systemPrompt).trim() || undefined
|
|
811
|
+
: requestDefaults.instructions,
|
|
812
|
+
previous_response_id: requestDefaults.previous_response_id === undefined
|
|
813
|
+
? inputBuild.previousResponseId
|
|
814
|
+
: requestDefaults.previous_response_id,
|
|
815
|
+
tools: tools.length > 0 ? tools : undefined,
|
|
816
|
+
});
|
|
817
|
+
async function emitMappedChunk(mapped) {
|
|
818
|
+
mappedChunks.push(mapped);
|
|
819
|
+
if (includeStreamTraceInOutput && persistedChunks.length < maxPersistedStreamEvents) {
|
|
820
|
+
persistedChunks.push(mapped);
|
|
821
|
+
}
|
|
822
|
+
chunkTypeCounters.set(mapped.chunkType, (chunkTypeCounters.get(mapped.chunkType) ?? 0) + 1);
|
|
823
|
+
const providerType = mapped.providerChunkType || "unknown";
|
|
824
|
+
providerChunkTypeCounters.set(providerType, (providerChunkTypeCounters.get(providerType) ?? 0) + 1);
|
|
825
|
+
const payload = {
|
|
826
|
+
at: mapped.at,
|
|
827
|
+
sequence: mapped.sequence,
|
|
828
|
+
chunkType: mapped.chunkType,
|
|
829
|
+
provider,
|
|
830
|
+
providerChunkType: mapped.providerChunkType,
|
|
831
|
+
partId: mapped.partId,
|
|
832
|
+
providerPartId: mapped.providerPartId,
|
|
833
|
+
partType: mapped.partType,
|
|
834
|
+
partSlot: mapped.partSlot,
|
|
835
|
+
actionRef: mapped.actionRef,
|
|
836
|
+
data: mapped.data,
|
|
837
|
+
raw: mapped.raw,
|
|
838
|
+
};
|
|
839
|
+
await contextWriter?.write(encodeContextStepStreamChunk(createContextStepStreamChunk({
|
|
840
|
+
...payload,
|
|
841
|
+
stepId: args.stepId,
|
|
842
|
+
})));
|
|
843
|
+
const event = {
|
|
844
|
+
type: "chunk.emitted",
|
|
845
|
+
contextId: args.contextId,
|
|
846
|
+
executionId: args.executionId,
|
|
847
|
+
stepId: args.stepId,
|
|
848
|
+
itemId: args.eventId,
|
|
849
|
+
...payload,
|
|
850
|
+
};
|
|
851
|
+
await workflowWriter?.write({
|
|
852
|
+
type: "data-chunk.emitted",
|
|
853
|
+
data: event,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
let finalMetrics;
|
|
857
|
+
try {
|
|
858
|
+
finalMetrics = await streamOpenAIResponsesWebSocket({
|
|
859
|
+
webSocketUrl: resolveOpenAIResponsesWebSocketUrl(args.config),
|
|
860
|
+
headers: resolveOpenAIResponsesHeaders(args.config),
|
|
861
|
+
handshakeTimeoutMs: args.config.handshakeTimeoutMs,
|
|
862
|
+
requestTimeoutMs: args.config.requestTimeoutMs,
|
|
863
|
+
reuseHotConnection: args.config.reuseHotConnection,
|
|
864
|
+
idleTtlMs: args.config.idleTtlMs,
|
|
865
|
+
maxHotConnections: args.config.maxHotConnections,
|
|
866
|
+
request,
|
|
867
|
+
onEvent: async (event, metrics) => {
|
|
868
|
+
state.finalMetrics = metrics;
|
|
869
|
+
sequence += 1;
|
|
870
|
+
await emitMappedChunk(mappedChunkFromEvent({
|
|
871
|
+
event,
|
|
872
|
+
sequence,
|
|
873
|
+
provider,
|
|
874
|
+
stepId: args.stepId,
|
|
875
|
+
includeRaw: includeRawProviderEventsInOutput,
|
|
876
|
+
state,
|
|
877
|
+
}));
|
|
878
|
+
},
|
|
879
|
+
});
|
|
880
|
+
state.finalMetrics = finalMetrics;
|
|
881
|
+
}
|
|
882
|
+
finally {
|
|
883
|
+
contextWriter?.releaseLock();
|
|
884
|
+
workflowWriter?.releaseLock();
|
|
885
|
+
}
|
|
886
|
+
const finishedAtMs = Date.now();
|
|
887
|
+
const connectionMode = finalMetrics?.connectionMode ?? "cold";
|
|
888
|
+
const usageMetrics = extractUsageMetrics(state.usage);
|
|
889
|
+
const parts = buildAssistantParts({
|
|
890
|
+
state,
|
|
891
|
+
provider,
|
|
892
|
+
executionId: args.executionId,
|
|
893
|
+
eventId: args.eventId,
|
|
894
|
+
});
|
|
895
|
+
const actionRequests = buildActionRequests(state);
|
|
896
|
+
const streamTrace = buildStreamTrace({
|
|
897
|
+
mappedChunks: persistedChunks,
|
|
898
|
+
chunkTypeCounters,
|
|
899
|
+
providerChunkTypeCounters,
|
|
900
|
+
includeChunks: includeStreamTraceInOutput,
|
|
901
|
+
});
|
|
902
|
+
const assistantEvent = {
|
|
903
|
+
id: args.eventId,
|
|
904
|
+
type: OUTPUT_ITEM_TYPE,
|
|
905
|
+
channel: "web",
|
|
906
|
+
createdAt: new Date().toISOString(),
|
|
907
|
+
status: "completed",
|
|
908
|
+
content: { parts },
|
|
909
|
+
};
|
|
910
|
+
const seenItemIds = new Set([
|
|
911
|
+
...args.events.map((event) => event.id).filter(Boolean),
|
|
912
|
+
args.eventId,
|
|
913
|
+
]);
|
|
914
|
+
const seenActionResultRefs = new Set(collectActionResultRefs(args.events));
|
|
915
|
+
return {
|
|
916
|
+
assistantEvent,
|
|
917
|
+
actionRequests,
|
|
918
|
+
messagesForModel,
|
|
919
|
+
llm: {
|
|
920
|
+
provider,
|
|
921
|
+
model: state.model || args.config.model,
|
|
922
|
+
promptTokens: usageMetrics.promptTokens,
|
|
923
|
+
promptTokensCached: usageMetrics.promptTokensCached,
|
|
924
|
+
promptTokensUncached: usageMetrics.promptTokensUncached,
|
|
925
|
+
completionTokens: usageMetrics.completionTokens,
|
|
926
|
+
totalTokens: usageMetrics.totalTokens,
|
|
927
|
+
latencyMs: Math.max(0, finishedAtMs - startedAtMs),
|
|
928
|
+
rawUsage: toJsonSafe(state.usage),
|
|
929
|
+
rawProviderMetadata: toJsonSafe({
|
|
930
|
+
responseId: state.responseId,
|
|
931
|
+
response: state.response,
|
|
932
|
+
streamTrace,
|
|
933
|
+
transport: finalMetrics
|
|
934
|
+
? {
|
|
935
|
+
...finalMetrics,
|
|
936
|
+
connectionMode,
|
|
937
|
+
}
|
|
938
|
+
: undefined,
|
|
939
|
+
input: {
|
|
940
|
+
usedPreviousResponseId: inputBuild.usedPreviousResponseId,
|
|
941
|
+
replayed: inputBuild.replayed,
|
|
942
|
+
itemCount: inputBuild.input.length,
|
|
943
|
+
},
|
|
944
|
+
}),
|
|
945
|
+
},
|
|
946
|
+
reactor: {
|
|
947
|
+
kind: provider,
|
|
948
|
+
state: {
|
|
949
|
+
responseId: state.responseId,
|
|
950
|
+
model: state.model || args.config.model,
|
|
951
|
+
connectionMode,
|
|
952
|
+
transportCacheKey: finalMetrics?.cacheKey,
|
|
953
|
+
lastMetrics: finalMetrics,
|
|
954
|
+
usedPreviousResponseId: inputBuild.usedPreviousResponseId,
|
|
955
|
+
replayedInput: inputBuild.replayed,
|
|
956
|
+
seenItemIds: [...seenItemIds],
|
|
957
|
+
seenActionResultRefs: [...seenActionResultRefs],
|
|
958
|
+
},
|
|
959
|
+
},
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
export function createOpenAIResponsesReactor(options) {
|
|
963
|
+
const includeStreamTraceInOutput = options.includeStreamTraceInOutput !== false;
|
|
964
|
+
const includeRawProviderEventsInOutput = Boolean(options.includeRawProviderEventsInOutput);
|
|
965
|
+
const maxPersistedStreamEvents = Math.max(0, Number(options.maxPersistedStreamEvents ?? 300));
|
|
966
|
+
return async (params) => {
|
|
967
|
+
const context = asRecord(params.context.content);
|
|
968
|
+
const config = await options.resolveConfig({
|
|
969
|
+
runtime: params.runtime,
|
|
970
|
+
context,
|
|
971
|
+
triggerEvent: params.triggerEvent,
|
|
972
|
+
contextId: params.contextId,
|
|
973
|
+
eventId: params.eventId,
|
|
974
|
+
executionId: params.executionId,
|
|
975
|
+
stepId: params.stepId,
|
|
976
|
+
iteration: params.iteration,
|
|
977
|
+
});
|
|
978
|
+
const actionSpecs = actionsToActionSpecs(params.actions);
|
|
979
|
+
const previousReactor = asRecord(params.context.reactor);
|
|
980
|
+
const previousReactorState = asRecord(previousReactor.state);
|
|
981
|
+
return await executeOpenAIResponsesReactionStep({
|
|
982
|
+
config,
|
|
983
|
+
systemPrompt: params.systemPrompt,
|
|
984
|
+
events: params.events,
|
|
985
|
+
triggerEvent: params.triggerEvent,
|
|
986
|
+
eventId: params.eventId,
|
|
987
|
+
executionId: params.executionId,
|
|
988
|
+
contextId: params.contextId,
|
|
989
|
+
stepId: params.stepId,
|
|
990
|
+
iteration: params.iteration,
|
|
991
|
+
maxModelSteps: params.maxModelSteps,
|
|
992
|
+
actionSpecs,
|
|
993
|
+
previousReactorState,
|
|
994
|
+
contextStepStream: params.contextStepStream,
|
|
995
|
+
writable: params.writable,
|
|
996
|
+
silent: params.silent,
|
|
997
|
+
includeStreamTraceInOutput,
|
|
998
|
+
includeRawProviderEventsInOutput,
|
|
999
|
+
maxPersistedStreamEvents,
|
|
1000
|
+
});
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
//# sourceMappingURL=responses.reactor.js.map
|