@nexface/agent 0.1.1-alpha.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/THIRD_PARTY_LICENSES.txt +245 -0
- package/dist/agent.d.ts +19 -0
- package/dist/agent.js +1509 -0
- package/dist/agent.js.map +1 -0
- package/dist/browser-prompt.d.ts +5 -0
- package/dist/browser-prompt.generated.d.ts +1 -0
- package/dist/browser-prompt.generated.js +50 -0
- package/dist/browser-prompt.generated.js.map +1 -0
- package/dist/browser-prompt.js +9 -0
- package/dist/browser-prompt.js.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.js +55 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/model/binding.d.ts +22 -0
- package/dist/model/binding.js +31 -0
- package/dist/model/binding.js.map +1 -0
- package/dist/model/error.d.ts +6 -0
- package/dist/model/error.js +10 -0
- package/dist/model/error.js.map +1 -0
- package/dist/model/internal-adapter.d.ts +8 -0
- package/dist/model/internal-adapter.js +13 -0
- package/dist/model/internal-adapter.js.map +1 -0
- package/dist/model/types.d.ts +9 -0
- package/dist/model/types.js +2 -0
- package/dist/model/types.js.map +1 -0
- package/dist/models/885.js +630 -0
- package/dist/models/956.js +5 -0
- package/dist/models/_chunks/35-e1813138.js +8304 -0
- package/dist/models/_chunks/879-7e580bb2.js +1189 -0
- package/dist/models/_chunks/958-bedced75.js +453 -0
- package/dist/models/_chunks/anthropic-messages~1-a4b25b48.js +8057 -0
- package/dist/models/_chunks/deferred-tools-90f3c977.js +37 -0
- package/dist/models/_chunks/error-body-8bee35c2.js +134 -0
- package/dist/models/_chunks/google-generative-ai~1-78bb2822.js +22104 -0
- package/dist/models/_chunks/openai-completions~1-7677fb83.js +1285 -0
- package/dist/models/_chunks/openai-responses~1-cc0a71cd.js +958 -0
- package/dist/models/anthropic-messages.d.ts +4 -0
- package/dist/models/anthropic-messages.js +32 -0
- package/dist/models/google-generative-ai.d.ts +4 -0
- package/dist/models/google-generative-ai.js +32 -0
- package/dist/models/openai-completions.d.ts +5 -0
- package/dist/models/openai-completions.js +16 -0
- package/dist/models/openai-responses.d.ts +5 -0
- package/dist/models/openai-responses.js +16 -0
- package/dist/models/rslib-runtime.js +59 -0
- package/dist/models/types.d.ts +26 -0
- package/dist/tool-bridge.d.ts +36 -0
- package/dist/tool-bridge.js +299 -0
- package/dist/tool-bridge.js.map +1 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,958 @@
|
|
|
1
|
+
import { __webpack_require__ } from "../rslib-runtime.js";
|
|
2
|
+
import { sanitizeSurrogates, appendGrammarToolInputJsonDelta, resolveGrammarConstrainedSampling, getGrammarToolInput, headersToRecord, clampThinkingLevel, resolveJsonSchemaStrictSampling, transformMessages, calculateCost, buildBaseOptions, retryProviderRequest, createGrammarToolInputProperties } from "./879-7e580bb2.js";
|
|
3
|
+
import { OpenAI, shortHash, clampOpenAIPromptCacheKey } from "./35-e1813138.js";
|
|
4
|
+
import { getProviderEnvValue, buildCopilotDynamicHeaders, parseStreamingJson, hasCopilotVisionInput } from "./958-bedced75.js";
|
|
5
|
+
import { formatProviderError, normalizeProviderError } from "./error-body-8bee35c2.js";
|
|
6
|
+
import { AssistantMessageEventStream } from "../885.js";
|
|
7
|
+
import { splitDeferredTools } from "./deferred-tools-90f3c977.js";
|
|
8
|
+
|
|
9
|
+
// NAMESPACE OBJECT: ../../../../../Users/bytedance/.codex/worktrees/c5f8/web-agent/node_modules/.pnpm/@earendil-works+pi-ai@0.84.1_@modelcontextprotocol+sdk@1.29.0_zod@4.4.3__ws@8.21.1_zod@4.4.3/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js
|
|
10
|
+
var openai_responses_namespaceObject = {};
|
|
11
|
+
__webpack_require__.r(openai_responses_namespaceObject);
|
|
12
|
+
__webpack_require__.d(openai_responses_namespaceObject, {
|
|
13
|
+
stream: () => (openai_responses_stream),
|
|
14
|
+
streamSimple: () => (streamSimple) });
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// Utilities
|
|
25
|
+
// =============================================================================
|
|
26
|
+
function encodeTextSignatureV1(id, phase) {
|
|
27
|
+
const payload = { v: 1, id };
|
|
28
|
+
if (phase)
|
|
29
|
+
payload.phase = phase;
|
|
30
|
+
return JSON.stringify(payload);
|
|
31
|
+
}
|
|
32
|
+
function parseTextSignature(signature) {
|
|
33
|
+
if (!signature)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (signature.startsWith("{")) {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(signature);
|
|
38
|
+
if (parsed.v === 1 && typeof parsed.id === "string") {
|
|
39
|
+
if (parsed.phase === "commentary" || parsed.phase === "final_answer") {
|
|
40
|
+
return { id: parsed.id, phase: parsed.phase };
|
|
41
|
+
}
|
|
42
|
+
return { id: parsed.id };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Fall through to legacy plain-string handling.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return { id: signature };
|
|
50
|
+
}
|
|
51
|
+
function convertToolResultOutput(model, content) {
|
|
52
|
+
const textResult = content
|
|
53
|
+
.filter((c) => c.type === "text")
|
|
54
|
+
.map((c) => c.text)
|
|
55
|
+
.join("\n");
|
|
56
|
+
const images = content.filter((c) => c.type === "image");
|
|
57
|
+
const hasText = textResult.length > 0;
|
|
58
|
+
if (images.length === 0 || !model.input.includes("image")) {
|
|
59
|
+
return sanitizeSurrogates(hasText ? textResult : images.length > 0 ? "(see attached image)" : "(no tool output)");
|
|
60
|
+
}
|
|
61
|
+
const output = [];
|
|
62
|
+
if (hasText) {
|
|
63
|
+
output.push({ type: "input_text", text: sanitizeSurrogates(textResult) });
|
|
64
|
+
}
|
|
65
|
+
for (const image of images) {
|
|
66
|
+
output.push({
|
|
67
|
+
type: "input_image",
|
|
68
|
+
detail: "auto",
|
|
69
|
+
image_url: `data:${image.mimeType};base64,${image.data}`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return output;
|
|
73
|
+
}
|
|
74
|
+
// =============================================================================
|
|
75
|
+
// Message conversion
|
|
76
|
+
// =============================================================================
|
|
77
|
+
function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
|
|
78
|
+
const messages = [];
|
|
79
|
+
const loadedToolNames = new Set();
|
|
80
|
+
const normalizeIdPart = (part) => {
|
|
81
|
+
const sanitized = part.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
82
|
+
const normalized = sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized;
|
|
83
|
+
return normalized.replace(/_+$/, "");
|
|
84
|
+
};
|
|
85
|
+
const buildForeignResponsesItemId = (itemId) => {
|
|
86
|
+
const normalized = `fc_${shortHash(itemId)}`;
|
|
87
|
+
return normalized.length > 64 ? normalized.slice(0, 64) : normalized;
|
|
88
|
+
};
|
|
89
|
+
const normalizeToolCallId = (id, _targetModel, source) => {
|
|
90
|
+
if (!allowedToolCallProviders.has(model.provider))
|
|
91
|
+
return normalizeIdPart(id);
|
|
92
|
+
if (!id.includes("|"))
|
|
93
|
+
return normalizeIdPart(id);
|
|
94
|
+
const [callId, itemId] = id.split("|");
|
|
95
|
+
const normalizedCallId = normalizeIdPart(callId);
|
|
96
|
+
const isForeignToolCall = source.provider !== model.provider || source.api !== model.api;
|
|
97
|
+
let normalizedItemId = isForeignToolCall ? buildForeignResponsesItemId(itemId) : normalizeIdPart(itemId);
|
|
98
|
+
// OpenAI Responses API requires item id to start with "fc"
|
|
99
|
+
if (!normalizedItemId.startsWith("fc_")) {
|
|
100
|
+
normalizedItemId = normalizeIdPart(`fc_${normalizedItemId}`);
|
|
101
|
+
}
|
|
102
|
+
return `${normalizedCallId}|${normalizedItemId}`;
|
|
103
|
+
};
|
|
104
|
+
const transformedMessages = transformMessages(context.messages, model, normalizeToolCallId);
|
|
105
|
+
const includeSystemPrompt = options?.includeSystemPrompt ?? true;
|
|
106
|
+
if (includeSystemPrompt && context.systemPrompt) {
|
|
107
|
+
const compat = model.compat;
|
|
108
|
+
const role = model.reasoning && compat?.supportsDeveloperRole !== false ? "developer" : "system";
|
|
109
|
+
messages.push({
|
|
110
|
+
role,
|
|
111
|
+
content: sanitizeSurrogates(context.systemPrompt),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
let msgIndex = 0;
|
|
115
|
+
for (const msg of transformedMessages) {
|
|
116
|
+
if (msg.role === "user") {
|
|
117
|
+
if (typeof msg.content === "string") {
|
|
118
|
+
messages.push({
|
|
119
|
+
role: "user",
|
|
120
|
+
content: [{ type: "input_text", text: sanitizeSurrogates(msg.content) }],
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
const content = msg.content.map((item) => {
|
|
125
|
+
if (item.type === "text") {
|
|
126
|
+
return {
|
|
127
|
+
type: "input_text",
|
|
128
|
+
text: sanitizeSurrogates(item.text),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
type: "input_image",
|
|
133
|
+
detail: "auto",
|
|
134
|
+
image_url: `data:${item.mimeType};base64,${item.data}`,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
if (content.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
messages.push({
|
|
140
|
+
role: "user",
|
|
141
|
+
content,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (msg.role === "assistant") {
|
|
146
|
+
const output = [];
|
|
147
|
+
const assistantMsg = msg;
|
|
148
|
+
const isDifferentModel = assistantMsg.model !== model.id &&
|
|
149
|
+
assistantMsg.provider === model.provider &&
|
|
150
|
+
assistantMsg.api === model.api;
|
|
151
|
+
let textBlockIndex = 0;
|
|
152
|
+
for (const block of msg.content) {
|
|
153
|
+
if (block.type === "thinking") {
|
|
154
|
+
if (block.thinkingSignature) {
|
|
155
|
+
const reasoningItem = JSON.parse(block.thinkingSignature);
|
|
156
|
+
output.push(reasoningItem);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (block.type === "text") {
|
|
160
|
+
const textBlock = block;
|
|
161
|
+
const parsedSignature = parseTextSignature(textBlock.textSignature);
|
|
162
|
+
const fallbackMessageId = textBlockIndex === 0 ? `msg_pi_${msgIndex}` : `msg_pi_${msgIndex}_${textBlockIndex}`;
|
|
163
|
+
textBlockIndex++;
|
|
164
|
+
// OpenAI requires id to be max 64 characters
|
|
165
|
+
let msgId = parsedSignature?.id;
|
|
166
|
+
if (!msgId) {
|
|
167
|
+
msgId = fallbackMessageId;
|
|
168
|
+
}
|
|
169
|
+
else if (msgId.length > 64) {
|
|
170
|
+
msgId = `msg_${shortHash(msgId)}`;
|
|
171
|
+
}
|
|
172
|
+
output.push({
|
|
173
|
+
type: "message",
|
|
174
|
+
role: "assistant",
|
|
175
|
+
content: [{ type: "output_text", text: sanitizeSurrogates(textBlock.text), annotations: [] }],
|
|
176
|
+
status: "completed",
|
|
177
|
+
id: msgId,
|
|
178
|
+
phase: parsedSignature?.phase,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
else if (block.type === "toolCall") {
|
|
182
|
+
const toolCall = block;
|
|
183
|
+
const [callId, itemIdRaw] = toolCall.id.split("|");
|
|
184
|
+
const customInputProperty = options?.grammarToolInputProperties?.get(toolCall.name);
|
|
185
|
+
let itemId = itemIdRaw;
|
|
186
|
+
// For different-model messages, set id to undefined to avoid pairing validation.
|
|
187
|
+
// OpenAI tracks which fc_xxx IDs were paired with rs_xxx reasoning items.
|
|
188
|
+
// By omitting the id, we avoid triggering that validation (like cross-provider does).
|
|
189
|
+
// When replaying custom-tool calls as a function_call, also drop non-fc_* ids such as
|
|
190
|
+
// ctc_* custom-tool ids because function_call item ids must be fc_*.
|
|
191
|
+
if ((isDifferentModel && itemId?.startsWith("fc_")) ||
|
|
192
|
+
(customInputProperty === undefined && !itemId?.startsWith("fc_"))) {
|
|
193
|
+
itemId = undefined;
|
|
194
|
+
}
|
|
195
|
+
if (customInputProperty !== undefined) {
|
|
196
|
+
output.push({
|
|
197
|
+
type: "custom_tool_call",
|
|
198
|
+
id: itemId,
|
|
199
|
+
call_id: callId,
|
|
200
|
+
name: toolCall.name,
|
|
201
|
+
input: sanitizeSurrogates(getGrammarToolInput(toolCall.name, toolCall.arguments, customInputProperty)),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
output.push({
|
|
206
|
+
type: "function_call",
|
|
207
|
+
id: itemId,
|
|
208
|
+
call_id: callId,
|
|
209
|
+
name: toolCall.name,
|
|
210
|
+
arguments: JSON.stringify(toolCall.arguments),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (output.length === 0)
|
|
216
|
+
continue;
|
|
217
|
+
messages.push(...output);
|
|
218
|
+
}
|
|
219
|
+
else if (msg.role === "toolResult") {
|
|
220
|
+
const [callId] = msg.toolCallId.split("|");
|
|
221
|
+
const output = convertToolResultOutput(model, msg.content);
|
|
222
|
+
if (options?.grammarToolInputProperties?.has(msg.toolName)) {
|
|
223
|
+
messages.push({
|
|
224
|
+
type: "custom_tool_call_output",
|
|
225
|
+
call_id: callId,
|
|
226
|
+
output,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
messages.push({
|
|
231
|
+
type: "function_call_output",
|
|
232
|
+
call_id: callId,
|
|
233
|
+
output,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const deferredTools = [];
|
|
237
|
+
for (const name of msg.addedToolNames ?? []) {
|
|
238
|
+
const tool = options?.deferredTools?.get(name);
|
|
239
|
+
if (!tool || loadedToolNames.has(name))
|
|
240
|
+
continue;
|
|
241
|
+
loadedToolNames.add(name);
|
|
242
|
+
deferredTools.push(tool);
|
|
243
|
+
}
|
|
244
|
+
if (deferredTools.length > 0) {
|
|
245
|
+
const names = deferredTools.map((tool) => tool.name);
|
|
246
|
+
const searchCallId = `pi_tool_load_${shortHash(`${msg.toolCallId}:${names.join(",")}`)}`;
|
|
247
|
+
messages.push({
|
|
248
|
+
type: "tool_search_call",
|
|
249
|
+
call_id: searchCallId,
|
|
250
|
+
execution: "client",
|
|
251
|
+
status: "completed",
|
|
252
|
+
arguments: { query: names.join(" "), limit: names.length },
|
|
253
|
+
});
|
|
254
|
+
messages.push({
|
|
255
|
+
type: "tool_search_output",
|
|
256
|
+
call_id: searchCallId,
|
|
257
|
+
execution: "client",
|
|
258
|
+
status: "completed",
|
|
259
|
+
tools: convertResponsesTools(deferredTools, {
|
|
260
|
+
...options?.toolOptions,
|
|
261
|
+
deferLoading: true,
|
|
262
|
+
}),
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
msgIndex++;
|
|
267
|
+
}
|
|
268
|
+
return messages;
|
|
269
|
+
}
|
|
270
|
+
// =============================================================================
|
|
271
|
+
// Tool conversion
|
|
272
|
+
// =============================================================================
|
|
273
|
+
function convertResponsesTools(tools, options) {
|
|
274
|
+
const defaultStrict = options?.strict === undefined ? false : options.strict;
|
|
275
|
+
const supportsStrictMode = options?.supportsStrictMode ?? true;
|
|
276
|
+
const supportsOpenAIGrammarTools = options?.supportsOpenAIGrammarTools ?? false;
|
|
277
|
+
return tools.map((tool) => {
|
|
278
|
+
const grammar = resolveGrammarConstrainedSampling(tool, supportsOpenAIGrammarTools);
|
|
279
|
+
if (grammar) {
|
|
280
|
+
return {
|
|
281
|
+
type: "custom",
|
|
282
|
+
name: tool.name,
|
|
283
|
+
description: tool.description,
|
|
284
|
+
format: {
|
|
285
|
+
type: "grammar",
|
|
286
|
+
syntax: grammar.format,
|
|
287
|
+
definition: grammar.definition,
|
|
288
|
+
},
|
|
289
|
+
...(options?.deferLoading ? { defer_loading: true } : {}),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const constrainedStrict = resolveJsonSchemaStrictSampling(tool, supportsStrictMode);
|
|
293
|
+
const functionTool = {
|
|
294
|
+
type: "function",
|
|
295
|
+
name: tool.name,
|
|
296
|
+
description: tool.description,
|
|
297
|
+
parameters: tool.parameters, // TypeBox already generates JSON Schema
|
|
298
|
+
...(options?.deferLoading ? { defer_loading: true } : {}),
|
|
299
|
+
};
|
|
300
|
+
if (supportsStrictMode) {
|
|
301
|
+
functionTool.strict = constrainedStrict ?? defaultStrict;
|
|
302
|
+
}
|
|
303
|
+
return functionTool;
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
function getCustomToolCallInput(block) {
|
|
307
|
+
const property = block.customInput?.property;
|
|
308
|
+
if (property === undefined)
|
|
309
|
+
return "";
|
|
310
|
+
const value = block.arguments[property];
|
|
311
|
+
return typeof value === "string" ? value : "";
|
|
312
|
+
}
|
|
313
|
+
function appendCustomToolCallInput(block, nextInput, close) {
|
|
314
|
+
const customInput = block.customInput;
|
|
315
|
+
if (!customInput)
|
|
316
|
+
return undefined;
|
|
317
|
+
const delta = appendGrammarToolInputJsonDelta(customInput.jsonBuffer, customInput.property, nextInput, close);
|
|
318
|
+
block.arguments = { [customInput.property]: nextInput };
|
|
319
|
+
return delta;
|
|
320
|
+
}
|
|
321
|
+
async function processResponsesStream(openaiStream, output, stream, model, options) {
|
|
322
|
+
let sawTerminalResponseEvent = false;
|
|
323
|
+
const outputSlots = new Map();
|
|
324
|
+
const reasoningBlocksById = new Map();
|
|
325
|
+
const applyMessagePhaseStopReason = (item) => {
|
|
326
|
+
if (item.type === "message" && item.phase === "final_answer") {
|
|
327
|
+
output.stopReason = "stop";
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
const getSlot = (outputIndex, type) => {
|
|
331
|
+
const slot = outputSlots.get(outputIndex);
|
|
332
|
+
return slot?.type === type ? slot : undefined;
|
|
333
|
+
};
|
|
334
|
+
const pushToolCallDelta = (slot, delta) => {
|
|
335
|
+
if (delta === undefined)
|
|
336
|
+
return;
|
|
337
|
+
stream.push({
|
|
338
|
+
type: "toolcall_delta",
|
|
339
|
+
contentIndex: slot.contentIndex,
|
|
340
|
+
delta,
|
|
341
|
+
partial: output,
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
const createSlot = (outputIndex, item) => {
|
|
345
|
+
if (item.type === "reasoning") {
|
|
346
|
+
const block = { type: "thinking", thinking: "" };
|
|
347
|
+
output.content.push(block);
|
|
348
|
+
const slot = {
|
|
349
|
+
type: "thinking",
|
|
350
|
+
block,
|
|
351
|
+
contentIndex: output.content.length - 1,
|
|
352
|
+
};
|
|
353
|
+
outputSlots.set(outputIndex, slot);
|
|
354
|
+
stream.push({ type: "thinking_start", contentIndex: slot.contentIndex, partial: output });
|
|
355
|
+
return slot;
|
|
356
|
+
}
|
|
357
|
+
if (item.type === "message") {
|
|
358
|
+
applyMessagePhaseStopReason(item);
|
|
359
|
+
const block = { type: "text", text: "" };
|
|
360
|
+
output.content.push(block);
|
|
361
|
+
const slot = { type: "text", block, contentIndex: output.content.length - 1 };
|
|
362
|
+
outputSlots.set(outputIndex, slot);
|
|
363
|
+
stream.push({ type: "text_start", contentIndex: slot.contentIndex, partial: output });
|
|
364
|
+
return slot;
|
|
365
|
+
}
|
|
366
|
+
if (item.type === "function_call") {
|
|
367
|
+
const block = {
|
|
368
|
+
type: "toolCall",
|
|
369
|
+
id: `${item.call_id}|${item.id}`,
|
|
370
|
+
name: item.name,
|
|
371
|
+
arguments: {},
|
|
372
|
+
partialJson: item.arguments || "",
|
|
373
|
+
};
|
|
374
|
+
output.content.push(block);
|
|
375
|
+
const slot = {
|
|
376
|
+
type: "toolCall",
|
|
377
|
+
block,
|
|
378
|
+
contentIndex: output.content.length - 1,
|
|
379
|
+
};
|
|
380
|
+
outputSlots.set(outputIndex, slot);
|
|
381
|
+
stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output });
|
|
382
|
+
return slot;
|
|
383
|
+
}
|
|
384
|
+
if (item.type === "custom_tool_call") {
|
|
385
|
+
const inputProperty = options?.grammarToolInputProperties?.get(item.name) ?? "input";
|
|
386
|
+
const input = item.input || "";
|
|
387
|
+
const block = {
|
|
388
|
+
type: "toolCall",
|
|
389
|
+
id: `${item.call_id}|${item.id}`,
|
|
390
|
+
name: item.name,
|
|
391
|
+
arguments: { [inputProperty]: input },
|
|
392
|
+
customInput: {
|
|
393
|
+
property: inputProperty,
|
|
394
|
+
jsonBuffer: { input: "", started: false, closed: false },
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
output.content.push(block);
|
|
398
|
+
const slot = {
|
|
399
|
+
type: "toolCall",
|
|
400
|
+
block,
|
|
401
|
+
contentIndex: output.content.length - 1,
|
|
402
|
+
};
|
|
403
|
+
outputSlots.set(outputIndex, slot);
|
|
404
|
+
stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output });
|
|
405
|
+
return slot;
|
|
406
|
+
}
|
|
407
|
+
return undefined;
|
|
408
|
+
};
|
|
409
|
+
const getOrCreateSlot = (outputIndex, item) => {
|
|
410
|
+
return outputSlots.get(outputIndex) ?? createSlot(outputIndex, item);
|
|
411
|
+
};
|
|
412
|
+
// Azure OpenAI can omit reasoning.encrypted_content from response.output_item.done
|
|
413
|
+
// and provide it only in response.completed.response.output. Backfill the
|
|
414
|
+
// persisted reasoning signature from the terminal response to keep store:false
|
|
415
|
+
// multi-turn replay stateless. See https://github.com/earendil-works/pi/issues/6409.
|
|
416
|
+
const backfillReasoningSignatures = (responseOutput) => {
|
|
417
|
+
for (const item of responseOutput) {
|
|
418
|
+
if (item.type !== "reasoning" || !item.encrypted_content)
|
|
419
|
+
continue;
|
|
420
|
+
const block = reasoningBlocksById.get(item.id);
|
|
421
|
+
if (!block?.thinkingSignature)
|
|
422
|
+
continue;
|
|
423
|
+
const storedItem = JSON.parse(block.thinkingSignature);
|
|
424
|
+
if (storedItem.encrypted_content)
|
|
425
|
+
continue;
|
|
426
|
+
block.thinkingSignature = JSON.stringify({
|
|
427
|
+
...storedItem,
|
|
428
|
+
encrypted_content: item.encrypted_content,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
const finalizeResponse = (response) => {
|
|
433
|
+
sawTerminalResponseEvent = true;
|
|
434
|
+
backfillReasoningSignatures(response.output ?? []);
|
|
435
|
+
if (response?.id) {
|
|
436
|
+
output.responseId = response.id;
|
|
437
|
+
}
|
|
438
|
+
if (response?.usage) {
|
|
439
|
+
const inputDetails = response.usage.input_tokens_details;
|
|
440
|
+
const cachedTokens = inputDetails?.cached_tokens || 0;
|
|
441
|
+
const cacheWriteTokens = inputDetails?.cache_write_tokens || 0;
|
|
442
|
+
output.usage = {
|
|
443
|
+
// OpenAI includes cached and cache-write tokens in input_tokens, so subtract both.
|
|
444
|
+
input: Math.max(0, (response.usage.input_tokens || 0) - cachedTokens - cacheWriteTokens),
|
|
445
|
+
output: response.usage.output_tokens || 0,
|
|
446
|
+
cacheRead: cachedTokens,
|
|
447
|
+
cacheWrite: cacheWriteTokens,
|
|
448
|
+
reasoning: response.usage.output_tokens_details?.reasoning_tokens || 0,
|
|
449
|
+
totalTokens: response.usage.total_tokens || 0,
|
|
450
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
calculateCost(model, output.usage);
|
|
454
|
+
if (options?.applyServiceTierPricing) {
|
|
455
|
+
const serviceTier = options.resolveServiceTier
|
|
456
|
+
? options.resolveServiceTier(response?.service_tier, options.serviceTier)
|
|
457
|
+
: (response?.service_tier ?? options.serviceTier);
|
|
458
|
+
options.applyServiceTierPricing(output.usage, serviceTier);
|
|
459
|
+
}
|
|
460
|
+
// Map status to stop reason. For incomplete responses, retain the provider's
|
|
461
|
+
// specific reason so max-output truncation and content filtering stay distinct.
|
|
462
|
+
const status = response?.status;
|
|
463
|
+
const incompleteDetails = response?.incomplete_details;
|
|
464
|
+
const incompleteReason = typeof incompleteDetails?.reason === "string" ? incompleteDetails.reason : undefined;
|
|
465
|
+
output.rawStopReason = incompleteReason ? `${status}.${incompleteReason}` : status;
|
|
466
|
+
const mappedStop = mapStopReason(status, incompleteReason);
|
|
467
|
+
output.stopReason = mappedStop.stopReason;
|
|
468
|
+
output.errorMessage = mappedStop.errorMessage;
|
|
469
|
+
if (output.content.some((b) => b.type === "toolCall") && output.stopReason === "stop") {
|
|
470
|
+
output.stopReason = "toolUse";
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
for await (const event of openaiStream) {
|
|
474
|
+
if (event.type === "response.created") {
|
|
475
|
+
output.responseId = event.response.id;
|
|
476
|
+
}
|
|
477
|
+
else if (event.type === "response.output_item.added") {
|
|
478
|
+
createSlot(event.output_index, event.item);
|
|
479
|
+
}
|
|
480
|
+
else if (event.type === "response.reasoning_summary_text.delta") {
|
|
481
|
+
const slot = getSlot(event.output_index, "thinking");
|
|
482
|
+
if (!slot)
|
|
483
|
+
continue;
|
|
484
|
+
slot.block.thinking += event.delta;
|
|
485
|
+
stream.push({
|
|
486
|
+
type: "thinking_delta",
|
|
487
|
+
contentIndex: slot.contentIndex,
|
|
488
|
+
delta: event.delta,
|
|
489
|
+
partial: output,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
else if (event.type === "response.reasoning_summary_part.done") {
|
|
493
|
+
const slot = getSlot(event.output_index, "thinking");
|
|
494
|
+
if (!slot)
|
|
495
|
+
continue;
|
|
496
|
+
slot.block.thinking += "\n\n";
|
|
497
|
+
stream.push({
|
|
498
|
+
type: "thinking_delta",
|
|
499
|
+
contentIndex: slot.contentIndex,
|
|
500
|
+
delta: "\n\n",
|
|
501
|
+
partial: output,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
else if (event.type === "response.reasoning_text.delta") {
|
|
505
|
+
const slot = getSlot(event.output_index, "thinking");
|
|
506
|
+
if (!slot)
|
|
507
|
+
continue;
|
|
508
|
+
slot.block.thinking += event.delta;
|
|
509
|
+
stream.push({
|
|
510
|
+
type: "thinking_delta",
|
|
511
|
+
contentIndex: slot.contentIndex,
|
|
512
|
+
delta: event.delta,
|
|
513
|
+
partial: output,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
else if (event.type === "response.output_text.delta") {
|
|
517
|
+
const slot = getSlot(event.output_index, "text");
|
|
518
|
+
if (!slot)
|
|
519
|
+
continue;
|
|
520
|
+
slot.block.text += event.delta;
|
|
521
|
+
stream.push({
|
|
522
|
+
type: "text_delta",
|
|
523
|
+
contentIndex: slot.contentIndex,
|
|
524
|
+
delta: event.delta,
|
|
525
|
+
partial: output,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
else if (event.type === "response.refusal.delta") {
|
|
529
|
+
const slot = getSlot(event.output_index, "text");
|
|
530
|
+
if (!slot)
|
|
531
|
+
continue;
|
|
532
|
+
slot.block.text += event.delta;
|
|
533
|
+
stream.push({
|
|
534
|
+
type: "text_delta",
|
|
535
|
+
contentIndex: slot.contentIndex,
|
|
536
|
+
delta: event.delta,
|
|
537
|
+
partial: output,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
else if (event.type === "response.function_call_arguments.delta") {
|
|
541
|
+
const slot = getSlot(event.output_index, "toolCall");
|
|
542
|
+
if (!slot || slot.block.partialJson === undefined)
|
|
543
|
+
continue;
|
|
544
|
+
slot.block.partialJson += event.delta;
|
|
545
|
+
slot.block.arguments = parseStreamingJson(slot.block.partialJson);
|
|
546
|
+
pushToolCallDelta(slot, event.delta);
|
|
547
|
+
}
|
|
548
|
+
else if (event.type === "response.function_call_arguments.done") {
|
|
549
|
+
const slot = getSlot(event.output_index, "toolCall");
|
|
550
|
+
if (!slot || slot.block.partialJson === undefined)
|
|
551
|
+
continue;
|
|
552
|
+
const previousPartialJson = slot.block.partialJson;
|
|
553
|
+
slot.block.partialJson = event.arguments;
|
|
554
|
+
slot.block.arguments = parseStreamingJson(slot.block.partialJson);
|
|
555
|
+
if (event.arguments.startsWith(previousPartialJson)) {
|
|
556
|
+
const delta = event.arguments.slice(previousPartialJson.length);
|
|
557
|
+
if (delta.length > 0)
|
|
558
|
+
pushToolCallDelta(slot, delta);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
else if (event.type === "response.custom_tool_call_input.delta") {
|
|
562
|
+
const slot = getSlot(event.output_index, "toolCall");
|
|
563
|
+
if (!slot || !slot.block.customInput)
|
|
564
|
+
continue;
|
|
565
|
+
pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, getCustomToolCallInput(slot.block) + event.delta, false));
|
|
566
|
+
}
|
|
567
|
+
else if (event.type === "response.custom_tool_call_input.done") {
|
|
568
|
+
const slot = getSlot(event.output_index, "toolCall");
|
|
569
|
+
if (!slot || !slot.block.customInput)
|
|
570
|
+
continue;
|
|
571
|
+
pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, event.input, true));
|
|
572
|
+
}
|
|
573
|
+
else if (event.type === "response.output_item.done") {
|
|
574
|
+
const item = event.item;
|
|
575
|
+
applyMessagePhaseStopReason(item);
|
|
576
|
+
const slot = getOrCreateSlot(event.output_index, item);
|
|
577
|
+
if (item.type === "reasoning" && slot?.type === "thinking") {
|
|
578
|
+
const summaryText = item.summary?.map((s) => s.text).join("\n\n") || "";
|
|
579
|
+
const contentText = item.content?.map((c) => c.text).join("\n\n") || "";
|
|
580
|
+
slot.block.thinking = summaryText || contentText || slot.block.thinking;
|
|
581
|
+
slot.block.thinkingSignature = JSON.stringify(item);
|
|
582
|
+
reasoningBlocksById.set(item.id, slot.block);
|
|
583
|
+
stream.push({
|
|
584
|
+
type: "thinking_end",
|
|
585
|
+
contentIndex: slot.contentIndex,
|
|
586
|
+
content: slot.block.thinking,
|
|
587
|
+
partial: output,
|
|
588
|
+
});
|
|
589
|
+
outputSlots.delete(event.output_index);
|
|
590
|
+
}
|
|
591
|
+
else if (item.type === "message" && slot?.type === "text") {
|
|
592
|
+
slot.block.text = item.content?.map((c) => (c.type === "output_text" ? c.text : c.refusal)).join("") || "";
|
|
593
|
+
slot.block.textSignature = encodeTextSignatureV1(item.id, item.phase ?? undefined);
|
|
594
|
+
stream.push({
|
|
595
|
+
type: "text_end",
|
|
596
|
+
contentIndex: slot.contentIndex,
|
|
597
|
+
content: slot.block.text,
|
|
598
|
+
partial: output,
|
|
599
|
+
});
|
|
600
|
+
outputSlots.delete(event.output_index);
|
|
601
|
+
}
|
|
602
|
+
else if (item.type === "function_call" &&
|
|
603
|
+
slot?.type === "toolCall" &&
|
|
604
|
+
slot.block.partialJson !== undefined) {
|
|
605
|
+
slot.block.arguments = parseStreamingJson(item.arguments || slot.block.partialJson || "{}");
|
|
606
|
+
// Finalize in-place and strip the scratch buffer so replay only
|
|
607
|
+
// carries parsed arguments.
|
|
608
|
+
delete slot.block.partialJson;
|
|
609
|
+
stream.push({
|
|
610
|
+
type: "toolcall_end",
|
|
611
|
+
contentIndex: slot.contentIndex,
|
|
612
|
+
toolCall: slot.block,
|
|
613
|
+
partial: output,
|
|
614
|
+
});
|
|
615
|
+
outputSlots.delete(event.output_index);
|
|
616
|
+
}
|
|
617
|
+
else if (item.type === "custom_tool_call" && slot?.type === "toolCall" && slot.block.customInput) {
|
|
618
|
+
pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, item.input ?? getCustomToolCallInput(slot.block), true));
|
|
619
|
+
delete slot.block.customInput;
|
|
620
|
+
stream.push({
|
|
621
|
+
type: "toolcall_end",
|
|
622
|
+
contentIndex: slot.contentIndex,
|
|
623
|
+
toolCall: slot.block,
|
|
624
|
+
partial: output,
|
|
625
|
+
});
|
|
626
|
+
outputSlots.delete(event.output_index);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
else if (event.type === "response.completed" || event.type === "response.incomplete") {
|
|
630
|
+
finalizeResponse(event.response);
|
|
631
|
+
}
|
|
632
|
+
else if (event.type === "error") {
|
|
633
|
+
throw new Error(`Error Code ${event.code}: ${event.message}` || "Unknown error");
|
|
634
|
+
}
|
|
635
|
+
else if (event.type === "response.failed") {
|
|
636
|
+
sawTerminalResponseEvent = true;
|
|
637
|
+
output.rawStopReason = event.response?.status;
|
|
638
|
+
const error = event.response?.error;
|
|
639
|
+
const details = event.response?.incomplete_details;
|
|
640
|
+
const msg = error
|
|
641
|
+
? `${error.code || "unknown"}: ${error.message || "no message"}`
|
|
642
|
+
: details?.reason
|
|
643
|
+
? `incomplete: ${details.reason}`
|
|
644
|
+
: "Unknown error (no error details in response)";
|
|
645
|
+
throw new Error(msg);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
if (!sawTerminalResponseEvent) {
|
|
649
|
+
throw new Error("OpenAI Responses stream ended before a terminal response event");
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function mapStopReason(status, incompleteReason) {
|
|
653
|
+
if (!status)
|
|
654
|
+
return { stopReason: "stop" };
|
|
655
|
+
switch (status) {
|
|
656
|
+
case "completed":
|
|
657
|
+
return { stopReason: "stop" };
|
|
658
|
+
case "incomplete":
|
|
659
|
+
if (incompleteReason === "max_output_tokens") {
|
|
660
|
+
return { stopReason: "length" };
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
stopReason: "error",
|
|
664
|
+
errorMessage: incompleteReason
|
|
665
|
+
? `Response incomplete: ${incompleteReason}`
|
|
666
|
+
: "Response incomplete without a provider reason",
|
|
667
|
+
};
|
|
668
|
+
case "failed":
|
|
669
|
+
case "cancelled":
|
|
670
|
+
return { stopReason: "error" };
|
|
671
|
+
// These two are wonky ...
|
|
672
|
+
case "in_progress":
|
|
673
|
+
case "queued":
|
|
674
|
+
return { stopReason: "stop" };
|
|
675
|
+
default: {
|
|
676
|
+
const _exhaustive = status;
|
|
677
|
+
throw new Error(`Unhandled stop reason: ${_exhaustive}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
//# sourceMappingURL=openai-responses-shared.js.map
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
const OPENAI_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
696
|
+
// OpenAI Responses rejects max_output_tokens below 16: https://github.com/earendil-works/pi/issues/6265
|
|
697
|
+
const OPENAI_RESPONSES_MIN_OUTPUT_TOKENS = 16;
|
|
698
|
+
function hasHeader(headers, name) {
|
|
699
|
+
if (!headers)
|
|
700
|
+
return false;
|
|
701
|
+
const expected = name.toLowerCase();
|
|
702
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
703
|
+
if (key.toLowerCase() === expected && value !== null && value.trim().length > 0)
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
function getClientApiKey(provider, apiKey, headers) {
|
|
709
|
+
if (apiKey)
|
|
710
|
+
return apiKey;
|
|
711
|
+
if (hasHeader(headers, "authorization") || hasHeader(headers, "cf-aig-authorization"))
|
|
712
|
+
return "unused";
|
|
713
|
+
throw new Error(`No API key for provider: ${provider}`);
|
|
714
|
+
}
|
|
715
|
+
function detectSessionAffinityFormat(model) {
|
|
716
|
+
return model.provider === "openrouter" || model.baseUrl.includes("openrouter.ai") ? "openrouter" : "openai";
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Resolve cache retention preference.
|
|
720
|
+
* Defaults to "short" and uses PI_CACHE_RETENTION for backward compatibility.
|
|
721
|
+
*/
|
|
722
|
+
function resolveCacheRetention(cacheRetention, env) {
|
|
723
|
+
if (cacheRetention) {
|
|
724
|
+
return cacheRetention;
|
|
725
|
+
}
|
|
726
|
+
if (getProviderEnvValue("PI_CACHE_RETENTION", env) === "long") {
|
|
727
|
+
return "long";
|
|
728
|
+
}
|
|
729
|
+
return "short";
|
|
730
|
+
}
|
|
731
|
+
function getCompat(model) {
|
|
732
|
+
return {
|
|
733
|
+
supportsDeveloperRole: model.compat?.supportsDeveloperRole ?? true,
|
|
734
|
+
sessionAffinityFormat: model.compat?.sessionAffinityFormat ?? detectSessionAffinityFormat(model),
|
|
735
|
+
supportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true,
|
|
736
|
+
supportsStrictMode: model.compat?.supportsStrictMode ?? false,
|
|
737
|
+
supportsOpenAIGrammarTools: model.compat?.supportsOpenAIGrammarTools ?? false,
|
|
738
|
+
supportsToolSearch: model.compat?.supportsToolSearch ?? false,
|
|
739
|
+
supportsExplicitPromptCacheMode: model.compat?.supportsExplicitPromptCacheMode ?? false,
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
function getPromptCacheRetention(compat, cacheRetention) {
|
|
743
|
+
return cacheRetention === "long" && compat.supportsLongCacheRetention ? "24h" : undefined;
|
|
744
|
+
}
|
|
745
|
+
function formatOpenAIResponsesError(error) {
|
|
746
|
+
return formatProviderError(normalizeProviderError(error), "OpenAI API error");
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Generate function for OpenAI Responses API
|
|
750
|
+
*/
|
|
751
|
+
const openai_responses_stream = (model, context, options) => {
|
|
752
|
+
const stream = new AssistantMessageEventStream();
|
|
753
|
+
// Start async processing
|
|
754
|
+
(async () => {
|
|
755
|
+
const output = {
|
|
756
|
+
role: "assistant",
|
|
757
|
+
content: [],
|
|
758
|
+
api: model.api,
|
|
759
|
+
provider: model.provider,
|
|
760
|
+
model: model.id,
|
|
761
|
+
usage: {
|
|
762
|
+
input: 0,
|
|
763
|
+
output: 0,
|
|
764
|
+
cacheRead: 0,
|
|
765
|
+
cacheWrite: 0,
|
|
766
|
+
totalTokens: 0,
|
|
767
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
768
|
+
},
|
|
769
|
+
stopReason: "pending",
|
|
770
|
+
timestamp: Date.now(),
|
|
771
|
+
};
|
|
772
|
+
try {
|
|
773
|
+
// Create OpenAI client
|
|
774
|
+
const apiKey = getClientApiKey(model.provider, options?.apiKey, options?.headers);
|
|
775
|
+
const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
|
|
776
|
+
const cacheSessionId = cacheRetention === "none" ? undefined : options?.sessionId;
|
|
777
|
+
const compat = getCompat(model);
|
|
778
|
+
const grammarToolInputProperties = createGrammarToolInputProperties(context.tools, compat.supportsOpenAIGrammarTools);
|
|
779
|
+
const client = createClient(model, context, apiKey, options?.headers, options?.fetch, cacheSessionId);
|
|
780
|
+
let params = buildParams(model, context, options, compat, grammarToolInputProperties);
|
|
781
|
+
const nextParams = await options?.onPayload?.(params, model);
|
|
782
|
+
if (nextParams !== undefined) {
|
|
783
|
+
params = nextParams;
|
|
784
|
+
}
|
|
785
|
+
const requestOptions = {
|
|
786
|
+
...(options?.signal ? { signal: options.signal } : {}),
|
|
787
|
+
...(options?.timeoutMs !== undefined ? { timeout: options.timeoutMs } : {}),
|
|
788
|
+
maxRetries: 0,
|
|
789
|
+
};
|
|
790
|
+
const { data: openaiStream, response } = await retryProviderRequest(() => client.responses.create(params, requestOptions).withResponse(), {
|
|
791
|
+
maxRetries: options?.maxRetries,
|
|
792
|
+
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
793
|
+
signal: options?.signal,
|
|
794
|
+
});
|
|
795
|
+
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
796
|
+
stream.push({ type: "start", partial: output });
|
|
797
|
+
await processResponsesStream(openaiStream, output, stream, model, {
|
|
798
|
+
serviceTier: options?.serviceTier,
|
|
799
|
+
grammarToolInputProperties,
|
|
800
|
+
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),
|
|
801
|
+
});
|
|
802
|
+
if (options?.signal?.aborted) {
|
|
803
|
+
throw new Error("Request was aborted");
|
|
804
|
+
}
|
|
805
|
+
if (output.stopReason === "pending") {
|
|
806
|
+
throw new Error("OpenAI Responses stream ended without a stop reason");
|
|
807
|
+
}
|
|
808
|
+
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
809
|
+
throw new Error(output.errorMessage || "An unknown error occurred");
|
|
810
|
+
}
|
|
811
|
+
stream.push({ type: "done", reason: output.stopReason, message: output });
|
|
812
|
+
stream.end();
|
|
813
|
+
}
|
|
814
|
+
catch (error) {
|
|
815
|
+
for (const block of output.content) {
|
|
816
|
+
delete block.index;
|
|
817
|
+
// Streaming scratch buffers are only used during parsing; never persist them.
|
|
818
|
+
delete block.partialJson;
|
|
819
|
+
delete block.customInput;
|
|
820
|
+
}
|
|
821
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
822
|
+
output.errorMessage = formatOpenAIResponsesError(error);
|
|
823
|
+
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
824
|
+
stream.end();
|
|
825
|
+
}
|
|
826
|
+
})();
|
|
827
|
+
return stream;
|
|
828
|
+
};
|
|
829
|
+
const streamSimple = (model, context, options) => {
|
|
830
|
+
getClientApiKey(model.provider, options?.apiKey, options?.headers);
|
|
831
|
+
const base = buildBaseOptions(model, context, options, options?.apiKey);
|
|
832
|
+
const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
|
|
833
|
+
const reasoningEffort = clampedReasoning === "off" ? undefined : clampedReasoning;
|
|
834
|
+
return openai_responses_stream(model, context, {
|
|
835
|
+
...base,
|
|
836
|
+
reasoningEffort,
|
|
837
|
+
});
|
|
838
|
+
};
|
|
839
|
+
function createClient(model, context, apiKey, optionsHeaders, fetch, sessionId) {
|
|
840
|
+
const compat = getCompat(model);
|
|
841
|
+
const headers = { ...model.headers };
|
|
842
|
+
if (model.provider === "github-copilot") {
|
|
843
|
+
const hasImages = hasCopilotVisionInput(context.messages);
|
|
844
|
+
const copilotHeaders = buildCopilotDynamicHeaders({
|
|
845
|
+
messages: context.messages,
|
|
846
|
+
hasImages,
|
|
847
|
+
});
|
|
848
|
+
Object.assign(headers, copilotHeaders);
|
|
849
|
+
}
|
|
850
|
+
if (sessionId) {
|
|
851
|
+
if (compat.sessionAffinityFormat === "openrouter") {
|
|
852
|
+
headers["x-session-id"] = sessionId;
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
if (compat.sessionAffinityFormat === "openai") {
|
|
856
|
+
headers.session_id = sessionId;
|
|
857
|
+
}
|
|
858
|
+
headers["x-client-request-id"] = sessionId;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
// Merge options headers last so they can override defaults
|
|
862
|
+
if (optionsHeaders) {
|
|
863
|
+
Object.assign(headers, optionsHeaders);
|
|
864
|
+
}
|
|
865
|
+
return new OpenAI({
|
|
866
|
+
apiKey,
|
|
867
|
+
baseURL: model.baseUrl,
|
|
868
|
+
dangerouslyAllowBrowser: true,
|
|
869
|
+
fetch,
|
|
870
|
+
defaultHeaders: headers,
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
function buildParams(model, context, options, compat = getCompat(model), grammarToolInputProperties = createGrammarToolInputProperties(context.tools, compat.supportsOpenAIGrammarTools)) {
|
|
874
|
+
const toolPlacement = splitDeferredTools(context, compat.supportsToolSearch);
|
|
875
|
+
const messages = convertResponsesMessages(model, context, OPENAI_TOOL_CALL_PROVIDERS, {
|
|
876
|
+
grammarToolInputProperties,
|
|
877
|
+
deferredTools: toolPlacement.deferred,
|
|
878
|
+
toolOptions: {
|
|
879
|
+
supportsStrictMode: compat.supportsStrictMode,
|
|
880
|
+
supportsOpenAIGrammarTools: compat.supportsOpenAIGrammarTools,
|
|
881
|
+
},
|
|
882
|
+
});
|
|
883
|
+
const cacheRetention = resolveCacheRetention(options?.cacheRetention, options?.env);
|
|
884
|
+
const disableImplicitPromptCache = cacheRetention === "none" && compat.supportsExplicitPromptCacheMode;
|
|
885
|
+
const params = {
|
|
886
|
+
model: model.id,
|
|
887
|
+
input: messages,
|
|
888
|
+
stream: true,
|
|
889
|
+
prompt_cache_key: cacheRetention === "none" ? undefined : clampOpenAIPromptCacheKey(options?.sessionId),
|
|
890
|
+
prompt_cache_retention: getPromptCacheRetention(compat, cacheRetention),
|
|
891
|
+
prompt_cache_options: disableImplicitPromptCache ? { mode: "explicit" } : undefined,
|
|
892
|
+
store: false,
|
|
893
|
+
};
|
|
894
|
+
if (options?.maxTokens) {
|
|
895
|
+
params.max_output_tokens = Math.max(options.maxTokens, OPENAI_RESPONSES_MIN_OUTPUT_TOKENS);
|
|
896
|
+
}
|
|
897
|
+
if (options?.temperature !== undefined) {
|
|
898
|
+
params.temperature = options?.temperature;
|
|
899
|
+
}
|
|
900
|
+
if (options?.serviceTier !== undefined) {
|
|
901
|
+
params.service_tier = options.serviceTier;
|
|
902
|
+
}
|
|
903
|
+
if (toolPlacement.immediate.length > 0) {
|
|
904
|
+
params.tools = convertResponsesTools(toolPlacement.immediate, {
|
|
905
|
+
supportsStrictMode: compat.supportsStrictMode,
|
|
906
|
+
supportsOpenAIGrammarTools: compat.supportsOpenAIGrammarTools,
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
if (options?.toolChoice !== undefined) {
|
|
910
|
+
params.tool_choice = options.toolChoice;
|
|
911
|
+
}
|
|
912
|
+
if (model.reasoning) {
|
|
913
|
+
if (options?.reasoningEffort || options?.reasoningSummary) {
|
|
914
|
+
const effort = options?.reasoningEffort
|
|
915
|
+
? (model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort)
|
|
916
|
+
: "medium";
|
|
917
|
+
params.reasoning = {
|
|
918
|
+
effort: effort,
|
|
919
|
+
summary: options?.reasoningSummary || "auto",
|
|
920
|
+
};
|
|
921
|
+
params.include = ["reasoning.encrypted_content"];
|
|
922
|
+
}
|
|
923
|
+
else if (model.provider !== "github-copilot" && model.thinkingLevelMap?.off !== null) {
|
|
924
|
+
params.reasoning = {
|
|
925
|
+
effort: (model.thinkingLevelMap?.off ?? "none"),
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
if (model.provider === "xai")
|
|
929
|
+
params.include = ["reasoning.encrypted_content"];
|
|
930
|
+
}
|
|
931
|
+
// Last so custom keys override the named request fields.
|
|
932
|
+
if (options?.samplingParams) {
|
|
933
|
+
Object.assign(params, options.samplingParams);
|
|
934
|
+
}
|
|
935
|
+
return params;
|
|
936
|
+
}
|
|
937
|
+
function getServiceTierCostMultiplier(model, serviceTier) {
|
|
938
|
+
switch (serviceTier) {
|
|
939
|
+
case "flex":
|
|
940
|
+
return 0.5;
|
|
941
|
+
case "priority":
|
|
942
|
+
return model.id === "gpt-5.5" ? 2.5 : 2;
|
|
943
|
+
default:
|
|
944
|
+
return 1;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
function applyServiceTierPricing(usage, serviceTier, model) {
|
|
948
|
+
const multiplier = getServiceTierCostMultiplier(model, serviceTier);
|
|
949
|
+
if (multiplier === 1)
|
|
950
|
+
return;
|
|
951
|
+
usage.cost.input *= multiplier;
|
|
952
|
+
usage.cost.output *= multiplier;
|
|
953
|
+
usage.cost.cacheRead *= multiplier;
|
|
954
|
+
usage.cost.cacheWrite *= multiplier;
|
|
955
|
+
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
956
|
+
}
|
|
957
|
+
//# sourceMappingURL=openai-responses.js.map
|
|
958
|
+
export { openai_responses_namespaceObject };
|