@codeworksh/aikit 0.4.1-dev.20260531052349 → 0.5.1-dev.20260613074656
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/LICENSE +1 -1
- package/cli.mjs +95 -5
- package/cli.mjs.map +1 -1
- package/{codex-DfYk740t.mjs → codex-C36oU6yH.mjs} +30 -23
- package/codex-C36oU6yH.mjs.map +1 -0
- package/index.d.mts +142 -75
- package/index.mjs +8 -3
- package/index.mjs.map +1 -1
- package/oauth/openai/codex.d.mts +27 -18
- package/oauth/openai/codex.mjs +2 -2
- package/openai-codex-CZrBnLAJ.mjs +753 -0
- package/openai-codex-CZrBnLAJ.mjs.map +1 -0
- package/openai-codex-bzfev3us.mjs +2 -0
- package/package.json +2 -1
- package/{registry-Cfdk78qk.mjs → registry-DUKrGxt4.mjs} +12 -7
- package/{registry-Cfdk78qk.mjs.map → registry-DUKrGxt4.mjs.map} +1 -1
- package/codex-DfYk740t.mjs.map +0 -1
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
import { o as getOpenAICodexAccountId } from "./codex-C36oU6yH.mjs";
|
|
2
|
+
import { APICallError, LoadAPIKeyError, NoSuchModelError } from "@ai-sdk/provider";
|
|
3
|
+
//#region src/providers/openai-codex/codex-error.ts
|
|
4
|
+
/**
|
|
5
|
+
* Turn a Codex error payload into a human-friendly message. ChatGPT plan usage
|
|
6
|
+
* limits deserve a clearer message than the raw backend error.
|
|
7
|
+
*/
|
|
8
|
+
function openAICodexErrorMessage(status, body, statusText) {
|
|
9
|
+
let message = body || statusText || "OpenAI Codex request failed";
|
|
10
|
+
try {
|
|
11
|
+
const error = JSON.parse(body)?.error;
|
|
12
|
+
if (error) {
|
|
13
|
+
const code = error.code ?? error.type ?? "";
|
|
14
|
+
if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || status === 429) {
|
|
15
|
+
const plan = error.plan_type ? ` (${error.plan_type.toLowerCase()} plan)` : "";
|
|
16
|
+
const minutes = error.resets_at ? Math.max(0, Math.round((error.resets_at * 1e3 - Date.now()) / 6e4)) : void 0;
|
|
17
|
+
return `You have hit your ChatGPT usage limit${plan}.${minutes === void 0 ? "" : ` Try again in ~${minutes} min.`}`.trim();
|
|
18
|
+
}
|
|
19
|
+
message = error.message || message;
|
|
20
|
+
}
|
|
21
|
+
} catch {}
|
|
22
|
+
return message;
|
|
23
|
+
}
|
|
24
|
+
async function createOpenAICodexAPICallError(args) {
|
|
25
|
+
const { response, url, requestBodyValues } = args;
|
|
26
|
+
const responseBody = await response.text().catch(() => "");
|
|
27
|
+
const responseHeaders = {};
|
|
28
|
+
response.headers.forEach((value, key) => {
|
|
29
|
+
responseHeaders[key] = value;
|
|
30
|
+
});
|
|
31
|
+
return new APICallError({
|
|
32
|
+
message: openAICodexErrorMessage(response.status, responseBody, response.statusText),
|
|
33
|
+
url,
|
|
34
|
+
requestBodyValues,
|
|
35
|
+
statusCode: response.status,
|
|
36
|
+
responseHeaders,
|
|
37
|
+
responseBody,
|
|
38
|
+
isRetryable: response.status === 408 || response.status === 409 || response.status >= 500
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/providers/openai-codex/codex-prompt.ts
|
|
43
|
+
const DEFAULT_INSTRUCTIONS = "You are a helpful assistant.";
|
|
44
|
+
function splitToolCallId(toolCallId) {
|
|
45
|
+
const separator = toolCallId.indexOf("|");
|
|
46
|
+
if (separator === -1) return {
|
|
47
|
+
callId: toolCallId,
|
|
48
|
+
itemId: toolCallId
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
callId: toolCallId.slice(0, separator),
|
|
52
|
+
itemId: toolCallId.slice(separator + 1)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function joinToolCallId(callId, itemId) {
|
|
56
|
+
return `${callId}|${itemId}`;
|
|
57
|
+
}
|
|
58
|
+
function uint8ArrayToBase64(bytes) {
|
|
59
|
+
let binary = "";
|
|
60
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
61
|
+
return btoa(binary);
|
|
62
|
+
}
|
|
63
|
+
function imageUrlFromFilePart(part) {
|
|
64
|
+
const data = part.data;
|
|
65
|
+
if (data instanceof URL) return data.toString();
|
|
66
|
+
if (typeof data === "string") {
|
|
67
|
+
if (data.startsWith("data:") || data.startsWith("http://") || data.startsWith("https://")) return data;
|
|
68
|
+
return `data:${part.mediaType};base64,${data}`;
|
|
69
|
+
}
|
|
70
|
+
if (data instanceof Uint8Array) return `data:${part.mediaType};base64,${uint8ArrayToBase64(data)}`;
|
|
71
|
+
}
|
|
72
|
+
function toolResultOutputToText(output) {
|
|
73
|
+
switch (output.type) {
|
|
74
|
+
case "text":
|
|
75
|
+
case "error-text": return output.value;
|
|
76
|
+
case "json":
|
|
77
|
+
case "error-json": return JSON.stringify(output.value);
|
|
78
|
+
case "execution-denied": return output.reason ?? "Tool execution denied";
|
|
79
|
+
case "content": return output.value.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
80
|
+
default: return JSON.stringify(output);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Convert an AI SDK V3 prompt into the Codex Responses request shape.
|
|
85
|
+
* System messages become `instructions`; everything else maps to `input` items.
|
|
86
|
+
*/
|
|
87
|
+
function convertToOpenAICodexPrompt(prompt) {
|
|
88
|
+
let instructions;
|
|
89
|
+
const input = [];
|
|
90
|
+
let syntheticMessageCounter = 0;
|
|
91
|
+
for (const message of prompt) switch (message.role) {
|
|
92
|
+
case "system":
|
|
93
|
+
instructions = instructions == null ? message.content : `${instructions}\n\n${message.content}`;
|
|
94
|
+
break;
|
|
95
|
+
case "user": {
|
|
96
|
+
const content = [];
|
|
97
|
+
for (const part of message.content) if (part.type === "text") content.push({
|
|
98
|
+
type: "input_text",
|
|
99
|
+
text: part.text
|
|
100
|
+
});
|
|
101
|
+
else if (part.type === "file" && part.mediaType.startsWith("image/")) {
|
|
102
|
+
const imageUrl = imageUrlFromFilePart(part);
|
|
103
|
+
if (imageUrl) content.push({
|
|
104
|
+
type: "input_image",
|
|
105
|
+
image_url: imageUrl,
|
|
106
|
+
detail: "auto"
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (content.length > 0) input.push({
|
|
110
|
+
role: "user",
|
|
111
|
+
content
|
|
112
|
+
});
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case "assistant": {
|
|
116
|
+
const textParts = [];
|
|
117
|
+
for (const part of message.content) if (part.type === "text") textParts.push(part.text);
|
|
118
|
+
else if (part.type === "tool-call") {
|
|
119
|
+
const { callId, itemId } = splitToolCallId(part.toolCallId);
|
|
120
|
+
input.push({
|
|
121
|
+
type: "function_call",
|
|
122
|
+
id: itemId,
|
|
123
|
+
call_id: callId,
|
|
124
|
+
name: part.toolName,
|
|
125
|
+
arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input ?? {})
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (textParts.length > 0) {
|
|
129
|
+
syntheticMessageCounter++;
|
|
130
|
+
input.push({
|
|
131
|
+
type: "message",
|
|
132
|
+
role: "assistant",
|
|
133
|
+
id: `msg_${Date.now()}_${syntheticMessageCounter}`,
|
|
134
|
+
content: [{
|
|
135
|
+
type: "output_text",
|
|
136
|
+
text: textParts.join("\n"),
|
|
137
|
+
annotations: []
|
|
138
|
+
}],
|
|
139
|
+
status: "completed"
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
case "tool":
|
|
145
|
+
for (const part of message.content) {
|
|
146
|
+
if (part.type !== "tool-result") continue;
|
|
147
|
+
const { callId } = splitToolCallId(part.toolCallId);
|
|
148
|
+
input.push({
|
|
149
|
+
type: "function_call_output",
|
|
150
|
+
call_id: callId,
|
|
151
|
+
output: toolResultOutputToText(part.output)
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
instructions: instructions || DEFAULT_INSTRUCTIONS,
|
|
158
|
+
input
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/providers/openai-codex/codex-sse.ts
|
|
163
|
+
/**
|
|
164
|
+
* Minimal SSE parsing for the Codex Responses endpoint. Each event is a JSON
|
|
165
|
+
* payload on `data:` lines; `[DONE]` terminates the stream.
|
|
166
|
+
*/
|
|
167
|
+
function parseOpenAICodexSSEStream(body) {
|
|
168
|
+
const decoder = new TextDecoder();
|
|
169
|
+
let buffer = "";
|
|
170
|
+
const parseChunk = (chunk, controller) => {
|
|
171
|
+
const dataLines = chunk.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim());
|
|
172
|
+
if (dataLines.length === 0) return;
|
|
173
|
+
const data = dataLines.join("\n").trim();
|
|
174
|
+
if (!data || data === "[DONE]") return;
|
|
175
|
+
try {
|
|
176
|
+
controller.enqueue(JSON.parse(data));
|
|
177
|
+
} catch (cause) {
|
|
178
|
+
controller.error(new Error(`Invalid OpenAI Codex SSE JSON: ${data.slice(0, 200)}`, { cause }));
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
return body.pipeThrough(new TransformStream({
|
|
182
|
+
transform(value, controller) {
|
|
183
|
+
buffer += decoder.decode(value, { stream: true });
|
|
184
|
+
let index = buffer.indexOf("\n\n");
|
|
185
|
+
while (index !== -1) {
|
|
186
|
+
const chunk = buffer.slice(0, index);
|
|
187
|
+
buffer = buffer.slice(index + 2);
|
|
188
|
+
parseChunk(chunk, controller);
|
|
189
|
+
index = buffer.indexOf("\n\n");
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
flush(controller) {
|
|
193
|
+
buffer += decoder.decode();
|
|
194
|
+
if (buffer.trim()) parseChunk(buffer, controller);
|
|
195
|
+
}
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/providers/openai-codex/codex-tools.ts
|
|
200
|
+
function prepareOpenAICodexTools({ tools, toolChoice }) {
|
|
201
|
+
const warnings = [];
|
|
202
|
+
if (!tools || tools.length === 0) return {
|
|
203
|
+
codexTools: void 0,
|
|
204
|
+
codexToolChoice: void 0,
|
|
205
|
+
warnings
|
|
206
|
+
};
|
|
207
|
+
const codexTools = [];
|
|
208
|
+
for (const tool of tools) {
|
|
209
|
+
if (tool.type !== "function") {
|
|
210
|
+
warnings.push({
|
|
211
|
+
type: "unsupported",
|
|
212
|
+
feature: `tool type ${tool.type}`,
|
|
213
|
+
details: `OpenAI Codex only supports function tools; ignoring ${tool.name}.`
|
|
214
|
+
});
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
codexTools.push({
|
|
218
|
+
type: "function",
|
|
219
|
+
name: tool.name,
|
|
220
|
+
description: tool.description,
|
|
221
|
+
parameters: tool.inputSchema,
|
|
222
|
+
strict: null
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
let codexToolChoice;
|
|
226
|
+
switch (toolChoice?.type) {
|
|
227
|
+
case void 0: break;
|
|
228
|
+
case "auto":
|
|
229
|
+
case "none":
|
|
230
|
+
case "required":
|
|
231
|
+
codexToolChoice = toolChoice.type;
|
|
232
|
+
break;
|
|
233
|
+
case "tool":
|
|
234
|
+
warnings.push({
|
|
235
|
+
type: "unsupported",
|
|
236
|
+
feature: "toolChoice tool name",
|
|
237
|
+
details: "OpenAI Codex does not support forcing a specific tool; using 'required' instead."
|
|
238
|
+
});
|
|
239
|
+
codexToolChoice = "required";
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
codexTools: codexTools.length > 0 ? codexTools : void 0,
|
|
244
|
+
codexToolChoice,
|
|
245
|
+
warnings
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region src/providers/openai-codex/codex-usage.ts
|
|
250
|
+
function convertOpenAICodexUsage(usage) {
|
|
251
|
+
const inputTotal = usage?.input_tokens;
|
|
252
|
+
const cachedTokens = usage?.input_tokens_details?.cached_tokens ?? 0;
|
|
253
|
+
const outputTotal = usage?.output_tokens;
|
|
254
|
+
const reasoningTokens = usage?.output_tokens_details?.reasoning_tokens ?? 0;
|
|
255
|
+
return {
|
|
256
|
+
inputTokens: {
|
|
257
|
+
total: inputTotal,
|
|
258
|
+
noCache: inputTotal === void 0 ? void 0 : inputTotal - cachedTokens,
|
|
259
|
+
cacheRead: inputTotal === void 0 ? void 0 : cachedTokens,
|
|
260
|
+
cacheWrite: inputTotal === void 0 ? void 0 : 0
|
|
261
|
+
},
|
|
262
|
+
outputTokens: {
|
|
263
|
+
total: outputTotal,
|
|
264
|
+
text: outputTotal === void 0 ? void 0 : outputTotal - reasoningTokens,
|
|
265
|
+
reasoning: outputTotal === void 0 ? void 0 : reasoningTokens
|
|
266
|
+
},
|
|
267
|
+
...usage ? { raw: usage } : {}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function mapOpenAICodexFinishReason(status, hasToolCalls) {
|
|
271
|
+
switch (status) {
|
|
272
|
+
case "completed": return {
|
|
273
|
+
unified: hasToolCalls ? "tool-calls" : "stop",
|
|
274
|
+
raw: status
|
|
275
|
+
};
|
|
276
|
+
case "incomplete": return {
|
|
277
|
+
unified: "length",
|
|
278
|
+
raw: status
|
|
279
|
+
};
|
|
280
|
+
case "failed":
|
|
281
|
+
case "cancelled": return {
|
|
282
|
+
unified: "error",
|
|
283
|
+
raw: status
|
|
284
|
+
};
|
|
285
|
+
case void 0: return {
|
|
286
|
+
unified: "other",
|
|
287
|
+
raw: void 0
|
|
288
|
+
};
|
|
289
|
+
default: return {
|
|
290
|
+
unified: "stop",
|
|
291
|
+
raw: status
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
296
|
+
//#region src/providers/openai-codex/codex-language-model.ts
|
|
297
|
+
const OPENAI_CODEX_DEFAULT_BASE_URL = "https://chatgpt.com/backend-api";
|
|
298
|
+
/**
|
|
299
|
+
* Resolve the Codex responses endpoint from a base URL that may already
|
|
300
|
+
* include the `/codex` or `/codex/responses` suffix.
|
|
301
|
+
*/
|
|
302
|
+
function resolveOpenAICodexUrl(baseUrl) {
|
|
303
|
+
const normalized = (baseUrl?.trim() ? baseUrl : OPENAI_CODEX_DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
304
|
+
if (normalized.endsWith("/codex/responses")) return normalized;
|
|
305
|
+
if (normalized.endsWith("/codex")) return `${normalized}/responses`;
|
|
306
|
+
return `${normalized}/codex/responses`;
|
|
307
|
+
}
|
|
308
|
+
function asRecord(value) {
|
|
309
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
310
|
+
}
|
|
311
|
+
function asString(value) {
|
|
312
|
+
return typeof value === "string" ? value : void 0;
|
|
313
|
+
}
|
|
314
|
+
function cleanHeaders(headers) {
|
|
315
|
+
const result = {};
|
|
316
|
+
for (const [key, value] of Object.entries(headers)) if (value !== void 0) result[key] = value;
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
var OpenAICodexLanguageModel = class {
|
|
320
|
+
specificationVersion = "v3";
|
|
321
|
+
modelId;
|
|
322
|
+
config;
|
|
323
|
+
constructor(modelId, config) {
|
|
324
|
+
this.modelId = modelId;
|
|
325
|
+
this.config = config;
|
|
326
|
+
}
|
|
327
|
+
get provider() {
|
|
328
|
+
return this.config.provider;
|
|
329
|
+
}
|
|
330
|
+
get supportedUrls() {
|
|
331
|
+
return {};
|
|
332
|
+
}
|
|
333
|
+
getArgs(options) {
|
|
334
|
+
const warnings = [];
|
|
335
|
+
if (options.topP != null) warnings.push({
|
|
336
|
+
type: "unsupported",
|
|
337
|
+
feature: "topP"
|
|
338
|
+
});
|
|
339
|
+
if (options.topK != null) warnings.push({
|
|
340
|
+
type: "unsupported",
|
|
341
|
+
feature: "topK"
|
|
342
|
+
});
|
|
343
|
+
if (options.presencePenalty != null) warnings.push({
|
|
344
|
+
type: "unsupported",
|
|
345
|
+
feature: "presencePenalty"
|
|
346
|
+
});
|
|
347
|
+
if (options.frequencyPenalty != null) warnings.push({
|
|
348
|
+
type: "unsupported",
|
|
349
|
+
feature: "frequencyPenalty"
|
|
350
|
+
});
|
|
351
|
+
if (options.seed != null) warnings.push({
|
|
352
|
+
type: "unsupported",
|
|
353
|
+
feature: "seed"
|
|
354
|
+
});
|
|
355
|
+
if (options.stopSequences != null) warnings.push({
|
|
356
|
+
type: "unsupported",
|
|
357
|
+
feature: "stopSequences"
|
|
358
|
+
});
|
|
359
|
+
if (options.maxOutputTokens != null) warnings.push({
|
|
360
|
+
type: "unsupported",
|
|
361
|
+
feature: "maxOutputTokens"
|
|
362
|
+
});
|
|
363
|
+
if (options.responseFormat && options.responseFormat.type !== "text") warnings.push({
|
|
364
|
+
type: "unsupported",
|
|
365
|
+
feature: "responseFormat"
|
|
366
|
+
});
|
|
367
|
+
const codexOptions = options.providerOptions?.["openai-codex"] ?? {};
|
|
368
|
+
const { instructions, input } = convertToOpenAICodexPrompt(options.prompt);
|
|
369
|
+
const tools = prepareOpenAICodexTools({
|
|
370
|
+
tools: options.tools,
|
|
371
|
+
toolChoice: options.toolChoice
|
|
372
|
+
});
|
|
373
|
+
warnings.push(...tools.warnings);
|
|
374
|
+
const args = {
|
|
375
|
+
model: this.modelId,
|
|
376
|
+
store: codexOptions.store ?? false,
|
|
377
|
+
instructions,
|
|
378
|
+
input,
|
|
379
|
+
tool_choice: tools.codexToolChoice ?? "auto",
|
|
380
|
+
parallel_tool_calls: true,
|
|
381
|
+
include: codexOptions.include ?? ["reasoning.encrypted_content"],
|
|
382
|
+
text: { verbosity: codexOptions.textVerbosity ?? "low" }
|
|
383
|
+
};
|
|
384
|
+
if (tools.codexTools) args.tools = tools.codexTools;
|
|
385
|
+
if (options.temperature != null) args.temperature = options.temperature;
|
|
386
|
+
const promptCacheKey = codexOptions.promptCacheKey ?? this.config.sessionId;
|
|
387
|
+
if (promptCacheKey) args.prompt_cache_key = promptCacheKey;
|
|
388
|
+
const serviceTier = codexOptions.serviceTier ?? this.config.serviceTier;
|
|
389
|
+
if (serviceTier) args.service_tier = serviceTier;
|
|
390
|
+
if (codexOptions.reasoningEffort) args.reasoning = {
|
|
391
|
+
effort: codexOptions.reasoningEffort,
|
|
392
|
+
summary: codexOptions.reasoningSummary ?? "auto"
|
|
393
|
+
};
|
|
394
|
+
return {
|
|
395
|
+
args,
|
|
396
|
+
warnings
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
async doGenerate(options) {
|
|
400
|
+
const { stream, request, response } = await this.doStream(options);
|
|
401
|
+
const content = [];
|
|
402
|
+
const textBlocks = /* @__PURE__ */ new Map();
|
|
403
|
+
const reasoningBlocks = /* @__PURE__ */ new Map();
|
|
404
|
+
const warnings = [];
|
|
405
|
+
let finishReason = {
|
|
406
|
+
unified: "other",
|
|
407
|
+
raw: void 0
|
|
408
|
+
};
|
|
409
|
+
let usage = convertOpenAICodexUsage(void 0);
|
|
410
|
+
let responseId;
|
|
411
|
+
let responseModelId;
|
|
412
|
+
const reader = stream.getReader();
|
|
413
|
+
while (true) {
|
|
414
|
+
const { done, value } = await reader.read();
|
|
415
|
+
if (done) break;
|
|
416
|
+
switch (value.type) {
|
|
417
|
+
case "stream-start":
|
|
418
|
+
warnings.push(...value.warnings);
|
|
419
|
+
break;
|
|
420
|
+
case "response-metadata":
|
|
421
|
+
responseId = value.id;
|
|
422
|
+
responseModelId = value.modelId;
|
|
423
|
+
break;
|
|
424
|
+
case "text-start": {
|
|
425
|
+
const block = {
|
|
426
|
+
type: "text",
|
|
427
|
+
text: ""
|
|
428
|
+
};
|
|
429
|
+
textBlocks.set(value.id, block);
|
|
430
|
+
content.push(block);
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
case "text-delta": {
|
|
434
|
+
const block = textBlocks.get(value.id);
|
|
435
|
+
if (block) block.text += value.delta;
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
case "reasoning-start": {
|
|
439
|
+
const block = {
|
|
440
|
+
type: "reasoning",
|
|
441
|
+
text: ""
|
|
442
|
+
};
|
|
443
|
+
reasoningBlocks.set(value.id, block);
|
|
444
|
+
content.push(block);
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
case "reasoning-delta": {
|
|
448
|
+
const block = reasoningBlocks.get(value.id);
|
|
449
|
+
if (block) block.text += value.delta;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
case "tool-call":
|
|
453
|
+
content.push(value);
|
|
454
|
+
break;
|
|
455
|
+
case "finish":
|
|
456
|
+
finishReason = value.finishReason;
|
|
457
|
+
usage = value.usage;
|
|
458
|
+
break;
|
|
459
|
+
case "error": throw value.error instanceof Error ? value.error : new Error(String(value.error));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
content: content.filter((part) => part.type === "text" || part.type === "reasoning" ? part.text !== "" : true),
|
|
464
|
+
finishReason,
|
|
465
|
+
usage,
|
|
466
|
+
request,
|
|
467
|
+
response: {
|
|
468
|
+
...response,
|
|
469
|
+
id: responseId,
|
|
470
|
+
modelId: responseModelId
|
|
471
|
+
},
|
|
472
|
+
warnings
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
async doStream(options) {
|
|
476
|
+
const { args, warnings } = this.getArgs(options);
|
|
477
|
+
const body = {
|
|
478
|
+
...args,
|
|
479
|
+
stream: true
|
|
480
|
+
};
|
|
481
|
+
const url = resolveOpenAICodexUrl(this.config.baseURL);
|
|
482
|
+
const headers = cleanHeaders({
|
|
483
|
+
...await this.config.headers(),
|
|
484
|
+
...options.headers
|
|
485
|
+
});
|
|
486
|
+
const response = await (this.config.fetch ?? globalThis.fetch)(url, {
|
|
487
|
+
method: "POST",
|
|
488
|
+
headers,
|
|
489
|
+
body: JSON.stringify(body),
|
|
490
|
+
signal: options.abortSignal
|
|
491
|
+
});
|
|
492
|
+
if (!response.ok) throw await createOpenAICodexAPICallError({
|
|
493
|
+
response,
|
|
494
|
+
url,
|
|
495
|
+
requestBodyValues: body
|
|
496
|
+
});
|
|
497
|
+
if (!response.body) throw new Error("OpenAI Codex response has no body");
|
|
498
|
+
const responseHeaders = {};
|
|
499
|
+
response.headers.forEach((value, key) => {
|
|
500
|
+
responseHeaders[key] = value;
|
|
501
|
+
});
|
|
502
|
+
return {
|
|
503
|
+
stream: parseOpenAICodexSSEStream(response.body).pipeThrough(this.createTransformStream(warnings, options.includeRawChunks ?? false)),
|
|
504
|
+
request: { body },
|
|
505
|
+
response: { headers: responseHeaders }
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
createTransformStream(warnings, includeRawChunks) {
|
|
509
|
+
let finishReason = {
|
|
510
|
+
unified: "other",
|
|
511
|
+
raw: void 0
|
|
512
|
+
};
|
|
513
|
+
let usage;
|
|
514
|
+
let hasToolCalls = false;
|
|
515
|
+
const toolCallIdsByItemId = /* @__PURE__ */ new Map();
|
|
516
|
+
const handleOutputItemAdded = (item, controller) => {
|
|
517
|
+
const itemType = asString(item.type);
|
|
518
|
+
const itemId = asString(item.id);
|
|
519
|
+
if (!itemType || !itemId) return;
|
|
520
|
+
if (itemType === "message") controller.enqueue({
|
|
521
|
+
type: "text-start",
|
|
522
|
+
id: itemId
|
|
523
|
+
});
|
|
524
|
+
else if (itemType === "reasoning") controller.enqueue({
|
|
525
|
+
type: "reasoning-start",
|
|
526
|
+
id: itemId
|
|
527
|
+
});
|
|
528
|
+
else if (itemType === "function_call") {
|
|
529
|
+
const toolCallId = joinToolCallId(asString(item.call_id) ?? itemId, itemId);
|
|
530
|
+
toolCallIdsByItemId.set(itemId, toolCallId);
|
|
531
|
+
hasToolCalls = true;
|
|
532
|
+
controller.enqueue({
|
|
533
|
+
type: "tool-input-start",
|
|
534
|
+
id: toolCallId,
|
|
535
|
+
toolName: asString(item.name) ?? ""
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
const handleOutputItemDone = (item, controller) => {
|
|
540
|
+
const itemType = asString(item.type);
|
|
541
|
+
const itemId = asString(item.id);
|
|
542
|
+
if (!itemType || !itemId) return;
|
|
543
|
+
if (itemType === "message") controller.enqueue({
|
|
544
|
+
type: "text-end",
|
|
545
|
+
id: itemId
|
|
546
|
+
});
|
|
547
|
+
else if (itemType === "reasoning") controller.enqueue({
|
|
548
|
+
type: "reasoning-end",
|
|
549
|
+
id: itemId
|
|
550
|
+
});
|
|
551
|
+
else if (itemType === "function_call") {
|
|
552
|
+
const toolCallId = toolCallIdsByItemId.get(itemId) ?? joinToolCallId(asString(item.call_id) ?? itemId, itemId);
|
|
553
|
+
hasToolCalls = true;
|
|
554
|
+
controller.enqueue({
|
|
555
|
+
type: "tool-input-end",
|
|
556
|
+
id: toolCallId
|
|
557
|
+
});
|
|
558
|
+
controller.enqueue({
|
|
559
|
+
type: "tool-call",
|
|
560
|
+
toolCallId,
|
|
561
|
+
toolName: asString(item.name) ?? "",
|
|
562
|
+
input: asString(item.arguments) ?? ""
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
return new TransformStream({
|
|
567
|
+
start(controller) {
|
|
568
|
+
controller.enqueue({
|
|
569
|
+
type: "stream-start",
|
|
570
|
+
warnings
|
|
571
|
+
});
|
|
572
|
+
},
|
|
573
|
+
transform(event, controller) {
|
|
574
|
+
if (includeRawChunks) controller.enqueue({
|
|
575
|
+
type: "raw",
|
|
576
|
+
rawValue: event
|
|
577
|
+
});
|
|
578
|
+
const type = asString(event.type);
|
|
579
|
+
if (!type) return;
|
|
580
|
+
switch (type) {
|
|
581
|
+
case "response.created": {
|
|
582
|
+
const response = asRecord(event.response);
|
|
583
|
+
controller.enqueue({
|
|
584
|
+
type: "response-metadata",
|
|
585
|
+
id: asString(response?.id),
|
|
586
|
+
modelId: asString(response?.model)
|
|
587
|
+
});
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
case "response.output_item.added": {
|
|
591
|
+
const item = asRecord(event.item);
|
|
592
|
+
if (item) handleOutputItemAdded(item, controller);
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
case "response.output_text.delta": {
|
|
596
|
+
const itemId = asString(event.item_id);
|
|
597
|
+
const delta = asString(event.delta);
|
|
598
|
+
if (itemId && delta) controller.enqueue({
|
|
599
|
+
type: "text-delta",
|
|
600
|
+
id: itemId,
|
|
601
|
+
delta
|
|
602
|
+
});
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
case "response.reasoning_text.delta":
|
|
606
|
+
case "response.reasoning_summary_text.delta": {
|
|
607
|
+
const itemId = asString(event.item_id);
|
|
608
|
+
const delta = asString(event.delta);
|
|
609
|
+
if (itemId && delta) controller.enqueue({
|
|
610
|
+
type: "reasoning-delta",
|
|
611
|
+
id: itemId,
|
|
612
|
+
delta
|
|
613
|
+
});
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
case "response.function_call_arguments.delta": {
|
|
617
|
+
const itemId = asString(event.item_id);
|
|
618
|
+
const delta = asString(event.delta);
|
|
619
|
+
const toolCallId = itemId ? toolCallIdsByItemId.get(itemId) : void 0;
|
|
620
|
+
if (toolCallId && delta) controller.enqueue({
|
|
621
|
+
type: "tool-input-delta",
|
|
622
|
+
id: toolCallId,
|
|
623
|
+
delta
|
|
624
|
+
});
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
case "response.output_item.done": {
|
|
628
|
+
const item = asRecord(event.item);
|
|
629
|
+
if (item) handleOutputItemDone(item, controller);
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
case "response.completed":
|
|
633
|
+
case "response.done":
|
|
634
|
+
case "response.incomplete": {
|
|
635
|
+
const response = asRecord(event.response);
|
|
636
|
+
usage = response?.usage ?? usage;
|
|
637
|
+
finishReason = mapOpenAICodexFinishReason(asString(response?.status) ?? (type === "response.incomplete" ? "incomplete" : "completed"), hasToolCalls);
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
case "response.failed": {
|
|
641
|
+
const error = asRecord(asRecord(event.response)?.error);
|
|
642
|
+
finishReason = {
|
|
643
|
+
unified: "error",
|
|
644
|
+
raw: "failed"
|
|
645
|
+
};
|
|
646
|
+
controller.enqueue({
|
|
647
|
+
type: "error",
|
|
648
|
+
error: new Error(asString(error?.message) ?? "OpenAI Codex response failed")
|
|
649
|
+
});
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
case "error": {
|
|
653
|
+
const code = asString(event.code);
|
|
654
|
+
const message = asString(event.message);
|
|
655
|
+
finishReason = {
|
|
656
|
+
unified: "error",
|
|
657
|
+
raw: code ?? "error"
|
|
658
|
+
};
|
|
659
|
+
controller.enqueue({
|
|
660
|
+
type: "error",
|
|
661
|
+
error: /* @__PURE__ */ new Error(`OpenAI Codex error${code ? ` ${code}` : ""}: ${message ?? JSON.stringify(event)}`)
|
|
662
|
+
});
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
665
|
+
default: break;
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
flush(controller) {
|
|
669
|
+
controller.enqueue({
|
|
670
|
+
type: "finish",
|
|
671
|
+
finishReason,
|
|
672
|
+
usage: convertOpenAICodexUsage(usage)
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
//#endregion
|
|
679
|
+
//#region src/providers/openai-codex/codex-provider.ts
|
|
680
|
+
const OPENAI_CODEX_API_KEY_ENV = "OPENAI_CODEX_API_KEY";
|
|
681
|
+
const DEFAULT_ORIGINATOR = "codework";
|
|
682
|
+
async function resolveApiKey(settings) {
|
|
683
|
+
const apiKey = typeof settings.apiKey === "function" ? await settings.apiKey() : settings.apiKey ?? (typeof process === "undefined" ? void 0 : process.env["OPENAI_CODEX_API_KEY"]);
|
|
684
|
+
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.` });
|
|
685
|
+
return apiKey;
|
|
686
|
+
}
|
|
687
|
+
function resolveAccountId(settings, apiKey) {
|
|
688
|
+
const accountId = settings.accountId ?? getOpenAICodexAccountId(apiKey);
|
|
689
|
+
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." });
|
|
690
|
+
return accountId;
|
|
691
|
+
}
|
|
692
|
+
function createOpenAICodex(options = {}) {
|
|
693
|
+
const baseURL = options.baseURL?.replace(/\/+$/, "") || "https://chatgpt.com/backend-api";
|
|
694
|
+
const getHeaders = async () => {
|
|
695
|
+
const apiKey = await resolveApiKey(options);
|
|
696
|
+
const accountId = resolveAccountId(options, apiKey);
|
|
697
|
+
return {
|
|
698
|
+
Authorization: `Bearer ${apiKey}`,
|
|
699
|
+
"chatgpt-account-id": accountId,
|
|
700
|
+
"OpenAI-Beta": "responses=experimental",
|
|
701
|
+
originator: options.originator ?? DEFAULT_ORIGINATOR,
|
|
702
|
+
accept: "text/event-stream",
|
|
703
|
+
"content-type": "application/json",
|
|
704
|
+
...options.sessionId ? {
|
|
705
|
+
"session-id": options.sessionId,
|
|
706
|
+
"x-client-request-id": options.sessionId
|
|
707
|
+
} : {},
|
|
708
|
+
...options.headers
|
|
709
|
+
};
|
|
710
|
+
};
|
|
711
|
+
const createLanguageModel = (modelId) => new OpenAICodexLanguageModel(modelId, {
|
|
712
|
+
provider: "openai-codex",
|
|
713
|
+
baseURL,
|
|
714
|
+
headers: getHeaders,
|
|
715
|
+
fetch: options.fetch,
|
|
716
|
+
sessionId: options.sessionId,
|
|
717
|
+
serviceTier: options.serviceTier
|
|
718
|
+
});
|
|
719
|
+
const provider = function(modelId) {
|
|
720
|
+
if (new.target) throw new Error("The OpenAI Codex model function cannot be called with the new keyword.");
|
|
721
|
+
return createLanguageModel(modelId);
|
|
722
|
+
};
|
|
723
|
+
provider.specificationVersion = "v3";
|
|
724
|
+
provider.languageModel = createLanguageModel;
|
|
725
|
+
provider.responses = createLanguageModel;
|
|
726
|
+
provider.embeddingModel = (modelId) => {
|
|
727
|
+
throw new NoSuchModelError({
|
|
728
|
+
modelId,
|
|
729
|
+
modelType: "embeddingModel"
|
|
730
|
+
});
|
|
731
|
+
};
|
|
732
|
+
provider.textEmbeddingModel = (modelId) => {
|
|
733
|
+
throw new NoSuchModelError({
|
|
734
|
+
modelId,
|
|
735
|
+
modelType: "embeddingModel"
|
|
736
|
+
});
|
|
737
|
+
};
|
|
738
|
+
provider.imageModel = (modelId) => {
|
|
739
|
+
throw new NoSuchModelError({
|
|
740
|
+
modelId,
|
|
741
|
+
modelType: "imageModel"
|
|
742
|
+
});
|
|
743
|
+
};
|
|
744
|
+
return provider;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Default provider instance using `OPENAI_CODEX_API_KEY`.
|
|
748
|
+
*/
|
|
749
|
+
const openaiCodex = createOpenAICodex();
|
|
750
|
+
//#endregion
|
|
751
|
+
export { OpenAICodexLanguageModel as a, mapOpenAICodexFinishReason as c, joinToolCallId as d, splitToolCallId as f, OPENAI_CODEX_DEFAULT_BASE_URL as i, prepareOpenAICodexTools as l, openAICodexErrorMessage as m, createOpenAICodex as n, resolveOpenAICodexUrl as o, createOpenAICodexAPICallError as p, openaiCodex as r, convertOpenAICodexUsage as s, OPENAI_CODEX_API_KEY_ENV as t, convertToOpenAICodexPrompt as u };
|
|
752
|
+
|
|
753
|
+
//# sourceMappingURL=openai-codex-CZrBnLAJ.mjs.map
|