@codeworksh/aikit 0.6.0-dev.20260613095948 → 0.7.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/cli.d.mts +1 -1
- package/cli.mjs +7 -238
- package/cli.mjs.map +1 -1
- package/{codex-C36oU6yH.mjs → codex-DyQHzQmn.mjs} +24 -24
- package/codex-DyQHzQmn.mjs.map +1 -0
- package/index.d.mts +7948 -6669
- package/index.mjs +788 -614
- package/index.mjs.map +1 -1
- package/model-En2JLLXC.d.mts +328 -0
- package/modelgen-BcsqB44a.mjs +325 -0
- package/modelgen-BcsqB44a.mjs.map +1 -0
- package/modelgen.d.mts +9 -0
- package/modelgen.mjs +2 -0
- package/oauth/openai/codex.mjs +1 -1
- package/openai-codex-FcyE8vxh.mjs +2 -0
- package/openai-codex-_3XkJ4bs.mjs +1158 -0
- package/openai-codex-_3XkJ4bs.mjs.map +1 -0
- package/package.json +18 -13
- package/{registry-DUKrGxt4.mjs → registry-CMkIE_B4.mjs} +591 -503
- package/{registry-DUKrGxt4.mjs.map → registry-CMkIE_B4.mjs.map} +1 -1
- package/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/codex-C36oU6yH.mjs.map +0 -1
- package/openai-codex-CZrBnLAJ.mjs +0 -753
- package/openai-codex-CZrBnLAJ.mjs.map +0 -1
- package/openai-codex-bzfev3us.mjs +0 -2
|
@@ -0,0 +1,1158 @@
|
|
|
1
|
+
import { o as getOpenAICodexAccountId } from "./codex-DyQHzQmn.mjs";
|
|
2
|
+
import { APICallError, LoadAPIKeyError, NoSuchModelError } from "@ai-sdk/provider";
|
|
3
|
+
//#region src/providers/openai-codex/codex-tools.ts
|
|
4
|
+
function appendOpenAICodexGrammarInputJsonDelta(buffer, inputProperty, nextInput, close) {
|
|
5
|
+
if (buffer.closed) {
|
|
6
|
+
if (close && nextInput === buffer.input) return void 0;
|
|
7
|
+
throw new Error(`grammar tool input for property "${inputProperty}" changed after it was closed`);
|
|
8
|
+
}
|
|
9
|
+
if (!nextInput.startsWith(buffer.input)) throw new Error(`grammar tool input for property "${inputProperty}" changed non-monotonically`);
|
|
10
|
+
const inputDelta = nextInput.slice(buffer.input.length);
|
|
11
|
+
if (!close && inputDelta.length === 0) return void 0;
|
|
12
|
+
let delta = "";
|
|
13
|
+
if (!buffer.started) {
|
|
14
|
+
delta += `{${JSON.stringify(inputProperty)}:"`;
|
|
15
|
+
buffer.started = true;
|
|
16
|
+
}
|
|
17
|
+
delta += JSON.stringify(inputDelta).slice(1, -1);
|
|
18
|
+
buffer.input = nextInput;
|
|
19
|
+
if (close) {
|
|
20
|
+
delta += "\"}";
|
|
21
|
+
buffer.closed = true;
|
|
22
|
+
}
|
|
23
|
+
return delta;
|
|
24
|
+
}
|
|
25
|
+
function inferGrammarInputProperty(parameters) {
|
|
26
|
+
const schema = parameters;
|
|
27
|
+
if (schema.type !== "object") throw new Error("grammar constrained sampling requires an object parameter schema");
|
|
28
|
+
if (!Array.isArray(schema.required) || schema.required.length !== 1 || typeof schema.required[0] !== "string") throw new Error("grammar constrained sampling requires exactly one required string property");
|
|
29
|
+
const inputProperty = schema.required[0];
|
|
30
|
+
if (!schema.properties?.[inputProperty]) throw new Error(`grammar constrained sampling requires a properties entry for ${inputProperty}`);
|
|
31
|
+
if (schema.properties[inputProperty]?.type !== "string") throw new Error(`grammar constrained sampling property ${inputProperty} must have type string`);
|
|
32
|
+
return inputProperty;
|
|
33
|
+
}
|
|
34
|
+
function resolveOpenAICodexToolConstraint(tool, compat) {
|
|
35
|
+
const config = tool.constrainedSampling;
|
|
36
|
+
if (!config) return void 0;
|
|
37
|
+
if (config.type === "json_schema") {
|
|
38
|
+
if (compat?.supportsStrictMode ?? true) return {
|
|
39
|
+
type: "json_schema",
|
|
40
|
+
strict: true
|
|
41
|
+
};
|
|
42
|
+
if (config.strict === "require") throw new Error(`Tool "${tool.name}" requires JSON-schema constrained sampling, but strict tools are unsupported.`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!(compat?.supportsOpenAIGrammarTools ?? false)) return void 0;
|
|
46
|
+
const lark = config.variants.openai_lark;
|
|
47
|
+
const regex = config.variants.openai_regex;
|
|
48
|
+
const hasLark = typeof lark === "string" && lark.trim().length > 0;
|
|
49
|
+
const hasRegex = typeof regex === "string" && regex.trim().length > 0;
|
|
50
|
+
if (!hasLark && !hasRegex) throw new Error(`Tool "${tool.name}" cannot use grammar constrained sampling: no supported grammar variant was provided.`);
|
|
51
|
+
try {
|
|
52
|
+
return {
|
|
53
|
+
type: "grammar",
|
|
54
|
+
format: hasLark ? "lark" : "regex",
|
|
55
|
+
definition: hasLark ? lark : regex,
|
|
56
|
+
inputProperty: inferGrammarInputProperty(tool.parameters)
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
60
|
+
throw new Error(`Tool "${tool.name}" cannot use grammar constrained sampling: ${message}.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const openAICodexTools = { custom(args) {
|
|
64
|
+
return {
|
|
65
|
+
name: args.name,
|
|
66
|
+
description: args.description,
|
|
67
|
+
parameters: args.parameters,
|
|
68
|
+
constrainedSampling: {
|
|
69
|
+
type: "grammar",
|
|
70
|
+
variants: args.format.syntax === "lark" ? { openai_lark: args.format.definition } : { openai_regex: args.format.definition }
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
} };
|
|
74
|
+
const TOOL_SEARCH_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
75
|
+
"gpt-5.4",
|
|
76
|
+
"gpt-5.4-mini",
|
|
77
|
+
"gpt-5.5",
|
|
78
|
+
"gpt-5.6-luna",
|
|
79
|
+
"gpt-5.6-sol",
|
|
80
|
+
"gpt-5.6-terra"
|
|
81
|
+
]);
|
|
82
|
+
const ADDITIONAL_TOOLS_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
83
|
+
"gpt-5.6-luna",
|
|
84
|
+
"gpt-5.6-sol",
|
|
85
|
+
"gpt-5.6-terra"
|
|
86
|
+
]);
|
|
87
|
+
function resolveOpenAICodexDeferredToolsMode(modelId, compat) {
|
|
88
|
+
if (compat?.supportsAdditionalTools ?? ADDITIONAL_TOOLS_MODEL_IDS.has(modelId)) return "additional-tools";
|
|
89
|
+
return compat?.supportsToolSearch ?? TOOL_SEARCH_MODEL_IDS.has(modelId) ? "tool-search" : void 0;
|
|
90
|
+
}
|
|
91
|
+
function prepareOpenAICodexTools({ tools, toolChoice, deferredToolNames }) {
|
|
92
|
+
const warnings = [];
|
|
93
|
+
const deferredCodexTools = /* @__PURE__ */ new Map();
|
|
94
|
+
const grammarToolInputProperties = /* @__PURE__ */ new Map();
|
|
95
|
+
if (!tools || tools.length === 0) return {
|
|
96
|
+
codexTools: void 0,
|
|
97
|
+
deferredCodexTools,
|
|
98
|
+
grammarToolInputProperties,
|
|
99
|
+
codexToolChoice: void 0,
|
|
100
|
+
warnings
|
|
101
|
+
};
|
|
102
|
+
const codexTools = [];
|
|
103
|
+
for (const tool of tools) {
|
|
104
|
+
if (tool.type !== "function") {
|
|
105
|
+
warnings.push({
|
|
106
|
+
type: "unsupported",
|
|
107
|
+
feature: `tool type ${tool.type}`,
|
|
108
|
+
details: `OpenAI Codex only supports function tools; ignoring ${tool.name}.`
|
|
109
|
+
});
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const grammar = (tool.providerOptions?.["openai-codex"])?.grammar;
|
|
113
|
+
const codexTool = grammar?.type === "grammar" ? {
|
|
114
|
+
type: "custom",
|
|
115
|
+
name: tool.name,
|
|
116
|
+
...tool.description !== void 0 && { description: tool.description },
|
|
117
|
+
format: {
|
|
118
|
+
type: "grammar",
|
|
119
|
+
syntax: grammar.format,
|
|
120
|
+
definition: grammar.definition
|
|
121
|
+
}
|
|
122
|
+
} : {
|
|
123
|
+
type: "function",
|
|
124
|
+
name: tool.name,
|
|
125
|
+
...tool.description !== void 0 && { description: tool.description },
|
|
126
|
+
parameters: tool.inputSchema,
|
|
127
|
+
strict: tool.strict ?? null
|
|
128
|
+
};
|
|
129
|
+
if (grammar?.type === "grammar") grammarToolInputProperties.set(tool.name, grammar.inputProperty);
|
|
130
|
+
if (deferredToolNames?.has(tool.name)) deferredCodexTools.set(tool.name, codexTool);
|
|
131
|
+
else codexTools.push(codexTool);
|
|
132
|
+
}
|
|
133
|
+
let codexToolChoice;
|
|
134
|
+
switch (toolChoice?.type) {
|
|
135
|
+
case void 0: break;
|
|
136
|
+
case "auto":
|
|
137
|
+
case "none":
|
|
138
|
+
case "required":
|
|
139
|
+
codexToolChoice = toolChoice.type;
|
|
140
|
+
break;
|
|
141
|
+
case "tool":
|
|
142
|
+
warnings.push({
|
|
143
|
+
type: "unsupported",
|
|
144
|
+
feature: "toolChoice tool name",
|
|
145
|
+
details: "OpenAI Codex does not support forcing a specific tool; using 'required' instead."
|
|
146
|
+
});
|
|
147
|
+
codexToolChoice = "required";
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
codexTools: codexTools.length > 0 ? codexTools : void 0,
|
|
151
|
+
deferredCodexTools,
|
|
152
|
+
grammarToolInputProperties,
|
|
153
|
+
codexToolChoice,
|
|
154
|
+
warnings
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/utils/hash.ts
|
|
159
|
+
/** Fast deterministic hash for shortening provider wire identifiers. */
|
|
160
|
+
function shortHash(value) {
|
|
161
|
+
let high = 3735928559;
|
|
162
|
+
let low = 1103547991;
|
|
163
|
+
for (let index = 0; index < value.length; index++) {
|
|
164
|
+
const character = value.charCodeAt(index);
|
|
165
|
+
high = Math.imul(high ^ character, 2654435761);
|
|
166
|
+
low = Math.imul(low ^ character, 1597334677);
|
|
167
|
+
}
|
|
168
|
+
high = Math.imul(high ^ high >>> 16, 2246822507) ^ Math.imul(low ^ low >>> 13, 3266489909);
|
|
169
|
+
low = Math.imul(low ^ low >>> 16, 2246822507) ^ Math.imul(high ^ high >>> 13, 3266489909);
|
|
170
|
+
return (low >>> 0).toString(36) + (high >>> 0).toString(36);
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/providers/openai-codex/codex-error.ts
|
|
174
|
+
function isTerminalRateLimitError(body) {
|
|
175
|
+
return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(body);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Turn a Codex error payload into a human-friendly message. ChatGPT plan usage
|
|
179
|
+
* limits deserve a clearer message than the raw backend error.
|
|
180
|
+
*/
|
|
181
|
+
function openAICodexErrorMessage(status, body, statusText) {
|
|
182
|
+
let message = body || statusText || "OpenAI Codex request failed";
|
|
183
|
+
try {
|
|
184
|
+
const error = JSON.parse(body)?.error;
|
|
185
|
+
if (error) {
|
|
186
|
+
const code = error.code ?? error.type ?? "";
|
|
187
|
+
if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || status === 429) {
|
|
188
|
+
const plan = error.plan_type ? ` (${error.plan_type.toLowerCase()} plan)` : "";
|
|
189
|
+
const minutes = error.resets_at ? Math.max(0, Math.round((error.resets_at * 1e3 - Date.now()) / 6e4)) : void 0;
|
|
190
|
+
return `You have hit your ChatGPT usage limit${plan}.${minutes === void 0 ? "" : ` Try again in ~${minutes} min.`}`.trim();
|
|
191
|
+
}
|
|
192
|
+
message = error.message || message;
|
|
193
|
+
}
|
|
194
|
+
} catch {}
|
|
195
|
+
return message;
|
|
196
|
+
}
|
|
197
|
+
async function createOpenAICodexAPICallError(args) {
|
|
198
|
+
const { response, url, requestBodyValues } = args;
|
|
199
|
+
const responseBody = await response.text().catch(() => "");
|
|
200
|
+
const responseHeaders = {};
|
|
201
|
+
response.headers.forEach((value, key) => {
|
|
202
|
+
responseHeaders[key] = value;
|
|
203
|
+
});
|
|
204
|
+
return new APICallError({
|
|
205
|
+
message: openAICodexErrorMessage(response.status, responseBody, response.statusText),
|
|
206
|
+
url,
|
|
207
|
+
requestBodyValues,
|
|
208
|
+
statusCode: response.status,
|
|
209
|
+
responseHeaders,
|
|
210
|
+
responseBody,
|
|
211
|
+
isRetryable: response.status === 408 || response.status === 409 || response.status === 429 && !isTerminalRateLimitError(responseBody) || response.status >= 500
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/providers/openai-codex/codex-prompt.ts
|
|
216
|
+
const DEFAULT_INSTRUCTIONS = "You are a helpful assistant.";
|
|
217
|
+
function splitToolCallId(toolCallId) {
|
|
218
|
+
const separator = toolCallId.indexOf("|");
|
|
219
|
+
if (separator === -1) return {
|
|
220
|
+
callId: toolCallId,
|
|
221
|
+
itemId: toolCallId
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
callId: toolCallId.slice(0, separator),
|
|
225
|
+
itemId: toolCallId.slice(separator + 1)
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function joinToolCallId(callId, itemId) {
|
|
229
|
+
return `${callId}|${itemId}`;
|
|
230
|
+
}
|
|
231
|
+
function uint8ArrayToBase64(bytes) {
|
|
232
|
+
let binary = "";
|
|
233
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
234
|
+
return btoa(binary);
|
|
235
|
+
}
|
|
236
|
+
function imageUrlFromFilePart(part) {
|
|
237
|
+
const data = part.data;
|
|
238
|
+
if (data instanceof URL) return data.toString();
|
|
239
|
+
if (typeof data === "string") {
|
|
240
|
+
if (data.startsWith("data:") || data.startsWith("http://") || data.startsWith("https://")) return data;
|
|
241
|
+
return `data:${part.mediaType};base64,${data}`;
|
|
242
|
+
}
|
|
243
|
+
if (data instanceof Uint8Array) return `data:${part.mediaType};base64,${uint8ArrayToBase64(data)}`;
|
|
244
|
+
if ("type" in data) {
|
|
245
|
+
if (data.type === "url") return data.url.toString();
|
|
246
|
+
if (data.type === "data") return typeof data.data === "string" ? `data:${part.mediaType};base64,${data.data}` : `data:${part.mediaType};base64,${uint8ArrayToBase64(data.data)}`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function toolResultOutput(output, supportsImages) {
|
|
250
|
+
switch (output.type) {
|
|
251
|
+
case "text":
|
|
252
|
+
case "error-text": return output.value || "(no tool output)";
|
|
253
|
+
case "json":
|
|
254
|
+
case "error-json": return JSON.stringify(output.value);
|
|
255
|
+
case "execution-denied": return output.reason ?? "Tool execution denied";
|
|
256
|
+
case "content": {
|
|
257
|
+
const text = output.value.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
258
|
+
const images = [];
|
|
259
|
+
if (supportsImages) {
|
|
260
|
+
for (const part of output.value) if (part.type === "file-data" && part.mediaType.startsWith("image/")) images.push({
|
|
261
|
+
type: "input_image",
|
|
262
|
+
image_url: `data:${part.mediaType};base64,${part.data}`,
|
|
263
|
+
detail: "auto"
|
|
264
|
+
});
|
|
265
|
+
else if (part.type === "file-url") images.push({
|
|
266
|
+
type: "input_image",
|
|
267
|
+
image_url: part.url,
|
|
268
|
+
detail: "auto"
|
|
269
|
+
});
|
|
270
|
+
else if (part.type === "file" && part.mediaType.startsWith("image/")) {
|
|
271
|
+
const data = part.data;
|
|
272
|
+
if (data.type === "url") images.push({
|
|
273
|
+
type: "input_image",
|
|
274
|
+
image_url: data.url.toString(),
|
|
275
|
+
detail: "auto"
|
|
276
|
+
});
|
|
277
|
+
else if (data.type === "data") {
|
|
278
|
+
const encoded = typeof data.data === "string" ? data.data : uint8ArrayToBase64(data.data);
|
|
279
|
+
images.push({
|
|
280
|
+
type: "input_image",
|
|
281
|
+
image_url: `data:${part.mediaType};base64,${encoded}`,
|
|
282
|
+
detail: "auto"
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (images.length === 0) return text || (output.value.length > 0 ? "(see attached image)" : "(no tool output)");
|
|
288
|
+
return [...text ? [{
|
|
289
|
+
type: "input_text",
|
|
290
|
+
text
|
|
291
|
+
}] : [], ...images];
|
|
292
|
+
}
|
|
293
|
+
default: return JSON.stringify(output);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function customToolInput(input, toolName, inputProperty) {
|
|
297
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) throw new Error(`Custom tool "${toolName}" requires object input with string property "${inputProperty}".`);
|
|
298
|
+
const value = input[inputProperty];
|
|
299
|
+
if (typeof value !== "string") throw new Error(`Custom tool "${toolName}" requires string input property "${inputProperty}".`);
|
|
300
|
+
return value;
|
|
301
|
+
}
|
|
302
|
+
function customToolOutput(output, supportsImages) {
|
|
303
|
+
const value = toolResultOutput(output, supportsImages);
|
|
304
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
305
|
+
}
|
|
306
|
+
function codexProviderMetadata(providerOptions) {
|
|
307
|
+
const codex = providerOptions?.["openai-codex"];
|
|
308
|
+
return typeof codex === "object" && codex !== null && !Array.isArray(codex) ? codex : void 0;
|
|
309
|
+
}
|
|
310
|
+
function addedToolNames(providerOptions) {
|
|
311
|
+
const names = codexProviderMetadata(providerOptions)?.addedToolNames;
|
|
312
|
+
return Array.isArray(names) ? names.filter((name) => typeof name === "string") : [];
|
|
313
|
+
}
|
|
314
|
+
function reasoningItem(providerOptions) {
|
|
315
|
+
const value = codexProviderMetadata(providerOptions)?.reasoningItem;
|
|
316
|
+
if (typeof value !== "string") return void 0;
|
|
317
|
+
try {
|
|
318
|
+
const parsed = JSON.parse(value);
|
|
319
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
320
|
+
const item = parsed;
|
|
321
|
+
return item.type === "reasoning" && typeof item.id === "string" ? item : void 0;
|
|
322
|
+
} catch {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/** Collect definitions that were introduced at a transcript load point rather than at request start. */
|
|
327
|
+
function collectOpenAICodexDeferredToolNames(prompt) {
|
|
328
|
+
const deferredNames = /* @__PURE__ */ new Set();
|
|
329
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
330
|
+
for (const message of prompt) {
|
|
331
|
+
if (message.role === "assistant") {
|
|
332
|
+
for (const part of message.content) {
|
|
333
|
+
if (part.type === "tool-call") usedNames.add(part.toolName);
|
|
334
|
+
if (part.type === "tool-result") {
|
|
335
|
+
for (const name of addedToolNames(part.providerOptions)) if (!usedNames.has(name)) deferredNames.add(name);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (message.role !== "tool") continue;
|
|
341
|
+
for (const part of message.content) {
|
|
342
|
+
if (part.type !== "tool-result") continue;
|
|
343
|
+
for (const name of addedToolNames(part.providerOptions)) if (!usedNames.has(name)) deferredNames.add(name);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return deferredNames;
|
|
347
|
+
}
|
|
348
|
+
function deferredToolCallId(toolCallId, names) {
|
|
349
|
+
return `aikit_tool_load_${shortHash(`${toolCallId}:${names.join(",")}`)}`;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Convert an AI SDK V3 prompt into the Codex Responses request shape.
|
|
353
|
+
* System messages become `instructions`; everything else maps to `input` items.
|
|
354
|
+
*/
|
|
355
|
+
function convertToOpenAICodexPrompt(prompt, options) {
|
|
356
|
+
let instructions;
|
|
357
|
+
const input = [];
|
|
358
|
+
let syntheticMessageCounter = 0;
|
|
359
|
+
const loadedToolNames = /* @__PURE__ */ new Set();
|
|
360
|
+
const appendDeferredTools = (toolCallId, names) => {
|
|
361
|
+
const tools = [];
|
|
362
|
+
for (const name of names) {
|
|
363
|
+
const tool = options?.deferredTools?.get(name);
|
|
364
|
+
if (!tool || loadedToolNames.has(name)) continue;
|
|
365
|
+
loadedToolNames.add(name);
|
|
366
|
+
tools.push(tool);
|
|
367
|
+
}
|
|
368
|
+
if (tools.length === 0) return;
|
|
369
|
+
if (options?.deferredToolsMode === "additional-tools") {
|
|
370
|
+
input.push({
|
|
371
|
+
type: "additional_tools",
|
|
372
|
+
role: "developer",
|
|
373
|
+
tools
|
|
374
|
+
});
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (options?.deferredToolsMode === "tool-search") {
|
|
378
|
+
const callId = deferredToolCallId(toolCallId, tools.map((tool) => tool.name));
|
|
379
|
+
input.push({
|
|
380
|
+
type: "tool_search_call",
|
|
381
|
+
call_id: callId,
|
|
382
|
+
execution: "client",
|
|
383
|
+
status: "completed",
|
|
384
|
+
arguments: {
|
|
385
|
+
query: tools.map((tool) => tool.name).join(" "),
|
|
386
|
+
limit: tools.length
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
input.push({
|
|
390
|
+
type: "tool_search_output",
|
|
391
|
+
call_id: callId,
|
|
392
|
+
execution: "client",
|
|
393
|
+
status: "completed",
|
|
394
|
+
tools: tools.map((tool) => ({
|
|
395
|
+
...tool,
|
|
396
|
+
defer_loading: true
|
|
397
|
+
}))
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
for (const message of prompt) switch (message.role) {
|
|
402
|
+
case "system":
|
|
403
|
+
instructions = instructions == null ? message.content : `${instructions}\n\n${message.content}`;
|
|
404
|
+
break;
|
|
405
|
+
case "user": {
|
|
406
|
+
const content = [];
|
|
407
|
+
for (const part of message.content) if (part.type === "text") content.push({
|
|
408
|
+
type: "input_text",
|
|
409
|
+
text: part.text
|
|
410
|
+
});
|
|
411
|
+
else if (part.type === "file" && part.mediaType.startsWith("image/")) {
|
|
412
|
+
const imageUrl = imageUrlFromFilePart(part);
|
|
413
|
+
if (imageUrl) content.push({
|
|
414
|
+
type: "input_image",
|
|
415
|
+
image_url: imageUrl,
|
|
416
|
+
detail: "auto"
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
if (content.length > 0) input.push({
|
|
420
|
+
role: "user",
|
|
421
|
+
content
|
|
422
|
+
});
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
case "assistant":
|
|
426
|
+
for (const part of message.content) if (part.type === "reasoning") {
|
|
427
|
+
const item = reasoningItem(part.providerOptions);
|
|
428
|
+
if (item) input.push(item);
|
|
429
|
+
} else if (part.type === "text") {
|
|
430
|
+
syntheticMessageCounter++;
|
|
431
|
+
const messageId = codexProviderMetadata(part.providerOptions)?.messageId;
|
|
432
|
+
input.push({
|
|
433
|
+
type: "message",
|
|
434
|
+
role: "assistant",
|
|
435
|
+
id: typeof messageId === "string" && messageId.length <= 64 ? messageId : `msg_aikit_${syntheticMessageCounter}`,
|
|
436
|
+
content: [{
|
|
437
|
+
type: "output_text",
|
|
438
|
+
text: part.text,
|
|
439
|
+
annotations: []
|
|
440
|
+
}],
|
|
441
|
+
status: "completed"
|
|
442
|
+
});
|
|
443
|
+
} else if (part.type === "tool-call") {
|
|
444
|
+
const { callId, itemId } = splitToolCallId(part.toolCallId);
|
|
445
|
+
const metadata = codexProviderMetadata(part.providerOptions);
|
|
446
|
+
const namespace = metadata?.namespace;
|
|
447
|
+
const inputProperty = options?.grammarToolInputProperties?.get(part.toolName);
|
|
448
|
+
if (inputProperty) input.push({
|
|
449
|
+
type: "custom_tool_call",
|
|
450
|
+
...metadata?.omitItemId === true || !itemId.startsWith("ctc_") ? {} : { id: itemId },
|
|
451
|
+
call_id: callId,
|
|
452
|
+
name: part.toolName,
|
|
453
|
+
input: customToolInput(part.input, part.toolName, inputProperty),
|
|
454
|
+
...typeof namespace === "string" ? { namespace } : {}
|
|
455
|
+
});
|
|
456
|
+
else input.push({
|
|
457
|
+
type: "function_call",
|
|
458
|
+
...metadata?.omitItemId === true || !itemId.startsWith("fc_") ? {} : { id: itemId },
|
|
459
|
+
call_id: callId,
|
|
460
|
+
name: part.toolName,
|
|
461
|
+
arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input ?? {}),
|
|
462
|
+
...typeof namespace === "string" ? { namespace } : {}
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
case "tool": for (const part of message.content) {
|
|
467
|
+
if (part.type !== "tool-result") continue;
|
|
468
|
+
const { callId } = splitToolCallId(part.toolCallId);
|
|
469
|
+
if (options?.grammarToolInputProperties?.has(part.toolName)) input.push({
|
|
470
|
+
type: "custom_tool_call_output",
|
|
471
|
+
call_id: callId,
|
|
472
|
+
output: customToolOutput(part.output, options?.supportsImages ?? true)
|
|
473
|
+
});
|
|
474
|
+
else input.push({
|
|
475
|
+
type: "function_call_output",
|
|
476
|
+
call_id: callId,
|
|
477
|
+
output: toolResultOutput(part.output, options?.supportsImages ?? true)
|
|
478
|
+
});
|
|
479
|
+
appendDeferredTools(part.toolCallId, addedToolNames(part.providerOptions));
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
instructions: instructions || DEFAULT_INSTRUCTIONS,
|
|
484
|
+
input
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
//#endregion
|
|
488
|
+
//#region src/providers/openai-codex/codex-sse.ts
|
|
489
|
+
/**
|
|
490
|
+
* Minimal SSE parsing for the Codex Responses endpoint. Each event is a JSON
|
|
491
|
+
* payload on `data:` lines; `[DONE]` terminates the stream.
|
|
492
|
+
*/
|
|
493
|
+
function parseOpenAICodexSSEStream(body) {
|
|
494
|
+
const decoder = new TextDecoder();
|
|
495
|
+
let buffer = "";
|
|
496
|
+
const parseChunk = (chunk, controller) => {
|
|
497
|
+
const dataLines = chunk.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim());
|
|
498
|
+
if (dataLines.length === 0) return;
|
|
499
|
+
const data = dataLines.join("\n").trim();
|
|
500
|
+
if (!data || data === "[DONE]") return;
|
|
501
|
+
try {
|
|
502
|
+
controller.enqueue(JSON.parse(data));
|
|
503
|
+
} catch (cause) {
|
|
504
|
+
controller.error(new Error(`Invalid OpenAI Codex SSE JSON: ${data.slice(0, 200)}`, { cause }));
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
return body.pipeThrough(new TransformStream({
|
|
508
|
+
transform(value, controller) {
|
|
509
|
+
buffer += decoder.decode(value, { stream: true });
|
|
510
|
+
let index = buffer.indexOf("\n\n");
|
|
511
|
+
while (index !== -1) {
|
|
512
|
+
const chunk = buffer.slice(0, index);
|
|
513
|
+
buffer = buffer.slice(index + 2);
|
|
514
|
+
parseChunk(chunk, controller);
|
|
515
|
+
index = buffer.indexOf("\n\n");
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
flush(controller) {
|
|
519
|
+
buffer += decoder.decode();
|
|
520
|
+
if (buffer.trim()) parseChunk(buffer, controller);
|
|
521
|
+
}
|
|
522
|
+
}));
|
|
523
|
+
}
|
|
524
|
+
//#endregion
|
|
525
|
+
//#region src/providers/openai-codex/codex-usage.ts
|
|
526
|
+
function convertOpenAICodexUsage(usage) {
|
|
527
|
+
const inputTotal = usage?.input_tokens;
|
|
528
|
+
const cachedTokens = usage?.input_tokens_details?.cached_tokens ?? 0;
|
|
529
|
+
const cacheWriteTokens = usage?.input_tokens_details?.cache_write_tokens ?? 0;
|
|
530
|
+
const outputTotal = usage?.output_tokens;
|
|
531
|
+
const reasoningTokens = usage?.output_tokens_details?.reasoning_tokens ?? 0;
|
|
532
|
+
return {
|
|
533
|
+
inputTokens: {
|
|
534
|
+
total: inputTotal,
|
|
535
|
+
noCache: inputTotal === void 0 ? void 0 : Math.max(inputTotal - cachedTokens - cacheWriteTokens, 0),
|
|
536
|
+
cacheRead: inputTotal === void 0 ? void 0 : cachedTokens,
|
|
537
|
+
cacheWrite: inputTotal === void 0 ? void 0 : cacheWriteTokens
|
|
538
|
+
},
|
|
539
|
+
outputTokens: {
|
|
540
|
+
total: outputTotal,
|
|
541
|
+
text: outputTotal === void 0 ? void 0 : outputTotal - reasoningTokens,
|
|
542
|
+
reasoning: outputTotal === void 0 ? void 0 : reasoningTokens
|
|
543
|
+
},
|
|
544
|
+
...usage ? { raw: usage } : {}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
function mapOpenAICodexFinishReason(status, hasToolCalls, incompleteReason) {
|
|
548
|
+
switch (status) {
|
|
549
|
+
case "completed": return {
|
|
550
|
+
unified: hasToolCalls ? "tool-calls" : "stop",
|
|
551
|
+
raw: status
|
|
552
|
+
};
|
|
553
|
+
case "incomplete": return incompleteReason === "max_output_tokens" ? {
|
|
554
|
+
unified: "length",
|
|
555
|
+
raw: `${status}.${incompleteReason}`
|
|
556
|
+
} : {
|
|
557
|
+
unified: "error",
|
|
558
|
+
raw: incompleteReason ? `${status}.${incompleteReason}` : status
|
|
559
|
+
};
|
|
560
|
+
case "failed":
|
|
561
|
+
case "cancelled": return {
|
|
562
|
+
unified: "error",
|
|
563
|
+
raw: status
|
|
564
|
+
};
|
|
565
|
+
case void 0: return {
|
|
566
|
+
unified: "other",
|
|
567
|
+
raw: void 0
|
|
568
|
+
};
|
|
569
|
+
default: return {
|
|
570
|
+
unified: "stop",
|
|
571
|
+
raw: status
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
//#endregion
|
|
576
|
+
//#region src/providers/openai-codex/codex-language-model.ts
|
|
577
|
+
const OPENAI_CODEX_DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
|
|
578
|
+
const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
|
|
579
|
+
function compressOpenAICodexRequest(body) {
|
|
580
|
+
if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) return void 0;
|
|
581
|
+
const zlib = process.getBuiltinModule?.("node:zlib");
|
|
582
|
+
if (!zlib || typeof zlib.zstdCompressSync !== "function") return void 0;
|
|
583
|
+
try {
|
|
584
|
+
const compressed = zlib.zstdCompressSync(body, { params: { [zlib.constants.ZSTD_c_compressionLevel]: REQUEST_COMPRESSION_ZSTD_LEVEL } });
|
|
585
|
+
return new Uint8Array(compressed.buffer, compressed.byteOffset, compressed.byteLength);
|
|
586
|
+
} catch {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function clampOpenAICodexPromptCacheKey(key) {
|
|
591
|
+
if (key === void 0) return void 0;
|
|
592
|
+
const characters = Array.from(key);
|
|
593
|
+
return characters.length <= 64 ? key : characters.slice(0, 64).join("");
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Resolve the Codex responses endpoint from a base URL that may already
|
|
597
|
+
* include the `/codex` or `/codex/responses` suffix.
|
|
598
|
+
*/
|
|
599
|
+
function resolveOpenAICodexUrl(baseUrl) {
|
|
600
|
+
const normalized = (baseUrl?.trim() ? baseUrl : OPENAI_CODEX_DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
601
|
+
if (normalized.endsWith("/codex/responses")) return normalized;
|
|
602
|
+
if (normalized.endsWith("/codex")) return `${normalized}/responses`;
|
|
603
|
+
return `${normalized}/codex/responses`;
|
|
604
|
+
}
|
|
605
|
+
function asRecord(value) {
|
|
606
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
607
|
+
}
|
|
608
|
+
function asString(value) {
|
|
609
|
+
return typeof value === "string" ? value : void 0;
|
|
610
|
+
}
|
|
611
|
+
function cleanHeaders(headers) {
|
|
612
|
+
const result = {};
|
|
613
|
+
for (const [key, value] of Object.entries(headers)) if (value !== void 0) result[key] = value;
|
|
614
|
+
return result;
|
|
615
|
+
}
|
|
616
|
+
var OpenAICodexLanguageModel = class {
|
|
617
|
+
specificationVersion = "v3";
|
|
618
|
+
modelId;
|
|
619
|
+
config;
|
|
620
|
+
constructor(modelId, config) {
|
|
621
|
+
this.modelId = modelId;
|
|
622
|
+
this.config = config;
|
|
623
|
+
}
|
|
624
|
+
get provider() {
|
|
625
|
+
return this.config.provider;
|
|
626
|
+
}
|
|
627
|
+
get supportedUrls() {
|
|
628
|
+
return {};
|
|
629
|
+
}
|
|
630
|
+
getArgs(options) {
|
|
631
|
+
const warnings = [];
|
|
632
|
+
if (options.topP != null) warnings.push({
|
|
633
|
+
type: "unsupported",
|
|
634
|
+
feature: "topP"
|
|
635
|
+
});
|
|
636
|
+
if (options.topK != null) warnings.push({
|
|
637
|
+
type: "unsupported",
|
|
638
|
+
feature: "topK"
|
|
639
|
+
});
|
|
640
|
+
if (options.presencePenalty != null) warnings.push({
|
|
641
|
+
type: "unsupported",
|
|
642
|
+
feature: "presencePenalty"
|
|
643
|
+
});
|
|
644
|
+
if (options.frequencyPenalty != null) warnings.push({
|
|
645
|
+
type: "unsupported",
|
|
646
|
+
feature: "frequencyPenalty"
|
|
647
|
+
});
|
|
648
|
+
if (options.seed != null) warnings.push({
|
|
649
|
+
type: "unsupported",
|
|
650
|
+
feature: "seed"
|
|
651
|
+
});
|
|
652
|
+
if (options.stopSequences != null) warnings.push({
|
|
653
|
+
type: "unsupported",
|
|
654
|
+
feature: "stopSequences"
|
|
655
|
+
});
|
|
656
|
+
if (options.maxOutputTokens != null) warnings.push({
|
|
657
|
+
type: "unsupported",
|
|
658
|
+
feature: "maxOutputTokens"
|
|
659
|
+
});
|
|
660
|
+
if (options.responseFormat && options.responseFormat.type !== "text") warnings.push({
|
|
661
|
+
type: "unsupported",
|
|
662
|
+
feature: "responseFormat"
|
|
663
|
+
});
|
|
664
|
+
const codexOptions = options.providerOptions?.["openai-codex"] ?? {};
|
|
665
|
+
const deferredToolsMode = resolveOpenAICodexDeferredToolsMode(this.modelId, this.config.compat);
|
|
666
|
+
const deferredToolNames = deferredToolsMode ? collectOpenAICodexDeferredToolNames(options.prompt) : void 0;
|
|
667
|
+
const tools = prepareOpenAICodexTools({
|
|
668
|
+
tools: options.tools,
|
|
669
|
+
toolChoice: options.toolChoice,
|
|
670
|
+
...deferredToolNames !== void 0 && { deferredToolNames }
|
|
671
|
+
});
|
|
672
|
+
const { instructions, input } = convertToOpenAICodexPrompt(options.prompt, {
|
|
673
|
+
deferredTools: tools.deferredCodexTools,
|
|
674
|
+
...deferredToolsMode !== void 0 && { deferredToolsMode },
|
|
675
|
+
grammarToolInputProperties: tools.grammarToolInputProperties,
|
|
676
|
+
supportsImages: this.config.compat?.supportsImages ?? this.modelId !== "gpt-5.3-codex-spark"
|
|
677
|
+
});
|
|
678
|
+
warnings.push(...tools.warnings);
|
|
679
|
+
const args = {
|
|
680
|
+
model: this.modelId,
|
|
681
|
+
store: false,
|
|
682
|
+
instructions,
|
|
683
|
+
input,
|
|
684
|
+
tool_choice: tools.codexToolChoice ?? "auto",
|
|
685
|
+
parallel_tool_calls: true,
|
|
686
|
+
include: codexOptions.include ?? ["reasoning.encrypted_content"],
|
|
687
|
+
text: { verbosity: codexOptions.textVerbosity ?? "low" }
|
|
688
|
+
};
|
|
689
|
+
if (tools.codexTools) args.tools = tools.codexTools;
|
|
690
|
+
if (options.temperature != null) args.temperature = options.temperature;
|
|
691
|
+
const promptCacheKey = clampOpenAICodexPromptCacheKey(codexOptions.promptCacheKey ?? this.config.sessionId);
|
|
692
|
+
if (promptCacheKey) args.prompt_cache_key = promptCacheKey;
|
|
693
|
+
const serviceTier = codexOptions.serviceTier ?? this.config.serviceTier;
|
|
694
|
+
if (serviceTier) args.service_tier = serviceTier;
|
|
695
|
+
if (codexOptions.reasoningEffort) args.reasoning = {
|
|
696
|
+
effort: codexOptions.reasoningEffort,
|
|
697
|
+
summary: codexOptions.reasoningSummary ?? "auto"
|
|
698
|
+
};
|
|
699
|
+
return {
|
|
700
|
+
args,
|
|
701
|
+
warnings,
|
|
702
|
+
grammarToolInputProperties: tools.grammarToolInputProperties
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
async doGenerate(options) {
|
|
706
|
+
const { stream, request, response } = await this.doStream(options);
|
|
707
|
+
const content = [];
|
|
708
|
+
const textBlocks = /* @__PURE__ */ new Map();
|
|
709
|
+
const reasoningBlocks = /* @__PURE__ */ new Map();
|
|
710
|
+
const warnings = [];
|
|
711
|
+
let finishReason = {
|
|
712
|
+
unified: "other",
|
|
713
|
+
raw: void 0
|
|
714
|
+
};
|
|
715
|
+
let usage = convertOpenAICodexUsage(void 0);
|
|
716
|
+
let responseId;
|
|
717
|
+
let responseModelId;
|
|
718
|
+
const reader = stream.getReader();
|
|
719
|
+
while (true) {
|
|
720
|
+
const { done, value } = await reader.read();
|
|
721
|
+
if (done) break;
|
|
722
|
+
switch (value.type) {
|
|
723
|
+
case "stream-start":
|
|
724
|
+
warnings.push(...value.warnings);
|
|
725
|
+
break;
|
|
726
|
+
case "response-metadata":
|
|
727
|
+
responseId = value.id;
|
|
728
|
+
responseModelId = value.modelId;
|
|
729
|
+
break;
|
|
730
|
+
case "text-start": {
|
|
731
|
+
const block = {
|
|
732
|
+
type: "text",
|
|
733
|
+
text: ""
|
|
734
|
+
};
|
|
735
|
+
textBlocks.set(value.id, block);
|
|
736
|
+
content.push(block);
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
case "text-delta": {
|
|
740
|
+
const block = textBlocks.get(value.id);
|
|
741
|
+
if (block) block.text += value.delta;
|
|
742
|
+
break;
|
|
743
|
+
}
|
|
744
|
+
case "text-end": {
|
|
745
|
+
const block = textBlocks.get(value.id);
|
|
746
|
+
if (block && value.providerMetadata !== void 0) block.providerMetadata = value.providerMetadata;
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
case "reasoning-start": {
|
|
750
|
+
const block = {
|
|
751
|
+
type: "reasoning",
|
|
752
|
+
text: ""
|
|
753
|
+
};
|
|
754
|
+
reasoningBlocks.set(value.id, block);
|
|
755
|
+
content.push(block);
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
case "reasoning-delta": {
|
|
759
|
+
const block = reasoningBlocks.get(value.id);
|
|
760
|
+
if (block) block.text += value.delta;
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
case "reasoning-end": {
|
|
764
|
+
const block = reasoningBlocks.get(value.id);
|
|
765
|
+
if (block && value.providerMetadata !== void 0) block.providerMetadata = value.providerMetadata;
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
case "tool-call":
|
|
769
|
+
content.push(value);
|
|
770
|
+
break;
|
|
771
|
+
case "finish":
|
|
772
|
+
finishReason = value.finishReason;
|
|
773
|
+
usage = value.usage;
|
|
774
|
+
break;
|
|
775
|
+
case "error": throw value.error instanceof Error ? value.error : new Error(String(value.error));
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
content: content.filter((part) => part.type === "text" || part.type === "reasoning" ? part.text !== "" || part.providerMetadata !== void 0 : true),
|
|
780
|
+
finishReason,
|
|
781
|
+
usage,
|
|
782
|
+
...request !== void 0 && { request },
|
|
783
|
+
response: {
|
|
784
|
+
...response,
|
|
785
|
+
...responseId !== void 0 && { id: responseId },
|
|
786
|
+
...responseModelId !== void 0 && { modelId: responseModelId }
|
|
787
|
+
},
|
|
788
|
+
warnings
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
async doStream(options) {
|
|
792
|
+
const { args, warnings, grammarToolInputProperties } = this.getArgs(options);
|
|
793
|
+
const body = {
|
|
794
|
+
...args,
|
|
795
|
+
stream: true
|
|
796
|
+
};
|
|
797
|
+
const url = resolveOpenAICodexUrl(this.config.baseURL);
|
|
798
|
+
const headers = cleanHeaders({
|
|
799
|
+
...await this.config.headers(),
|
|
800
|
+
...options.headers
|
|
801
|
+
});
|
|
802
|
+
const bodyJson = JSON.stringify(body);
|
|
803
|
+
const compressedBody = compressOpenAICodexRequest(bodyJson);
|
|
804
|
+
if (compressedBody) headers["content-encoding"] = "zstd";
|
|
805
|
+
const response = await (this.config.fetch ?? globalThis.fetch)(url, {
|
|
806
|
+
method: "POST",
|
|
807
|
+
headers,
|
|
808
|
+
body: compressedBody ?? bodyJson,
|
|
809
|
+
...options.abortSignal !== void 0 && { signal: options.abortSignal }
|
|
810
|
+
});
|
|
811
|
+
if (!response.ok) throw await createOpenAICodexAPICallError({
|
|
812
|
+
response,
|
|
813
|
+
url,
|
|
814
|
+
requestBodyValues: body
|
|
815
|
+
});
|
|
816
|
+
if (!response.body) throw new Error("OpenAI Codex response has no body");
|
|
817
|
+
const responseHeaders = {};
|
|
818
|
+
response.headers.forEach((value, key) => {
|
|
819
|
+
responseHeaders[key] = value;
|
|
820
|
+
});
|
|
821
|
+
return {
|
|
822
|
+
stream: parseOpenAICodexSSEStream(response.body).pipeThrough(this.createTransformStream(warnings, options.includeRawChunks ?? false, grammarToolInputProperties)),
|
|
823
|
+
request: { body },
|
|
824
|
+
response: { headers: responseHeaders }
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
createTransformStream(warnings, includeRawChunks, grammarToolInputProperties) {
|
|
828
|
+
let finishReason = {
|
|
829
|
+
unified: "other",
|
|
830
|
+
raw: void 0
|
|
831
|
+
};
|
|
832
|
+
let usage;
|
|
833
|
+
let hasToolCalls = false;
|
|
834
|
+
let finished = false;
|
|
835
|
+
const toolCallIdsByItemId = /* @__PURE__ */ new Map();
|
|
836
|
+
const customToolInputsByItemId = /* @__PURE__ */ new Map();
|
|
837
|
+
const finish = (controller) => {
|
|
838
|
+
if (finished) return;
|
|
839
|
+
finished = true;
|
|
840
|
+
controller.enqueue({
|
|
841
|
+
type: "finish",
|
|
842
|
+
finishReason,
|
|
843
|
+
usage: convertOpenAICodexUsage(usage)
|
|
844
|
+
});
|
|
845
|
+
};
|
|
846
|
+
const handleOutputItemAdded = (item, controller) => {
|
|
847
|
+
const itemType = asString(item.type);
|
|
848
|
+
const itemId = asString(item.id);
|
|
849
|
+
if (!itemType || !itemId) return;
|
|
850
|
+
if (itemType === "message") controller.enqueue({
|
|
851
|
+
type: "text-start",
|
|
852
|
+
id: itemId
|
|
853
|
+
});
|
|
854
|
+
else if (itemType === "reasoning") controller.enqueue({
|
|
855
|
+
type: "reasoning-start",
|
|
856
|
+
id: itemId
|
|
857
|
+
});
|
|
858
|
+
else if (itemType === "function_call") {
|
|
859
|
+
const toolCallId = joinToolCallId(asString(item.call_id) ?? itemId, itemId);
|
|
860
|
+
toolCallIdsByItemId.set(itemId, toolCallId);
|
|
861
|
+
hasToolCalls = true;
|
|
862
|
+
controller.enqueue({
|
|
863
|
+
type: "tool-input-start",
|
|
864
|
+
id: toolCallId,
|
|
865
|
+
toolName: asString(item.name) ?? ""
|
|
866
|
+
});
|
|
867
|
+
} else if (itemType === "custom_tool_call") {
|
|
868
|
+
const toolName = asString(item.name) ?? "";
|
|
869
|
+
const inputProperty = grammarToolInputProperties.get(toolName) ?? "input";
|
|
870
|
+
const toolCallId = joinToolCallId(asString(item.call_id) ?? itemId, itemId);
|
|
871
|
+
customToolInputsByItemId.set(itemId, {
|
|
872
|
+
toolCallId,
|
|
873
|
+
toolName,
|
|
874
|
+
inputProperty,
|
|
875
|
+
buffer: {
|
|
876
|
+
input: "",
|
|
877
|
+
started: false,
|
|
878
|
+
closed: false
|
|
879
|
+
}
|
|
880
|
+
});
|
|
881
|
+
hasToolCalls = true;
|
|
882
|
+
controller.enqueue({
|
|
883
|
+
type: "tool-input-start",
|
|
884
|
+
id: toolCallId,
|
|
885
|
+
toolName
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
const handleOutputItemDone = (item, controller) => {
|
|
890
|
+
const itemType = asString(item.type);
|
|
891
|
+
const itemId = asString(item.id);
|
|
892
|
+
if (!itemType || !itemId) return;
|
|
893
|
+
if (itemType === "message") controller.enqueue({
|
|
894
|
+
type: "text-end",
|
|
895
|
+
id: itemId,
|
|
896
|
+
providerMetadata: { "openai-codex": { messageId: itemId } }
|
|
897
|
+
});
|
|
898
|
+
else if (itemType === "reasoning") controller.enqueue({
|
|
899
|
+
type: "reasoning-end",
|
|
900
|
+
id: itemId,
|
|
901
|
+
providerMetadata: { "openai-codex": { reasoningItem: JSON.stringify(item) } }
|
|
902
|
+
});
|
|
903
|
+
else if (itemType === "function_call") {
|
|
904
|
+
const toolCallId = toolCallIdsByItemId.get(itemId) ?? joinToolCallId(asString(item.call_id) ?? itemId, itemId);
|
|
905
|
+
hasToolCalls = true;
|
|
906
|
+
controller.enqueue({
|
|
907
|
+
type: "tool-input-end",
|
|
908
|
+
id: toolCallId
|
|
909
|
+
});
|
|
910
|
+
controller.enqueue({
|
|
911
|
+
type: "tool-call",
|
|
912
|
+
toolCallId,
|
|
913
|
+
toolName: asString(item.name) ?? "",
|
|
914
|
+
input: asString(item.arguments) ?? "",
|
|
915
|
+
...asString(item.namespace) ? { providerMetadata: { "openai-codex": { namespace: asString(item.namespace) } } } : {}
|
|
916
|
+
});
|
|
917
|
+
} else if (itemType === "custom_tool_call") {
|
|
918
|
+
const state = customToolInputsByItemId.get(itemId);
|
|
919
|
+
if (!state) return;
|
|
920
|
+
const input = asString(item.input) ?? state.buffer.input;
|
|
921
|
+
const delta = appendOpenAICodexGrammarInputJsonDelta(state.buffer, state.inputProperty, input, true);
|
|
922
|
+
if (delta) controller.enqueue({
|
|
923
|
+
type: "tool-input-delta",
|
|
924
|
+
id: state.toolCallId,
|
|
925
|
+
delta
|
|
926
|
+
});
|
|
927
|
+
hasToolCalls = true;
|
|
928
|
+
controller.enqueue({
|
|
929
|
+
type: "tool-input-end",
|
|
930
|
+
id: state.toolCallId
|
|
931
|
+
});
|
|
932
|
+
controller.enqueue({
|
|
933
|
+
type: "tool-call",
|
|
934
|
+
toolCallId: state.toolCallId,
|
|
935
|
+
toolName: state.toolName,
|
|
936
|
+
input: JSON.stringify({ [state.inputProperty]: input }),
|
|
937
|
+
...asString(item.namespace) ? { providerMetadata: { "openai-codex": { namespace: asString(item.namespace) } } } : {}
|
|
938
|
+
});
|
|
939
|
+
customToolInputsByItemId.delete(itemId);
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
return new TransformStream({
|
|
943
|
+
start(controller) {
|
|
944
|
+
controller.enqueue({
|
|
945
|
+
type: "stream-start",
|
|
946
|
+
warnings
|
|
947
|
+
});
|
|
948
|
+
},
|
|
949
|
+
transform(event, controller) {
|
|
950
|
+
if (includeRawChunks) controller.enqueue({
|
|
951
|
+
type: "raw",
|
|
952
|
+
rawValue: event
|
|
953
|
+
});
|
|
954
|
+
const type = asString(event.type);
|
|
955
|
+
if (!type) return;
|
|
956
|
+
switch (type) {
|
|
957
|
+
case "response.created": {
|
|
958
|
+
const response = asRecord(event.response);
|
|
959
|
+
const id = asString(response?.id);
|
|
960
|
+
const modelId = asString(response?.model);
|
|
961
|
+
controller.enqueue({
|
|
962
|
+
type: "response-metadata",
|
|
963
|
+
...id !== void 0 && { id },
|
|
964
|
+
...modelId !== void 0 && { modelId }
|
|
965
|
+
});
|
|
966
|
+
break;
|
|
967
|
+
}
|
|
968
|
+
case "response.output_item.added": {
|
|
969
|
+
const item = asRecord(event.item);
|
|
970
|
+
if (item) handleOutputItemAdded(item, controller);
|
|
971
|
+
break;
|
|
972
|
+
}
|
|
973
|
+
case "response.output_text.delta": {
|
|
974
|
+
const itemId = asString(event.item_id);
|
|
975
|
+
const delta = asString(event.delta);
|
|
976
|
+
if (itemId && delta) controller.enqueue({
|
|
977
|
+
type: "text-delta",
|
|
978
|
+
id: itemId,
|
|
979
|
+
delta
|
|
980
|
+
});
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
case "response.reasoning_text.delta":
|
|
984
|
+
case "response.reasoning_summary_text.delta": {
|
|
985
|
+
const itemId = asString(event.item_id);
|
|
986
|
+
const delta = asString(event.delta);
|
|
987
|
+
if (itemId && delta) controller.enqueue({
|
|
988
|
+
type: "reasoning-delta",
|
|
989
|
+
id: itemId,
|
|
990
|
+
delta
|
|
991
|
+
});
|
|
992
|
+
break;
|
|
993
|
+
}
|
|
994
|
+
case "response.function_call_arguments.delta": {
|
|
995
|
+
const itemId = asString(event.item_id);
|
|
996
|
+
const delta = asString(event.delta);
|
|
997
|
+
const toolCallId = itemId ? toolCallIdsByItemId.get(itemId) : void 0;
|
|
998
|
+
if (toolCallId && delta) controller.enqueue({
|
|
999
|
+
type: "tool-input-delta",
|
|
1000
|
+
id: toolCallId,
|
|
1001
|
+
delta
|
|
1002
|
+
});
|
|
1003
|
+
break;
|
|
1004
|
+
}
|
|
1005
|
+
case "response.custom_tool_call_input.delta": {
|
|
1006
|
+
const itemId = asString(event.item_id);
|
|
1007
|
+
const delta = asString(event.delta);
|
|
1008
|
+
const state = itemId ? customToolInputsByItemId.get(itemId) : void 0;
|
|
1009
|
+
if (!state || delta === void 0) break;
|
|
1010
|
+
const jsonDelta = appendOpenAICodexGrammarInputJsonDelta(state.buffer, state.inputProperty, state.buffer.input + delta, false);
|
|
1011
|
+
if (jsonDelta) controller.enqueue({
|
|
1012
|
+
type: "tool-input-delta",
|
|
1013
|
+
id: state.toolCallId,
|
|
1014
|
+
delta: jsonDelta
|
|
1015
|
+
});
|
|
1016
|
+
break;
|
|
1017
|
+
}
|
|
1018
|
+
case "response.custom_tool_call_input.done": {
|
|
1019
|
+
const itemId = asString(event.item_id);
|
|
1020
|
+
const input = asString(event.input);
|
|
1021
|
+
const state = itemId ? customToolInputsByItemId.get(itemId) : void 0;
|
|
1022
|
+
if (!state || input === void 0) break;
|
|
1023
|
+
const jsonDelta = appendOpenAICodexGrammarInputJsonDelta(state.buffer, state.inputProperty, input, true);
|
|
1024
|
+
if (jsonDelta) controller.enqueue({
|
|
1025
|
+
type: "tool-input-delta",
|
|
1026
|
+
id: state.toolCallId,
|
|
1027
|
+
delta: jsonDelta
|
|
1028
|
+
});
|
|
1029
|
+
break;
|
|
1030
|
+
}
|
|
1031
|
+
case "response.output_item.done": {
|
|
1032
|
+
const item = asRecord(event.item);
|
|
1033
|
+
if (item) handleOutputItemDone(item, controller);
|
|
1034
|
+
break;
|
|
1035
|
+
}
|
|
1036
|
+
case "response.completed":
|
|
1037
|
+
case "response.done":
|
|
1038
|
+
case "response.incomplete": {
|
|
1039
|
+
const response = asRecord(event.response);
|
|
1040
|
+
usage = response?.usage ?? usage;
|
|
1041
|
+
const status = asString(response?.status) ?? (type === "response.incomplete" ? "incomplete" : "completed");
|
|
1042
|
+
const incompleteReason = asString(asRecord(response?.incomplete_details)?.reason);
|
|
1043
|
+
finishReason = mapOpenAICodexFinishReason(status, hasToolCalls, incompleteReason);
|
|
1044
|
+
finish(controller);
|
|
1045
|
+
controller.terminate();
|
|
1046
|
+
break;
|
|
1047
|
+
}
|
|
1048
|
+
case "response.failed": {
|
|
1049
|
+
const error = asRecord(asRecord(event.response)?.error);
|
|
1050
|
+
finishReason = {
|
|
1051
|
+
unified: "error",
|
|
1052
|
+
raw: "failed"
|
|
1053
|
+
};
|
|
1054
|
+
controller.enqueue({
|
|
1055
|
+
type: "error",
|
|
1056
|
+
error: new Error(asString(error?.message) ?? "OpenAI Codex response failed")
|
|
1057
|
+
});
|
|
1058
|
+
break;
|
|
1059
|
+
}
|
|
1060
|
+
case "error": {
|
|
1061
|
+
const code = asString(event.code);
|
|
1062
|
+
const message = asString(event.message);
|
|
1063
|
+
finishReason = {
|
|
1064
|
+
unified: "error",
|
|
1065
|
+
raw: code ?? "error"
|
|
1066
|
+
};
|
|
1067
|
+
controller.enqueue({
|
|
1068
|
+
type: "error",
|
|
1069
|
+
error: /* @__PURE__ */ new Error(`OpenAI Codex error${code ? ` ${code}` : ""}: ${message ?? JSON.stringify(event)}`)
|
|
1070
|
+
});
|
|
1071
|
+
break;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
},
|
|
1075
|
+
flush(controller) {
|
|
1076
|
+
finish(controller);
|
|
1077
|
+
}
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
//#endregion
|
|
1082
|
+
//#region src/providers/openai-codex/codex-provider.ts
|
|
1083
|
+
const OPENAI_CODEX_API_KEY_ENV = "OPENAI_CODEX_API_KEY";
|
|
1084
|
+
const DEFAULT_ORIGINATOR = "codework";
|
|
1085
|
+
async function resolveApiKey(settings) {
|
|
1086
|
+
const apiKey = typeof settings.apiKey === "function" ? await settings.apiKey() : settings.apiKey ?? (typeof process === "undefined" ? void 0 : process.env["OPENAI_CODEX_API_KEY"]);
|
|
1087
|
+
if (!apiKey) throw new LoadAPIKeyError({ message: `OpenAI Codex API key is missing. Pass it using the 'apiKey' option or the ${OPENAI_CODEX_API_KEY_ENV} environment variable.` });
|
|
1088
|
+
return apiKey;
|
|
1089
|
+
}
|
|
1090
|
+
function resolveAccountId(settings, apiKey) {
|
|
1091
|
+
const accountId = settings.accountId ?? getOpenAICodexAccountId(apiKey);
|
|
1092
|
+
if (!accountId) throw new LoadAPIKeyError({ message: "Failed to extract the ChatGPT account id from the OpenAI Codex access token. Provide it using the 'accountId' option or pass a valid ChatGPT OAuth JWT as the API key." });
|
|
1093
|
+
return accountId;
|
|
1094
|
+
}
|
|
1095
|
+
function createOpenAICodex(options = {}) {
|
|
1096
|
+
const baseURL = options.baseURL?.replace(/\/+$/, "") || "https://chatgpt.com/backend-api";
|
|
1097
|
+
const sessionId = clampOpenAICodexPromptCacheKey(options.sessionId);
|
|
1098
|
+
const getHeaders = async () => {
|
|
1099
|
+
const apiKey = await resolveApiKey(options);
|
|
1100
|
+
const accountId = resolveAccountId(options, apiKey);
|
|
1101
|
+
return {
|
|
1102
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1103
|
+
"chatgpt-account-id": accountId,
|
|
1104
|
+
"OpenAI-Beta": "responses=experimental",
|
|
1105
|
+
originator: options.originator ?? DEFAULT_ORIGINATOR,
|
|
1106
|
+
accept: "text/event-stream",
|
|
1107
|
+
"content-type": "application/json",
|
|
1108
|
+
...sessionId ? {
|
|
1109
|
+
"session-id": sessionId,
|
|
1110
|
+
"x-client-request-id": sessionId
|
|
1111
|
+
} : {},
|
|
1112
|
+
...options.headers
|
|
1113
|
+
};
|
|
1114
|
+
};
|
|
1115
|
+
const createLanguageModel = (modelId) => new OpenAICodexLanguageModel(modelId, {
|
|
1116
|
+
provider: "openai-codex",
|
|
1117
|
+
baseURL,
|
|
1118
|
+
headers: getHeaders,
|
|
1119
|
+
...options.fetch !== void 0 && { fetch: options.fetch },
|
|
1120
|
+
...sessionId !== void 0 && { sessionId },
|
|
1121
|
+
...options.serviceTier !== void 0 && { serviceTier: options.serviceTier },
|
|
1122
|
+
...options.compat !== void 0 && { compat: options.compat }
|
|
1123
|
+
});
|
|
1124
|
+
const provider = function(modelId) {
|
|
1125
|
+
if (new.target) throw new Error("The OpenAI Codex model function cannot be called with the new keyword.");
|
|
1126
|
+
return createLanguageModel(modelId);
|
|
1127
|
+
};
|
|
1128
|
+
provider.specificationVersion = "v3";
|
|
1129
|
+
provider.languageModel = createLanguageModel;
|
|
1130
|
+
provider.responses = createLanguageModel;
|
|
1131
|
+
provider.embeddingModel = (modelId) => {
|
|
1132
|
+
throw new NoSuchModelError({
|
|
1133
|
+
modelId,
|
|
1134
|
+
modelType: "embeddingModel"
|
|
1135
|
+
});
|
|
1136
|
+
};
|
|
1137
|
+
provider.textEmbeddingModel = (modelId) => {
|
|
1138
|
+
throw new NoSuchModelError({
|
|
1139
|
+
modelId,
|
|
1140
|
+
modelType: "embeddingModel"
|
|
1141
|
+
});
|
|
1142
|
+
};
|
|
1143
|
+
provider.imageModel = (modelId) => {
|
|
1144
|
+
throw new NoSuchModelError({
|
|
1145
|
+
modelId,
|
|
1146
|
+
modelType: "imageModel"
|
|
1147
|
+
});
|
|
1148
|
+
};
|
|
1149
|
+
return provider;
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Default provider instance using `OPENAI_CODEX_API_KEY`.
|
|
1153
|
+
*/
|
|
1154
|
+
const openaiCodex = createOpenAICodex();
|
|
1155
|
+
//#endregion
|
|
1156
|
+
export { appendOpenAICodexGrammarInputJsonDelta as _, OpenAICodexLanguageModel as a, resolveOpenAICodexDeferredToolsMode as b, convertOpenAICodexUsage as c, convertToOpenAICodexPrompt as d, joinToolCallId as f, shortHash as g, openAICodexErrorMessage as h, OPENAI_CODEX_DEFAULT_BASE_URL as i, mapOpenAICodexFinishReason as l, createOpenAICodexAPICallError as m, createOpenAICodex as n, clampOpenAICodexPromptCacheKey as o, splitToolCallId as p, openaiCodex as r, resolveOpenAICodexUrl as s, OPENAI_CODEX_API_KEY_ENV as t, collectOpenAICodexDeferredToolNames as u, openAICodexTools as v, resolveOpenAICodexToolConstraint as x, prepareOpenAICodexTools as y };
|
|
1157
|
+
|
|
1158
|
+
//# sourceMappingURL=openai-codex-_3XkJ4bs.mjs.map
|