@openclaw/ai 2026.7.2-beta.6 → 2026.7.33
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/README.md +5 -6
- package/dist/{anthropic-CH4UUnZr.mjs → anthropic-BML72tLz.mjs} +468 -101
- package/dist/{api-registry-DlMgPR39.d.mts → api-registry-BXYnCOIR.d.mts} +2 -1
- package/dist/{azure-openai-responses-client-compat-C7K7QfUE.mjs → azure-openai-responses-client-compat-a_O_GVQV.mjs} +2 -23
- package/dist/{azure-openai-responses-CImcwB83.mjs → azure-openai-responses-kEN8II8I.mjs} +13 -11
- package/dist/{env-api-keys-DrgeBuva.mjs → env-api-keys-CtMlqaQ4.mjs} +4 -5
- package/dist/{event-stream-YjaPW20U.d.mts → event-stream-0nZeBKl2.d.mts} +1 -1
- package/dist/{event-stream-D8n2uFee.mjs → event-stream-ReMmOTzX.mjs} +6 -14
- package/dist/event-stream.d.mts +1 -1
- package/dist/event-stream.mjs +1 -1
- package/dist/{github-copilot-headers-NCJtz9i0.mjs → github-copilot-headers-BsH5cqGj.mjs} +12 -1
- package/dist/{google-CtSg0iTS.mjs → google-DTM4O3e9.mjs} +5 -5
- package/dist/{google-shared-DNBz5rcD.mjs → google-shared-BAP0BQVi.mjs} +22 -34
- package/dist/{google-vertex-31f1uS9L.mjs → google-vertex-D58MZHCQ.mjs} +19 -6
- package/dist/host-4t713IeR.mjs +37 -0
- package/dist/{anthropic-SrGtwsJu.d.mts → index-BoTnz8cv.d.mts} +1 -27
- package/dist/index.d.mts +50 -7
- package/dist/index.mjs +5 -5
- package/dist/internal/anthropic.d.mts +62 -22
- package/dist/internal/anthropic.mjs +4 -5
- package/dist/internal/openai.d.mts +82 -98
- package/dist/internal/openai.mjs +6 -8
- package/dist/internal/runtime.d.mts +34 -13
- package/dist/internal/runtime.mjs +14 -26
- package/dist/internal/shared.d.mts +9 -26
- package/dist/internal/shared.mjs +2 -6
- package/dist/{json-parse-BvXNt1-7.mjs → json-parse-DzNSIQBq.mjs} +6 -4
- package/dist/{llm-request-activity-BjtkplhG.mjs → llm-request-activity-CehVkZP-.mjs} +19 -1
- package/dist/{mistral-CWmpvWYh.mjs → mistral-CePVNdws.mjs} +39 -141
- package/dist/model-utils-DgmOla96.mjs +69 -0
- package/dist/{openai-chatgpt-responses-B84Ibtrd.mjs → openai-chatgpt-responses-BHqbcipc.mjs} +154 -101
- package/dist/{openai-completions-DsOxhOD1.mjs → openai-completions-9qYFVS7H.mjs} +295 -81
- package/dist/{openai-responses-BT7A3sLu.mjs → openai-responses-D6l9URyV.mjs} +8 -10
- package/dist/openai-responses-shared-L3PWZvkm.mjs +1949 -0
- package/dist/{openai-tool-projection-OhX64DoP.mjs → openai-tool-projection-BPlyXt8H.mjs} +12 -32
- package/dist/providers.d.mts +2 -3
- package/dist/providers.mjs +10 -11
- package/dist/reasoning-tag-text-partitioner-axhAdUwg.mjs +394 -0
- package/dist/{src-QkygScBs.mjs → src-CZ503MYJ.mjs} +5 -24
- package/dist/{stream-first-event-timeout-BBys9hSb.mjs → stream-first-event-timeout-RjWszj8c.mjs} +22 -2
- package/dist/transform-messages-BhGF_fF4.mjs +507 -0
- package/dist/{types-bzp5k29J.d.mts → types-DRgdPqaZ.d.mts} +0 -19
- package/dist/types.d.mts +5 -5
- package/dist/types.mjs +4 -4
- package/dist/{validation-B-j7cOYp.d.mts → validation-BDMWOr8d.d.mts} +1 -1
- package/dist/{validation-DAa_yFOM.mjs → validation-FrchoOlv.mjs} +7 -2
- package/dist/validation.d.mts +1 -1
- package/dist/validation.mjs +1 -1
- package/npm-shrinkwrap.json +645 -0
- package/package.json +11 -36
- package/dist/anthropic-usage-DWU-x8MI.mjs +0 -459
- package/dist/cache-retention-0x979a5V.mjs +0 -12
- package/dist/deferred-event-buffer-DAvyP7qA.mjs +0 -19
- package/dist/error-coercion-DgxlWC0n.mjs +0 -15
- package/dist/host-B9GUmcra.d.mts +0 -173
- package/dist/host-Dog2WQiR.mjs +0 -369
- package/dist/index-BVVgDSdq.d.mts +0 -1
- package/dist/internal/retry-after.d.mts +0 -5
- package/dist/internal/retry-after.mjs +0 -101
- package/dist/number-coercion-DvG7SNMg.mjs +0 -129
- package/dist/openai-completions-compat-DBWjXoMZ.d.mts +0 -43
- package/dist/openai-prompt-cache-2uo_1OR1.d.mts +0 -7
- package/dist/openai-prompt-cache-mZTCdRPo.mjs +0 -12
- package/dist/openai-reasoning-compat-YgeLncHw.mjs +0 -396
- package/dist/openai-responses-shared-pXl6Wd8S.mjs +0 -392
- package/dist/openai-responses-stream-internal-Cw5txaGW.mjs +0 -2964
- package/dist/prompt-cache-stability-Cwcjv_fx.d.mts +0 -13
- package/dist/provider-error-CAEvRjry.mjs +0 -47
- package/dist/provider-options-D8bB3z9b.d.mts +0 -144
- package/dist/reasoning-tag-text-partitioner-CGDyLWUR.mjs +0 -12209
- package/dist/simple-options-9lhRrN73.mjs +0 -50
- package/dist/stream-first-event-timeout-DvDeSucC.d.mts +0 -29
- package/dist/tls-certificate-errors-DXSpluKI.mjs +0 -93
- package/dist/tool-result-text-CTpIRbYd.mjs +0 -225
- package/dist/transform-messages-C8mBqZxF.mjs +0 -2
- package/dist/transport-stream-shared-D81p90xq.mjs +0 -297
- package/dist/transports.d.mts +0 -573
- package/dist/transports.mjs +0 -4110
package/dist/{openai-chatgpt-responses-B84Ibtrd.mjs → openai-chatgpt-responses-BHqbcipc.mjs}
RENAMED
|
@@ -1,41 +1,14 @@
|
|
|
1
|
-
import { n as getEnvApiKey } from "./env-api-keys-
|
|
1
|
+
import { n as getEnvApiKey } from "./env-api-keys-CtMlqaQ4.mjs";
|
|
2
2
|
import { i as formatThrownValue, n as createAssistantMessageDiagnostic, t as appendAssistantMessageDiagnostic } from "./diagnostics-COpOtRwq.mjs";
|
|
3
|
-
import { t as AssistantMessageEventStream } from "./event-stream-
|
|
4
|
-
import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-
|
|
5
|
-
import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
|
|
6
|
-
import { d as stripSystemPromptCacheBoundary } from "./tool-result-text-CTpIRbYd.mjs";
|
|
7
|
-
import { t as toErrorObject } from "./error-coercion-DgxlWC0n.mjs";
|
|
8
|
-
import { n as clampTimerTimeoutMs, o as resolveTimerTimeoutMs } from "./number-coercion-DvG7SNMg.mjs";
|
|
9
|
-
import { parseRetryAfterHttpDateMs } from "./internal/retry-after.mjs";
|
|
10
|
-
import { d as transportAbortError } from "./transport-stream-shared-D81p90xq.mjs";
|
|
11
|
-
import { _t as supportsOpenAITemperature, n as processResponsesStream } from "./openai-responses-stream-internal-Cw5txaGW.mjs";
|
|
12
|
-
import { i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-BBys9hSb.mjs";
|
|
3
|
+
import { t as AssistantMessageEventStream } from "./event-stream-ReMmOTzX.mjs";
|
|
4
|
+
import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-4t713IeR.mjs";
|
|
13
5
|
import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
6
|
+
import { b as stripSystemPromptCacheBoundary, o as buildBaseOptions } from "./transform-messages-BhGF_fF4.mjs";
|
|
7
|
+
import { a as resolveResponsesReasoningEffort, i as processResponsesStream, n as convertResponsesMessages, s as convertResponsesToolPayload } from "./openai-responses-shared-L3PWZvkm.mjs";
|
|
8
|
+
import { i as getFirstStreamEventTimeoutMs, o as clampTimerTimeoutMs, r as getFirstStreamEventTimeoutHandler, s as resolveTimerTimeoutMs, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-RjWszj8c.mjs";
|
|
9
|
+
import { a as clampOpenAIPromptCacheKey } from "./openai-tool-projection-BPlyXt8H.mjs";
|
|
16
10
|
import { i as registerSessionResourceCleanup, n as resolveOpenAICodexAccountId } from "./openai-chatgpt-jwt-DhAAzLkj.mjs";
|
|
17
11
|
import { t as createSseByteGuard } from "./streaming-byte-guard-BrbkbwUu.mjs";
|
|
18
|
-
import { t as inspectTlsCertificateError } from "./tls-certificate-errors-DXSpluKI.mjs";
|
|
19
|
-
//#region packages/ai/src/internal/retry-sleep.ts
|
|
20
|
-
function sleepWithAbort(ms, signal) {
|
|
21
|
-
return new Promise((resolve, reject) => {
|
|
22
|
-
if (signal?.aborted) {
|
|
23
|
-
reject(/* @__PURE__ */ new Error("Request was aborted"));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
const onAbort = () => {
|
|
27
|
-
clearTimeout(timeout);
|
|
28
|
-
signal?.removeEventListener("abort", onAbort);
|
|
29
|
-
reject(/* @__PURE__ */ new Error("Request was aborted"));
|
|
30
|
-
};
|
|
31
|
-
const timeout = setTimeout(() => {
|
|
32
|
-
signal?.removeEventListener("abort", onAbort);
|
|
33
|
-
resolve();
|
|
34
|
-
}, ms);
|
|
35
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
//#endregion
|
|
39
12
|
//#region packages/ai/src/providers/openai-chatgpt-responses.ts
|
|
40
13
|
const dynamicImport = (specifier) => import(specifier);
|
|
41
14
|
function loadNodeOs() {
|
|
@@ -44,9 +17,10 @@ function loadNodeOs() {
|
|
|
44
17
|
}
|
|
45
18
|
const os = loadNodeOs();
|
|
46
19
|
const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
47
|
-
const
|
|
20
|
+
const MAX_RETRIES = 3;
|
|
48
21
|
const BASE_DELAY_MS = 1e3;
|
|
49
22
|
const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
|
|
23
|
+
const RETRY_AFTER_HTTP_DATE_RE = /^(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT|(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), \d{2}-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\d{2} \d{2}:\d{2}:\d{2} GMT|(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ \d]\d \d{2}:\d{2}:\d{2} \d{4})$/;
|
|
50
24
|
const CODEX_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set(["openai", "opencode"]);
|
|
51
25
|
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
|
52
26
|
const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
|
|
@@ -64,21 +38,18 @@ function isRetryableError(status, errorText) {
|
|
|
64
38
|
if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) return true;
|
|
65
39
|
return /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);
|
|
66
40
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (/^\d+$/.test(trimmed) && Number.isFinite(seconds)) return clampTimerTimeoutMs(seconds * 1e3, 0) ?? fallbackMs;
|
|
80
|
-
const retryAt = parseRetryAfterHttpDateMs(trimmed);
|
|
81
|
-
return retryAt === void 0 ? fallbackMs : clampTimerTimeoutMs(retryAt - Date.now(), 0) ?? fallbackMs;
|
|
41
|
+
function sleep(ms, signal) {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
if (signal?.aborted) {
|
|
44
|
+
reject(/* @__PURE__ */ new Error("Request was aborted"));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const timeout = setTimeout(resolve, ms);
|
|
48
|
+
signal?.addEventListener("abort", () => {
|
|
49
|
+
clearTimeout(timeout);
|
|
50
|
+
reject(/* @__PURE__ */ new Error("Request was aborted"));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
82
53
|
}
|
|
83
54
|
function resolveRequestTimeoutMs(options) {
|
|
84
55
|
const timeoutMs = options?.timeoutMs;
|
|
@@ -149,7 +120,10 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
149
120
|
let body = buildRequestBody(model, context, options);
|
|
150
121
|
const nextBody = await options?.onPayload?.(body, model);
|
|
151
122
|
if (nextBody !== void 0) body = nextBody;
|
|
152
|
-
const
|
|
123
|
+
const websocketRequestId = options?.sessionId || createCodexRequestId();
|
|
124
|
+
const sseHeaders = buildSSEHeaders(modelHeaders, optionHeaders, accountId, apiKey, options?.sessionId);
|
|
125
|
+
const websocketHeaders = buildWebSocketHeaders(modelHeaders, optionHeaders, accountId, apiKey, websocketRequestId);
|
|
126
|
+
const bodyJson = JSON.stringify(body);
|
|
153
127
|
requestTimeoutMs = resolveRequestTimeoutMs(options);
|
|
154
128
|
requestTimeoutSignal = buildRequestSignal(options?.signal, requestTimeoutMs);
|
|
155
129
|
firstEventAbort = createFirstStreamEventAbortController(requestTimeoutSignal);
|
|
@@ -160,8 +134,8 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
160
134
|
};
|
|
161
135
|
const transport = options?.transport || "auto";
|
|
162
136
|
const websocketDisabledForSession = transport === "auto" && isWebSocketSseFallbackActive(options?.sessionId);
|
|
137
|
+
if (websocketDisabledForSession) recordWebSocketSseFallback(options?.sessionId);
|
|
163
138
|
if (transport !== "sse" && !websocketDisabledForSession) {
|
|
164
|
-
const websocketHeaders = buildWebSocketHeaders(modelHeaders, optionHeaders, accountId, apiKey, sessionId || createCodexRequestId());
|
|
165
139
|
let websocketStarted = false;
|
|
166
140
|
let retriedWebSocketConnectionLimit = false;
|
|
167
141
|
while (true) {
|
|
@@ -170,7 +144,7 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
170
144
|
await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
|
|
171
145
|
websocketStarted = true;
|
|
172
146
|
}, requestOptions, firstEventAbort.abort);
|
|
173
|
-
if (activeSignal?.aborted) throw
|
|
147
|
+
if (activeSignal?.aborted) throw new Error("Request was aborted");
|
|
174
148
|
stream.push({
|
|
175
149
|
type: "done",
|
|
176
150
|
reason: output.stopReason,
|
|
@@ -191,60 +165,75 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
191
165
|
fallbackTransport: transport === "auto" && !websocketStarted ? "sse" : void 0,
|
|
192
166
|
eventsEmitted: websocketStarted,
|
|
193
167
|
phase: websocketStarted ? "after_message_stream_start" : "before_message_stream_start",
|
|
194
|
-
requestBytes: new TextEncoder().encode(
|
|
168
|
+
requestBytes: new TextEncoder().encode(bodyJson).byteLength
|
|
195
169
|
}));
|
|
196
|
-
|
|
170
|
+
recordWebSocketFailure(options?.sessionId, error, { activateSseFallback: transport === "auto" });
|
|
197
171
|
if (websocketStarted || transport !== "auto") throw error;
|
|
172
|
+
recordWebSocketSseFallback(options?.sessionId);
|
|
198
173
|
break;
|
|
199
174
|
}
|
|
200
175
|
}
|
|
201
176
|
}
|
|
202
|
-
const sseHeaders = buildSSEHeaders(modelHeaders, optionHeaders, accountId, apiKey, sessionId);
|
|
203
|
-
const bodyJson = JSON.stringify(body);
|
|
204
177
|
const compressedBody = model.provider === "openai" && !sseHeaders.has("content-encoding") ? compressRequestBodyZstd(bodyJson) : null;
|
|
205
178
|
if (compressedBody) sseHeaders.set("content-encoding", "zstd");
|
|
206
179
|
const sseBody = compressedBody ?? bodyJson;
|
|
207
180
|
let response;
|
|
208
181
|
let lastError;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (activeSignal?.aborted) throw transportAbortError(activeSignal);
|
|
212
|
-
let attemptResponse;
|
|
213
|
-
let errorText;
|
|
182
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
183
|
+
if (activeSignal?.aborted) throw new Error("Request was aborted");
|
|
214
184
|
try {
|
|
215
|
-
|
|
185
|
+
response = await fetch(resolveCodexUrl(model.baseUrl), {
|
|
216
186
|
method: "POST",
|
|
217
187
|
headers: sseHeaders,
|
|
218
188
|
body: sseBody,
|
|
219
189
|
signal: activeSignal
|
|
220
190
|
});
|
|
221
|
-
response = attemptResponse;
|
|
222
191
|
await options?.onResponse?.({
|
|
223
|
-
status:
|
|
224
|
-
headers: headersToRecord(
|
|
192
|
+
status: response.status,
|
|
193
|
+
headers: headersToRecord(response.headers)
|
|
225
194
|
}, model);
|
|
226
|
-
if (
|
|
227
|
-
errorText = await readChatGptResponsesErrorTextLimited(
|
|
195
|
+
if (response.ok) break;
|
|
196
|
+
const errorText = await readChatGptResponsesErrorTextLimited(response);
|
|
197
|
+
if (attempt < MAX_RETRIES && isRetryableError(response.status, errorText)) {
|
|
198
|
+
let delayMs = BASE_DELAY_MS * 2 ** attempt;
|
|
199
|
+
const retryAfterMs = response.headers.get("retry-after-ms");
|
|
200
|
+
if (retryAfterMs !== null) {
|
|
201
|
+
const trimmedRetryAfterMs = retryAfterMs.trim();
|
|
202
|
+
const millis = Number(trimmedRetryAfterMs);
|
|
203
|
+
if (/^\d+(?:\.\d+)?$/.test(trimmedRetryAfterMs) && Number.isFinite(millis)) delayMs = clampTimerTimeoutMs(millis, 0) ?? delayMs;
|
|
204
|
+
} else {
|
|
205
|
+
const retryAfter = response.headers.get("retry-after");
|
|
206
|
+
if (retryAfter) {
|
|
207
|
+
const trimmedRetryAfter = retryAfter.trim();
|
|
208
|
+
const seconds = Number(trimmedRetryAfter);
|
|
209
|
+
if (/^\d+$/.test(trimmedRetryAfter) && Number.isFinite(seconds)) delayMs = clampTimerTimeoutMs(seconds * 1e3, 0) ?? delayMs;
|
|
210
|
+
else if (RETRY_AFTER_HTTP_DATE_RE.test(trimmedRetryAfter)) {
|
|
211
|
+
const date = Date.parse(trimmedRetryAfter);
|
|
212
|
+
if (!Number.isNaN(date)) delayMs = clampTimerTimeoutMs(date - Date.now(), 0) ?? delayMs;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
await sleep(delayMs, activeSignal);
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const info = await parseErrorResponse(new Response(errorText, {
|
|
220
|
+
status: response.status,
|
|
221
|
+
statusText: response.statusText
|
|
222
|
+
}));
|
|
223
|
+
throw new Error(info.friendlyMessage || info.message);
|
|
228
224
|
} catch (error) {
|
|
229
225
|
if (error instanceof Error) {
|
|
230
226
|
if (isRequestTimeoutError(error, options?.signal, requestTimeoutSignal, requestTimeoutMs) && requestTimeoutMs !== void 0) throw formatRequestTimeoutError(requestTimeoutMs, error);
|
|
231
227
|
if (error.name === "AbortError" || error.message === "Request was aborted") throw new Error("Request was aborted", { cause: error });
|
|
232
228
|
if (error.name === "TimeoutError" && requestTimeoutMs !== void 0) throw new Error(`Request timed out after ${requestTimeoutMs}ms`, { cause: error });
|
|
233
229
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
await sleepWithAbort(BASE_DELAY_MS * 2 ** attempt, activeSignal);
|
|
230
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
231
|
+
if (attempt < MAX_RETRIES && !lastError.message.includes("usage limit")) {
|
|
232
|
+
await sleep(BASE_DELAY_MS * 2 ** attempt, activeSignal);
|
|
238
233
|
continue;
|
|
239
234
|
}
|
|
240
235
|
throw lastError;
|
|
241
236
|
}
|
|
242
|
-
if (attempt < maxRetries && isRetryableError(attemptResponse.status, errorText)) {
|
|
243
|
-
await sleepWithAbort(resolveHttpRetryDelayMs(attemptResponse, attempt), activeSignal);
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
const info = parseErrorResponseText(errorText, attemptResponse.status, attemptResponse.statusText);
|
|
247
|
-
throw new Error(info.friendlyMessage || info.message);
|
|
248
237
|
}
|
|
249
238
|
if (!response?.ok) throw lastError ?? /* @__PURE__ */ new Error("Failed after retries");
|
|
250
239
|
if (!response.body) throw new Error("No response body");
|
|
@@ -253,7 +242,7 @@ const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
253
242
|
partial: output
|
|
254
243
|
});
|
|
255
244
|
await processStream(response, output, stream, model, options, firstEventAbort.abort);
|
|
256
|
-
if (activeSignal?.aborted) throw
|
|
245
|
+
if (activeSignal?.aborted) throw new Error("Request was aborted");
|
|
257
246
|
stream.push({
|
|
258
247
|
type: "done",
|
|
259
248
|
reason: output.stopReason,
|
|
@@ -303,7 +292,7 @@ function buildRequestBody(model, context, options) {
|
|
|
303
292
|
tool_choice: "auto",
|
|
304
293
|
parallel_tool_calls: true
|
|
305
294
|
};
|
|
306
|
-
if (options?.temperature !== void 0
|
|
295
|
+
if (options?.temperature !== void 0) body.temperature = options.temperature;
|
|
307
296
|
if (options?.serviceTier !== void 0) body.service_tier = options.serviceTier;
|
|
308
297
|
if (context.tools) {
|
|
309
298
|
const converted = convertResponsesToolPayload(context.tools, { strict: null });
|
|
@@ -363,7 +352,6 @@ async function processStream(response, output, stream, model, options, abortFirs
|
|
|
363
352
|
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
364
353
|
abortFirstEventStream,
|
|
365
354
|
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
366
|
-
signal: options?.signal,
|
|
367
355
|
resolveServiceTier: resolveCodexServiceTier,
|
|
368
356
|
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
|
|
369
357
|
});
|
|
@@ -485,10 +473,36 @@ const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
|
|
|
485
473
|
const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
|
|
486
474
|
const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
|
|
487
475
|
const websocketSessionCache = /* @__PURE__ */ new Map();
|
|
476
|
+
const websocketDebugStats = /* @__PURE__ */ new Map();
|
|
488
477
|
const websocketSseFallbackSessions = /* @__PURE__ */ new Set();
|
|
489
478
|
let cachedWebsocket = null;
|
|
490
|
-
function
|
|
479
|
+
function getOrCreateWebSocketDebugStats(sessionId) {
|
|
480
|
+
let stats = websocketDebugStats.get(sessionId);
|
|
481
|
+
if (!stats) {
|
|
482
|
+
stats = {
|
|
483
|
+
requests: 0,
|
|
484
|
+
connectionsCreated: 0,
|
|
485
|
+
connectionsReused: 0,
|
|
486
|
+
cachedContextRequests: 0,
|
|
487
|
+
storeTrueRequests: 0,
|
|
488
|
+
fullContextRequests: 0,
|
|
489
|
+
deltaRequests: 0,
|
|
490
|
+
lastInputItems: 0,
|
|
491
|
+
websocketFailures: 0,
|
|
492
|
+
sseFallbacks: 0
|
|
493
|
+
};
|
|
494
|
+
websocketDebugStats.set(sessionId, stats);
|
|
495
|
+
}
|
|
496
|
+
return stats;
|
|
497
|
+
}
|
|
498
|
+
function resetOpenAICodexWebSocketDebugStats(sessionId) {
|
|
491
499
|
cachedWebsocket = null;
|
|
500
|
+
if (sessionId) {
|
|
501
|
+
websocketDebugStats.delete(sessionId);
|
|
502
|
+
websocketSseFallbackSessions.delete(sessionId);
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
websocketDebugStats.clear();
|
|
492
506
|
websocketSseFallbackSessions.clear();
|
|
493
507
|
}
|
|
494
508
|
function closeOpenAICodexWebSocketSessions(sessionId) {
|
|
@@ -509,6 +523,20 @@ registerSessionResourceCleanup(closeOpenAICodexWebSocketSessions);
|
|
|
509
523
|
function isWebSocketSseFallbackActive(sessionId) {
|
|
510
524
|
return sessionId ? websocketSseFallbackSessions.has(sessionId) : false;
|
|
511
525
|
}
|
|
526
|
+
function recordWebSocketSseFallback(sessionId) {
|
|
527
|
+
if (!sessionId) return;
|
|
528
|
+
const stats = getOrCreateWebSocketDebugStats(sessionId);
|
|
529
|
+
stats.sseFallbacks++;
|
|
530
|
+
stats.websocketFallbackActive = isWebSocketSseFallbackActive(sessionId);
|
|
531
|
+
}
|
|
532
|
+
function recordWebSocketFailure(sessionId, error, options) {
|
|
533
|
+
if (!sessionId) return;
|
|
534
|
+
if (options.activateSseFallback) websocketSseFallbackSessions.add(sessionId);
|
|
535
|
+
const stats = getOrCreateWebSocketDebugStats(sessionId);
|
|
536
|
+
stats.websocketFailures++;
|
|
537
|
+
stats.lastWebSocketError = formatThrownValue(error);
|
|
538
|
+
stats.websocketFallbackActive = isWebSocketSseFallbackActive(sessionId);
|
|
539
|
+
}
|
|
512
540
|
async function getWebSocketConstructor() {
|
|
513
541
|
if (cachedWebsocket) return cachedWebsocket;
|
|
514
542
|
if (process?.versions?.bun && (process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy)) {
|
|
@@ -556,15 +584,12 @@ function closeWebSocketSilently(socket, code = 1e3, reason = "done") {
|
|
|
556
584
|
socket.close(code, reason);
|
|
557
585
|
} catch {}
|
|
558
586
|
}
|
|
559
|
-
function deleteOwnedWebSocketSession(sessionId, entry) {
|
|
560
|
-
if (websocketSessionCache.get(sessionId) === entry) websocketSessionCache.delete(sessionId);
|
|
561
|
-
}
|
|
562
587
|
function scheduleSessionWebSocketExpiry(sessionId, entry) {
|
|
563
588
|
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
564
589
|
entry.idleTimer = setTimeout(() => {
|
|
565
590
|
if (entry.busy) return;
|
|
566
591
|
closeWebSocketSilently(entry.socket, 1e3, "idle_timeout");
|
|
567
|
-
|
|
592
|
+
websocketSessionCache.delete(sessionId);
|
|
568
593
|
}, SESSION_WEBSOCKET_CACHE_TTL_MS);
|
|
569
594
|
}
|
|
570
595
|
async function connectWebSocket(url, headers, signal) {
|
|
@@ -629,6 +654,7 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
629
654
|
const socket = await connectWebSocket(url, headers, signal);
|
|
630
655
|
return {
|
|
631
656
|
socket,
|
|
657
|
+
reused: false,
|
|
632
658
|
release: ({ keep } = {}) => {
|
|
633
659
|
if (keep === false) {
|
|
634
660
|
closeWebSocketSilently(socket);
|
|
@@ -652,10 +678,11 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
652
678
|
return {
|
|
653
679
|
socket: cached.socket,
|
|
654
680
|
entry: cached,
|
|
681
|
+
reused: true,
|
|
655
682
|
release: ({ keep } = {}) => {
|
|
656
683
|
if (!keep || !isWebSocketReusable(cached.socket)) {
|
|
657
684
|
closeWebSocketSilently(cached.socket);
|
|
658
|
-
|
|
685
|
+
websocketSessionCache.delete(sessionId);
|
|
659
686
|
return;
|
|
660
687
|
}
|
|
661
688
|
cached.busy = false;
|
|
@@ -667,6 +694,7 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
667
694
|
const socket = await connectWebSocket(url, headers, signal);
|
|
668
695
|
return {
|
|
669
696
|
socket,
|
|
697
|
+
reused: false,
|
|
670
698
|
release: () => {
|
|
671
699
|
closeWebSocketSilently(socket);
|
|
672
700
|
}
|
|
@@ -687,11 +715,12 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
687
715
|
return {
|
|
688
716
|
socket,
|
|
689
717
|
entry,
|
|
718
|
+
reused: false,
|
|
690
719
|
release: ({ keep } = {}) => {
|
|
691
720
|
if (!keep || !isWebSocketReusable(entry.socket)) {
|
|
692
721
|
closeWebSocketSilently(entry.socket);
|
|
693
722
|
if (entry.idleTimer) clearTimeout(entry.idleTimer);
|
|
694
|
-
|
|
723
|
+
if (websocketSessionCache.get(sessionId) === entry) websocketSessionCache.delete(sessionId);
|
|
695
724
|
return;
|
|
696
725
|
}
|
|
697
726
|
entry.busy = false;
|
|
@@ -804,10 +833,9 @@ async function* parseWebSocket(socket, signal) {
|
|
|
804
833
|
signal?.addEventListener("abort", onAbort);
|
|
805
834
|
try {
|
|
806
835
|
while (true) {
|
|
807
|
-
if (signal?.aborted) throw
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
yield next;
|
|
836
|
+
if (signal?.aborted) throw new Error("Request was aborted");
|
|
837
|
+
if (queue.length > 0) {
|
|
838
|
+
yield queue.shift();
|
|
811
839
|
continue;
|
|
812
840
|
}
|
|
813
841
|
if (done) break;
|
|
@@ -815,7 +843,7 @@ async function* parseWebSocket(socket, signal) {
|
|
|
815
843
|
pending = resolve;
|
|
816
844
|
});
|
|
817
845
|
}
|
|
818
|
-
if (failed) throw
|
|
846
|
+
if (failed) throw toLintErrorObject(failed, "Non-Error thrown");
|
|
819
847
|
if (!sawCompletion) throw new Error("WebSocket stream closed before response.completed");
|
|
820
848
|
} finally {
|
|
821
849
|
socket.removeEventListener("message", onMessage);
|
|
@@ -871,13 +899,31 @@ async function* startWebSocketOutputOnFirstEvent(events, output, stream, onStart
|
|
|
871
899
|
}
|
|
872
900
|
}
|
|
873
901
|
async function processWebSocketStream(url, body, headers, output, stream, model, onStart, options, abortFirstEventStream) {
|
|
874
|
-
const { socket, entry, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal);
|
|
902
|
+
const { socket, entry, reused, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal);
|
|
875
903
|
let keepConnection = true;
|
|
876
904
|
const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
|
|
877
905
|
const fullBody = body;
|
|
878
906
|
const requestBody = useCachedContext && entry ? buildCachedWebSocketRequestBody(entry, fullBody) : fullBody;
|
|
907
|
+
const stats = options?.sessionId ? getOrCreateWebSocketDebugStats(options.sessionId) : void 0;
|
|
908
|
+
if (stats) {
|
|
909
|
+
stats.requests++;
|
|
910
|
+
if (reused) stats.connectionsReused++;
|
|
911
|
+
else stats.connectionsCreated++;
|
|
912
|
+
if (useCachedContext) stats.cachedContextRequests++;
|
|
913
|
+
if (requestBody.store === true) stats.storeTrueRequests++;
|
|
914
|
+
stats.lastInputItems = requestBody.input?.length ?? 0;
|
|
915
|
+
if (requestBody.previous_response_id) {
|
|
916
|
+
stats.deltaRequests++;
|
|
917
|
+
stats.lastDeltaInputItems = requestBody.input?.length ?? 0;
|
|
918
|
+
stats.lastPreviousResponseId = requestBody.previous_response_id;
|
|
919
|
+
} else {
|
|
920
|
+
stats.fullContextRequests++;
|
|
921
|
+
stats.lastDeltaInputItems = void 0;
|
|
922
|
+
stats.lastPreviousResponseId = void 0;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
879
925
|
try {
|
|
880
|
-
if (options?.signal?.aborted) throw
|
|
926
|
+
if (options?.signal?.aborted) throw new Error("Request was aborted");
|
|
881
927
|
socket.send(JSON.stringify({
|
|
882
928
|
type: "response.create",
|
|
883
929
|
...requestBody
|
|
@@ -887,7 +933,6 @@ async function processWebSocketStream(url, body, headers, output, stream, model,
|
|
|
887
933
|
firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
|
|
888
934
|
abortFirstEventStream,
|
|
889
935
|
onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
|
|
890
|
-
signal: options?.signal,
|
|
891
936
|
resolveServiceTier: resolveCodexServiceTier,
|
|
892
937
|
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model)
|
|
893
938
|
});
|
|
@@ -936,7 +981,7 @@ async function readChatGptResponsesErrorTextLimited(response) {
|
|
|
936
981
|
break;
|
|
937
982
|
}
|
|
938
983
|
}
|
|
939
|
-
|
|
984
|
+
text += decoder.decode();
|
|
940
985
|
} finally {
|
|
941
986
|
if (reachedLimit) await reader.cancel().catch(() => {});
|
|
942
987
|
try {
|
|
@@ -945,14 +990,15 @@ async function readChatGptResponsesErrorTextLimited(response) {
|
|
|
945
990
|
}
|
|
946
991
|
return text;
|
|
947
992
|
}
|
|
948
|
-
function
|
|
949
|
-
|
|
993
|
+
async function parseErrorResponse(response) {
|
|
994
|
+
const raw = await readChatGptResponsesErrorTextLimited(response);
|
|
995
|
+
let message = raw || response.statusText || "Request failed";
|
|
950
996
|
let friendlyMessage;
|
|
951
997
|
try {
|
|
952
998
|
const err = JSON.parse(raw)?.error;
|
|
953
999
|
if (err) {
|
|
954
1000
|
const code = err.code || err.type || "";
|
|
955
|
-
if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || status === 429) {
|
|
1001
|
+
if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || response.status === 429) {
|
|
956
1002
|
const plan = err.plan_type ? ` (${err.plan_type.toLowerCase()} plan)` : "";
|
|
957
1003
|
const mins = err.resets_at ? Math.max(0, Math.round((err.resets_at * 1e3 - Date.now()) / 6e4)) : void 0;
|
|
958
1004
|
friendlyMessage = `You have hit your ChatGPT usage limit${plan}.${mins !== void 0 ? ` Try again in ~${mins} min.` : ""}`.trim();
|
|
@@ -1011,5 +1057,12 @@ function buildWebSocketHeaders(initHeaders, additionalHeaders, accountId, token,
|
|
|
1011
1057
|
headers.set("session_id", requestId);
|
|
1012
1058
|
return headers;
|
|
1013
1059
|
}
|
|
1060
|
+
function toLintErrorObject(value, fallbackMessage) {
|
|
1061
|
+
if (value instanceof Error) return value;
|
|
1062
|
+
if (typeof value === "string") return new Error(value);
|
|
1063
|
+
const error = new Error(fallbackMessage, { cause: value });
|
|
1064
|
+
if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
|
|
1065
|
+
return error;
|
|
1066
|
+
}
|
|
1014
1067
|
//#endregion
|
|
1015
|
-
export { closeOpenAICodexWebSocketSessions, extractOpenAICodexAccountId, parseSSEForTest,
|
|
1068
|
+
export { closeOpenAICodexWebSocketSessions, extractOpenAICodexAccountId, parseSSEForTest, resetOpenAICodexWebSocketDebugStats, streamOpenAICodexResponses, streamSimpleOpenAICodexResponses };
|