@opengeni/codex 0.2.13 → 0.2.16
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 +9 -1
- package/dist/images.d.ts +34 -0
- package/dist/index.d.ts +17 -16
- package/dist/index.js +483 -158
- 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/request-context.d.ts +6 -0
- 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 +410 -208
- package/src/images.ts +209 -0
- package/src/index.ts +1 -0
- package/src/model-output-truncation.ts +7 -1
- package/src/normalize.ts +26 -0
- package/src/request-context.ts +6 -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;
|
|
@@ -894,15 +914,90 @@ function parseCodexUsageHeaders(headers) {
|
|
|
894
914
|
checkedAt: new Date(nowMs)
|
|
895
915
|
};
|
|
896
916
|
}
|
|
897
|
-
|
|
898
|
-
|
|
917
|
+
function classifyCodexSseTerminal(ev) {
|
|
918
|
+
if (ev.type === "response.failed") {
|
|
919
|
+
return {
|
|
920
|
+
phase: "failed",
|
|
921
|
+
rawError: ev.response?.error,
|
|
922
|
+
fallbackCode: "response_failed",
|
|
923
|
+
fallbackMessage: "The Codex response failed"
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
if (ev.type === "error" || ev.type === "response.error") {
|
|
927
|
+
return {
|
|
928
|
+
phase: "failed",
|
|
929
|
+
rawError: ev.error ?? ev.response?.error ?? ev,
|
|
930
|
+
fallbackCode: "response_error",
|
|
931
|
+
fallbackMessage: "The Codex response stream reported an error"
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
if (ev.type === "response.incomplete") {
|
|
935
|
+
const details = ev.response?.incomplete_details;
|
|
936
|
+
const reason = details && typeof details === "object" ? details.reason : void 0;
|
|
937
|
+
return {
|
|
938
|
+
phase: "failed",
|
|
939
|
+
rawError: {
|
|
940
|
+
code: "response_incomplete",
|
|
941
|
+
message: typeof reason === "string" && reason.length > 0 ? `The Codex response was incomplete (${reason})` : "The Codex response was incomplete"
|
|
942
|
+
},
|
|
943
|
+
fallbackCode: "response_incomplete",
|
|
944
|
+
fallbackMessage: "The Codex response was incomplete"
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
if (ev.type !== "response.completed" && ev.type !== "response.done") {
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
if (!ev.response) {
|
|
951
|
+
return null;
|
|
952
|
+
}
|
|
953
|
+
const responseStatus = ev.response.status;
|
|
954
|
+
if (responseStatus !== void 0 && responseStatus !== "completed" || ev.response.error !== null && ev.response.error !== void 0) {
|
|
955
|
+
const incomplete = responseStatus === "incomplete";
|
|
956
|
+
return {
|
|
957
|
+
phase: "failed",
|
|
958
|
+
rawError: ev.response.error,
|
|
959
|
+
fallbackCode: incomplete ? "response_incomplete" : "response_failed",
|
|
960
|
+
fallbackMessage: incomplete ? "The Codex response was incomplete" : "The Codex response failed"
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
return { phase: "completed" };
|
|
964
|
+
}
|
|
965
|
+
function markSemanticTerminal(state, phase) {
|
|
966
|
+
if (state.phase === null) {
|
|
967
|
+
state.phase = phase;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function terminalOutcomeForPhase(phase) {
|
|
971
|
+
if (phase === "completed" || phase === "failed" || phase === "timed_out") {
|
|
972
|
+
return phase;
|
|
973
|
+
}
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
976
|
+
function requestEventFor(audit, event) {
|
|
977
|
+
return {
|
|
899
978
|
requestId: audit.requestId,
|
|
900
979
|
transportAttempt: audit.transportAttempt,
|
|
901
980
|
...audit.model ? { model: audit.model } : {},
|
|
902
|
-
durationMs: Math.max(0,
|
|
981
|
+
durationMs: Math.max(0, performance.now() - audit.attemptStartedAtMonotonic),
|
|
903
982
|
timeoutPolicy: audit.policy,
|
|
904
983
|
...event
|
|
905
|
-
}
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
async function emitRequestEvent(audit, event) {
|
|
987
|
+
const terminalOutcome = terminalOutcomeForPhase(event.phase);
|
|
988
|
+
if (terminalOutcome !== null) {
|
|
989
|
+
if (audit.terminalOutcome !== null) {
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
audit.terminalOutcome = terminalOutcome;
|
|
993
|
+
}
|
|
994
|
+
const observed = requestEventFor(audit, event);
|
|
995
|
+
try {
|
|
996
|
+
audit.ctx.onModelRequestDiagnostic?.(observed);
|
|
997
|
+
} catch {
|
|
998
|
+
}
|
|
999
|
+
await audit.ctx.onModelRequestEvent?.(observed);
|
|
1000
|
+
return true;
|
|
906
1001
|
}
|
|
907
1002
|
function providerRequestId(headers) {
|
|
908
1003
|
return headers.get("x-request-id") ?? headers.get("request-id") ?? void 0;
|
|
@@ -943,11 +1038,12 @@ async function fetchBeforeHeaders(base, input, init, audit) {
|
|
|
943
1038
|
externalSignal?.removeEventListener("abort", forwardAbort);
|
|
944
1039
|
}
|
|
945
1040
|
}
|
|
946
|
-
async function observedResponse(res, audit, externalSignal) {
|
|
1041
|
+
async function observedResponse(res, audit, externalSignal, semanticTerminal) {
|
|
947
1042
|
const requestId = providerRequestId(res.headers);
|
|
948
1043
|
if (!res.body) {
|
|
1044
|
+
if (semanticTerminal) markSemanticTerminal(semanticTerminal, "failed");
|
|
949
1045
|
await emitRequestEvent(audit, {
|
|
950
|
-
phase: res.ok ? "completed" : "failed",
|
|
1046
|
+
phase: semanticTerminal?.phase ?? (res.ok ? "completed" : "failed"),
|
|
951
1047
|
responseObserved: true,
|
|
952
1048
|
status: res.status,
|
|
953
1049
|
...requestId ? { providerRequestId: requestId } : {}
|
|
@@ -972,17 +1068,19 @@ async function observedResponse(res, audit, externalSignal) {
|
|
|
972
1068
|
if (terminal) return;
|
|
973
1069
|
terminal = true;
|
|
974
1070
|
clearTimers();
|
|
1071
|
+
const semanticPhase = semanticTerminal?.phase;
|
|
1072
|
+
const phase = semanticPhase ?? "timed_out";
|
|
975
1073
|
const error = new CodexResponseTimeoutError(klass, audit.requestId, true);
|
|
976
1074
|
void reader.cancel(error).catch(() => void 0);
|
|
977
1075
|
void emitRequestEvent(audit, {
|
|
978
|
-
phase
|
|
1076
|
+
phase,
|
|
979
1077
|
responseObserved: true,
|
|
980
|
-
timeoutClass: klass,
|
|
1078
|
+
...phase === "timed_out" ? { timeoutClass: klass } : {},
|
|
981
1079
|
status: res.status,
|
|
982
1080
|
...requestId ? { providerRequestId: requestId } : {}
|
|
983
1081
|
}).then(
|
|
984
|
-
() => controller.error(error),
|
|
985
|
-
() => controller.error(error)
|
|
1082
|
+
() => phase === "completed" ? controller.close() : controller.error(error),
|
|
1083
|
+
() => phase === "completed" ? controller.close() : controller.error(error)
|
|
986
1084
|
);
|
|
987
1085
|
};
|
|
988
1086
|
armIdle = () => {
|
|
@@ -1002,13 +1100,13 @@ async function observedResponse(res, audit, externalSignal) {
|
|
|
1002
1100
|
const reason = externalSignal?.reason ?? new DOMException("Aborted", "AbortError");
|
|
1003
1101
|
void reader.cancel(reason).catch(() => void 0);
|
|
1004
1102
|
void emitRequestEvent(audit, {
|
|
1005
|
-
phase: "failed",
|
|
1103
|
+
phase: semanticTerminal?.phase ?? "failed",
|
|
1006
1104
|
responseObserved: true,
|
|
1007
1105
|
status: res.status,
|
|
1008
1106
|
...requestId ? { providerRequestId: requestId } : {}
|
|
1009
1107
|
}).then(
|
|
1010
|
-
() => controller.error(reason),
|
|
1011
|
-
() => controller.error(reason)
|
|
1108
|
+
() => semanticTerminal?.phase === "completed" ? controller.close() : controller.error(reason),
|
|
1109
|
+
() => semanticTerminal?.phase === "completed" ? controller.close() : controller.error(reason)
|
|
1012
1110
|
);
|
|
1013
1111
|
};
|
|
1014
1112
|
if (externalSignal?.aborted) {
|
|
@@ -1027,45 +1125,67 @@ async function observedResponse(res, audit, externalSignal) {
|
|
|
1027
1125
|
if (chunk.done) {
|
|
1028
1126
|
terminal = true;
|
|
1029
1127
|
clearTimers();
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1128
|
+
if (semanticTerminal && semanticTerminal.phase === null) {
|
|
1129
|
+
if (!semanticTerminal.deferTransportTerminal) {
|
|
1130
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (!semanticTerminal?.deferTransportTerminal || semanticTerminal.phase !== null) {
|
|
1134
|
+
await emitRequestEvent(audit, {
|
|
1135
|
+
phase: semanticTerminal?.phase ?? (res.ok ? "completed" : "failed"),
|
|
1136
|
+
responseObserved: true,
|
|
1137
|
+
status: res.status,
|
|
1138
|
+
...requestId ? { providerRequestId: requestId } : {}
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1036
1141
|
controller.close();
|
|
1037
1142
|
return;
|
|
1038
1143
|
}
|
|
1039
|
-
armIdle();
|
|
1040
1144
|
if (!firstByte) {
|
|
1041
1145
|
firstByte = true;
|
|
1146
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
1147
|
+
controller.enqueue(chunk.value);
|
|
1042
1148
|
await emitRequestEvent(audit, {
|
|
1043
1149
|
phase: "first_byte",
|
|
1044
1150
|
responseObserved: true,
|
|
1045
1151
|
status: res.status,
|
|
1046
1152
|
...requestId ? { providerRequestId: requestId } : {}
|
|
1047
1153
|
});
|
|
1154
|
+
if (!terminal) armIdle();
|
|
1155
|
+
return;
|
|
1048
1156
|
}
|
|
1157
|
+
armIdle();
|
|
1049
1158
|
controller.enqueue(chunk.value);
|
|
1050
1159
|
} catch (error) {
|
|
1051
1160
|
if (terminal) return;
|
|
1052
1161
|
terminal = true;
|
|
1053
1162
|
clearTimers();
|
|
1163
|
+
const semanticPhase = semanticTerminal?.phase;
|
|
1164
|
+
if (semanticTerminal && semanticPhase === null) {
|
|
1165
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1166
|
+
}
|
|
1054
1167
|
await emitRequestEvent(audit, {
|
|
1055
|
-
phase: "failed",
|
|
1168
|
+
phase: semanticPhase ?? "failed",
|
|
1056
1169
|
responseObserved: true,
|
|
1057
1170
|
status: res.status,
|
|
1058
1171
|
...requestId ? { providerRequestId: requestId } : {}
|
|
1059
1172
|
});
|
|
1060
|
-
|
|
1173
|
+
if (semanticPhase === "completed") {
|
|
1174
|
+
controller.close();
|
|
1175
|
+
} else {
|
|
1176
|
+
controller.error(error);
|
|
1177
|
+
}
|
|
1061
1178
|
}
|
|
1062
1179
|
},
|
|
1063
1180
|
async cancel(reason) {
|
|
1064
1181
|
if (!terminal) {
|
|
1065
1182
|
terminal = true;
|
|
1066
1183
|
clearTimers();
|
|
1184
|
+
if (semanticTerminal && semanticTerminal.phase === null) {
|
|
1185
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1186
|
+
}
|
|
1067
1187
|
await emitRequestEvent(audit, {
|
|
1068
|
-
phase: "failed",
|
|
1188
|
+
phase: semanticTerminal?.phase ?? "failed",
|
|
1069
1189
|
responseObserved: true,
|
|
1070
1190
|
status: res.status,
|
|
1071
1191
|
...requestId ? { providerRequestId: requestId } : {}
|
|
@@ -1113,11 +1233,19 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1113
1233
|
const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
1114
1234
|
const rewritten = rawUrl.replace(/(?<!\/codex)\/responses(\b|$)/, "/codex/responses$1");
|
|
1115
1235
|
const policy = resolveCodexResponseTimeoutPolicy(ctx.responseTimeoutPolicy);
|
|
1116
|
-
const
|
|
1236
|
+
const handedRequestId = new Headers(init?.headers).get(CODEX_REQUEST_ID_HEADER);
|
|
1237
|
+
const requestId = handedRequestId ?? ctx.nextRequestId?.() ?? randomUUID();
|
|
1117
1238
|
const logicalStartedAt = Date.now();
|
|
1118
1239
|
let transportAttempt = 0;
|
|
1119
1240
|
const attempt = async (auth, authenticationAttempt) => {
|
|
1120
1241
|
const headers = new Headers(init?.headers);
|
|
1242
|
+
const bodyAlreadyNormalized = headers.get(CODEX_REQUEST_BODY_NORMALIZED_HEADER) === "1";
|
|
1243
|
+
const normalizedModel = headers.get(CODEX_REQUEST_MODEL_HEADER) ?? void 0;
|
|
1244
|
+
const normalizedCallerStream = headers.get(CODEX_REQUEST_CALLER_STREAM_HEADER);
|
|
1245
|
+
headers.delete(CODEX_REQUEST_BODY_NORMALIZED_HEADER);
|
|
1246
|
+
headers.delete(CODEX_REQUEST_MODEL_HEADER);
|
|
1247
|
+
headers.delete(CODEX_REQUEST_ID_HEADER);
|
|
1248
|
+
headers.delete(CODEX_REQUEST_CALLER_STREAM_HEADER);
|
|
1121
1249
|
headers.set("Authorization", `Bearer ${auth.accessToken}`);
|
|
1122
1250
|
if (auth.chatgptAccountId) {
|
|
1123
1251
|
headers.set("ChatGPT-Account-ID", auth.chatgptAccountId);
|
|
@@ -1141,11 +1269,16 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1141
1269
|
if (ctx.turnMetadata && Object.keys(ctx.turnMetadata).length > 0) {
|
|
1142
1270
|
headers.set("x-codex-turn-metadata", JSON.stringify(ctx.turnMetadata));
|
|
1143
1271
|
}
|
|
1144
|
-
let callerWantsStream = true;
|
|
1145
|
-
let model;
|
|
1272
|
+
let callerWantsStream = bodyAlreadyNormalized ? normalizedCallerStream !== "0" : true;
|
|
1273
|
+
let model = normalizedModel;
|
|
1146
1274
|
let requestOpaqueArtifacts = [];
|
|
1147
|
-
const
|
|
1148
|
-
|
|
1275
|
+
const replayableBodyFactory = init?.[REPLAYABLE_REQUEST_BODY_FACTORY];
|
|
1276
|
+
const nextInit = {
|
|
1277
|
+
...init,
|
|
1278
|
+
headers,
|
|
1279
|
+
...replayableBodyFactory ? { body: replayableBodyFactory() } : {}
|
|
1280
|
+
};
|
|
1281
|
+
if (!bodyAlreadyNormalized && typeof init?.body === "string") {
|
|
1149
1282
|
try {
|
|
1150
1283
|
const parsed = JSON.parse(init.body);
|
|
1151
1284
|
callerWantsStream = parsed.stream === true;
|
|
@@ -1154,9 +1287,14 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1154
1287
|
nextInit.body = JSON.stringify(normalized);
|
|
1155
1288
|
requestOpaqueArtifacts = opaqueProviderArtifactFingerprints(normalized.input);
|
|
1156
1289
|
} catch {
|
|
1290
|
+
throw new Error("Model request could not be prepared");
|
|
1157
1291
|
}
|
|
1292
|
+
} else if (!bodyAlreadyNormalized) {
|
|
1293
|
+
throw new Error("Model request could not be prepared");
|
|
1294
|
+
}
|
|
1295
|
+
if (!bodyAlreadyNormalized) {
|
|
1296
|
+
ctx.onRequestOpaqueArtifacts?.({ requestId, fingerprints: requestOpaqueArtifacts });
|
|
1158
1297
|
}
|
|
1159
|
-
ctx.onRequestOpaqueArtifacts?.({ requestId, fingerprints: requestOpaqueArtifacts });
|
|
1160
1298
|
headers.set(
|
|
1161
1299
|
"Idempotency-Key",
|
|
1162
1300
|
authenticationAttempt === 0 ? requestId : `${requestId}:auth-${authenticationAttempt}`
|
|
@@ -1177,13 +1315,18 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1177
1315
|
transportAttempt,
|
|
1178
1316
|
...model ? { model } : {},
|
|
1179
1317
|
logicalStartedAt,
|
|
1180
|
-
|
|
1181
|
-
policy
|
|
1318
|
+
attemptStartedAtMonotonic: performance.now(),
|
|
1319
|
+
policy,
|
|
1320
|
+
terminalOutcome: null
|
|
1182
1321
|
};
|
|
1183
1322
|
await emitRequestEvent(audit, {
|
|
1184
1323
|
phase: "started",
|
|
1185
1324
|
responseObserved: false
|
|
1186
1325
|
});
|
|
1326
|
+
const semanticTerminal = {
|
|
1327
|
+
phase: null,
|
|
1328
|
+
deferTransportTerminal: !callerWantsStream
|
|
1329
|
+
};
|
|
1187
1330
|
try {
|
|
1188
1331
|
res = await fetchBeforeHeaders(base, rewritten, nextInit, audit);
|
|
1189
1332
|
const upstreamRequestId = providerRequestId(res.headers);
|
|
@@ -1193,8 +1336,7 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1193
1336
|
status: res.status,
|
|
1194
1337
|
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1195
1338
|
});
|
|
1196
|
-
|
|
1197
|
-
res = observed;
|
|
1339
|
+
res = await observedResponse(res, audit, nextInit.signal, semanticTerminal);
|
|
1198
1340
|
} catch (error) {
|
|
1199
1341
|
if (nextInit.signal?.aborted) {
|
|
1200
1342
|
await emitRequestEvent(audit, {
|
|
@@ -1231,9 +1373,25 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1231
1373
|
});
|
|
1232
1374
|
}
|
|
1233
1375
|
if (!res.ok) {
|
|
1234
|
-
|
|
1376
|
+
const buffered = await bufferCodexErrorResponse(res);
|
|
1377
|
+
const upstreamRequestId = providerRequestId(res.headers);
|
|
1378
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1379
|
+
await emitRequestEvent(audit, {
|
|
1380
|
+
phase: "failed",
|
|
1381
|
+
responseObserved: true,
|
|
1382
|
+
status: res.status,
|
|
1383
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1384
|
+
}).catch(() => void 0);
|
|
1385
|
+
return buffered;
|
|
1235
1386
|
}
|
|
1236
|
-
|
|
1387
|
+
if (callerWantsStream) {
|
|
1388
|
+
res = validateCodexStream(res, (phase) => {
|
|
1389
|
+
markSemanticTerminal(semanticTerminal, phase);
|
|
1390
|
+
});
|
|
1391
|
+
} else {
|
|
1392
|
+
res = await sseToJsonResponse(res, audit, semanticTerminal);
|
|
1393
|
+
}
|
|
1394
|
+
return res;
|
|
1237
1395
|
};
|
|
1238
1396
|
try {
|
|
1239
1397
|
let res = await attempt(await ctx.getToken(), 0);
|
|
@@ -1271,28 +1429,77 @@ function classifyCodexUsageLimitError(error) {
|
|
|
1271
1429
|
return null;
|
|
1272
1430
|
}
|
|
1273
1431
|
async function bufferCodexErrorResponse(res) {
|
|
1274
|
-
const bodyText = await
|
|
1432
|
+
const { text: bodyText, truncated } = await readBoundedResponseText(
|
|
1433
|
+
res,
|
|
1434
|
+
MAX_CODEX_ERROR_BODY_BYTES
|
|
1435
|
+
);
|
|
1275
1436
|
const headers = new Headers(res.headers);
|
|
1276
1437
|
headers.set("content-type", "application/json");
|
|
1277
1438
|
headers.set(CODEX_TRANSPORT_ERROR_HEADER, "1");
|
|
1278
1439
|
headers.delete("content-length");
|
|
1279
1440
|
headers.delete("content-encoding");
|
|
1280
1441
|
let errorType;
|
|
1442
|
+
let responseBody = bodyText;
|
|
1281
1443
|
try {
|
|
1282
1444
|
const parsed = JSON.parse(bodyText);
|
|
1283
1445
|
errorType = typeof parsed.error?.type === "string" ? parsed.error.type : void 0;
|
|
1284
1446
|
} catch {
|
|
1285
1447
|
}
|
|
1448
|
+
if (truncated) {
|
|
1449
|
+
responseBody = JSON.stringify({
|
|
1450
|
+
error: {
|
|
1451
|
+
type: "provider_error_body_too_large",
|
|
1452
|
+
code: "provider_error_body_too_large",
|
|
1453
|
+
message: `The provider returned an error body larger than ${MAX_CODEX_ERROR_BODY_BYTES} bytes`
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
headers.set("x-opengeni-provider-error-truncated", "1");
|
|
1457
|
+
}
|
|
1286
1458
|
if (errorType === CODEX_USAGE_LIMIT_ERROR_TYPE) {
|
|
1287
1459
|
headers.set("x-should-retry", "false");
|
|
1288
1460
|
}
|
|
1289
|
-
return new Response(
|
|
1461
|
+
return new Response(responseBody, {
|
|
1290
1462
|
status: res.status,
|
|
1291
1463
|
statusText: res.statusText,
|
|
1292
1464
|
headers
|
|
1293
1465
|
});
|
|
1294
1466
|
}
|
|
1295
|
-
async function
|
|
1467
|
+
async function readBoundedResponseText(response, maxBytes) {
|
|
1468
|
+
if (!response.body) return { text: "", truncated: false };
|
|
1469
|
+
const reader = response.body.getReader();
|
|
1470
|
+
const decoder = new TextDecoder();
|
|
1471
|
+
const parts = [];
|
|
1472
|
+
let bytes = 0;
|
|
1473
|
+
let truncated = false;
|
|
1474
|
+
try {
|
|
1475
|
+
while (bytes < maxBytes) {
|
|
1476
|
+
const next = await reader.read();
|
|
1477
|
+
if (next.done) {
|
|
1478
|
+
parts.push(decoder.decode());
|
|
1479
|
+
return { text: parts.join(""), truncated };
|
|
1480
|
+
}
|
|
1481
|
+
const remaining = maxBytes - bytes;
|
|
1482
|
+
const accepted = next.value.byteLength > remaining ? next.value.subarray(0, remaining) : next.value;
|
|
1483
|
+
bytes += accepted.byteLength;
|
|
1484
|
+
parts.push(decoder.decode(accepted, { stream: true }));
|
|
1485
|
+
if (accepted.byteLength !== next.value.byteLength) {
|
|
1486
|
+
truncated = true;
|
|
1487
|
+
break;
|
|
1488
|
+
}
|
|
1489
|
+
if (bytes >= maxBytes) {
|
|
1490
|
+
truncated = true;
|
|
1491
|
+
break;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
} catch {
|
|
1495
|
+
truncated = true;
|
|
1496
|
+
} finally {
|
|
1497
|
+
if (truncated) void reader.cancel().catch(() => void 0);
|
|
1498
|
+
}
|
|
1499
|
+
return { text: parts.join(""), truncated };
|
|
1500
|
+
}
|
|
1501
|
+
async function sseToJsonResponse(res, audit, semanticTerminal) {
|
|
1502
|
+
const upstreamRequestId = providerRequestId(res.headers);
|
|
1296
1503
|
const text = await res.text();
|
|
1297
1504
|
let final = null;
|
|
1298
1505
|
let terminalError = null;
|
|
@@ -1305,57 +1512,45 @@ async function sseToJsonResponse(res) {
|
|
|
1305
1512
|
const ev = JSON.parse(data);
|
|
1306
1513
|
if (ev.type === "response.output_item.done" && ev.item !== void 0) {
|
|
1307
1514
|
items.push(ev.item);
|
|
1308
|
-
} else
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
"The Codex response stream reported an error",
|
|
1326
|
-
{
|
|
1327
|
-
eventType: ev.type,
|
|
1328
|
-
responseId: ev.response?.id,
|
|
1329
|
-
responseStatus: ev.response?.status
|
|
1330
|
-
}
|
|
1331
|
-
);
|
|
1332
|
-
} else if (ev.type === "response.incomplete") {
|
|
1333
|
-
const details = ev.response?.incomplete_details;
|
|
1334
|
-
const reason = details && typeof details === "object" ? details.reason : void 0;
|
|
1335
|
-
terminalError = codexSseFailureResponse(
|
|
1336
|
-
res,
|
|
1337
|
-
{
|
|
1338
|
-
code: "response_incomplete",
|
|
1339
|
-
message: typeof reason === "string" && reason.length > 0 ? `The Codex response was incomplete (${reason})` : "The Codex response was incomplete"
|
|
1340
|
-
},
|
|
1341
|
-
"response_incomplete",
|
|
1342
|
-
"The Codex response was incomplete",
|
|
1343
|
-
{
|
|
1344
|
-
eventType: ev.type,
|
|
1345
|
-
responseId: ev.response?.id,
|
|
1346
|
-
responseStatus: ev.response?.status
|
|
1347
|
-
}
|
|
1348
|
-
);
|
|
1349
|
-
} else if (ev.type === "response.completed" || ev.type === "response.done") {
|
|
1350
|
-
final = ev.response ?? null;
|
|
1515
|
+
} else {
|
|
1516
|
+
const terminal = classifyCodexSseTerminal(ev);
|
|
1517
|
+
if (terminal?.phase === "failed") {
|
|
1518
|
+
terminalError = codexSseFailureResponse(
|
|
1519
|
+
res,
|
|
1520
|
+
terminal.rawError,
|
|
1521
|
+
terminal.fallbackCode,
|
|
1522
|
+
terminal.fallbackMessage,
|
|
1523
|
+
{
|
|
1524
|
+
eventType: ev.type,
|
|
1525
|
+
responseId: ev.response?.id,
|
|
1526
|
+
responseStatus: ev.response?.status
|
|
1527
|
+
}
|
|
1528
|
+
);
|
|
1529
|
+
} else if (terminal?.phase === "completed") {
|
|
1530
|
+
final = ev.response ?? null;
|
|
1531
|
+
}
|
|
1351
1532
|
}
|
|
1352
1533
|
} catch {
|
|
1353
1534
|
}
|
|
1354
1535
|
}
|
|
1355
1536
|
if (terminalError) {
|
|
1537
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1538
|
+
await emitRequestEvent(audit, {
|
|
1539
|
+
phase: "failed",
|
|
1540
|
+
responseObserved: true,
|
|
1541
|
+
status: res.status,
|
|
1542
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1543
|
+
});
|
|
1356
1544
|
return terminalError;
|
|
1357
1545
|
}
|
|
1358
1546
|
if (!final) {
|
|
1547
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1548
|
+
await emitRequestEvent(audit, {
|
|
1549
|
+
phase: "failed",
|
|
1550
|
+
responseObserved: true,
|
|
1551
|
+
status: res.status,
|
|
1552
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1553
|
+
});
|
|
1359
1554
|
return codexSseFailureResponse(
|
|
1360
1555
|
res,
|
|
1361
1556
|
null,
|
|
@@ -1371,6 +1566,13 @@ async function sseToJsonResponse(res) {
|
|
|
1371
1566
|
`[codex-debug] sse->json items=${items.length} outputLen=${Array.isArray(final?.output) ? final.output.length : "?"}`
|
|
1372
1567
|
);
|
|
1373
1568
|
}
|
|
1569
|
+
markSemanticTerminal(semanticTerminal, "completed");
|
|
1570
|
+
await emitRequestEvent(audit, {
|
|
1571
|
+
phase: "completed",
|
|
1572
|
+
responseObserved: true,
|
|
1573
|
+
status: res.status,
|
|
1574
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1575
|
+
});
|
|
1374
1576
|
const headers = new Headers(res.headers);
|
|
1375
1577
|
headers.set("content-type", "application/json");
|
|
1376
1578
|
headers.delete("content-length");
|
|
@@ -1523,8 +1725,9 @@ function codexSseFailureError(source, rawError, fallbackCode, publicMessage, met
|
|
|
1523
1725
|
codexSseFailureProjection(source, rawError, fallbackCode, publicMessage, metadata)
|
|
1524
1726
|
);
|
|
1525
1727
|
}
|
|
1526
|
-
function
|
|
1728
|
+
function validateCodexStream(res, onSemanticTerminal) {
|
|
1527
1729
|
if (!res.body) {
|
|
1730
|
+
onSemanticTerminal?.("failed");
|
|
1528
1731
|
const error = codexSseFailureError(
|
|
1529
1732
|
res,
|
|
1530
1733
|
null,
|
|
@@ -1544,7 +1747,6 @@ function repairCodexStream(res) {
|
|
|
1544
1747
|
headers: headers2
|
|
1545
1748
|
});
|
|
1546
1749
|
}
|
|
1547
|
-
const items = [];
|
|
1548
1750
|
const decoder = new TextDecoder();
|
|
1549
1751
|
const encoder = new TextEncoder();
|
|
1550
1752
|
let buffer = "";
|
|
@@ -1555,9 +1757,8 @@ function repairCodexStream(res) {
|
|
|
1555
1757
|
const block = buffer.slice(0, boundary.start);
|
|
1556
1758
|
const separator = buffer.slice(boundary.start, boundary.end);
|
|
1557
1759
|
buffer = buffer.slice(boundary.end);
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
controller.enqueue(encoder.encode(`${patched.block}${separator}`));
|
|
1760
|
+
successfulTerminalSeen ||= inspectCodexSseBlock(block, res, onSemanticTerminal);
|
|
1761
|
+
controller.enqueue(encoder.encode(`${block}${separator}`));
|
|
1561
1762
|
boundary = findSseBlockBoundary(buffer, final);
|
|
1562
1763
|
}
|
|
1563
1764
|
};
|
|
@@ -1570,9 +1771,8 @@ function repairCodexStream(res) {
|
|
|
1570
1771
|
buffer += decoder.decode();
|
|
1571
1772
|
emitCompleteBlocks(controller, true);
|
|
1572
1773
|
if (buffer.length > 0) {
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
controller.enqueue(encoder.encode(patched.block));
|
|
1774
|
+
successfulTerminalSeen ||= inspectCodexSseBlock(buffer, res, onSemanticTerminal);
|
|
1775
|
+
controller.enqueue(encoder.encode(buffer));
|
|
1576
1776
|
buffer = "";
|
|
1577
1777
|
}
|
|
1578
1778
|
if (!successfulTerminalSeen) {
|
|
@@ -1613,59 +1813,37 @@ function sseLineEndingEnd(value, index, final) {
|
|
|
1613
1813
|
}
|
|
1614
1814
|
return final ? index + 1 : null;
|
|
1615
1815
|
}
|
|
1616
|
-
|
|
1816
|
+
var CODEX_TERMINAL_TYPE_HINTS = [
|
|
1817
|
+
'"response.completed"',
|
|
1818
|
+
'"response.done"',
|
|
1819
|
+
'"response.failed"',
|
|
1820
|
+
'"response.incomplete"',
|
|
1821
|
+
'"response.error"',
|
|
1822
|
+
'"error"'
|
|
1823
|
+
];
|
|
1824
|
+
function inspectCodexSseBlock(block, source, onSemanticTerminal) {
|
|
1617
1825
|
const lines = block.split(/\r\n|\r|\n/);
|
|
1618
1826
|
const dataStr = lines.filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("\n");
|
|
1619
1827
|
if (!dataStr || dataStr === "[DONE]") {
|
|
1620
|
-
return
|
|
1828
|
+
return false;
|
|
1829
|
+
}
|
|
1830
|
+
if (!CODEX_TERMINAL_TYPE_HINTS.some((terminalType) => dataStr.includes(terminalType))) {
|
|
1831
|
+
return false;
|
|
1621
1832
|
}
|
|
1622
1833
|
let ev;
|
|
1623
1834
|
try {
|
|
1624
1835
|
ev = JSON.parse(dataStr);
|
|
1625
1836
|
} catch {
|
|
1626
|
-
return
|
|
1627
|
-
}
|
|
1628
|
-
if (ev.type === "response.output_item.done" && ev.item !== void 0) {
|
|
1629
|
-
items.push(ev.item);
|
|
1630
|
-
return { block, successfulTerminal: false };
|
|
1631
|
-
}
|
|
1632
|
-
if (ev.type === "response.failed") {
|
|
1633
|
-
throw codexSseFailureError(
|
|
1634
|
-
source,
|
|
1635
|
-
ev.response?.error,
|
|
1636
|
-
"response_failed",
|
|
1637
|
-
"The Codex response failed",
|
|
1638
|
-
{
|
|
1639
|
-
eventType: ev.type,
|
|
1640
|
-
responseId: ev.response?.id,
|
|
1641
|
-
responseStatus: ev.response?.status
|
|
1642
|
-
}
|
|
1643
|
-
);
|
|
1644
|
-
}
|
|
1645
|
-
if (ev.type === "error" || ev.type === "response.error") {
|
|
1646
|
-
throw codexSseFailureError(
|
|
1647
|
-
source,
|
|
1648
|
-
ev.error ?? ev.response?.error ?? ev,
|
|
1649
|
-
"response_error",
|
|
1650
|
-
"The Codex response stream reported an error",
|
|
1651
|
-
{
|
|
1652
|
-
eventType: ev.type,
|
|
1653
|
-
responseId: ev.response?.id,
|
|
1654
|
-
responseStatus: ev.response?.status
|
|
1655
|
-
}
|
|
1656
|
-
);
|
|
1837
|
+
return false;
|
|
1657
1838
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1839
|
+
const terminal = classifyCodexSseTerminal(ev);
|
|
1840
|
+
if (terminal?.phase === "failed") {
|
|
1841
|
+
onSemanticTerminal?.("failed");
|
|
1661
1842
|
throw codexSseFailureError(
|
|
1662
1843
|
source,
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
},
|
|
1667
|
-
"response_incomplete",
|
|
1668
|
-
"The Codex response was incomplete",
|
|
1844
|
+
terminal.rawError,
|
|
1845
|
+
terminal.fallbackCode,
|
|
1846
|
+
terminal.fallbackMessage,
|
|
1669
1847
|
{
|
|
1670
1848
|
eventType: ev.type,
|
|
1671
1849
|
responseId: ev.response?.id,
|
|
@@ -1673,33 +1851,11 @@ function patchSseBlock(block, items, source) {
|
|
|
1673
1851
|
}
|
|
1674
1852
|
);
|
|
1675
1853
|
}
|
|
1676
|
-
if (
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
source,
|
|
1680
|
-
ev.response.error,
|
|
1681
|
-
ev.response.status === "incomplete" ? "response_incomplete" : "response_failed",
|
|
1682
|
-
ev.response.status === "incomplete" ? "The Codex response was incomplete" : "The Codex response failed",
|
|
1683
|
-
{
|
|
1684
|
-
eventType: ev.type,
|
|
1685
|
-
responseId: ev.response.id,
|
|
1686
|
-
responseStatus: ev.response.status
|
|
1687
|
-
}
|
|
1688
|
-
);
|
|
1689
|
-
}
|
|
1690
|
-
const out = ev.response.output;
|
|
1691
|
-
if ((!Array.isArray(out) || out.length === 0) && items.length > 0) {
|
|
1692
|
-
ev.response = { ...ev.response, output: items };
|
|
1693
|
-
const nonData = lines.filter((l) => !l.startsWith("data:"));
|
|
1694
|
-
const lineEnding = block.match(/\r\n|\r|\n/)?.[0] ?? "\n";
|
|
1695
|
-
return {
|
|
1696
|
-
block: [...nonData, `data: ${JSON.stringify(ev)}`].join(lineEnding),
|
|
1697
|
-
successfulTerminal: true
|
|
1698
|
-
};
|
|
1699
|
-
}
|
|
1700
|
-
return { block, successfulTerminal: true };
|
|
1854
|
+
if (terminal?.phase === "completed") {
|
|
1855
|
+
onSemanticTerminal?.("completed");
|
|
1856
|
+
return true;
|
|
1701
1857
|
}
|
|
1702
|
-
return
|
|
1858
|
+
return false;
|
|
1703
1859
|
}
|
|
1704
1860
|
|
|
1705
1861
|
// src/mcp-sanitize.ts
|
|
@@ -1962,7 +2118,13 @@ function boundModelToolOutputItem(item, policyTokens = DEFAULT_MODEL_TOOL_OUTPUT
|
|
|
1962
2118
|
return boundedOutput === item.output ? item : { ...item, output: boundedOutput };
|
|
1963
2119
|
}
|
|
1964
2120
|
function boundModelToolOutputItems(items, policyTokens = DEFAULT_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS) {
|
|
1965
|
-
|
|
2121
|
+
let bounded = null;
|
|
2122
|
+
for (const [index, item] of items.entries()) {
|
|
2123
|
+
const next = boundModelToolOutputItem(item, policyTokens);
|
|
2124
|
+
if (next !== item && bounded === null) bounded = items.slice(0, index);
|
|
2125
|
+
bounded?.push(next);
|
|
2126
|
+
}
|
|
2127
|
+
return bounded ?? items;
|
|
1966
2128
|
}
|
|
1967
2129
|
function boundToolOutputValue(output, budgetTokens) {
|
|
1968
2130
|
const state = modelOutputBoundState(budgetTokens);
|
|
@@ -2277,6 +2439,161 @@ function isImageDataUrl(value) {
|
|
|
2277
2439
|
return /^data:image\/[a-z0-9.+-]+;base64,/i.test(value);
|
|
2278
2440
|
}
|
|
2279
2441
|
|
|
2442
|
+
// src/images.ts
|
|
2443
|
+
import { pinnedFetch, readJsonBase64Field, readResponseTextBounded } from "@opengeni/network";
|
|
2444
|
+
var CODEX_IMAGE_MODEL = "gpt-image-2";
|
|
2445
|
+
var CODEX_IMAGE_RESPONSE_MAX_BYTES = 90 * 1024 * 1024;
|
|
2446
|
+
var CODEX_IMAGE_ERROR_MAX_BYTES = 64 * 1024;
|
|
2447
|
+
var CODEX_IMAGE_MAX_BYTES = 64 * 1024 * 1024;
|
|
2448
|
+
var CODEX_IMAGE_REQUEST_TIMEOUT_MS = 5 * 6e4;
|
|
2449
|
+
var CODEX_IMAGE_MAX_REFERENCES = 5;
|
|
2450
|
+
var codexImageFetch = async (input, init) => await pinnedFetch(
|
|
2451
|
+
input,
|
|
2452
|
+
init,
|
|
2453
|
+
{
|
|
2454
|
+
environment: "production",
|
|
2455
|
+
integrationsAllowPrivateNetworkTargets: false
|
|
2456
|
+
},
|
|
2457
|
+
{
|
|
2458
|
+
label: "Codex image generation",
|
|
2459
|
+
requireHttpsOutsideLocalTest: true
|
|
2460
|
+
}
|
|
2461
|
+
);
|
|
2462
|
+
var CodexImageApiError = class extends Error {
|
|
2463
|
+
constructor(status, message) {
|
|
2464
|
+
super(message);
|
|
2465
|
+
this.status = status;
|
|
2466
|
+
this.name = "CodexImageApiError";
|
|
2467
|
+
}
|
|
2468
|
+
};
|
|
2469
|
+
var CodexImageRequestTimeoutError = class extends Error {
|
|
2470
|
+
constructor(timeoutMs) {
|
|
2471
|
+
super(`Codex image generation timed out after ${Math.ceil(timeoutMs / 1e3)} seconds`);
|
|
2472
|
+
this.timeoutMs = timeoutMs;
|
|
2473
|
+
this.name = "CodexImageRequestTimeoutError";
|
|
2474
|
+
}
|
|
2475
|
+
};
|
|
2476
|
+
async function generateCodexSubscriptionImage(input) {
|
|
2477
|
+
const fetchImpl = input.fetch ?? codexImageFetch;
|
|
2478
|
+
const timeoutMs = input.requestTimeoutMs ?? CODEX_IMAGE_REQUEST_TIMEOUT_MS;
|
|
2479
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
|
|
2480
|
+
throw new RangeError("Codex image request timeout must be a positive safe integer");
|
|
2481
|
+
}
|
|
2482
|
+
const references = input.references ?? [];
|
|
2483
|
+
if (references.length > CODEX_IMAGE_MAX_REFERENCES) {
|
|
2484
|
+
throw new RangeError(
|
|
2485
|
+
`Codex image editing accepts at most ${CODEX_IMAGE_MAX_REFERENCES} images`
|
|
2486
|
+
);
|
|
2487
|
+
}
|
|
2488
|
+
for (const reference of references) {
|
|
2489
|
+
if (reference.bytes.byteLength === 0) throw new Error("Codex image reference is empty");
|
|
2490
|
+
}
|
|
2491
|
+
const deadline = new AbortController();
|
|
2492
|
+
const timer = setTimeout(
|
|
2493
|
+
() => deadline.abort(new CodexImageRequestTimeoutError(timeoutMs)),
|
|
2494
|
+
timeoutMs
|
|
2495
|
+
);
|
|
2496
|
+
const signal = input.abortSignal ? AbortSignal.any([input.abortSignal, deadline.signal]) : deadline.signal;
|
|
2497
|
+
const request = async (auth) => {
|
|
2498
|
+
const headers = codexImageHeaders(auth, input.context.clientVersion, input.turnId);
|
|
2499
|
+
return await fetchImpl(
|
|
2500
|
+
`${CODEX_RESPONSES_BASE}/${references.length > 0 ? "images/edits" : "images/generations"}`,
|
|
2501
|
+
{
|
|
2502
|
+
method: "POST",
|
|
2503
|
+
redirect: "error",
|
|
2504
|
+
headers,
|
|
2505
|
+
body: JSON.stringify(
|
|
2506
|
+
references.length > 0 ? {
|
|
2507
|
+
images: references.map((reference) => ({
|
|
2508
|
+
image_url: `data:${reference.mediaType};base64,${Buffer.from(reference.bytes).toString("base64")}`
|
|
2509
|
+
})),
|
|
2510
|
+
prompt: input.prompt,
|
|
2511
|
+
background: "auto",
|
|
2512
|
+
model: CODEX_IMAGE_MODEL,
|
|
2513
|
+
quality: "auto",
|
|
2514
|
+
size: "auto"
|
|
2515
|
+
} : {
|
|
2516
|
+
prompt: input.prompt,
|
|
2517
|
+
background: "auto",
|
|
2518
|
+
model: CODEX_IMAGE_MODEL,
|
|
2519
|
+
quality: "auto",
|
|
2520
|
+
size: "auto"
|
|
2521
|
+
}
|
|
2522
|
+
),
|
|
2523
|
+
signal
|
|
2524
|
+
}
|
|
2525
|
+
);
|
|
2526
|
+
};
|
|
2527
|
+
const operation = (async () => {
|
|
2528
|
+
let response = await request(await input.context.getToken());
|
|
2529
|
+
if (response.status === 401) {
|
|
2530
|
+
await response.body?.cancel().catch(() => void 0);
|
|
2531
|
+
response = await request(await input.context.refresh());
|
|
2532
|
+
}
|
|
2533
|
+
if (!response.ok) {
|
|
2534
|
+
const detail = await readResponseTextBounded(
|
|
2535
|
+
response,
|
|
2536
|
+
CODEX_IMAGE_ERROR_MAX_BYTES,
|
|
2537
|
+
"Codex image error",
|
|
2538
|
+
{ signal }
|
|
2539
|
+
).catch(() => "");
|
|
2540
|
+
throw new CodexImageApiError(
|
|
2541
|
+
response.status,
|
|
2542
|
+
detail ? `Codex image generation failed (${response.status}): ${boundedErrorMessage(detail)}` : `Codex image generation failed (${response.status})`
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
const bytes = await readJsonBase64Field(response, {
|
|
2546
|
+
fieldName: "b64_json",
|
|
2547
|
+
shape: "string",
|
|
2548
|
+
maxResponseBytes: CODEX_IMAGE_RESPONSE_MAX_BYTES,
|
|
2549
|
+
maxDecodedBytes: CODEX_IMAGE_MAX_BYTES,
|
|
2550
|
+
label: "Codex image generation",
|
|
2551
|
+
signal
|
|
2552
|
+
});
|
|
2553
|
+
return { bytes, declaredMediaType: "image/png" };
|
|
2554
|
+
})();
|
|
2555
|
+
let removeAbortListener = () => void 0;
|
|
2556
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
2557
|
+
const onAbort = () => reject(signal.reason);
|
|
2558
|
+
if (signal.aborted) {
|
|
2559
|
+
onAbort();
|
|
2560
|
+
return;
|
|
2561
|
+
}
|
|
2562
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
2563
|
+
removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
2564
|
+
});
|
|
2565
|
+
try {
|
|
2566
|
+
return await Promise.race([operation, aborted]);
|
|
2567
|
+
} finally {
|
|
2568
|
+
removeAbortListener();
|
|
2569
|
+
clearTimeout(timer);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
function codexImageHeaders(auth, clientVersion, turnId) {
|
|
2573
|
+
const headers = new Headers({
|
|
2574
|
+
Authorization: `Bearer ${auth.accessToken}`,
|
|
2575
|
+
accept: "application/json",
|
|
2576
|
+
"content-type": "application/json",
|
|
2577
|
+
originator: CODEX_ORIGINATOR,
|
|
2578
|
+
"User-Agent": `${CODEX_ORIGINATOR}/${clientVersion || CODEX_CLIENT_VERSION}`,
|
|
2579
|
+
version: clientVersion || CODEX_CLIENT_VERSION,
|
|
2580
|
+
"x-codex-image-turn-id": turnId
|
|
2581
|
+
});
|
|
2582
|
+
if (auth.chatgptAccountId) headers.set("ChatGPT-Account-ID", auth.chatgptAccountId);
|
|
2583
|
+
if (auth.isFedramp) headers.set("X-OpenAI-Fedramp", "true");
|
|
2584
|
+
return headers;
|
|
2585
|
+
}
|
|
2586
|
+
function boundedErrorMessage(body) {
|
|
2587
|
+
let message = body;
|
|
2588
|
+
try {
|
|
2589
|
+
const value = JSON.parse(body);
|
|
2590
|
+
const candidate = value.error?.message ?? value.message;
|
|
2591
|
+
if (typeof candidate === "string") message = candidate;
|
|
2592
|
+
} catch {
|
|
2593
|
+
}
|
|
2594
|
+
return message.replace(/\s+/g, " ").trim().slice(0, 1e3);
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2280
2597
|
// src/realtime.ts
|
|
2281
2598
|
var CODEX_REALTIME_CALL_URL = `${CODEX_RESPONSES_BASE}/realtime/calls?intent=quicksilver&architecture=avas`;
|
|
2282
2599
|
var MAX_REALTIME_SDP_BYTES = 1024 * 1024;
|
|
@@ -2578,6 +2895,10 @@ export {
|
|
|
2578
2895
|
CODEX_REALTIME_VOICES,
|
|
2579
2896
|
CODEX_REFRESH_FALLBACK_MS,
|
|
2580
2897
|
CODEX_REFRESH_WINDOW_MS,
|
|
2898
|
+
CODEX_REQUEST_BODY_NORMALIZED_HEADER,
|
|
2899
|
+
CODEX_REQUEST_CALLER_STREAM_HEADER,
|
|
2900
|
+
CODEX_REQUEST_ID_HEADER,
|
|
2901
|
+
CODEX_REQUEST_MODEL_HEADER,
|
|
2581
2902
|
CODEX_RESPONSES_BASE,
|
|
2582
2903
|
CODEX_RESPONSE_HEADERS_TIMEOUT_MS,
|
|
2583
2904
|
CODEX_RESPONSE_NO_BYTE_RETRIES,
|
|
@@ -2593,6 +2914,8 @@ export {
|
|
|
2593
2914
|
CODEX_WEEKLY_WINDOW_SECONDS,
|
|
2594
2915
|
CODEX_WHAM_BASE,
|
|
2595
2916
|
CodexDeviceError,
|
|
2917
|
+
CodexImageApiError,
|
|
2918
|
+
CodexImageRequestTimeoutError,
|
|
2596
2919
|
CodexRealtimeError,
|
|
2597
2920
|
CodexRefreshTransient,
|
|
2598
2921
|
CodexReloginRequired,
|
|
@@ -2626,12 +2949,14 @@ export {
|
|
|
2626
2949
|
fetchCodexModels,
|
|
2627
2950
|
fetchCodexRateLimitResetCredits,
|
|
2628
2951
|
fetchCodexUsage,
|
|
2952
|
+
generateCodexSubscriptionImage,
|
|
2629
2953
|
isCodexBilledModel,
|
|
2630
2954
|
isCodexTransportError,
|
|
2631
2955
|
isPreHeadersTimeoutError,
|
|
2632
2956
|
modelToolOutputSerializationBudgetTokens,
|
|
2633
2957
|
normalizeCodexRequestBody,
|
|
2634
2958
|
normalizeCodexUsage,
|
|
2959
|
+
normalizedCodexRequestBody,
|
|
2635
2960
|
opaqueProviderArtifactFingerprint,
|
|
2636
2961
|
opaqueProviderArtifactFingerprints,
|
|
2637
2962
|
parseCodexRateLimitResetConsumeResponse,
|