@opengeni/codex 0.2.11 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-client.d.ts +2 -2
- package/dist/fetch.d.ts +13 -5
- package/dist/images.d.ts +34 -0
- package/dist/index.d.ts +17 -16
- package/dist/index.js +316 -62
- package/dist/index.js.map +1 -1
- package/dist/mcp-sanitize.d.ts +1 -1
- package/dist/model-output-truncation.d.ts +1 -1
- package/dist/normalize.d.ts +6 -0
- package/dist/realtime.d.ts +4 -4
- package/dist/refresh.d.ts +1 -1
- package/dist/response-timeout.d.ts +1 -1
- package/dist/usage-normalize.d.ts +1 -1
- package/package.json +2 -1
- package/src/fetch.ts +166 -77
- package/src/images.ts +209 -0
- package/src/index.ts +1 -0
- package/src/mcp-sanitize.ts +12 -8
- package/src/model-output-truncation.ts +7 -1
- package/src/normalize.ts +26 -0
package/dist/index.js
CHANGED
|
@@ -326,6 +326,20 @@ function normalizeCodexRequestBody(body, resolveModel) {
|
|
|
326
326
|
}
|
|
327
327
|
return body;
|
|
328
328
|
}
|
|
329
|
+
function normalizedCodexRequestBody(body, resolveModel) {
|
|
330
|
+
const projected = { ...body };
|
|
331
|
+
if (body.reasoning && typeof body.reasoning === "object" && !Array.isArray(body.reasoning)) {
|
|
332
|
+
projected.reasoning = { ...body.reasoning };
|
|
333
|
+
}
|
|
334
|
+
if (Array.isArray(body.input)) {
|
|
335
|
+
projected.input = body.input.map((item) => {
|
|
336
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return item;
|
|
337
|
+
const record = item;
|
|
338
|
+
return "id" in record || record.type === "tool_search_call" && typeof record.arguments === "string" ? { ...record } : item;
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return normalizeCodexRequestBody(projected, resolveModel);
|
|
342
|
+
}
|
|
329
343
|
function buildModelResolver(liveSlugs, fallbackSlug) {
|
|
330
344
|
return (requested) => {
|
|
331
345
|
const stripped = requested.includes("/") ? requested.slice(requested.indexOf("/") + 1) : requested;
|
|
@@ -808,6 +822,12 @@ function opaqueProviderArtifactFingerprints(input) {
|
|
|
808
822
|
|
|
809
823
|
// src/fetch.ts
|
|
810
824
|
var CODEX_TRANSPORT_ERROR_HEADER = "x-opengeni-codex-transport-error";
|
|
825
|
+
var CODEX_REQUEST_BODY_NORMALIZED_HEADER = "x-opengeni-request-body-normalized";
|
|
826
|
+
var REPLAYABLE_REQUEST_BODY_FACTORY = Symbol.for("opengeni.replayable-request-body-factory");
|
|
827
|
+
var CODEX_REQUEST_MODEL_HEADER = "x-opengeni-request-model";
|
|
828
|
+
var CODEX_REQUEST_ID_HEADER = "x-opengeni-request-id";
|
|
829
|
+
var CODEX_REQUEST_CALLER_STREAM_HEADER = "x-opengeni-request-caller-stream";
|
|
830
|
+
var MAX_CODEX_ERROR_BODY_BYTES = 64 * 1024;
|
|
811
831
|
function headersCarryCodexTransportMarker(headers) {
|
|
812
832
|
if (!headers || typeof headers !== "object") return false;
|
|
813
833
|
const getter = headers.get;
|
|
@@ -1036,16 +1056,20 @@ async function observedResponse(res, audit, externalSignal) {
|
|
|
1036
1056
|
controller.close();
|
|
1037
1057
|
return;
|
|
1038
1058
|
}
|
|
1039
|
-
armIdle();
|
|
1040
1059
|
if (!firstByte) {
|
|
1041
1060
|
firstByte = true;
|
|
1061
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
1062
|
+
controller.enqueue(chunk.value);
|
|
1042
1063
|
await emitRequestEvent(audit, {
|
|
1043
1064
|
phase: "first_byte",
|
|
1044
1065
|
responseObserved: true,
|
|
1045
1066
|
status: res.status,
|
|
1046
1067
|
...requestId ? { providerRequestId: requestId } : {}
|
|
1047
1068
|
});
|
|
1069
|
+
if (!terminal) armIdle();
|
|
1070
|
+
return;
|
|
1048
1071
|
}
|
|
1072
|
+
armIdle();
|
|
1049
1073
|
controller.enqueue(chunk.value);
|
|
1050
1074
|
} catch (error) {
|
|
1051
1075
|
if (terminal) return;
|
|
@@ -1113,11 +1137,19 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1113
1137
|
const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
1114
1138
|
const rewritten = rawUrl.replace(/(?<!\/codex)\/responses(\b|$)/, "/codex/responses$1");
|
|
1115
1139
|
const policy = resolveCodexResponseTimeoutPolicy(ctx.responseTimeoutPolicy);
|
|
1116
|
-
const
|
|
1140
|
+
const handedRequestId = new Headers(init?.headers).get(CODEX_REQUEST_ID_HEADER);
|
|
1141
|
+
const requestId = handedRequestId ?? ctx.nextRequestId?.() ?? randomUUID();
|
|
1117
1142
|
const logicalStartedAt = Date.now();
|
|
1118
1143
|
let transportAttempt = 0;
|
|
1119
1144
|
const attempt = async (auth, authenticationAttempt) => {
|
|
1120
1145
|
const headers = new Headers(init?.headers);
|
|
1146
|
+
const bodyAlreadyNormalized = headers.get(CODEX_REQUEST_BODY_NORMALIZED_HEADER) === "1";
|
|
1147
|
+
const normalizedModel = headers.get(CODEX_REQUEST_MODEL_HEADER) ?? void 0;
|
|
1148
|
+
const normalizedCallerStream = headers.get(CODEX_REQUEST_CALLER_STREAM_HEADER);
|
|
1149
|
+
headers.delete(CODEX_REQUEST_BODY_NORMALIZED_HEADER);
|
|
1150
|
+
headers.delete(CODEX_REQUEST_MODEL_HEADER);
|
|
1151
|
+
headers.delete(CODEX_REQUEST_ID_HEADER);
|
|
1152
|
+
headers.delete(CODEX_REQUEST_CALLER_STREAM_HEADER);
|
|
1121
1153
|
headers.set("Authorization", `Bearer ${auth.accessToken}`);
|
|
1122
1154
|
if (auth.chatgptAccountId) {
|
|
1123
1155
|
headers.set("ChatGPT-Account-ID", auth.chatgptAccountId);
|
|
@@ -1141,11 +1173,16 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1141
1173
|
if (ctx.turnMetadata && Object.keys(ctx.turnMetadata).length > 0) {
|
|
1142
1174
|
headers.set("x-codex-turn-metadata", JSON.stringify(ctx.turnMetadata));
|
|
1143
1175
|
}
|
|
1144
|
-
let callerWantsStream = true;
|
|
1145
|
-
let model;
|
|
1176
|
+
let callerWantsStream = bodyAlreadyNormalized ? normalizedCallerStream !== "0" : true;
|
|
1177
|
+
let model = normalizedModel;
|
|
1146
1178
|
let requestOpaqueArtifacts = [];
|
|
1147
|
-
const
|
|
1148
|
-
|
|
1179
|
+
const replayableBodyFactory = init?.[REPLAYABLE_REQUEST_BODY_FACTORY];
|
|
1180
|
+
const nextInit = {
|
|
1181
|
+
...init,
|
|
1182
|
+
headers,
|
|
1183
|
+
...replayableBodyFactory ? { body: replayableBodyFactory() } : {}
|
|
1184
|
+
};
|
|
1185
|
+
if (!bodyAlreadyNormalized && typeof init?.body === "string") {
|
|
1149
1186
|
try {
|
|
1150
1187
|
const parsed = JSON.parse(init.body);
|
|
1151
1188
|
callerWantsStream = parsed.stream === true;
|
|
@@ -1154,24 +1191,25 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1154
1191
|
nextInit.body = JSON.stringify(normalized);
|
|
1155
1192
|
requestOpaqueArtifacts = opaqueProviderArtifactFingerprints(normalized.input);
|
|
1156
1193
|
} catch {
|
|
1194
|
+
throw new Error("Model request could not be prepared");
|
|
1157
1195
|
}
|
|
1196
|
+
} else if (!bodyAlreadyNormalized) {
|
|
1197
|
+
throw new Error("Model request could not be prepared");
|
|
1198
|
+
}
|
|
1199
|
+
if (!bodyAlreadyNormalized) {
|
|
1200
|
+
ctx.onRequestOpaqueArtifacts?.({ requestId, fingerprints: requestOpaqueArtifacts });
|
|
1158
1201
|
}
|
|
1159
|
-
ctx.onRequestOpaqueArtifacts?.({ requestId, fingerprints: requestOpaqueArtifacts });
|
|
1160
1202
|
headers.set(
|
|
1161
1203
|
"Idempotency-Key",
|
|
1162
1204
|
authenticationAttempt === 0 ? requestId : `${requestId}:auth-${authenticationAttempt}`
|
|
1163
1205
|
);
|
|
1164
1206
|
if (process.env.CODEX_DEBUG) {
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1172
|
-
console.error(
|
|
1173
|
-
`[codex-debug] POST ${rewritten} stream=${callerWantsStream} bodyKeys=[${keys.join(",")}]`
|
|
1174
|
-
);
|
|
1207
|
+
console.error("[codex-debug] request dispatched", {
|
|
1208
|
+
method: "POST",
|
|
1209
|
+
origin: "codex-subscription",
|
|
1210
|
+
route: "codex_responses",
|
|
1211
|
+
stream: callerWantsStream
|
|
1212
|
+
});
|
|
1175
1213
|
}
|
|
1176
1214
|
let res;
|
|
1177
1215
|
transportAttempt += 1;
|
|
@@ -1228,14 +1266,16 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1228
1266
|
ctx.onUsageHeaders?.(usage);
|
|
1229
1267
|
}
|
|
1230
1268
|
if (process.env.CODEX_DEBUG && !res.ok) {
|
|
1231
|
-
console.error(
|
|
1232
|
-
|
|
1233
|
-
|
|
1269
|
+
console.error("[codex-debug] request failed", {
|
|
1270
|
+
origin: "codex-subscription",
|
|
1271
|
+
route: "codex_responses",
|
|
1272
|
+
status: res.status
|
|
1273
|
+
});
|
|
1234
1274
|
}
|
|
1235
1275
|
if (!res.ok) {
|
|
1236
1276
|
return await bufferCodexErrorResponse(res);
|
|
1237
1277
|
}
|
|
1238
|
-
return callerWantsStream ?
|
|
1278
|
+
return callerWantsStream ? validateCodexStream(res) : await sseToJsonResponse(res);
|
|
1239
1279
|
};
|
|
1240
1280
|
try {
|
|
1241
1281
|
let res = await attempt(await ctx.getToken(), 0);
|
|
@@ -1273,27 +1313,75 @@ function classifyCodexUsageLimitError(error) {
|
|
|
1273
1313
|
return null;
|
|
1274
1314
|
}
|
|
1275
1315
|
async function bufferCodexErrorResponse(res) {
|
|
1276
|
-
const bodyText = await
|
|
1316
|
+
const { text: bodyText, truncated } = await readBoundedResponseText(
|
|
1317
|
+
res,
|
|
1318
|
+
MAX_CODEX_ERROR_BODY_BYTES
|
|
1319
|
+
);
|
|
1277
1320
|
const headers = new Headers(res.headers);
|
|
1278
1321
|
headers.set("content-type", "application/json");
|
|
1279
1322
|
headers.set(CODEX_TRANSPORT_ERROR_HEADER, "1");
|
|
1280
1323
|
headers.delete("content-length");
|
|
1281
1324
|
headers.delete("content-encoding");
|
|
1282
1325
|
let errorType;
|
|
1326
|
+
let responseBody = bodyText;
|
|
1283
1327
|
try {
|
|
1284
1328
|
const parsed = JSON.parse(bodyText);
|
|
1285
1329
|
errorType = typeof parsed.error?.type === "string" ? parsed.error.type : void 0;
|
|
1286
1330
|
} catch {
|
|
1287
1331
|
}
|
|
1332
|
+
if (truncated) {
|
|
1333
|
+
responseBody = JSON.stringify({
|
|
1334
|
+
error: {
|
|
1335
|
+
type: "provider_error_body_too_large",
|
|
1336
|
+
code: "provider_error_body_too_large",
|
|
1337
|
+
message: `The provider returned an error body larger than ${MAX_CODEX_ERROR_BODY_BYTES} bytes`
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
headers.set("x-opengeni-provider-error-truncated", "1");
|
|
1341
|
+
}
|
|
1288
1342
|
if (errorType === CODEX_USAGE_LIMIT_ERROR_TYPE) {
|
|
1289
1343
|
headers.set("x-should-retry", "false");
|
|
1290
1344
|
}
|
|
1291
|
-
return new Response(
|
|
1345
|
+
return new Response(responseBody, {
|
|
1292
1346
|
status: res.status,
|
|
1293
1347
|
statusText: res.statusText,
|
|
1294
1348
|
headers
|
|
1295
1349
|
});
|
|
1296
1350
|
}
|
|
1351
|
+
async function readBoundedResponseText(response, maxBytes) {
|
|
1352
|
+
if (!response.body) return { text: "", truncated: false };
|
|
1353
|
+
const reader = response.body.getReader();
|
|
1354
|
+
const decoder = new TextDecoder();
|
|
1355
|
+
const parts = [];
|
|
1356
|
+
let bytes = 0;
|
|
1357
|
+
let truncated = false;
|
|
1358
|
+
try {
|
|
1359
|
+
while (bytes < maxBytes) {
|
|
1360
|
+
const next = await reader.read();
|
|
1361
|
+
if (next.done) {
|
|
1362
|
+
parts.push(decoder.decode());
|
|
1363
|
+
return { text: parts.join(""), truncated };
|
|
1364
|
+
}
|
|
1365
|
+
const remaining = maxBytes - bytes;
|
|
1366
|
+
const accepted = next.value.byteLength > remaining ? next.value.subarray(0, remaining) : next.value;
|
|
1367
|
+
bytes += accepted.byteLength;
|
|
1368
|
+
parts.push(decoder.decode(accepted, { stream: true }));
|
|
1369
|
+
if (accepted.byteLength !== next.value.byteLength) {
|
|
1370
|
+
truncated = true;
|
|
1371
|
+
break;
|
|
1372
|
+
}
|
|
1373
|
+
if (bytes >= maxBytes) {
|
|
1374
|
+
truncated = true;
|
|
1375
|
+
break;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
} catch {
|
|
1379
|
+
truncated = true;
|
|
1380
|
+
} finally {
|
|
1381
|
+
if (truncated) void reader.cancel().catch(() => void 0);
|
|
1382
|
+
}
|
|
1383
|
+
return { text: parts.join(""), truncated };
|
|
1384
|
+
}
|
|
1297
1385
|
async function sseToJsonResponse(res) {
|
|
1298
1386
|
const text = await res.text();
|
|
1299
1387
|
let final = null;
|
|
@@ -1492,8 +1580,8 @@ var CodexStreamingTerminalError = class extends Error {
|
|
|
1492
1580
|
responseStatus;
|
|
1493
1581
|
headers;
|
|
1494
1582
|
error;
|
|
1495
|
-
constructor(projection
|
|
1496
|
-
super(
|
|
1583
|
+
constructor(projection) {
|
|
1584
|
+
super(projection.error.message);
|
|
1497
1585
|
this.name = "CodexStreamingTerminalError";
|
|
1498
1586
|
this.status = projection.status;
|
|
1499
1587
|
this.code = projection.error.code;
|
|
@@ -1511,6 +1599,8 @@ var CodexStreamingTerminalError = class extends Error {
|
|
|
1511
1599
|
this.error = {
|
|
1512
1600
|
type: projection.error.type,
|
|
1513
1601
|
code: projection.error.code,
|
|
1602
|
+
message: projection.error.message,
|
|
1603
|
+
...projection.error.param ? { param: projection.error.param } : {},
|
|
1514
1604
|
...projection.error.event_type ? { event_type: projection.error.event_type } : {},
|
|
1515
1605
|
...projection.error.response_id ? { response_id: projection.error.response_id } : {},
|
|
1516
1606
|
...projection.error.response_status ? { response_status: projection.error.response_status } : {},
|
|
@@ -1520,11 +1610,10 @@ var CodexStreamingTerminalError = class extends Error {
|
|
|
1520
1610
|
};
|
|
1521
1611
|
function codexSseFailureError(source, rawError, fallbackCode, publicMessage, metadata = {}) {
|
|
1522
1612
|
return new CodexStreamingTerminalError(
|
|
1523
|
-
codexSseFailureProjection(source, rawError, fallbackCode, publicMessage, metadata)
|
|
1524
|
-
publicMessage
|
|
1613
|
+
codexSseFailureProjection(source, rawError, fallbackCode, publicMessage, metadata)
|
|
1525
1614
|
);
|
|
1526
1615
|
}
|
|
1527
|
-
function
|
|
1616
|
+
function validateCodexStream(res) {
|
|
1528
1617
|
if (!res.body) {
|
|
1529
1618
|
const error = codexSseFailureError(
|
|
1530
1619
|
res,
|
|
@@ -1545,7 +1634,6 @@ function repairCodexStream(res) {
|
|
|
1545
1634
|
headers: headers2
|
|
1546
1635
|
});
|
|
1547
1636
|
}
|
|
1548
|
-
const items = [];
|
|
1549
1637
|
const decoder = new TextDecoder();
|
|
1550
1638
|
const encoder = new TextEncoder();
|
|
1551
1639
|
let buffer = "";
|
|
@@ -1556,9 +1644,8 @@ function repairCodexStream(res) {
|
|
|
1556
1644
|
const block = buffer.slice(0, boundary.start);
|
|
1557
1645
|
const separator = buffer.slice(boundary.start, boundary.end);
|
|
1558
1646
|
buffer = buffer.slice(boundary.end);
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
controller.enqueue(encoder.encode(`${patched.block}${separator}`));
|
|
1647
|
+
successfulTerminalSeen ||= inspectCodexSseBlock(block, res);
|
|
1648
|
+
controller.enqueue(encoder.encode(`${block}${separator}`));
|
|
1562
1649
|
boundary = findSseBlockBoundary(buffer, final);
|
|
1563
1650
|
}
|
|
1564
1651
|
};
|
|
@@ -1571,9 +1658,8 @@ function repairCodexStream(res) {
|
|
|
1571
1658
|
buffer += decoder.decode();
|
|
1572
1659
|
emitCompleteBlocks(controller, true);
|
|
1573
1660
|
if (buffer.length > 0) {
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
controller.enqueue(encoder.encode(patched.block));
|
|
1661
|
+
successfulTerminalSeen ||= inspectCodexSseBlock(buffer, res);
|
|
1662
|
+
controller.enqueue(encoder.encode(buffer));
|
|
1577
1663
|
buffer = "";
|
|
1578
1664
|
}
|
|
1579
1665
|
if (!successfulTerminalSeen) {
|
|
@@ -1614,21 +1700,28 @@ function sseLineEndingEnd(value, index, final) {
|
|
|
1614
1700
|
}
|
|
1615
1701
|
return final ? index + 1 : null;
|
|
1616
1702
|
}
|
|
1617
|
-
|
|
1703
|
+
var CODEX_TERMINAL_TYPE_HINTS = [
|
|
1704
|
+
'"response.completed"',
|
|
1705
|
+
'"response.done"',
|
|
1706
|
+
'"response.failed"',
|
|
1707
|
+
'"response.incomplete"',
|
|
1708
|
+
'"response.error"',
|
|
1709
|
+
'"error"'
|
|
1710
|
+
];
|
|
1711
|
+
function inspectCodexSseBlock(block, source) {
|
|
1618
1712
|
const lines = block.split(/\r\n|\r|\n/);
|
|
1619
1713
|
const dataStr = lines.filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("\n");
|
|
1620
1714
|
if (!dataStr || dataStr === "[DONE]") {
|
|
1621
|
-
return
|
|
1715
|
+
return false;
|
|
1716
|
+
}
|
|
1717
|
+
if (!CODEX_TERMINAL_TYPE_HINTS.some((terminalType) => dataStr.includes(terminalType))) {
|
|
1718
|
+
return false;
|
|
1622
1719
|
}
|
|
1623
1720
|
let ev;
|
|
1624
1721
|
try {
|
|
1625
1722
|
ev = JSON.parse(dataStr);
|
|
1626
1723
|
} catch {
|
|
1627
|
-
return
|
|
1628
|
-
}
|
|
1629
|
-
if (ev.type === "response.output_item.done" && ev.item !== void 0) {
|
|
1630
|
-
items.push(ev.item);
|
|
1631
|
-
return { block, successfulTerminal: false };
|
|
1724
|
+
return false;
|
|
1632
1725
|
}
|
|
1633
1726
|
if (ev.type === "response.failed") {
|
|
1634
1727
|
throw codexSseFailureError(
|
|
@@ -1675,7 +1768,7 @@ function patchSseBlock(block, items, source) {
|
|
|
1675
1768
|
);
|
|
1676
1769
|
}
|
|
1677
1770
|
if ((ev.type === "response.completed" || ev.type === "response.done") && ev.response) {
|
|
1678
|
-
if (ev.response.status
|
|
1771
|
+
if (ev.response.status !== void 0 && ev.response.status !== "completed" || ev.response.error !== null && ev.response.error !== void 0) {
|
|
1679
1772
|
throw codexSseFailureError(
|
|
1680
1773
|
source,
|
|
1681
1774
|
ev.response.error,
|
|
@@ -1688,19 +1781,9 @@ function patchSseBlock(block, items, source) {
|
|
|
1688
1781
|
}
|
|
1689
1782
|
);
|
|
1690
1783
|
}
|
|
1691
|
-
|
|
1692
|
-
if ((!Array.isArray(out) || out.length === 0) && items.length > 0) {
|
|
1693
|
-
ev.response = { ...ev.response, output: items };
|
|
1694
|
-
const nonData = lines.filter((l) => !l.startsWith("data:"));
|
|
1695
|
-
const lineEnding = block.match(/\r\n|\r|\n/)?.[0] ?? "\n";
|
|
1696
|
-
return {
|
|
1697
|
-
block: [...nonData, `data: ${JSON.stringify(ev)}`].join(lineEnding),
|
|
1698
|
-
successfulTerminal: true
|
|
1699
|
-
};
|
|
1700
|
-
}
|
|
1701
|
-
return { block, successfulTerminal: true };
|
|
1784
|
+
return true;
|
|
1702
1785
|
}
|
|
1703
|
-
return
|
|
1786
|
+
return false;
|
|
1704
1787
|
}
|
|
1705
1788
|
|
|
1706
1789
|
// src/mcp-sanitize.ts
|
|
@@ -1786,13 +1869,15 @@ function inlineStructuredContentInRpcMessage(message) {
|
|
|
1786
1869
|
return;
|
|
1787
1870
|
}
|
|
1788
1871
|
const structured = record.structuredContent;
|
|
1789
|
-
if (structured
|
|
1790
|
-
|
|
1872
|
+
if (structured !== void 0 && structured !== null) {
|
|
1873
|
+
const text = typeof structured === "string" ? structured : JSON.stringify(structured);
|
|
1874
|
+
const content = Array.isArray(record.content) ? [...record.content] : [];
|
|
1875
|
+
content.push({ type: "text", text });
|
|
1876
|
+
record.content = content;
|
|
1877
|
+
}
|
|
1878
|
+
if (typeof structured !== "object" || structured === null || Array.isArray(structured)) {
|
|
1879
|
+
delete record.structuredContent;
|
|
1791
1880
|
}
|
|
1792
|
-
const text = typeof structured === "string" ? structured : JSON.stringify(structured);
|
|
1793
|
-
const content = Array.isArray(record.content) ? [...record.content] : [];
|
|
1794
|
-
content.push({ type: "text", text });
|
|
1795
|
-
record.content = content;
|
|
1796
1881
|
}
|
|
1797
1882
|
function sanitizeMcpJsonBody(text, mapper = new ToolNameMapper(), namespaceSink) {
|
|
1798
1883
|
try {
|
|
@@ -1961,7 +2046,13 @@ function boundModelToolOutputItem(item, policyTokens = DEFAULT_MODEL_TOOL_OUTPUT
|
|
|
1961
2046
|
return boundedOutput === item.output ? item : { ...item, output: boundedOutput };
|
|
1962
2047
|
}
|
|
1963
2048
|
function boundModelToolOutputItems(items, policyTokens = DEFAULT_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS) {
|
|
1964
|
-
|
|
2049
|
+
let bounded = null;
|
|
2050
|
+
for (const [index, item] of items.entries()) {
|
|
2051
|
+
const next = boundModelToolOutputItem(item, policyTokens);
|
|
2052
|
+
if (next !== item && bounded === null) bounded = items.slice(0, index);
|
|
2053
|
+
bounded?.push(next);
|
|
2054
|
+
}
|
|
2055
|
+
return bounded ?? items;
|
|
1965
2056
|
}
|
|
1966
2057
|
function boundToolOutputValue(output, budgetTokens) {
|
|
1967
2058
|
const state = modelOutputBoundState(budgetTokens);
|
|
@@ -2276,6 +2367,161 @@ function isImageDataUrl(value) {
|
|
|
2276
2367
|
return /^data:image\/[a-z0-9.+-]+;base64,/i.test(value);
|
|
2277
2368
|
}
|
|
2278
2369
|
|
|
2370
|
+
// src/images.ts
|
|
2371
|
+
import { pinnedFetch, readJsonBase64Field, readResponseTextBounded } from "@opengeni/network";
|
|
2372
|
+
var CODEX_IMAGE_MODEL = "gpt-image-2";
|
|
2373
|
+
var CODEX_IMAGE_RESPONSE_MAX_BYTES = 90 * 1024 * 1024;
|
|
2374
|
+
var CODEX_IMAGE_ERROR_MAX_BYTES = 64 * 1024;
|
|
2375
|
+
var CODEX_IMAGE_MAX_BYTES = 64 * 1024 * 1024;
|
|
2376
|
+
var CODEX_IMAGE_REQUEST_TIMEOUT_MS = 5 * 6e4;
|
|
2377
|
+
var CODEX_IMAGE_MAX_REFERENCES = 5;
|
|
2378
|
+
var codexImageFetch = async (input, init) => await pinnedFetch(
|
|
2379
|
+
input,
|
|
2380
|
+
init,
|
|
2381
|
+
{
|
|
2382
|
+
environment: "production",
|
|
2383
|
+
integrationsAllowPrivateNetworkTargets: false
|
|
2384
|
+
},
|
|
2385
|
+
{
|
|
2386
|
+
label: "Codex image generation",
|
|
2387
|
+
requireHttpsOutsideLocalTest: true
|
|
2388
|
+
}
|
|
2389
|
+
);
|
|
2390
|
+
var CodexImageApiError = class extends Error {
|
|
2391
|
+
constructor(status, message) {
|
|
2392
|
+
super(message);
|
|
2393
|
+
this.status = status;
|
|
2394
|
+
this.name = "CodexImageApiError";
|
|
2395
|
+
}
|
|
2396
|
+
};
|
|
2397
|
+
var CodexImageRequestTimeoutError = class extends Error {
|
|
2398
|
+
constructor(timeoutMs) {
|
|
2399
|
+
super(`Codex image generation timed out after ${Math.ceil(timeoutMs / 1e3)} seconds`);
|
|
2400
|
+
this.timeoutMs = timeoutMs;
|
|
2401
|
+
this.name = "CodexImageRequestTimeoutError";
|
|
2402
|
+
}
|
|
2403
|
+
};
|
|
2404
|
+
async function generateCodexSubscriptionImage(input) {
|
|
2405
|
+
const fetchImpl = input.fetch ?? codexImageFetch;
|
|
2406
|
+
const timeoutMs = input.requestTimeoutMs ?? CODEX_IMAGE_REQUEST_TIMEOUT_MS;
|
|
2407
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
|
|
2408
|
+
throw new RangeError("Codex image request timeout must be a positive safe integer");
|
|
2409
|
+
}
|
|
2410
|
+
const references = input.references ?? [];
|
|
2411
|
+
if (references.length > CODEX_IMAGE_MAX_REFERENCES) {
|
|
2412
|
+
throw new RangeError(
|
|
2413
|
+
`Codex image editing accepts at most ${CODEX_IMAGE_MAX_REFERENCES} images`
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
for (const reference of references) {
|
|
2417
|
+
if (reference.bytes.byteLength === 0) throw new Error("Codex image reference is empty");
|
|
2418
|
+
}
|
|
2419
|
+
const deadline = new AbortController();
|
|
2420
|
+
const timer = setTimeout(
|
|
2421
|
+
() => deadline.abort(new CodexImageRequestTimeoutError(timeoutMs)),
|
|
2422
|
+
timeoutMs
|
|
2423
|
+
);
|
|
2424
|
+
const signal = input.abortSignal ? AbortSignal.any([input.abortSignal, deadline.signal]) : deadline.signal;
|
|
2425
|
+
const request = async (auth) => {
|
|
2426
|
+
const headers = codexImageHeaders(auth, input.context.clientVersion, input.turnId);
|
|
2427
|
+
return await fetchImpl(
|
|
2428
|
+
`${CODEX_RESPONSES_BASE}/${references.length > 0 ? "images/edits" : "images/generations"}`,
|
|
2429
|
+
{
|
|
2430
|
+
method: "POST",
|
|
2431
|
+
redirect: "error",
|
|
2432
|
+
headers,
|
|
2433
|
+
body: JSON.stringify(
|
|
2434
|
+
references.length > 0 ? {
|
|
2435
|
+
images: references.map((reference) => ({
|
|
2436
|
+
image_url: `data:${reference.mediaType};base64,${Buffer.from(reference.bytes).toString("base64")}`
|
|
2437
|
+
})),
|
|
2438
|
+
prompt: input.prompt,
|
|
2439
|
+
background: "auto",
|
|
2440
|
+
model: CODEX_IMAGE_MODEL,
|
|
2441
|
+
quality: "auto",
|
|
2442
|
+
size: "auto"
|
|
2443
|
+
} : {
|
|
2444
|
+
prompt: input.prompt,
|
|
2445
|
+
background: "auto",
|
|
2446
|
+
model: CODEX_IMAGE_MODEL,
|
|
2447
|
+
quality: "auto",
|
|
2448
|
+
size: "auto"
|
|
2449
|
+
}
|
|
2450
|
+
),
|
|
2451
|
+
signal
|
|
2452
|
+
}
|
|
2453
|
+
);
|
|
2454
|
+
};
|
|
2455
|
+
const operation = (async () => {
|
|
2456
|
+
let response = await request(await input.context.getToken());
|
|
2457
|
+
if (response.status === 401) {
|
|
2458
|
+
await response.body?.cancel().catch(() => void 0);
|
|
2459
|
+
response = await request(await input.context.refresh());
|
|
2460
|
+
}
|
|
2461
|
+
if (!response.ok) {
|
|
2462
|
+
const detail = await readResponseTextBounded(
|
|
2463
|
+
response,
|
|
2464
|
+
CODEX_IMAGE_ERROR_MAX_BYTES,
|
|
2465
|
+
"Codex image error",
|
|
2466
|
+
{ signal }
|
|
2467
|
+
).catch(() => "");
|
|
2468
|
+
throw new CodexImageApiError(
|
|
2469
|
+
response.status,
|
|
2470
|
+
detail ? `Codex image generation failed (${response.status}): ${boundedErrorMessage(detail)}` : `Codex image generation failed (${response.status})`
|
|
2471
|
+
);
|
|
2472
|
+
}
|
|
2473
|
+
const bytes = await readJsonBase64Field(response, {
|
|
2474
|
+
fieldName: "b64_json",
|
|
2475
|
+
shape: "string",
|
|
2476
|
+
maxResponseBytes: CODEX_IMAGE_RESPONSE_MAX_BYTES,
|
|
2477
|
+
maxDecodedBytes: CODEX_IMAGE_MAX_BYTES,
|
|
2478
|
+
label: "Codex image generation",
|
|
2479
|
+
signal
|
|
2480
|
+
});
|
|
2481
|
+
return { bytes, declaredMediaType: "image/png" };
|
|
2482
|
+
})();
|
|
2483
|
+
let removeAbortListener = () => void 0;
|
|
2484
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
2485
|
+
const onAbort = () => reject(signal.reason);
|
|
2486
|
+
if (signal.aborted) {
|
|
2487
|
+
onAbort();
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2491
|
+
removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
2492
|
+
});
|
|
2493
|
+
try {
|
|
2494
|
+
return await Promise.race([operation, aborted]);
|
|
2495
|
+
} finally {
|
|
2496
|
+
removeAbortListener();
|
|
2497
|
+
clearTimeout(timer);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
function codexImageHeaders(auth, clientVersion, turnId) {
|
|
2501
|
+
const headers = new Headers({
|
|
2502
|
+
Authorization: `Bearer ${auth.accessToken}`,
|
|
2503
|
+
accept: "application/json",
|
|
2504
|
+
"content-type": "application/json",
|
|
2505
|
+
originator: CODEX_ORIGINATOR,
|
|
2506
|
+
"User-Agent": `${CODEX_ORIGINATOR}/${clientVersion || CODEX_CLIENT_VERSION}`,
|
|
2507
|
+
version: clientVersion || CODEX_CLIENT_VERSION,
|
|
2508
|
+
"x-codex-image-turn-id": turnId
|
|
2509
|
+
});
|
|
2510
|
+
if (auth.chatgptAccountId) headers.set("ChatGPT-Account-ID", auth.chatgptAccountId);
|
|
2511
|
+
if (auth.isFedramp) headers.set("X-OpenAI-Fedramp", "true");
|
|
2512
|
+
return headers;
|
|
2513
|
+
}
|
|
2514
|
+
function boundedErrorMessage(body) {
|
|
2515
|
+
let message = body;
|
|
2516
|
+
try {
|
|
2517
|
+
const value = JSON.parse(body);
|
|
2518
|
+
const candidate = value.error?.message ?? value.message;
|
|
2519
|
+
if (typeof candidate === "string") message = candidate;
|
|
2520
|
+
} catch {
|
|
2521
|
+
}
|
|
2522
|
+
return message.replace(/\s+/g, " ").trim().slice(0, 1e3);
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2279
2525
|
// src/realtime.ts
|
|
2280
2526
|
var CODEX_REALTIME_CALL_URL = `${CODEX_RESPONSES_BASE}/realtime/calls?intent=quicksilver&architecture=avas`;
|
|
2281
2527
|
var MAX_REALTIME_SDP_BYTES = 1024 * 1024;
|
|
@@ -2577,6 +2823,10 @@ export {
|
|
|
2577
2823
|
CODEX_REALTIME_VOICES,
|
|
2578
2824
|
CODEX_REFRESH_FALLBACK_MS,
|
|
2579
2825
|
CODEX_REFRESH_WINDOW_MS,
|
|
2826
|
+
CODEX_REQUEST_BODY_NORMALIZED_HEADER,
|
|
2827
|
+
CODEX_REQUEST_CALLER_STREAM_HEADER,
|
|
2828
|
+
CODEX_REQUEST_ID_HEADER,
|
|
2829
|
+
CODEX_REQUEST_MODEL_HEADER,
|
|
2580
2830
|
CODEX_RESPONSES_BASE,
|
|
2581
2831
|
CODEX_RESPONSE_HEADERS_TIMEOUT_MS,
|
|
2582
2832
|
CODEX_RESPONSE_NO_BYTE_RETRIES,
|
|
@@ -2592,6 +2842,8 @@ export {
|
|
|
2592
2842
|
CODEX_WEEKLY_WINDOW_SECONDS,
|
|
2593
2843
|
CODEX_WHAM_BASE,
|
|
2594
2844
|
CodexDeviceError,
|
|
2845
|
+
CodexImageApiError,
|
|
2846
|
+
CodexImageRequestTimeoutError,
|
|
2595
2847
|
CodexRealtimeError,
|
|
2596
2848
|
CodexRefreshTransient,
|
|
2597
2849
|
CodexReloginRequired,
|
|
@@ -2625,12 +2877,14 @@ export {
|
|
|
2625
2877
|
fetchCodexModels,
|
|
2626
2878
|
fetchCodexRateLimitResetCredits,
|
|
2627
2879
|
fetchCodexUsage,
|
|
2880
|
+
generateCodexSubscriptionImage,
|
|
2628
2881
|
isCodexBilledModel,
|
|
2629
2882
|
isCodexTransportError,
|
|
2630
2883
|
isPreHeadersTimeoutError,
|
|
2631
2884
|
modelToolOutputSerializationBudgetTokens,
|
|
2632
2885
|
normalizeCodexRequestBody,
|
|
2633
2886
|
normalizeCodexUsage,
|
|
2887
|
+
normalizedCodexRequestBody,
|
|
2634
2888
|
opaqueProviderArtifactFingerprint,
|
|
2635
2889
|
opaqueProviderArtifactFingerprints,
|
|
2636
2890
|
parseCodexRateLimitResetConsumeResponse,
|