@ljoukov/llm 3.0.1 → 3.0.3
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 +17 -11
- package/dist/index.cjs +875 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -8
- package/dist/index.d.ts +34 -8
- package/dist/index.js +859 -37
- package/dist/index.js.map +1 -1
- package/package.json +9 -7
package/dist/index.js
CHANGED
|
@@ -136,7 +136,7 @@ function getGeminiProPricing(modelId) {
|
|
|
136
136
|
if (modelId.includes("gemini-2.5-pro")) {
|
|
137
137
|
return GEMINI_2_5_PRO_PRICING;
|
|
138
138
|
}
|
|
139
|
-
if (modelId.includes("gemini-3-pro")) {
|
|
139
|
+
if (modelId.includes("gemini-3-pro") || modelId.includes("gemini-3.1-pro")) {
|
|
140
140
|
return GEMINI_3_PRO_PREVIEW_PRICING;
|
|
141
141
|
}
|
|
142
142
|
return void 0;
|
|
@@ -171,9 +171,6 @@ function getOpenAiPricing(modelId) {
|
|
|
171
171
|
if (modelId.includes("gpt-5.3-codex")) {
|
|
172
172
|
return OPENAI_GPT_53_CODEX_PRICING;
|
|
173
173
|
}
|
|
174
|
-
if (modelId.includes("gpt-5-codex")) {
|
|
175
|
-
return OPENAI_GPT_53_CODEX_PRICING;
|
|
176
|
-
}
|
|
177
174
|
if (modelId.includes("gpt-5.2")) {
|
|
178
175
|
return OPENAI_GPT_52_PRICING;
|
|
179
176
|
}
|
|
@@ -336,8 +333,6 @@ function parseEnvLine(line) {
|
|
|
336
333
|
// src/openai/chatgpt-auth.ts
|
|
337
334
|
var CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_URL";
|
|
338
335
|
var CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_STORE";
|
|
339
|
-
var CHATGPT_AUTH_SERVER_URL_ENV = "CHATGPT_AUTH_SERVER_URL";
|
|
340
|
-
var CHATGPT_AUTH_SERVER_STORE_ENV = "CHATGPT_AUTH_SERVER_STORE";
|
|
341
336
|
var CHATGPT_AUTH_API_KEY_ENV = "CHATGPT_AUTH_API_KEY";
|
|
342
337
|
var CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV = "CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY";
|
|
343
338
|
var CHATGPT_OAUTH_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
@@ -469,7 +464,7 @@ async function refreshChatGptOauthToken(refreshToken, fallback) {
|
|
|
469
464
|
}
|
|
470
465
|
async function getChatGptAuthProfile() {
|
|
471
466
|
loadLocalEnv();
|
|
472
|
-
const tokenProviderUrl = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV]
|
|
467
|
+
const tokenProviderUrl = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_URL_ENV];
|
|
473
468
|
const tokenProviderKey = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_API_KEY_ENV] ?? process.env[CHATGPT_AUTH_API_KEY_ENV];
|
|
474
469
|
if (tokenProviderUrl && tokenProviderUrl.trim().length > 0 && tokenProviderKey && tokenProviderKey.trim().length > 0) {
|
|
475
470
|
if (cachedProfile && !isExpired(cachedProfile)) {
|
|
@@ -480,7 +475,7 @@ async function getChatGptAuthProfile() {
|
|
|
480
475
|
}
|
|
481
476
|
refreshPromise = (async () => {
|
|
482
477
|
try {
|
|
483
|
-
const store = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV]
|
|
478
|
+
const store = process.env[CHATGPT_AUTH_TOKEN_PROVIDER_STORE_ENV];
|
|
484
479
|
const profile = await fetchChatGptAuthProfileFromTokenProvider({
|
|
485
480
|
baseUrl: tokenProviderUrl,
|
|
486
481
|
apiKey: tokenProviderKey,
|
|
@@ -683,22 +678,624 @@ function extractChatGptAccountId(token) {
|
|
|
683
678
|
return typeof namespaced === "string" && namespaced.length > 0 ? namespaced : void 0;
|
|
684
679
|
}
|
|
685
680
|
|
|
681
|
+
// src/openai/responses-websocket.ts
|
|
682
|
+
import WebSocket from "ws";
|
|
683
|
+
var OPENAI_BETA_RESPONSES_WEBSOCKETS_V2 = "responses_websockets=2026-02-06";
|
|
684
|
+
var ResponsesWebSocketHttpError = class extends Error {
|
|
685
|
+
status;
|
|
686
|
+
body;
|
|
687
|
+
headers;
|
|
688
|
+
constructor(options) {
|
|
689
|
+
super(options.message);
|
|
690
|
+
this.name = "ResponsesWebSocketHttpError";
|
|
691
|
+
this.status = options.status;
|
|
692
|
+
this.body = options.body;
|
|
693
|
+
this.headers = options.headers;
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
var UNSUPPORTED_WEBSOCKET_STATUS_CODES = /* @__PURE__ */ new Set([400, 404, 405, 406, 426, 501]);
|
|
697
|
+
var WEBSOCKET_CONNECT_TIMEOUT_MS = 3e4;
|
|
698
|
+
function parseUnexpectedServerResponseStatus(message) {
|
|
699
|
+
const match = /unexpected server response:\s*(\d+)/i.exec(message);
|
|
700
|
+
if (!match) {
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
const status = Number(match[1]);
|
|
704
|
+
if (!Number.isFinite(status) || status <= 0) {
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
return status;
|
|
708
|
+
}
|
|
709
|
+
function supportsUnexpectedResponseEvent() {
|
|
710
|
+
return !("bun" in process.versions);
|
|
711
|
+
}
|
|
712
|
+
function resolveResponsesWebSocketMode(raw, fallback = "auto") {
|
|
713
|
+
const value = raw?.trim().toLowerCase();
|
|
714
|
+
if (value === "auto" || value === "off" || value === "only") {
|
|
715
|
+
return value;
|
|
716
|
+
}
|
|
717
|
+
return fallback;
|
|
718
|
+
}
|
|
719
|
+
function mergeOpenAiBetaHeader(existing, required) {
|
|
720
|
+
const parts = /* @__PURE__ */ new Set();
|
|
721
|
+
for (const part of (existing ?? "").split(",")) {
|
|
722
|
+
const trimmed = part.trim();
|
|
723
|
+
if (trimmed.length > 0) {
|
|
724
|
+
parts.add(trimmed);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const normalizedRequired = required.trim();
|
|
728
|
+
if (normalizedRequired.length > 0) {
|
|
729
|
+
parts.add(normalizedRequired);
|
|
730
|
+
}
|
|
731
|
+
return Array.from(parts).join(", ");
|
|
732
|
+
}
|
|
733
|
+
function toWebSocketUrl(httpOrHttpsUrl) {
|
|
734
|
+
const parsed = new URL(httpOrHttpsUrl);
|
|
735
|
+
if (parsed.protocol === "https:") {
|
|
736
|
+
parsed.protocol = "wss:";
|
|
737
|
+
} else if (parsed.protocol === "http:") {
|
|
738
|
+
parsed.protocol = "ws:";
|
|
739
|
+
} else if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
|
|
740
|
+
throw new Error(`Unsupported websocket URL protocol: ${parsed.protocol}`);
|
|
741
|
+
}
|
|
742
|
+
return parsed.toString();
|
|
743
|
+
}
|
|
744
|
+
function isResponsesWebSocketUnsupportedError(error) {
|
|
745
|
+
if (error instanceof ResponsesWebSocketHttpError) {
|
|
746
|
+
return UNSUPPORTED_WEBSOCKET_STATUS_CODES.has(error.status);
|
|
747
|
+
}
|
|
748
|
+
const message = error instanceof Error ? error.message.toLowerCase() : "";
|
|
749
|
+
const status = parseUnexpectedServerResponseStatus(message);
|
|
750
|
+
if (status !== null) {
|
|
751
|
+
return UNSUPPORTED_WEBSOCKET_STATUS_CODES.has(status);
|
|
752
|
+
}
|
|
753
|
+
return message.includes("unexpected server response: 426");
|
|
754
|
+
}
|
|
755
|
+
function createAdaptiveResponsesStream(options) {
|
|
756
|
+
let resolved = null;
|
|
757
|
+
let websocketSelected = false;
|
|
758
|
+
let fallbackSelected = false;
|
|
759
|
+
const activateFallback = (error) => {
|
|
760
|
+
options.onWebSocketFallback?.(error);
|
|
761
|
+
fallbackSelected = true;
|
|
762
|
+
websocketSelected = false;
|
|
763
|
+
const fallback = options.createFallbackStream();
|
|
764
|
+
resolved = Promise.resolve(fallback);
|
|
765
|
+
return fallback;
|
|
766
|
+
};
|
|
767
|
+
const getStream = async () => {
|
|
768
|
+
if (resolved) {
|
|
769
|
+
return await resolved;
|
|
770
|
+
}
|
|
771
|
+
resolved = (async () => {
|
|
772
|
+
if (options.mode === "off") {
|
|
773
|
+
fallbackSelected = true;
|
|
774
|
+
return options.createFallbackStream();
|
|
775
|
+
}
|
|
776
|
+
try {
|
|
777
|
+
const stream = await options.createWebSocketStream();
|
|
778
|
+
websocketSelected = true;
|
|
779
|
+
return stream;
|
|
780
|
+
} catch (error) {
|
|
781
|
+
if (options.mode === "only") {
|
|
782
|
+
throw error;
|
|
783
|
+
}
|
|
784
|
+
return activateFallback(error);
|
|
785
|
+
}
|
|
786
|
+
})();
|
|
787
|
+
return await resolved;
|
|
788
|
+
};
|
|
789
|
+
return {
|
|
790
|
+
async *[Symbol.asyncIterator]() {
|
|
791
|
+
const stream = await getStream();
|
|
792
|
+
let yielded = 0;
|
|
793
|
+
try {
|
|
794
|
+
for await (const event of stream) {
|
|
795
|
+
yielded += 1;
|
|
796
|
+
yield event;
|
|
797
|
+
}
|
|
798
|
+
} catch (error) {
|
|
799
|
+
if (options.mode !== "only" && websocketSelected && !fallbackSelected && yielded === 0) {
|
|
800
|
+
const fallback = activateFallback(error);
|
|
801
|
+
for await (const event of fallback) {
|
|
802
|
+
yield event;
|
|
803
|
+
}
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
throw error;
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
async finalResponse() {
|
|
810
|
+
const stream = await getStream();
|
|
811
|
+
try {
|
|
812
|
+
return await stream.finalResponse();
|
|
813
|
+
} catch (error) {
|
|
814
|
+
if (options.mode === "only" || !websocketSelected || fallbackSelected) {
|
|
815
|
+
throw error;
|
|
816
|
+
}
|
|
817
|
+
const fallback = activateFallback(error);
|
|
818
|
+
return await fallback.finalResponse();
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
close() {
|
|
822
|
+
void getStream().then((stream) => stream.close()).catch(() => {
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
async function createResponsesWebSocketStream(options) {
|
|
828
|
+
const completionTypes = new Set(
|
|
829
|
+
options.completionEventTypes ?? ["response.completed", "response.failed", "response.done"]
|
|
830
|
+
);
|
|
831
|
+
const { socket, responseHeaders } = await connectWebSocket({
|
|
832
|
+
url: options.url,
|
|
833
|
+
headers: options.headers,
|
|
834
|
+
signal: options.signal
|
|
835
|
+
});
|
|
836
|
+
const queue = createAsyncQueue();
|
|
837
|
+
let settled = false;
|
|
838
|
+
let finalResponse = null;
|
|
839
|
+
let latestResponse = null;
|
|
840
|
+
let idleTimer = null;
|
|
841
|
+
let resolveFinal = null;
|
|
842
|
+
let rejectFinal = null;
|
|
843
|
+
const finalPromise = new Promise((resolve, reject) => {
|
|
844
|
+
resolveFinal = resolve;
|
|
845
|
+
rejectFinal = reject;
|
|
846
|
+
});
|
|
847
|
+
void finalPromise.catch(() => {
|
|
848
|
+
});
|
|
849
|
+
const clearIdleTimer = () => {
|
|
850
|
+
if (idleTimer) {
|
|
851
|
+
clearTimeout(idleTimer);
|
|
852
|
+
idleTimer = null;
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
const closeSocket = () => {
|
|
856
|
+
try {
|
|
857
|
+
if (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING) {
|
|
858
|
+
socket.close();
|
|
859
|
+
}
|
|
860
|
+
} catch {
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
const complete = (response) => {
|
|
864
|
+
if (settled) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
settled = true;
|
|
868
|
+
clearIdleTimer();
|
|
869
|
+
finalResponse = response;
|
|
870
|
+
resolveFinal?.(response);
|
|
871
|
+
queue.close();
|
|
872
|
+
closeSocket();
|
|
873
|
+
};
|
|
874
|
+
const fail = (error) => {
|
|
875
|
+
if (settled) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
settled = true;
|
|
879
|
+
clearIdleTimer();
|
|
880
|
+
rejectFinal?.(error);
|
|
881
|
+
queue.fail(error);
|
|
882
|
+
closeSocket();
|
|
883
|
+
};
|
|
884
|
+
const restartIdleTimer = () => {
|
|
885
|
+
clearIdleTimer();
|
|
886
|
+
const idleTimeoutMs = options.idleTimeoutMs;
|
|
887
|
+
if (!idleTimeoutMs || idleTimeoutMs <= 0 || settled) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
idleTimer = setTimeout(() => {
|
|
891
|
+
fail(new Error(`Responses WebSocket idle timeout after ${idleTimeoutMs}ms.`));
|
|
892
|
+
}, idleTimeoutMs);
|
|
893
|
+
};
|
|
894
|
+
const onAbort = () => {
|
|
895
|
+
const error = createAbortError(options.signal?.reason);
|
|
896
|
+
fail(error);
|
|
897
|
+
};
|
|
898
|
+
if (options.signal) {
|
|
899
|
+
if (options.signal.aborted) {
|
|
900
|
+
socket.close();
|
|
901
|
+
throw createAbortError(options.signal.reason);
|
|
902
|
+
}
|
|
903
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
904
|
+
}
|
|
905
|
+
const cleanup = () => {
|
|
906
|
+
clearIdleTimer();
|
|
907
|
+
socket.removeAllListeners();
|
|
908
|
+
if (options.signal) {
|
|
909
|
+
options.signal.removeEventListener("abort", onAbort);
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
socket.on("message", (raw) => {
|
|
913
|
+
restartIdleTimer();
|
|
914
|
+
const parsed = parseWebSocketPayload(raw);
|
|
915
|
+
if (!parsed) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const error = mapWebSocketErrorEvent(parsed);
|
|
919
|
+
if (error) {
|
|
920
|
+
fail(error);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
const event = parsed;
|
|
924
|
+
if (isObjectWithResponse(event)) {
|
|
925
|
+
latestResponse = event.response;
|
|
926
|
+
}
|
|
927
|
+
queue.push(event);
|
|
928
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
929
|
+
if (completionTypes.has(type)) {
|
|
930
|
+
const completedResponse = normalizeFinalResponse(
|
|
931
|
+
type,
|
|
932
|
+
event.response,
|
|
933
|
+
latestResponse,
|
|
934
|
+
responseHeaders
|
|
935
|
+
);
|
|
936
|
+
complete(completedResponse);
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
socket.on("error", (error) => {
|
|
940
|
+
fail(new Error(`Responses WebSocket error: ${error.message}`));
|
|
941
|
+
});
|
|
942
|
+
socket.on("close", (_code, _reason) => {
|
|
943
|
+
if (settled) {
|
|
944
|
+
cleanup();
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
fail(new Error("Responses WebSocket closed before completion."));
|
|
948
|
+
cleanup();
|
|
949
|
+
});
|
|
950
|
+
restartIdleTimer();
|
|
951
|
+
const payload = serializeRequestPayload(options.request);
|
|
952
|
+
await new Promise((resolve, reject) => {
|
|
953
|
+
socket.send(payload, (error) => {
|
|
954
|
+
if (error) {
|
|
955
|
+
reject(error);
|
|
956
|
+
} else {
|
|
957
|
+
resolve();
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
}).catch((error) => {
|
|
961
|
+
fail(new Error(`Failed to send Responses WebSocket request: ${errorToMessage(error)}`));
|
|
962
|
+
throw error instanceof Error ? error : new Error(errorToMessage(error));
|
|
963
|
+
});
|
|
964
|
+
return {
|
|
965
|
+
async *[Symbol.asyncIterator]() {
|
|
966
|
+
try {
|
|
967
|
+
for await (const event of queue.iterable) {
|
|
968
|
+
yield event;
|
|
969
|
+
}
|
|
970
|
+
} finally {
|
|
971
|
+
if (!settled) {
|
|
972
|
+
closeSocket();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
},
|
|
976
|
+
async finalResponse() {
|
|
977
|
+
return await finalPromise;
|
|
978
|
+
},
|
|
979
|
+
close() {
|
|
980
|
+
if (settled) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const response = finalResponse ?? latestResponse ?? { status: "cancelled" };
|
|
984
|
+
complete(response);
|
|
985
|
+
cleanup();
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
async function connectWebSocket(options) {
|
|
990
|
+
return await new Promise((resolve, reject) => {
|
|
991
|
+
const shouldListenForUnexpectedResponse = supportsUnexpectedResponseEvent();
|
|
992
|
+
const socket = new WebSocket(options.url, {
|
|
993
|
+
headers: options.headers,
|
|
994
|
+
handshakeTimeout: WEBSOCKET_CONNECT_TIMEOUT_MS
|
|
995
|
+
});
|
|
996
|
+
let settled = false;
|
|
997
|
+
let responseBody = "";
|
|
998
|
+
let connectTimeout = setTimeout(() => {
|
|
999
|
+
rejectOnce(
|
|
1000
|
+
new Error(
|
|
1001
|
+
`Responses WebSocket connection timed out after ${WEBSOCKET_CONNECT_TIMEOUT_MS}ms.`
|
|
1002
|
+
)
|
|
1003
|
+
);
|
|
1004
|
+
}, WEBSOCKET_CONNECT_TIMEOUT_MS);
|
|
1005
|
+
const rejectOnce = (error) => {
|
|
1006
|
+
if (settled) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
settled = true;
|
|
1010
|
+
cleanup();
|
|
1011
|
+
try {
|
|
1012
|
+
socket.terminate();
|
|
1013
|
+
} catch {
|
|
1014
|
+
}
|
|
1015
|
+
reject(error);
|
|
1016
|
+
};
|
|
1017
|
+
const resolveOnce = (result) => {
|
|
1018
|
+
if (settled) {
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
settled = true;
|
|
1022
|
+
cleanup(false);
|
|
1023
|
+
resolve(result);
|
|
1024
|
+
};
|
|
1025
|
+
const onAbort = () => {
|
|
1026
|
+
rejectOnce(createAbortError(options.signal?.reason));
|
|
1027
|
+
};
|
|
1028
|
+
const cleanup = (removeAbortListener = true) => {
|
|
1029
|
+
if (connectTimeout) {
|
|
1030
|
+
clearTimeout(connectTimeout);
|
|
1031
|
+
connectTimeout = null;
|
|
1032
|
+
}
|
|
1033
|
+
socket.removeListener("open", onOpen);
|
|
1034
|
+
socket.removeListener("error", onError);
|
|
1035
|
+
if (shouldListenForUnexpectedResponse) {
|
|
1036
|
+
socket.removeListener("unexpected-response", onUnexpectedResponse);
|
|
1037
|
+
}
|
|
1038
|
+
if (removeAbortListener && options.signal) {
|
|
1039
|
+
options.signal.removeEventListener("abort", onAbort);
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
const onOpen = () => {
|
|
1043
|
+
const headers = normalizeUpgradeHeaders(socket);
|
|
1044
|
+
resolveOnce({ socket, responseHeaders: headers });
|
|
1045
|
+
};
|
|
1046
|
+
const onError = (error) => {
|
|
1047
|
+
rejectOnce(new Error(`Responses WebSocket connection failed: ${error.message}`));
|
|
1048
|
+
};
|
|
1049
|
+
const onUnexpectedResponse = (_request, response) => {
|
|
1050
|
+
if (typeof response.setEncoding === "function") {
|
|
1051
|
+
response.setEncoding("utf8");
|
|
1052
|
+
}
|
|
1053
|
+
response.on("data", (chunk) => {
|
|
1054
|
+
responseBody += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
1055
|
+
});
|
|
1056
|
+
response.on("end", () => {
|
|
1057
|
+
const status = Number(response.statusCode ?? 0);
|
|
1058
|
+
const headers = {};
|
|
1059
|
+
const rawHeaders = response.headers ?? {};
|
|
1060
|
+
for (const [key, value] of Object.entries(rawHeaders)) {
|
|
1061
|
+
if (typeof value === "string") {
|
|
1062
|
+
headers[key] = value;
|
|
1063
|
+
} else if (Array.isArray(value)) {
|
|
1064
|
+
headers[key] = value.join(", ");
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
rejectOnce(
|
|
1068
|
+
new ResponsesWebSocketHttpError({
|
|
1069
|
+
status: Number.isFinite(status) && status > 0 ? status : 500,
|
|
1070
|
+
message: `Responses WebSocket upgrade failed${status ? ` (${status})` : ""}.`,
|
|
1071
|
+
body: responseBody || void 0,
|
|
1072
|
+
headers
|
|
1073
|
+
})
|
|
1074
|
+
);
|
|
1075
|
+
});
|
|
1076
|
+
response.on("error", (error) => {
|
|
1077
|
+
rejectOnce(
|
|
1078
|
+
new ResponsesWebSocketHttpError({
|
|
1079
|
+
status: Number(response.statusCode ?? 500),
|
|
1080
|
+
message: `Responses WebSocket upgrade failed: ${error.message}`,
|
|
1081
|
+
body: responseBody || void 0
|
|
1082
|
+
})
|
|
1083
|
+
);
|
|
1084
|
+
});
|
|
1085
|
+
};
|
|
1086
|
+
socket.once("open", onOpen);
|
|
1087
|
+
socket.once("error", onError);
|
|
1088
|
+
if (shouldListenForUnexpectedResponse) {
|
|
1089
|
+
socket.once("unexpected-response", onUnexpectedResponse);
|
|
1090
|
+
}
|
|
1091
|
+
if (options.signal) {
|
|
1092
|
+
if (options.signal.aborted) {
|
|
1093
|
+
onAbort();
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
1097
|
+
}
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
function normalizeUpgradeHeaders(socket) {
|
|
1101
|
+
const maybeUpgradeResponse = socket._req?.res;
|
|
1102
|
+
const raw = maybeUpgradeResponse?.headers ?? {};
|
|
1103
|
+
const normalized = {};
|
|
1104
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
1105
|
+
if (typeof value === "string") {
|
|
1106
|
+
normalized[key.toLowerCase()] = value;
|
|
1107
|
+
} else if (Array.isArray(value)) {
|
|
1108
|
+
normalized[key.toLowerCase()] = value.join(", ");
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return normalized;
|
|
1112
|
+
}
|
|
1113
|
+
function parseWebSocketPayload(raw) {
|
|
1114
|
+
const text = toUtf8(raw);
|
|
1115
|
+
if (!text) {
|
|
1116
|
+
return null;
|
|
1117
|
+
}
|
|
1118
|
+
try {
|
|
1119
|
+
const parsed = JSON.parse(text);
|
|
1120
|
+
if (parsed && typeof parsed === "object") {
|
|
1121
|
+
return parsed;
|
|
1122
|
+
}
|
|
1123
|
+
} catch {
|
|
1124
|
+
return null;
|
|
1125
|
+
}
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
function toUtf8(raw) {
|
|
1129
|
+
if (typeof raw === "string") {
|
|
1130
|
+
return raw;
|
|
1131
|
+
}
|
|
1132
|
+
if (raw instanceof ArrayBuffer) {
|
|
1133
|
+
return Buffer.from(raw).toString("utf8");
|
|
1134
|
+
}
|
|
1135
|
+
if (Array.isArray(raw)) {
|
|
1136
|
+
const chunks = raw.map((chunk) => {
|
|
1137
|
+
if (typeof chunk === "string") {
|
|
1138
|
+
return Buffer.from(chunk, "utf8");
|
|
1139
|
+
}
|
|
1140
|
+
return Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1141
|
+
});
|
|
1142
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
1143
|
+
}
|
|
1144
|
+
return Buffer.isBuffer(raw) ? raw.toString("utf8") : Buffer.from(raw).toString("utf8");
|
|
1145
|
+
}
|
|
1146
|
+
function mapWebSocketErrorEvent(payload) {
|
|
1147
|
+
if (payload.type !== "error") {
|
|
1148
|
+
return null;
|
|
1149
|
+
}
|
|
1150
|
+
const status = resolveNumericStatus(payload.status) ?? resolveNumericStatus(payload.status_code);
|
|
1151
|
+
if (!status || status < 400) {
|
|
1152
|
+
const message = errorToMessage(payload.error) || "Responses WebSocket returned an error event.";
|
|
1153
|
+
return new Error(message);
|
|
1154
|
+
}
|
|
1155
|
+
const headers = mapErrorHeaders(payload.headers);
|
|
1156
|
+
const body = payload.error && typeof payload.error === "object" ? JSON.stringify({ error: payload.error }, null, 2) : void 0;
|
|
1157
|
+
return new ResponsesWebSocketHttpError({
|
|
1158
|
+
status,
|
|
1159
|
+
message: `Responses WebSocket returned status ${status}.`,
|
|
1160
|
+
body,
|
|
1161
|
+
headers
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
function mapErrorHeaders(value) {
|
|
1165
|
+
if (!value || typeof value !== "object") {
|
|
1166
|
+
return void 0;
|
|
1167
|
+
}
|
|
1168
|
+
const headers = {};
|
|
1169
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1170
|
+
if (typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean") {
|
|
1171
|
+
headers[key.toLowerCase()] = String(entry);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
1175
|
+
}
|
|
1176
|
+
function resolveNumericStatus(value) {
|
|
1177
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1178
|
+
return Math.floor(value);
|
|
1179
|
+
}
|
|
1180
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
1181
|
+
const parsed = Number(value);
|
|
1182
|
+
if (Number.isFinite(parsed)) {
|
|
1183
|
+
return Math.floor(parsed);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
return null;
|
|
1187
|
+
}
|
|
1188
|
+
function normalizeFinalResponse(eventType, eventResponse, latestResponse, responseHeaders) {
|
|
1189
|
+
const response = eventResponse && typeof eventResponse === "object" ? { ...eventResponse } : latestResponse ? { ...latestResponse } : {};
|
|
1190
|
+
if (typeof response.status !== "string") {
|
|
1191
|
+
if (eventType === "response.failed") {
|
|
1192
|
+
response.status = "failed";
|
|
1193
|
+
} else if (eventType === "response.done") {
|
|
1194
|
+
response.status = "completed";
|
|
1195
|
+
} else if (eventType === "response.completed") {
|
|
1196
|
+
response.status = "completed";
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
const upgradeModel = responseHeaders["openai-model"];
|
|
1200
|
+
if (typeof response.model !== "string" && upgradeModel) {
|
|
1201
|
+
response.model = upgradeModel;
|
|
1202
|
+
}
|
|
1203
|
+
return response;
|
|
1204
|
+
}
|
|
1205
|
+
function serializeRequestPayload(request) {
|
|
1206
|
+
if (!request || typeof request !== "object" || Array.isArray(request)) {
|
|
1207
|
+
throw new Error("Responses WebSocket request must be a JSON object.");
|
|
1208
|
+
}
|
|
1209
|
+
const body = request;
|
|
1210
|
+
const payload = typeof body.type === "string" ? body : { type: "response.create", ...body };
|
|
1211
|
+
return JSON.stringify(payload);
|
|
1212
|
+
}
|
|
1213
|
+
function isObjectWithResponse(event) {
|
|
1214
|
+
return Boolean(event.response && typeof event.response === "object");
|
|
1215
|
+
}
|
|
1216
|
+
function errorToMessage(error) {
|
|
1217
|
+
if (error instanceof Error) {
|
|
1218
|
+
return error.message;
|
|
1219
|
+
}
|
|
1220
|
+
if (typeof error === "string") {
|
|
1221
|
+
return error;
|
|
1222
|
+
}
|
|
1223
|
+
try {
|
|
1224
|
+
return JSON.stringify(error);
|
|
1225
|
+
} catch {
|
|
1226
|
+
return String(error);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
function createAbortError(reason) {
|
|
1230
|
+
const error = new Error(
|
|
1231
|
+
reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "Request aborted."
|
|
1232
|
+
);
|
|
1233
|
+
error.name = "AbortError";
|
|
1234
|
+
return error;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
686
1237
|
// src/openai/chatgpt-codex.ts
|
|
687
1238
|
var CHATGPT_CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
1239
|
+
var CHATGPT_RESPONSES_EXPERIMENTAL_HEADER = "responses=experimental";
|
|
1240
|
+
var cachedResponsesWebSocketMode = null;
|
|
1241
|
+
var chatGptResponsesWebSocketDisabled = false;
|
|
688
1242
|
async function streamChatGptCodexResponse(options) {
|
|
689
1243
|
const { access, accountId } = await getChatGptAuthProfile();
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
1244
|
+
const mode = resolveChatGptResponsesWebSocketMode();
|
|
1245
|
+
const fallbackStreamFactory = () => {
|
|
1246
|
+
const streamPromise = streamChatGptCodexResponseSse({
|
|
1247
|
+
request: options.request,
|
|
1248
|
+
access,
|
|
1249
|
+
accountId,
|
|
1250
|
+
sessionId: options.sessionId,
|
|
1251
|
+
signal: options.signal
|
|
1252
|
+
});
|
|
1253
|
+
return {
|
|
1254
|
+
async *[Symbol.asyncIterator]() {
|
|
1255
|
+
const stream = await streamPromise;
|
|
1256
|
+
for await (const event of stream) {
|
|
1257
|
+
yield event;
|
|
1258
|
+
}
|
|
1259
|
+
},
|
|
1260
|
+
async finalResponse() {
|
|
1261
|
+
return {};
|
|
1262
|
+
},
|
|
1263
|
+
close() {
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
698
1266
|
};
|
|
699
|
-
if (
|
|
700
|
-
|
|
1267
|
+
if (mode === "off" || chatGptResponsesWebSocketDisabled) {
|
|
1268
|
+
return fallbackStreamFactory();
|
|
701
1269
|
}
|
|
1270
|
+
const websocketHeaders = buildChatGptCodexHeaders({
|
|
1271
|
+
access,
|
|
1272
|
+
accountId,
|
|
1273
|
+
sessionId: options.sessionId,
|
|
1274
|
+
useWebSocket: true
|
|
1275
|
+
});
|
|
1276
|
+
return createAdaptiveResponsesStream({
|
|
1277
|
+
mode,
|
|
1278
|
+
createWebSocketStream: async () => await createResponsesWebSocketStream({
|
|
1279
|
+
url: toWebSocketUrl(CHATGPT_CODEX_ENDPOINT),
|
|
1280
|
+
headers: websocketHeaders,
|
|
1281
|
+
request: options.request,
|
|
1282
|
+
signal: options.signal
|
|
1283
|
+
}),
|
|
1284
|
+
createFallbackStream: fallbackStreamFactory,
|
|
1285
|
+
onWebSocketFallback: () => {
|
|
1286
|
+
chatGptResponsesWebSocketDisabled = true;
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
async function streamChatGptCodexResponseSse(options) {
|
|
1291
|
+
const headers = buildChatGptCodexHeaders({
|
|
1292
|
+
access: options.access,
|
|
1293
|
+
accountId: options.accountId,
|
|
1294
|
+
sessionId: options.sessionId,
|
|
1295
|
+
useWebSocket: false
|
|
1296
|
+
});
|
|
1297
|
+
headers.Accept = "text/event-stream";
|
|
1298
|
+
headers["Content-Type"] = "application/json";
|
|
702
1299
|
const response = await fetch(CHATGPT_CODEX_ENDPOINT, {
|
|
703
1300
|
method: "POST",
|
|
704
1301
|
headers,
|
|
@@ -715,20 +1312,67 @@ async function streamChatGptCodexResponse(options) {
|
|
|
715
1312
|
}
|
|
716
1313
|
return parseEventStream(body);
|
|
717
1314
|
}
|
|
1315
|
+
function resolveChatGptResponsesWebSocketMode() {
|
|
1316
|
+
if (cachedResponsesWebSocketMode) {
|
|
1317
|
+
return cachedResponsesWebSocketMode;
|
|
1318
|
+
}
|
|
1319
|
+
cachedResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
1320
|
+
process.env.CHATGPT_RESPONSES_WEBSOCKET_MODE ?? process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
|
|
1321
|
+
"auto"
|
|
1322
|
+
);
|
|
1323
|
+
return cachedResponsesWebSocketMode;
|
|
1324
|
+
}
|
|
1325
|
+
function buildChatGptCodexHeaders(options) {
|
|
1326
|
+
const openAiBeta = options.useWebSocket ? mergeOpenAiBetaHeader(
|
|
1327
|
+
CHATGPT_RESPONSES_EXPERIMENTAL_HEADER,
|
|
1328
|
+
OPENAI_BETA_RESPONSES_WEBSOCKETS_V2
|
|
1329
|
+
) : CHATGPT_RESPONSES_EXPERIMENTAL_HEADER;
|
|
1330
|
+
const headers = {
|
|
1331
|
+
Authorization: `Bearer ${options.access}`,
|
|
1332
|
+
"chatgpt-account-id": options.accountId,
|
|
1333
|
+
"OpenAI-Beta": openAiBeta,
|
|
1334
|
+
originator: "llm",
|
|
1335
|
+
"User-Agent": buildUserAgent()
|
|
1336
|
+
};
|
|
1337
|
+
if (options.sessionId) {
|
|
1338
|
+
headers.session_id = options.sessionId;
|
|
1339
|
+
}
|
|
1340
|
+
return headers;
|
|
1341
|
+
}
|
|
718
1342
|
async function collectChatGptCodexResponse(options) {
|
|
719
|
-
let
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
1343
|
+
let requestForAttempt = options.request;
|
|
1344
|
+
let retriedWithoutReasoningSummary = false;
|
|
1345
|
+
let retriedViaSseFallback = false;
|
|
1346
|
+
while (true) {
|
|
1347
|
+
let sawAnyDelta = false;
|
|
1348
|
+
try {
|
|
1349
|
+
const stream = await streamChatGptCodexResponse({
|
|
725
1350
|
...options,
|
|
726
|
-
request:
|
|
1351
|
+
request: requestForAttempt
|
|
727
1352
|
});
|
|
728
|
-
|
|
1353
|
+
return await collectChatGptCodexStream({
|
|
1354
|
+
stream,
|
|
1355
|
+
onDelta: (delta) => {
|
|
1356
|
+
sawAnyDelta = true;
|
|
1357
|
+
options.onDelta?.(delta);
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
} catch (error) {
|
|
1361
|
+
if (!sawAnyDelta && !retriedViaSseFallback && shouldRetryViaSseFallback(error) && !chatGptResponsesWebSocketDisabled) {
|
|
1362
|
+
chatGptResponsesWebSocketDisabled = true;
|
|
1363
|
+
retriedViaSseFallback = true;
|
|
1364
|
+
continue;
|
|
1365
|
+
}
|
|
1366
|
+
if (!retriedWithoutReasoningSummary && shouldRetryWithoutReasoningSummary(requestForAttempt, error)) {
|
|
1367
|
+
requestForAttempt = removeReasoningSummary(requestForAttempt);
|
|
1368
|
+
retriedWithoutReasoningSummary = true;
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
729
1371
|
throw error;
|
|
730
1372
|
}
|
|
731
1373
|
}
|
|
1374
|
+
}
|
|
1375
|
+
async function collectChatGptCodexStream(options) {
|
|
732
1376
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
733
1377
|
const toolCallOrder = [];
|
|
734
1378
|
const webSearchCalls = /* @__PURE__ */ new Map();
|
|
@@ -741,7 +1385,7 @@ async function collectChatGptCodexResponse(options) {
|
|
|
741
1385
|
let model;
|
|
742
1386
|
let status;
|
|
743
1387
|
let blocked = false;
|
|
744
|
-
for await (const event of stream) {
|
|
1388
|
+
for await (const event of options.stream) {
|
|
745
1389
|
const type = typeof event.type === "string" ? event.type : void 0;
|
|
746
1390
|
if (type === "response.output_text.delta") {
|
|
747
1391
|
const delta = typeof event.delta === "string" ? event.delta : "";
|
|
@@ -865,6 +1509,16 @@ function shouldRetryWithoutReasoningSummary(request, error) {
|
|
|
865
1509
|
const message = error.message.toLowerCase();
|
|
866
1510
|
return message.includes("unsupported parameter") && message.includes("reasoning.summary");
|
|
867
1511
|
}
|
|
1512
|
+
function shouldRetryViaSseFallback(error) {
|
|
1513
|
+
if (!(error instanceof Error)) {
|
|
1514
|
+
return false;
|
|
1515
|
+
}
|
|
1516
|
+
if (error.name === "AbortError") {
|
|
1517
|
+
return false;
|
|
1518
|
+
}
|
|
1519
|
+
const message = error.message.toLowerCase();
|
|
1520
|
+
return message.includes("responses websocket");
|
|
1521
|
+
}
|
|
868
1522
|
function removeReasoningSummary(request) {
|
|
869
1523
|
const reasoning = request.reasoning;
|
|
870
1524
|
if (!reasoning?.summary) {
|
|
@@ -1112,14 +1766,16 @@ async function runFireworksCall(fn) {
|
|
|
1112
1766
|
}
|
|
1113
1767
|
|
|
1114
1768
|
// src/fireworks/models.ts
|
|
1115
|
-
var FIREWORKS_MODEL_IDS = ["kimi-k2.5", "glm-5", "minimax-m2.1"];
|
|
1769
|
+
var FIREWORKS_MODEL_IDS = ["kimi-k2.5", "glm-5", "minimax-m2.1", "gpt-oss-120b"];
|
|
1116
1770
|
var FIREWORKS_DEFAULT_KIMI_MODEL = "kimi-k2.5";
|
|
1117
1771
|
var FIREWORKS_DEFAULT_GLM_MODEL = "glm-5";
|
|
1118
1772
|
var FIREWORKS_DEFAULT_MINIMAX_MODEL = "minimax-m2.1";
|
|
1773
|
+
var FIREWORKS_DEFAULT_GPT_OSS_120B_MODEL = "gpt-oss-120b";
|
|
1119
1774
|
var FIREWORKS_CANONICAL_MODEL_IDS = {
|
|
1120
1775
|
"kimi-k2.5": "accounts/fireworks/models/kimi-k2p5",
|
|
1121
1776
|
"glm-5": "accounts/fireworks/models/glm-5",
|
|
1122
|
-
"minimax-m2.1": "accounts/fireworks/models/minimax-m2p1"
|
|
1777
|
+
"minimax-m2.1": "accounts/fireworks/models/minimax-m2p1",
|
|
1778
|
+
"gpt-oss-120b": "accounts/fireworks/models/gpt-oss-120b"
|
|
1123
1779
|
};
|
|
1124
1780
|
function isFireworksModelId(value) {
|
|
1125
1781
|
return FIREWORKS_MODEL_IDS.includes(value.trim());
|
|
@@ -1198,16 +1854,25 @@ function getGoogleAuthOptions(scopes) {
|
|
|
1198
1854
|
}
|
|
1199
1855
|
|
|
1200
1856
|
// src/google/client.ts
|
|
1201
|
-
var
|
|
1857
|
+
var GEMINI_TEXT_MODEL_IDS = [
|
|
1202
1858
|
"gemini-3-pro-preview",
|
|
1859
|
+
"gemini-3.1-pro-preview",
|
|
1203
1860
|
"gemini-3-flash-preview",
|
|
1204
1861
|
"gemini-2.5-pro",
|
|
1205
1862
|
"gemini-flash-latest",
|
|
1206
1863
|
"gemini-flash-lite-latest"
|
|
1207
1864
|
];
|
|
1865
|
+
var GEMINI_IMAGE_MODEL_IDS = ["gemini-3-pro-image-preview"];
|
|
1866
|
+
var GEMINI_MODEL_IDS = [...GEMINI_TEXT_MODEL_IDS, ...GEMINI_IMAGE_MODEL_IDS];
|
|
1208
1867
|
function isGeminiModelId(value) {
|
|
1209
1868
|
return GEMINI_MODEL_IDS.includes(value);
|
|
1210
1869
|
}
|
|
1870
|
+
function isGeminiTextModelId(value) {
|
|
1871
|
+
return GEMINI_TEXT_MODEL_IDS.includes(value);
|
|
1872
|
+
}
|
|
1873
|
+
function isGeminiImageModelId(value) {
|
|
1874
|
+
return GEMINI_IMAGE_MODEL_IDS.includes(value);
|
|
1875
|
+
}
|
|
1211
1876
|
var CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
|
|
1212
1877
|
var DEFAULT_VERTEX_LOCATION = "global";
|
|
1213
1878
|
var geminiConfiguration = {};
|
|
@@ -1466,6 +2131,8 @@ var cachedApiKey2 = null;
|
|
|
1466
2131
|
var cachedClient2 = null;
|
|
1467
2132
|
var cachedFetch2 = null;
|
|
1468
2133
|
var cachedTimeoutMs2 = null;
|
|
2134
|
+
var openAiResponsesWebSocketMode = null;
|
|
2135
|
+
var openAiResponsesWebSocketDisabled = false;
|
|
1469
2136
|
var DEFAULT_OPENAI_TIMEOUT_MS = 15 * 6e4;
|
|
1470
2137
|
function resolveOpenAiTimeoutMs() {
|
|
1471
2138
|
if (cachedTimeoutMs2 !== null) {
|
|
@@ -1493,6 +2160,97 @@ function getOpenAiFetch() {
|
|
|
1493
2160
|
});
|
|
1494
2161
|
return cachedFetch2;
|
|
1495
2162
|
}
|
|
2163
|
+
function resolveOpenAiBaseUrl() {
|
|
2164
|
+
loadLocalEnv();
|
|
2165
|
+
return process.env.OPENAI_BASE_URL?.trim() || "https://api.openai.com/v1";
|
|
2166
|
+
}
|
|
2167
|
+
function resolveOpenAiResponsesWebSocketMode() {
|
|
2168
|
+
if (openAiResponsesWebSocketMode) {
|
|
2169
|
+
return openAiResponsesWebSocketMode;
|
|
2170
|
+
}
|
|
2171
|
+
loadLocalEnv();
|
|
2172
|
+
openAiResponsesWebSocketMode = resolveResponsesWebSocketMode(
|
|
2173
|
+
process.env.OPENAI_RESPONSES_WEBSOCKET_MODE,
|
|
2174
|
+
"auto"
|
|
2175
|
+
);
|
|
2176
|
+
return openAiResponsesWebSocketMode;
|
|
2177
|
+
}
|
|
2178
|
+
function wrapFallbackStream(stream) {
|
|
2179
|
+
return {
|
|
2180
|
+
async *[Symbol.asyncIterator]() {
|
|
2181
|
+
for await (const event of stream) {
|
|
2182
|
+
yield event;
|
|
2183
|
+
}
|
|
2184
|
+
},
|
|
2185
|
+
async finalResponse() {
|
|
2186
|
+
return await stream.finalResponse();
|
|
2187
|
+
},
|
|
2188
|
+
close() {
|
|
2189
|
+
const maybeClose = stream;
|
|
2190
|
+
if (typeof maybeClose.close === "function") {
|
|
2191
|
+
maybeClose.close();
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
function buildOpenAiResponsesEndpointUrl() {
|
|
2197
|
+
const base = resolveOpenAiBaseUrl();
|
|
2198
|
+
const normalized = base.endsWith("/") ? base : `${base}/`;
|
|
2199
|
+
return new URL("responses", normalized).toString();
|
|
2200
|
+
}
|
|
2201
|
+
function buildOpenAiResponsesWebSocketHeaders(apiKey) {
|
|
2202
|
+
const headers = {
|
|
2203
|
+
Authorization: `Bearer ${apiKey}`,
|
|
2204
|
+
"OpenAI-Beta": mergeOpenAiBetaHeader(
|
|
2205
|
+
process.env.OPENAI_BETA,
|
|
2206
|
+
OPENAI_BETA_RESPONSES_WEBSOCKETS_V2
|
|
2207
|
+
)
|
|
2208
|
+
};
|
|
2209
|
+
const organization = process.env.OPENAI_ORGANIZATION?.trim();
|
|
2210
|
+
if (organization) {
|
|
2211
|
+
headers["OpenAI-Organization"] = organization;
|
|
2212
|
+
}
|
|
2213
|
+
const project = process.env.OPENAI_PROJECT?.trim();
|
|
2214
|
+
if (project) {
|
|
2215
|
+
headers["OpenAI-Project"] = project;
|
|
2216
|
+
}
|
|
2217
|
+
return headers;
|
|
2218
|
+
}
|
|
2219
|
+
function installResponsesWebSocketTransport(client, apiKey) {
|
|
2220
|
+
const responsesApi = client.responses;
|
|
2221
|
+
const streamMethod = responsesApi?.stream;
|
|
2222
|
+
if (typeof streamMethod !== "function") {
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
const originalStream = streamMethod.bind(client.responses);
|
|
2226
|
+
responsesApi.stream = (request, options) => {
|
|
2227
|
+
const mode = resolveOpenAiResponsesWebSocketMode();
|
|
2228
|
+
const fallbackStreamFactory = () => wrapFallbackStream(originalStream(request, options));
|
|
2229
|
+
if (mode === "off" || openAiResponsesWebSocketDisabled) {
|
|
2230
|
+
return fallbackStreamFactory();
|
|
2231
|
+
}
|
|
2232
|
+
const signal = options && typeof options === "object" ? options.signal ?? void 0 : void 0;
|
|
2233
|
+
const websocketUrl = toWebSocketUrl(buildOpenAiResponsesEndpointUrl());
|
|
2234
|
+
const headers = buildOpenAiResponsesWebSocketHeaders(apiKey);
|
|
2235
|
+
const timeoutMs = resolveOpenAiTimeoutMs();
|
|
2236
|
+
return createAdaptiveResponsesStream({
|
|
2237
|
+
mode,
|
|
2238
|
+
createWebSocketStream: async () => await createResponsesWebSocketStream({
|
|
2239
|
+
url: websocketUrl,
|
|
2240
|
+
headers,
|
|
2241
|
+
request,
|
|
2242
|
+
signal,
|
|
2243
|
+
idleTimeoutMs: timeoutMs
|
|
2244
|
+
}),
|
|
2245
|
+
createFallbackStream: fallbackStreamFactory,
|
|
2246
|
+
onWebSocketFallback: (error) => {
|
|
2247
|
+
if (isResponsesWebSocketUnsupportedError(error)) {
|
|
2248
|
+
openAiResponsesWebSocketDisabled = true;
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
});
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
1496
2254
|
function getOpenAiApiKey() {
|
|
1497
2255
|
if (cachedApiKey2 !== null) {
|
|
1498
2256
|
return cachedApiKey2;
|
|
@@ -1510,6 +2268,7 @@ function getOpenAiClient() {
|
|
|
1510
2268
|
if (cachedClient2) {
|
|
1511
2269
|
return cachedClient2;
|
|
1512
2270
|
}
|
|
2271
|
+
loadLocalEnv();
|
|
1513
2272
|
const apiKey = getOpenAiApiKey();
|
|
1514
2273
|
const timeoutMs = resolveOpenAiTimeoutMs();
|
|
1515
2274
|
cachedClient2 = new OpenAI2({
|
|
@@ -1517,6 +2276,7 @@ function getOpenAiClient() {
|
|
|
1517
2276
|
fetch: getOpenAiFetch(),
|
|
1518
2277
|
timeout: timeoutMs
|
|
1519
2278
|
});
|
|
2279
|
+
installResponsesWebSocketTransport(cachedClient2, apiKey);
|
|
1520
2280
|
return cachedClient2;
|
|
1521
2281
|
}
|
|
1522
2282
|
|
|
@@ -1531,11 +2291,51 @@ async function runOpenAiCall(fn) {
|
|
|
1531
2291
|
return scheduler3.run(async () => fn(getOpenAiClient()));
|
|
1532
2292
|
}
|
|
1533
2293
|
|
|
2294
|
+
// src/openai/models.ts
|
|
2295
|
+
var OPENAI_MODEL_IDS = [
|
|
2296
|
+
"gpt-5.3-codex",
|
|
2297
|
+
"gpt-5.3-codex-spark",
|
|
2298
|
+
"gpt-5.2",
|
|
2299
|
+
"gpt-5.1-codex-mini"
|
|
2300
|
+
];
|
|
2301
|
+
function isOpenAiModelId(value) {
|
|
2302
|
+
return OPENAI_MODEL_IDS.includes(value);
|
|
2303
|
+
}
|
|
2304
|
+
var CHATGPT_MODEL_IDS = [
|
|
2305
|
+
"chatgpt-gpt-5.3-codex",
|
|
2306
|
+
"chatgpt-gpt-5.3-codex-spark",
|
|
2307
|
+
"chatgpt-gpt-5.2",
|
|
2308
|
+
"chatgpt-gpt-5.1-codex-mini"
|
|
2309
|
+
];
|
|
2310
|
+
function isChatGptModelId(value) {
|
|
2311
|
+
return CHATGPT_MODEL_IDS.includes(value);
|
|
2312
|
+
}
|
|
2313
|
+
function stripChatGptPrefix(model) {
|
|
2314
|
+
return model.slice("chatgpt-".length);
|
|
2315
|
+
}
|
|
2316
|
+
|
|
1534
2317
|
// src/llm.ts
|
|
1535
2318
|
var toolCallContextStorage = new AsyncLocalStorage();
|
|
1536
2319
|
function getCurrentToolCallContext() {
|
|
1537
2320
|
return toolCallContextStorage.getStore() ?? null;
|
|
1538
2321
|
}
|
|
2322
|
+
var LLM_TEXT_MODEL_IDS = [
|
|
2323
|
+
...OPENAI_MODEL_IDS,
|
|
2324
|
+
...CHATGPT_MODEL_IDS,
|
|
2325
|
+
...FIREWORKS_MODEL_IDS,
|
|
2326
|
+
...GEMINI_TEXT_MODEL_IDS
|
|
2327
|
+
];
|
|
2328
|
+
var LLM_IMAGE_MODEL_IDS = [...GEMINI_IMAGE_MODEL_IDS];
|
|
2329
|
+
var LLM_MODEL_IDS = [...LLM_TEXT_MODEL_IDS, ...LLM_IMAGE_MODEL_IDS];
|
|
2330
|
+
function isLlmTextModelId(value) {
|
|
2331
|
+
return isOpenAiModelId(value) || isChatGptModelId(value) || isFireworksModelId(value) || isGeminiTextModelId(value);
|
|
2332
|
+
}
|
|
2333
|
+
function isLlmImageModelId(value) {
|
|
2334
|
+
return isGeminiImageModelId(value);
|
|
2335
|
+
}
|
|
2336
|
+
function isLlmModelId(value) {
|
|
2337
|
+
return isLlmTextModelId(value) || isLlmImageModelId(value);
|
|
2338
|
+
}
|
|
1539
2339
|
var LlmJsonCallError = class extends Error {
|
|
1540
2340
|
constructor(message, attempts) {
|
|
1541
2341
|
super(message);
|
|
@@ -1893,17 +2693,22 @@ function convertLlmContentToGeminiContent(content) {
|
|
|
1893
2693
|
};
|
|
1894
2694
|
}
|
|
1895
2695
|
function resolveProvider(model) {
|
|
1896
|
-
if (model
|
|
1897
|
-
return { provider: "chatgpt", model: model
|
|
2696
|
+
if (isChatGptModelId(model)) {
|
|
2697
|
+
return { provider: "chatgpt", model: stripChatGptPrefix(model) };
|
|
1898
2698
|
}
|
|
1899
|
-
if (model
|
|
2699
|
+
if (isGeminiTextModelId(model) || isGeminiImageModelId(model)) {
|
|
1900
2700
|
return { provider: "gemini", model };
|
|
1901
2701
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
2702
|
+
if (isFireworksModelId(model)) {
|
|
2703
|
+
const fireworksModel = resolveFireworksModelId(model);
|
|
2704
|
+
if (fireworksModel) {
|
|
2705
|
+
return { provider: "fireworks", model: fireworksModel };
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
if (isOpenAiModelId(model)) {
|
|
2709
|
+
return { provider: "openai", model };
|
|
1905
2710
|
}
|
|
1906
|
-
|
|
2711
|
+
throw new Error(`Unsupported text model: ${model}`);
|
|
1907
2712
|
}
|
|
1908
2713
|
function isOpenAiCodexModel(modelId) {
|
|
1909
2714
|
return modelId.includes("codex");
|
|
@@ -3090,6 +3895,7 @@ function extractFireworksToolCalls(message) {
|
|
|
3090
3895
|
function resolveGeminiThinkingConfig(modelId) {
|
|
3091
3896
|
switch (modelId) {
|
|
3092
3897
|
case "gemini-3-pro-preview":
|
|
3898
|
+
case "gemini-3.1-pro-preview":
|
|
3093
3899
|
return { includeThoughts: true };
|
|
3094
3900
|
case "gemini-3-flash-preview":
|
|
3095
3901
|
return { includeThoughts: true, thinkingBudget: 16384 };
|
|
@@ -6485,15 +7291,24 @@ function mergeToolSets(base, extra) {
|
|
|
6485
7291
|
return merged;
|
|
6486
7292
|
}
|
|
6487
7293
|
export {
|
|
7294
|
+
CHATGPT_MODEL_IDS,
|
|
6488
7295
|
CODEX_APPLY_PATCH_FREEFORM_TOOL_DESCRIPTION,
|
|
6489
7296
|
CODEX_APPLY_PATCH_JSON_TOOL_DESCRIPTION,
|
|
6490
7297
|
CODEX_APPLY_PATCH_LARK_GRAMMAR,
|
|
6491
7298
|
FIREWORKS_DEFAULT_GLM_MODEL,
|
|
7299
|
+
FIREWORKS_DEFAULT_GPT_OSS_120B_MODEL,
|
|
6492
7300
|
FIREWORKS_DEFAULT_KIMI_MODEL,
|
|
6493
7301
|
FIREWORKS_DEFAULT_MINIMAX_MODEL,
|
|
6494
7302
|
FIREWORKS_MODEL_IDS,
|
|
7303
|
+
GEMINI_IMAGE_MODEL_IDS,
|
|
7304
|
+
GEMINI_MODEL_IDS,
|
|
7305
|
+
GEMINI_TEXT_MODEL_IDS,
|
|
6495
7306
|
InMemoryAgentFilesystem,
|
|
7307
|
+
LLM_IMAGE_MODEL_IDS,
|
|
7308
|
+
LLM_MODEL_IDS,
|
|
7309
|
+
LLM_TEXT_MODEL_IDS,
|
|
6496
7310
|
LlmJsonCallError,
|
|
7311
|
+
OPENAI_MODEL_IDS,
|
|
6497
7312
|
appendMarkdownSourcesSection,
|
|
6498
7313
|
applyPatch,
|
|
6499
7314
|
configureGemini,
|
|
@@ -6528,8 +7343,15 @@ export {
|
|
|
6528
7343
|
generateText,
|
|
6529
7344
|
getChatGptAuthProfile,
|
|
6530
7345
|
getCurrentToolCallContext,
|
|
7346
|
+
isChatGptModelId,
|
|
6531
7347
|
isFireworksModelId,
|
|
7348
|
+
isGeminiImageModelId,
|
|
6532
7349
|
isGeminiModelId,
|
|
7350
|
+
isGeminiTextModelId,
|
|
7351
|
+
isLlmImageModelId,
|
|
7352
|
+
isLlmModelId,
|
|
7353
|
+
isLlmTextModelId,
|
|
7354
|
+
isOpenAiModelId,
|
|
6533
7355
|
loadEnvFromFile,
|
|
6534
7356
|
loadLocalEnv,
|
|
6535
7357
|
parseJsonFromLlmText,
|