@opengeni/codex 0.2.15 → 0.2.17
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/index.js +212 -127
- package/dist/index.js.map +1 -1
- package/dist/request-context.d.ts +9 -0
- package/package.json +1 -1
- package/src/fetch.ts +291 -162
- package/src/request-context.ts +13 -0
package/dist/index.js
CHANGED
|
@@ -821,6 +821,12 @@ function opaqueProviderArtifactFingerprints(input) {
|
|
|
821
821
|
}
|
|
822
822
|
|
|
823
823
|
// src/fetch.ts
|
|
824
|
+
function emitRequestPreparationDiagnostic(ctx, phase) {
|
|
825
|
+
try {
|
|
826
|
+
ctx.onRequestPreparationDiagnostic?.(phase);
|
|
827
|
+
} catch {
|
|
828
|
+
}
|
|
829
|
+
}
|
|
824
830
|
var CODEX_TRANSPORT_ERROR_HEADER = "x-opengeni-codex-transport-error";
|
|
825
831
|
var CODEX_REQUEST_BODY_NORMALIZED_HEADER = "x-opengeni-request-body-normalized";
|
|
826
832
|
var REPLAYABLE_REQUEST_BODY_FACTORY = Symbol.for("opengeni.replayable-request-body-factory");
|
|
@@ -914,15 +920,90 @@ function parseCodexUsageHeaders(headers) {
|
|
|
914
920
|
checkedAt: new Date(nowMs)
|
|
915
921
|
};
|
|
916
922
|
}
|
|
917
|
-
|
|
918
|
-
|
|
923
|
+
function classifyCodexSseTerminal(ev) {
|
|
924
|
+
if (ev.type === "response.failed") {
|
|
925
|
+
return {
|
|
926
|
+
phase: "failed",
|
|
927
|
+
rawError: ev.response?.error,
|
|
928
|
+
fallbackCode: "response_failed",
|
|
929
|
+
fallbackMessage: "The Codex response failed"
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
if (ev.type === "error" || ev.type === "response.error") {
|
|
933
|
+
return {
|
|
934
|
+
phase: "failed",
|
|
935
|
+
rawError: ev.error ?? ev.response?.error ?? ev,
|
|
936
|
+
fallbackCode: "response_error",
|
|
937
|
+
fallbackMessage: "The Codex response stream reported an error"
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
if (ev.type === "response.incomplete") {
|
|
941
|
+
const details = ev.response?.incomplete_details;
|
|
942
|
+
const reason = details && typeof details === "object" ? details.reason : void 0;
|
|
943
|
+
return {
|
|
944
|
+
phase: "failed",
|
|
945
|
+
rawError: {
|
|
946
|
+
code: "response_incomplete",
|
|
947
|
+
message: typeof reason === "string" && reason.length > 0 ? `The Codex response was incomplete (${reason})` : "The Codex response was incomplete"
|
|
948
|
+
},
|
|
949
|
+
fallbackCode: "response_incomplete",
|
|
950
|
+
fallbackMessage: "The Codex response was incomplete"
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
if (ev.type !== "response.completed" && ev.type !== "response.done") {
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
if (!ev.response) {
|
|
957
|
+
return null;
|
|
958
|
+
}
|
|
959
|
+
const responseStatus = ev.response.status;
|
|
960
|
+
if (responseStatus !== void 0 && responseStatus !== "completed" || ev.response.error !== null && ev.response.error !== void 0) {
|
|
961
|
+
const incomplete = responseStatus === "incomplete";
|
|
962
|
+
return {
|
|
963
|
+
phase: "failed",
|
|
964
|
+
rawError: ev.response.error,
|
|
965
|
+
fallbackCode: incomplete ? "response_incomplete" : "response_failed",
|
|
966
|
+
fallbackMessage: incomplete ? "The Codex response was incomplete" : "The Codex response failed"
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
return { phase: "completed" };
|
|
970
|
+
}
|
|
971
|
+
function markSemanticTerminal(state, phase) {
|
|
972
|
+
if (state.phase === null) {
|
|
973
|
+
state.phase = phase;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
function terminalOutcomeForPhase(phase) {
|
|
977
|
+
if (phase === "completed" || phase === "failed" || phase === "timed_out") {
|
|
978
|
+
return phase;
|
|
979
|
+
}
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
function requestEventFor(audit, event) {
|
|
983
|
+
return {
|
|
919
984
|
requestId: audit.requestId,
|
|
920
985
|
transportAttempt: audit.transportAttempt,
|
|
921
986
|
...audit.model ? { model: audit.model } : {},
|
|
922
|
-
durationMs: Math.max(0,
|
|
987
|
+
durationMs: Math.max(0, performance.now() - audit.attemptStartedAtMonotonic),
|
|
923
988
|
timeoutPolicy: audit.policy,
|
|
924
989
|
...event
|
|
925
|
-
}
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
async function emitRequestEvent(audit, event) {
|
|
993
|
+
const terminalOutcome = terminalOutcomeForPhase(event.phase);
|
|
994
|
+
if (terminalOutcome !== null) {
|
|
995
|
+
if (audit.terminalOutcome !== null) {
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
audit.terminalOutcome = terminalOutcome;
|
|
999
|
+
}
|
|
1000
|
+
const observed = requestEventFor(audit, event);
|
|
1001
|
+
try {
|
|
1002
|
+
audit.ctx.onModelRequestDiagnostic?.(observed);
|
|
1003
|
+
} catch {
|
|
1004
|
+
}
|
|
1005
|
+
await audit.ctx.onModelRequestEvent?.(observed);
|
|
1006
|
+
return true;
|
|
926
1007
|
}
|
|
927
1008
|
function providerRequestId(headers) {
|
|
928
1009
|
return headers.get("x-request-id") ?? headers.get("request-id") ?? void 0;
|
|
@@ -963,11 +1044,12 @@ async function fetchBeforeHeaders(base, input, init, audit) {
|
|
|
963
1044
|
externalSignal?.removeEventListener("abort", forwardAbort);
|
|
964
1045
|
}
|
|
965
1046
|
}
|
|
966
|
-
async function observedResponse(res, audit, externalSignal) {
|
|
1047
|
+
async function observedResponse(res, audit, externalSignal, semanticTerminal) {
|
|
967
1048
|
const requestId = providerRequestId(res.headers);
|
|
968
1049
|
if (!res.body) {
|
|
1050
|
+
if (semanticTerminal) markSemanticTerminal(semanticTerminal, "failed");
|
|
969
1051
|
await emitRequestEvent(audit, {
|
|
970
|
-
phase: res.ok ? "completed" : "failed",
|
|
1052
|
+
phase: semanticTerminal?.phase ?? (res.ok ? "completed" : "failed"),
|
|
971
1053
|
responseObserved: true,
|
|
972
1054
|
status: res.status,
|
|
973
1055
|
...requestId ? { providerRequestId: requestId } : {}
|
|
@@ -992,17 +1074,19 @@ async function observedResponse(res, audit, externalSignal) {
|
|
|
992
1074
|
if (terminal) return;
|
|
993
1075
|
terminal = true;
|
|
994
1076
|
clearTimers();
|
|
1077
|
+
const semanticPhase = semanticTerminal?.phase;
|
|
1078
|
+
const phase = semanticPhase ?? "timed_out";
|
|
995
1079
|
const error = new CodexResponseTimeoutError(klass, audit.requestId, true);
|
|
996
1080
|
void reader.cancel(error).catch(() => void 0);
|
|
997
1081
|
void emitRequestEvent(audit, {
|
|
998
|
-
phase
|
|
1082
|
+
phase,
|
|
999
1083
|
responseObserved: true,
|
|
1000
|
-
timeoutClass: klass,
|
|
1084
|
+
...phase === "timed_out" ? { timeoutClass: klass } : {},
|
|
1001
1085
|
status: res.status,
|
|
1002
1086
|
...requestId ? { providerRequestId: requestId } : {}
|
|
1003
1087
|
}).then(
|
|
1004
|
-
() => controller.error(error),
|
|
1005
|
-
() => controller.error(error)
|
|
1088
|
+
() => phase === "completed" ? controller.close() : controller.error(error),
|
|
1089
|
+
() => phase === "completed" ? controller.close() : controller.error(error)
|
|
1006
1090
|
);
|
|
1007
1091
|
};
|
|
1008
1092
|
armIdle = () => {
|
|
@@ -1022,13 +1106,13 @@ async function observedResponse(res, audit, externalSignal) {
|
|
|
1022
1106
|
const reason = externalSignal?.reason ?? new DOMException("Aborted", "AbortError");
|
|
1023
1107
|
void reader.cancel(reason).catch(() => void 0);
|
|
1024
1108
|
void emitRequestEvent(audit, {
|
|
1025
|
-
phase: "failed",
|
|
1109
|
+
phase: semanticTerminal?.phase ?? "failed",
|
|
1026
1110
|
responseObserved: true,
|
|
1027
1111
|
status: res.status,
|
|
1028
1112
|
...requestId ? { providerRequestId: requestId } : {}
|
|
1029
1113
|
}).then(
|
|
1030
|
-
() => controller.error(reason),
|
|
1031
|
-
() => controller.error(reason)
|
|
1114
|
+
() => semanticTerminal?.phase === "completed" ? controller.close() : controller.error(reason),
|
|
1115
|
+
() => semanticTerminal?.phase === "completed" ? controller.close() : controller.error(reason)
|
|
1032
1116
|
);
|
|
1033
1117
|
};
|
|
1034
1118
|
if (externalSignal?.aborted) {
|
|
@@ -1047,12 +1131,19 @@ async function observedResponse(res, audit, externalSignal) {
|
|
|
1047
1131
|
if (chunk.done) {
|
|
1048
1132
|
terminal = true;
|
|
1049
1133
|
clearTimers();
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1134
|
+
if (semanticTerminal && semanticTerminal.phase === null) {
|
|
1135
|
+
if (!semanticTerminal.deferTransportTerminal) {
|
|
1136
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
if (!semanticTerminal?.deferTransportTerminal || semanticTerminal.phase !== null) {
|
|
1140
|
+
await emitRequestEvent(audit, {
|
|
1141
|
+
phase: semanticTerminal?.phase ?? (res.ok ? "completed" : "failed"),
|
|
1142
|
+
responseObserved: true,
|
|
1143
|
+
status: res.status,
|
|
1144
|
+
...requestId ? { providerRequestId: requestId } : {}
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1056
1147
|
controller.close();
|
|
1057
1148
|
return;
|
|
1058
1149
|
}
|
|
@@ -1075,21 +1166,32 @@ async function observedResponse(res, audit, externalSignal) {
|
|
|
1075
1166
|
if (terminal) return;
|
|
1076
1167
|
terminal = true;
|
|
1077
1168
|
clearTimers();
|
|
1169
|
+
const semanticPhase = semanticTerminal?.phase;
|
|
1170
|
+
if (semanticTerminal && semanticPhase === null) {
|
|
1171
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1172
|
+
}
|
|
1078
1173
|
await emitRequestEvent(audit, {
|
|
1079
|
-
phase: "failed",
|
|
1174
|
+
phase: semanticPhase ?? "failed",
|
|
1080
1175
|
responseObserved: true,
|
|
1081
1176
|
status: res.status,
|
|
1082
1177
|
...requestId ? { providerRequestId: requestId } : {}
|
|
1083
1178
|
});
|
|
1084
|
-
|
|
1179
|
+
if (semanticPhase === "completed") {
|
|
1180
|
+
controller.close();
|
|
1181
|
+
} else {
|
|
1182
|
+
controller.error(error);
|
|
1183
|
+
}
|
|
1085
1184
|
}
|
|
1086
1185
|
},
|
|
1087
1186
|
async cancel(reason) {
|
|
1088
1187
|
if (!terminal) {
|
|
1089
1188
|
terminal = true;
|
|
1090
1189
|
clearTimers();
|
|
1190
|
+
if (semanticTerminal && semanticTerminal.phase === null) {
|
|
1191
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1192
|
+
}
|
|
1091
1193
|
await emitRequestEvent(audit, {
|
|
1092
|
-
phase: "failed",
|
|
1194
|
+
phase: semanticTerminal?.phase ?? "failed",
|
|
1093
1195
|
responseObserved: true,
|
|
1094
1196
|
status: res.status,
|
|
1095
1197
|
...requestId ? { providerRequestId: requestId } : {}
|
|
@@ -1134,6 +1236,7 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1134
1236
|
if (!ctx) {
|
|
1135
1237
|
return base(input, init);
|
|
1136
1238
|
}
|
|
1239
|
+
emitRequestPreparationDiagnostic(ctx, "transport_entry");
|
|
1137
1240
|
const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
1138
1241
|
const rewritten = rawUrl.replace(/(?<!\/codex)\/responses(\b|$)/, "/codex/responses$1");
|
|
1139
1242
|
const policy = resolveCodexResponseTimeoutPolicy(ctx.responseTimeoutPolicy);
|
|
@@ -1197,7 +1300,10 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1197
1300
|
throw new Error("Model request could not be prepared");
|
|
1198
1301
|
}
|
|
1199
1302
|
if (!bodyAlreadyNormalized) {
|
|
1200
|
-
ctx.onRequestOpaqueArtifacts?.({
|
|
1303
|
+
ctx.onRequestOpaqueArtifacts?.({
|
|
1304
|
+
requestId,
|
|
1305
|
+
fingerprints: requestOpaqueArtifacts
|
|
1306
|
+
});
|
|
1201
1307
|
}
|
|
1202
1308
|
headers.set(
|
|
1203
1309
|
"Idempotency-Key",
|
|
@@ -1219,13 +1325,19 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1219
1325
|
transportAttempt,
|
|
1220
1326
|
...model ? { model } : {},
|
|
1221
1327
|
logicalStartedAt,
|
|
1222
|
-
|
|
1223
|
-
policy
|
|
1328
|
+
attemptStartedAtMonotonic: performance.now(),
|
|
1329
|
+
policy,
|
|
1330
|
+
terminalOutcome: null
|
|
1224
1331
|
};
|
|
1332
|
+
emitRequestPreparationDiagnostic(ctx, "wire_request_ready");
|
|
1225
1333
|
await emitRequestEvent(audit, {
|
|
1226
1334
|
phase: "started",
|
|
1227
1335
|
responseObserved: false
|
|
1228
1336
|
});
|
|
1337
|
+
const semanticTerminal = {
|
|
1338
|
+
phase: null,
|
|
1339
|
+
deferTransportTerminal: !callerWantsStream
|
|
1340
|
+
};
|
|
1229
1341
|
try {
|
|
1230
1342
|
res = await fetchBeforeHeaders(base, rewritten, nextInit, audit);
|
|
1231
1343
|
const upstreamRequestId = providerRequestId(res.headers);
|
|
@@ -1235,8 +1347,7 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1235
1347
|
status: res.status,
|
|
1236
1348
|
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1237
1349
|
});
|
|
1238
|
-
|
|
1239
|
-
res = observed;
|
|
1350
|
+
res = await observedResponse(res, audit, nextInit.signal, semanticTerminal);
|
|
1240
1351
|
} catch (error) {
|
|
1241
1352
|
if (nextInit.signal?.aborted) {
|
|
1242
1353
|
await emitRequestEvent(audit, {
|
|
@@ -1273,12 +1384,30 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
|
|
|
1273
1384
|
});
|
|
1274
1385
|
}
|
|
1275
1386
|
if (!res.ok) {
|
|
1276
|
-
|
|
1387
|
+
const buffered = await bufferCodexErrorResponse(res);
|
|
1388
|
+
const upstreamRequestId = providerRequestId(res.headers);
|
|
1389
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1390
|
+
await emitRequestEvent(audit, {
|
|
1391
|
+
phase: "failed",
|
|
1392
|
+
responseObserved: true,
|
|
1393
|
+
status: res.status,
|
|
1394
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1395
|
+
}).catch(() => void 0);
|
|
1396
|
+
return buffered;
|
|
1277
1397
|
}
|
|
1278
|
-
|
|
1398
|
+
if (callerWantsStream) {
|
|
1399
|
+
res = validateCodexStream(res, (phase) => {
|
|
1400
|
+
markSemanticTerminal(semanticTerminal, phase);
|
|
1401
|
+
});
|
|
1402
|
+
} else {
|
|
1403
|
+
res = await sseToJsonResponse(res, audit, semanticTerminal);
|
|
1404
|
+
}
|
|
1405
|
+
return res;
|
|
1279
1406
|
};
|
|
1280
1407
|
try {
|
|
1281
|
-
|
|
1408
|
+
const token = await ctx.getToken();
|
|
1409
|
+
emitRequestPreparationDiagnostic(ctx, "credential_ready");
|
|
1410
|
+
let res = await attempt(token, 0);
|
|
1282
1411
|
if (res.status === 401) {
|
|
1283
1412
|
res = await attempt(await ctx.refresh(), 1);
|
|
1284
1413
|
}
|
|
@@ -1382,7 +1511,8 @@ async function readBoundedResponseText(response, maxBytes) {
|
|
|
1382
1511
|
}
|
|
1383
1512
|
return { text: parts.join(""), truncated };
|
|
1384
1513
|
}
|
|
1385
|
-
async function sseToJsonResponse(res) {
|
|
1514
|
+
async function sseToJsonResponse(res, audit, semanticTerminal) {
|
|
1515
|
+
const upstreamRequestId = providerRequestId(res.headers);
|
|
1386
1516
|
const text = await res.text();
|
|
1387
1517
|
let final = null;
|
|
1388
1518
|
let terminalError = null;
|
|
@@ -1395,57 +1525,45 @@ async function sseToJsonResponse(res) {
|
|
|
1395
1525
|
const ev = JSON.parse(data);
|
|
1396
1526
|
if (ev.type === "response.output_item.done" && ev.item !== void 0) {
|
|
1397
1527
|
items.push(ev.item);
|
|
1398
|
-
} else
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
"The Codex response stream reported an error",
|
|
1416
|
-
{
|
|
1417
|
-
eventType: ev.type,
|
|
1418
|
-
responseId: ev.response?.id,
|
|
1419
|
-
responseStatus: ev.response?.status
|
|
1420
|
-
}
|
|
1421
|
-
);
|
|
1422
|
-
} else if (ev.type === "response.incomplete") {
|
|
1423
|
-
const details = ev.response?.incomplete_details;
|
|
1424
|
-
const reason = details && typeof details === "object" ? details.reason : void 0;
|
|
1425
|
-
terminalError = codexSseFailureResponse(
|
|
1426
|
-
res,
|
|
1427
|
-
{
|
|
1428
|
-
code: "response_incomplete",
|
|
1429
|
-
message: typeof reason === "string" && reason.length > 0 ? `The Codex response was incomplete (${reason})` : "The Codex response was incomplete"
|
|
1430
|
-
},
|
|
1431
|
-
"response_incomplete",
|
|
1432
|
-
"The Codex response was incomplete",
|
|
1433
|
-
{
|
|
1434
|
-
eventType: ev.type,
|
|
1435
|
-
responseId: ev.response?.id,
|
|
1436
|
-
responseStatus: ev.response?.status
|
|
1437
|
-
}
|
|
1438
|
-
);
|
|
1439
|
-
} else if (ev.type === "response.completed" || ev.type === "response.done") {
|
|
1440
|
-
final = ev.response ?? null;
|
|
1528
|
+
} else {
|
|
1529
|
+
const terminal = classifyCodexSseTerminal(ev);
|
|
1530
|
+
if (terminal?.phase === "failed") {
|
|
1531
|
+
terminalError = codexSseFailureResponse(
|
|
1532
|
+
res,
|
|
1533
|
+
terminal.rawError,
|
|
1534
|
+
terminal.fallbackCode,
|
|
1535
|
+
terminal.fallbackMessage,
|
|
1536
|
+
{
|
|
1537
|
+
eventType: ev.type,
|
|
1538
|
+
responseId: ev.response?.id,
|
|
1539
|
+
responseStatus: ev.response?.status
|
|
1540
|
+
}
|
|
1541
|
+
);
|
|
1542
|
+
} else if (terminal?.phase === "completed") {
|
|
1543
|
+
final = ev.response ?? null;
|
|
1544
|
+
}
|
|
1441
1545
|
}
|
|
1442
1546
|
} catch {
|
|
1443
1547
|
}
|
|
1444
1548
|
}
|
|
1445
1549
|
if (terminalError) {
|
|
1550
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1551
|
+
await emitRequestEvent(audit, {
|
|
1552
|
+
phase: "failed",
|
|
1553
|
+
responseObserved: true,
|
|
1554
|
+
status: res.status,
|
|
1555
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1556
|
+
});
|
|
1446
1557
|
return terminalError;
|
|
1447
1558
|
}
|
|
1448
1559
|
if (!final) {
|
|
1560
|
+
markSemanticTerminal(semanticTerminal, "failed");
|
|
1561
|
+
await emitRequestEvent(audit, {
|
|
1562
|
+
phase: "failed",
|
|
1563
|
+
responseObserved: true,
|
|
1564
|
+
status: res.status,
|
|
1565
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1566
|
+
});
|
|
1449
1567
|
return codexSseFailureResponse(
|
|
1450
1568
|
res,
|
|
1451
1569
|
null,
|
|
@@ -1461,6 +1579,13 @@ async function sseToJsonResponse(res) {
|
|
|
1461
1579
|
`[codex-debug] sse->json items=${items.length} outputLen=${Array.isArray(final?.output) ? final.output.length : "?"}`
|
|
1462
1580
|
);
|
|
1463
1581
|
}
|
|
1582
|
+
markSemanticTerminal(semanticTerminal, "completed");
|
|
1583
|
+
await emitRequestEvent(audit, {
|
|
1584
|
+
phase: "completed",
|
|
1585
|
+
responseObserved: true,
|
|
1586
|
+
status: res.status,
|
|
1587
|
+
...upstreamRequestId ? { providerRequestId: upstreamRequestId } : {}
|
|
1588
|
+
});
|
|
1464
1589
|
const headers = new Headers(res.headers);
|
|
1465
1590
|
headers.set("content-type", "application/json");
|
|
1466
1591
|
headers.delete("content-length");
|
|
@@ -1613,8 +1738,9 @@ function codexSseFailureError(source, rawError, fallbackCode, publicMessage, met
|
|
|
1613
1738
|
codexSseFailureProjection(source, rawError, fallbackCode, publicMessage, metadata)
|
|
1614
1739
|
);
|
|
1615
1740
|
}
|
|
1616
|
-
function validateCodexStream(res) {
|
|
1741
|
+
function validateCodexStream(res, onSemanticTerminal) {
|
|
1617
1742
|
if (!res.body) {
|
|
1743
|
+
onSemanticTerminal?.("failed");
|
|
1618
1744
|
const error = codexSseFailureError(
|
|
1619
1745
|
res,
|
|
1620
1746
|
null,
|
|
@@ -1644,7 +1770,7 @@ function validateCodexStream(res) {
|
|
|
1644
1770
|
const block = buffer.slice(0, boundary.start);
|
|
1645
1771
|
const separator = buffer.slice(boundary.start, boundary.end);
|
|
1646
1772
|
buffer = buffer.slice(boundary.end);
|
|
1647
|
-
successfulTerminalSeen ||= inspectCodexSseBlock(block, res);
|
|
1773
|
+
successfulTerminalSeen ||= inspectCodexSseBlock(block, res, onSemanticTerminal);
|
|
1648
1774
|
controller.enqueue(encoder.encode(`${block}${separator}`));
|
|
1649
1775
|
boundary = findSseBlockBoundary(buffer, final);
|
|
1650
1776
|
}
|
|
@@ -1658,7 +1784,7 @@ function validateCodexStream(res) {
|
|
|
1658
1784
|
buffer += decoder.decode();
|
|
1659
1785
|
emitCompleteBlocks(controller, true);
|
|
1660
1786
|
if (buffer.length > 0) {
|
|
1661
|
-
successfulTerminalSeen ||= inspectCodexSseBlock(buffer, res);
|
|
1787
|
+
successfulTerminalSeen ||= inspectCodexSseBlock(buffer, res, onSemanticTerminal);
|
|
1662
1788
|
controller.enqueue(encoder.encode(buffer));
|
|
1663
1789
|
buffer = "";
|
|
1664
1790
|
}
|
|
@@ -1708,7 +1834,7 @@ var CODEX_TERMINAL_TYPE_HINTS = [
|
|
|
1708
1834
|
'"response.error"',
|
|
1709
1835
|
'"error"'
|
|
1710
1836
|
];
|
|
1711
|
-
function inspectCodexSseBlock(block, source) {
|
|
1837
|
+
function inspectCodexSseBlock(block, source, onSemanticTerminal) {
|
|
1712
1838
|
const lines = block.split(/\r\n|\r|\n/);
|
|
1713
1839
|
const dataStr = lines.filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("\n");
|
|
1714
1840
|
if (!dataStr || dataStr === "[DONE]") {
|
|
@@ -1723,43 +1849,14 @@ function inspectCodexSseBlock(block, source) {
|
|
|
1723
1849
|
} catch {
|
|
1724
1850
|
return false;
|
|
1725
1851
|
}
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
ev.response?.error,
|
|
1730
|
-
"response_failed",
|
|
1731
|
-
"The Codex response failed",
|
|
1732
|
-
{
|
|
1733
|
-
eventType: ev.type,
|
|
1734
|
-
responseId: ev.response?.id,
|
|
1735
|
-
responseStatus: ev.response?.status
|
|
1736
|
-
}
|
|
1737
|
-
);
|
|
1738
|
-
}
|
|
1739
|
-
if (ev.type === "error" || ev.type === "response.error") {
|
|
1740
|
-
throw codexSseFailureError(
|
|
1741
|
-
source,
|
|
1742
|
-
ev.error ?? ev.response?.error ?? ev,
|
|
1743
|
-
"response_error",
|
|
1744
|
-
"The Codex response stream reported an error",
|
|
1745
|
-
{
|
|
1746
|
-
eventType: ev.type,
|
|
1747
|
-
responseId: ev.response?.id,
|
|
1748
|
-
responseStatus: ev.response?.status
|
|
1749
|
-
}
|
|
1750
|
-
);
|
|
1751
|
-
}
|
|
1752
|
-
if (ev.type === "response.incomplete") {
|
|
1753
|
-
const details = ev.response?.incomplete_details;
|
|
1754
|
-
const reason = details && typeof details === "object" ? details.reason : void 0;
|
|
1852
|
+
const terminal = classifyCodexSseTerminal(ev);
|
|
1853
|
+
if (terminal?.phase === "failed") {
|
|
1854
|
+
onSemanticTerminal?.("failed");
|
|
1755
1855
|
throw codexSseFailureError(
|
|
1756
1856
|
source,
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
},
|
|
1761
|
-
"response_incomplete",
|
|
1762
|
-
"The Codex response was incomplete",
|
|
1857
|
+
terminal.rawError,
|
|
1858
|
+
terminal.fallbackCode,
|
|
1859
|
+
terminal.fallbackMessage,
|
|
1763
1860
|
{
|
|
1764
1861
|
eventType: ev.type,
|
|
1765
1862
|
responseId: ev.response?.id,
|
|
@@ -1767,20 +1864,8 @@ function inspectCodexSseBlock(block, source) {
|
|
|
1767
1864
|
}
|
|
1768
1865
|
);
|
|
1769
1866
|
}
|
|
1770
|
-
if (
|
|
1771
|
-
|
|
1772
|
-
throw codexSseFailureError(
|
|
1773
|
-
source,
|
|
1774
|
-
ev.response.error,
|
|
1775
|
-
ev.response.status === "incomplete" ? "response_incomplete" : "response_failed",
|
|
1776
|
-
ev.response.status === "incomplete" ? "The Codex response was incomplete" : "The Codex response failed",
|
|
1777
|
-
{
|
|
1778
|
-
eventType: ev.type,
|
|
1779
|
-
responseId: ev.response.id,
|
|
1780
|
-
responseStatus: ev.response.status
|
|
1781
|
-
}
|
|
1782
|
-
);
|
|
1783
|
-
}
|
|
1867
|
+
if (terminal?.phase === "completed") {
|
|
1868
|
+
onSemanticTerminal?.("completed");
|
|
1784
1869
|
return true;
|
|
1785
1870
|
}
|
|
1786
1871
|
return false;
|