@eddyskywalker/dsh-chatgpt-subscription 0.10.21 → 0.11.0
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/CHANGELOG.md +5 -0
- package/lib/client.js +53 -8
- package/lib/client.js.map +1 -1
- package/lib/index.js +2409 -304
- package/lib/types/client/command-code/CommandCodeSection.d.ts.map +1 -1
- package/lib/types/client/command-code/locales.d.ts +14 -2
- package/lib/types/client/command-code/locales.d.ts.map +1 -1
- package/lib/types/host/codex-account-pool.d.ts +2 -8
- package/lib/types/host/codex-account-pool.d.ts.map +1 -1
- package/lib/types/host/command-code/adapter.d.ts +19 -0
- package/lib/types/host/command-code/adapter.d.ts.map +1 -1
- package/lib/types/host/command-code/client.d.ts +4 -2
- package/lib/types/host/command-code/client.d.ts.map +1 -1
- package/lib/types/host/command-code/mapper.d.ts +11 -0
- package/lib/types/host/command-code/mapper.d.ts.map +1 -1
- package/lib/types/host/command-code/routes.d.ts +2 -0
- package/lib/types/host/command-code/routes.d.ts.map +1 -1
- package/lib/types/host/command-code/token-store.d.ts +7 -0
- package/lib/types/host/command-code/token-store.d.ts.map +1 -1
- package/lib/types/host/command-code/types.d.ts +15 -0
- package/lib/types/host/command-code/types.d.ts.map +1 -1
- package/lib/types/host/common/capabilities.d.ts +100 -0
- package/lib/types/host/common/capabilities.d.ts.map +1 -0
- package/lib/types/host/common/concurrency-gate.d.ts +46 -0
- package/lib/types/host/common/concurrency-gate.d.ts.map +1 -0
- package/lib/types/host/common/model-request-control.d.ts +21 -0
- package/lib/types/host/common/model-request-control.d.ts.map +1 -0
- package/lib/types/host/common/request-diagnostics.d.ts +79 -0
- package/lib/types/host/common/request-diagnostics.d.ts.map +1 -0
- package/lib/types/host/minimax-code/adapter.d.ts.map +1 -1
- package/lib/types/host/minimax-code/mapper.d.ts +32 -1
- package/lib/types/host/minimax-code/mapper.d.ts.map +1 -1
- package/lib/types/host/minimax-code/model-catalog.d.ts +7 -2
- package/lib/types/host/minimax-code/model-catalog.d.ts.map +1 -1
- package/lib/types/host/minimax-code/types.d.ts +12 -0
- package/lib/types/host/minimax-code/types.d.ts.map +1 -1
- package/lib/types/host/ollama/adapter.d.ts +18 -4
- package/lib/types/host/ollama/adapter.d.ts.map +1 -1
- package/lib/types/host/ollama/client.d.ts +14 -0
- package/lib/types/host/ollama/client.d.ts.map +1 -1
- package/lib/types/host/ollama/mapper.d.ts +5 -0
- package/lib/types/host/ollama/mapper.d.ts.map +1 -1
- package/lib/types/host/ollama/types.d.ts +23 -0
- package/lib/types/host/ollama/types.d.ts.map +1 -1
- package/lib/types/host/responses-client.d.ts +27 -10
- package/lib/types/host/responses-client.d.ts.map +1 -1
- package/lib/types/host/wire-auth.d.ts +19 -5
- package/lib/types/host/wire-auth.d.ts.map +1 -1
- package/lib/types/host/workbuddy/mapper.d.ts +1 -0
- package/lib/types/host/workbuddy/mapper.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/command-code-contracts.d.ts +15 -2
- package/lib/types/shared/command-code-contracts.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { HarnessError, LlmAdapter, LlmError, LlmError as LlmError$1, ProviderReq
|
|
|
5
5
|
import fs, { chmod, lstat, mkdir, open, readFile, rename, stat, unlink } from "node:fs/promises";
|
|
6
6
|
import path, { basename, dirname, join, relative, resolve } from "node:path";
|
|
7
7
|
import os, { homedir } from "node:os";
|
|
8
|
-
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
8
|
+
import { createHash, createHmac, randomBytes, randomUUID } from "node:crypto";
|
|
9
9
|
import { isDeepStrictEqual } from "node:util";
|
|
10
10
|
import { execSync, spawn } from "node:child_process";
|
|
11
11
|
import fs$1, { appendFileSync, constants, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
|
|
@@ -612,10 +612,16 @@ async function rehydrateCatalogCache(name, parseModels, accept, currentFetchedAt
|
|
|
612
612
|
/**
|
|
613
613
|
* Turn-state header the Codex backend hands back on a responses response.
|
|
614
614
|
*
|
|
615
|
-
* The official CLI reads it and echoes it on the next request
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
615
|
+
* The official CLI reads it and echoes it on the SAME TURN's next request — a
|
|
616
|
+
* retry, an incremental append or a continuation — and the backend uses it to
|
|
617
|
+
* resume instead of re-ingesting the whole history.
|
|
618
|
+
*
|
|
619
|
+
* Its scope is exactly one turn. The official client builds a fresh per-turn
|
|
620
|
+
* session and documents that reusing one across turns violates the
|
|
621
|
+
* client/server contract and can cause routing bugs; the token is also
|
|
622
|
+
* account-scoped and is discarded when auth ownership changes. It is opaque,
|
|
623
|
+
* short-lived, and threaded through rather than computed (see
|
|
624
|
+
* `responses-client.ts`).
|
|
619
625
|
*/
|
|
620
626
|
const CODEX_TURN_STATE_HEADER = "x-codex-turn-state";
|
|
621
627
|
function codexHeaders(credentials, sessionId, options = {}) {
|
|
@@ -630,6 +636,16 @@ function codexHeaders(credentials, sessionId, options = {}) {
|
|
|
630
636
|
...options.turnState ? { [CODEX_TURN_STATE_HEADER]: options.turnState } : {}
|
|
631
637
|
};
|
|
632
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Opaque local identity of whoever signs a request.
|
|
641
|
+
*
|
|
642
|
+
* Turn state is account-scoped, so the client has to detect when the signer
|
|
643
|
+
* changed without ever putting that fact on the wire. The digest is computed
|
|
644
|
+
* and compared locally: it is not a header, not a credential, and never logged.
|
|
645
|
+
*/
|
|
646
|
+
function authOwnerKey(credentials) {
|
|
647
|
+
return createHash("sha256").update(credentials.accountId ?? "").update("\0").update(credentials.accessToken).digest("hex").slice(0, 32);
|
|
648
|
+
}
|
|
633
649
|
function stableSessionId(value) {
|
|
634
650
|
const source = value === void 0 || value === "" ? randomUUID() : value;
|
|
635
651
|
return `dsh-${createHash("sha256").update(source).digest("hex").slice(0, 32)}`;
|
|
@@ -775,7 +791,7 @@ function parseListing(payload) {
|
|
|
775
791
|
function parseListingEntry$1(value) {
|
|
776
792
|
const record = asRecord$10(value);
|
|
777
793
|
if (record === void 0) return [];
|
|
778
|
-
const id = asString$
|
|
794
|
+
const id = asString$15(record.slug) ?? asString$15(record.id);
|
|
779
795
|
if (id === void 0) return [];
|
|
780
796
|
const contextWindow = asNumber$4(record.context_window) ?? asNumber$4(record.contextWindow);
|
|
781
797
|
const modalities = parseModalities(record.input_modalities);
|
|
@@ -783,18 +799,18 @@ function parseListingEntry$1(value) {
|
|
|
783
799
|
const fallback = CODEX_MODEL_CATALOG.find((entry) => entry.id === id);
|
|
784
800
|
return [{
|
|
785
801
|
id,
|
|
786
|
-
name: asString$
|
|
802
|
+
name: asString$15(record.display_name) ?? asString$15(record.displayName) ?? fallback?.name ?? id,
|
|
787
803
|
contextWindow: contextWindow !== void 0 && contextWindow > 0 ? contextWindow : null,
|
|
788
804
|
inputModalities: modalities ?? (fallback ? [...fallback.inputModalities] : ["text"]),
|
|
789
805
|
...efforts !== null ? { reasoningEfforts: efforts } : {},
|
|
790
|
-
defaultReasoningEffort: asString$
|
|
806
|
+
defaultReasoningEffort: asString$15(record.default_reasoning_level) ?? fallback?.defaultReasoningEffort ?? null
|
|
791
807
|
}];
|
|
792
808
|
}
|
|
793
809
|
function parseModalities(value) {
|
|
794
810
|
if (!Array.isArray(value)) return null;
|
|
795
811
|
const out = [];
|
|
796
812
|
for (const item of value) {
|
|
797
|
-
const name = asString$
|
|
813
|
+
const name = asString$15(item);
|
|
798
814
|
if (name === "text" || name === "image") out.push(name);
|
|
799
815
|
}
|
|
800
816
|
return out.length > 0 ? out : null;
|
|
@@ -809,7 +825,7 @@ function parseEfforts(value) {
|
|
|
809
825
|
if (!Array.isArray(value)) return null;
|
|
810
826
|
const out = [];
|
|
811
827
|
for (const item of value) {
|
|
812
|
-
const effort = asString$
|
|
828
|
+
const effort = asString$15(item) ?? asString$15(asRecord$10(item)?.effort);
|
|
813
829
|
if (effort !== void 0 && !out.includes(effort)) out.push(effort);
|
|
814
830
|
}
|
|
815
831
|
return out.length > 0 ? out : null;
|
|
@@ -827,7 +843,7 @@ function accountKeyFor$1(credentials) {
|
|
|
827
843
|
function asRecord$10(value) {
|
|
828
844
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
829
845
|
}
|
|
830
|
-
function asString$
|
|
846
|
+
function asString$15(value) {
|
|
831
847
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
832
848
|
}
|
|
833
849
|
function asNumber$4(value) {
|
|
@@ -2072,6 +2088,104 @@ function createPlatformTokenStore(platform = process.platform) {
|
|
|
2072
2088
|
throw new Error(`Unsupported platform ${platform}; dsh-chatgpt-subscription supports Windows, macOS, and Linux.`);
|
|
2073
2089
|
}
|
|
2074
2090
|
//#endregion
|
|
2091
|
+
//#region src/host/common/concurrency-gate.ts
|
|
2092
|
+
var ConcurrencyGate = class {
|
|
2093
|
+
held = /* @__PURE__ */ new Map();
|
|
2094
|
+
queues = /* @__PURE__ */ new Map();
|
|
2095
|
+
limits = /* @__PURE__ */ new Map();
|
|
2096
|
+
/**
|
|
2097
|
+
* Record the concurrency limit for one account.
|
|
2098
|
+
*
|
|
2099
|
+
* A non-positive or non-finite limit clears the cap, which is the same as
|
|
2100
|
+
* never having set one: requests then pass through unbounded rather than
|
|
2101
|
+
* being blocked by a value nobody verified.
|
|
2102
|
+
*/
|
|
2103
|
+
setLimit(accountId, limit) {
|
|
2104
|
+
if (!Number.isFinite(limit) || limit <= 0) {
|
|
2105
|
+
this.limits.delete(accountId);
|
|
2106
|
+
this.drain(accountId);
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
this.limits.set(accountId, Math.max(1, Math.floor(limit)));
|
|
2110
|
+
this.drain(accountId);
|
|
2111
|
+
}
|
|
2112
|
+
/** Limit currently in force for one account, or undefined when uncapped. */
|
|
2113
|
+
limitFor(accountId) {
|
|
2114
|
+
return this.limits.get(accountId);
|
|
2115
|
+
}
|
|
2116
|
+
/** Requests currently in flight for one account. */
|
|
2117
|
+
inFlight(accountId) {
|
|
2118
|
+
return this.held.get(accountId) ?? 0;
|
|
2119
|
+
}
|
|
2120
|
+
/**
|
|
2121
|
+
* Take one slot, waiting for it when the account is at its limit.
|
|
2122
|
+
*
|
|
2123
|
+
* The returned function releases the slot and is safe to call more than once:
|
|
2124
|
+
* a double release on an aborted request must not hand a stranger a slot that
|
|
2125
|
+
* this holder never had.
|
|
2126
|
+
*/
|
|
2127
|
+
async acquire(accountId, signal) {
|
|
2128
|
+
if (signal?.aborted === true) throw signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
2129
|
+
const limit = this.limits.get(accountId);
|
|
2130
|
+
const current = this.held.get(accountId) ?? 0;
|
|
2131
|
+
if (limit === void 0 || current < limit) return this.take(accountId);
|
|
2132
|
+
await new Promise((resolve, reject) => {
|
|
2133
|
+
const queue = this.queues.get(accountId) ?? [];
|
|
2134
|
+
const waiter = { resolve: () => {
|
|
2135
|
+
remove();
|
|
2136
|
+
resolve();
|
|
2137
|
+
} };
|
|
2138
|
+
const onAbort = () => {
|
|
2139
|
+
remove();
|
|
2140
|
+
reject(signal?.reason ?? new DOMException("Aborted", "AbortError"));
|
|
2141
|
+
};
|
|
2142
|
+
const remove = () => {
|
|
2143
|
+
const index = queue.indexOf(waiter);
|
|
2144
|
+
if (index !== -1) queue.splice(index, 1);
|
|
2145
|
+
if (queue.length === 0) this.queues.delete(accountId);
|
|
2146
|
+
signal?.removeEventListener("abort", onAbort);
|
|
2147
|
+
};
|
|
2148
|
+
queue.push(waiter);
|
|
2149
|
+
this.queues.set(accountId, queue);
|
|
2150
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2151
|
+
});
|
|
2152
|
+
const release = this.releaseFn(accountId);
|
|
2153
|
+
if (signal?.aborted) {
|
|
2154
|
+
release();
|
|
2155
|
+
throw signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
2156
|
+
}
|
|
2157
|
+
return release;
|
|
2158
|
+
}
|
|
2159
|
+
take(accountId) {
|
|
2160
|
+
this.held.set(accountId, (this.held.get(accountId) ?? 0) + 1);
|
|
2161
|
+
return this.releaseFn(accountId);
|
|
2162
|
+
}
|
|
2163
|
+
releaseFn(accountId) {
|
|
2164
|
+
let released = false;
|
|
2165
|
+
return () => {
|
|
2166
|
+
if (released) return;
|
|
2167
|
+
released = true;
|
|
2168
|
+
const next = (this.held.get(accountId) ?? 1) - 1;
|
|
2169
|
+
if (next <= 0) this.held.delete(accountId);
|
|
2170
|
+
else this.held.set(accountId, next);
|
|
2171
|
+
this.drain(accountId);
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
/** Hand slots to as many waiters as the current limit allows. */
|
|
2175
|
+
drain(accountId) {
|
|
2176
|
+
const queue = this.queues.get(accountId);
|
|
2177
|
+
if (queue === void 0 || queue.length === 0) return;
|
|
2178
|
+
const limit = this.limits.get(accountId);
|
|
2179
|
+
const room = limit === void 0 ? queue.length : limit - (this.held.get(accountId) ?? 0);
|
|
2180
|
+
for (let index = 0; index < room && queue.length > 0; index++) {
|
|
2181
|
+
const waiter = queue.shift();
|
|
2182
|
+
this.held.set(accountId, (this.held.get(accountId) ?? 0) + 1);
|
|
2183
|
+
waiter.resolve();
|
|
2184
|
+
}
|
|
2185
|
+
if (queue.length === 0) this.queues.delete(accountId);
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
//#endregion
|
|
2075
2189
|
//#region src/host/callback-server.ts
|
|
2076
2190
|
/** One-shot localhost OAuth callback listener. */
|
|
2077
2191
|
var OAuthCallbackServer = class {
|
|
@@ -2750,6 +2864,17 @@ function parseCodexPoolData(value) {
|
|
|
2750
2864
|
* hooks the core cannot know — how to refresh one account's tokens and which
|
|
2751
2865
|
* accounts a cached quota window already rules out.
|
|
2752
2866
|
*/
|
|
2867
|
+
/**
|
|
2868
|
+
* Per-account in-flight requests, shared by every ChatGPT request this host
|
|
2869
|
+
* makes. A fan-out of subagents is what reaches a plan's concurrency bound, so
|
|
2870
|
+
* the bound is enforced here rather than discovered by cooling every account at
|
|
2871
|
+
* once. Limits are learned from upstream evidence (see
|
|
2872
|
+
* {@link CodexAccountPool.noteConcurrencySignal}), never assumed.
|
|
2873
|
+
*/
|
|
2874
|
+
const concurrency = new ConcurrencyGate();
|
|
2875
|
+
function chatGPTConcurrency() {
|
|
2876
|
+
return concurrency;
|
|
2877
|
+
}
|
|
2753
2878
|
var CodexAccountPool = class extends AccountPoolCore {
|
|
2754
2879
|
refresherRef;
|
|
2755
2880
|
store;
|
|
@@ -3156,13 +3281,13 @@ function createCodexFetchProvider(options = {}) {
|
|
|
3156
3281
|
//#region src/host/common/llm-compat.ts
|
|
3157
3282
|
/** This package's kind in the harness message-source map. */
|
|
3158
3283
|
const PLUGIN_MESSAGE_SOURCE_KIND = "dsh-chatgpt-subscription";
|
|
3159
|
-
function isRecord$
|
|
3284
|
+
function isRecord$24(value) {
|
|
3160
3285
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3161
3286
|
}
|
|
3162
3287
|
/** Read a message body's blocks, keeping every entry that carries a block tag. */
|
|
3163
3288
|
function readBlocks(raw) {
|
|
3164
3289
|
if (!Array.isArray(raw)) return [];
|
|
3165
|
-
return raw.filter((block) => isRecord$
|
|
3290
|
+
return raw.filter((block) => isRecord$24(block) && typeof block.type === "string");
|
|
3166
3291
|
}
|
|
3167
3292
|
/**
|
|
3168
3293
|
* Normalize one harness message list into {@link Message}.
|
|
@@ -3177,10 +3302,10 @@ function readBlocks(raw) {
|
|
|
3177
3302
|
function normalizeMessages(messages) {
|
|
3178
3303
|
const normalized = [];
|
|
3179
3304
|
for (const raw of messages ?? []) {
|
|
3180
|
-
if (!isRecord$
|
|
3305
|
+
if (!isRecord$24(raw)) continue;
|
|
3181
3306
|
const content = readBlocks(raw.content);
|
|
3182
3307
|
if (raw.role === "tool") {
|
|
3183
|
-
const source = isRecord$
|
|
3308
|
+
const source = isRecord$24(raw.source) ? raw.source : void 0;
|
|
3184
3309
|
const callId = String(raw.toolCallId ?? source?.callId ?? "");
|
|
3185
3310
|
normalized.push({
|
|
3186
3311
|
id: raw.id,
|
|
@@ -3202,7 +3327,7 @@ function normalizeMessages(messages) {
|
|
|
3202
3327
|
id: raw.id,
|
|
3203
3328
|
role: typeof raw.role === "string" ? raw.role : "user",
|
|
3204
3329
|
content,
|
|
3205
|
-
...isRecord$
|
|
3330
|
+
...isRecord$24(raw.source) ? { source: raw.source } : {}
|
|
3206
3331
|
});
|
|
3207
3332
|
}
|
|
3208
3333
|
return normalized;
|
|
@@ -3744,6 +3869,666 @@ var ProxyManager = class {
|
|
|
3744
3869
|
}
|
|
3745
3870
|
};
|
|
3746
3871
|
//#endregion
|
|
3872
|
+
//#region src/host/common/model-request-control.ts
|
|
3873
|
+
const CONTROLLED_PROVIDERS = [
|
|
3874
|
+
"codex-chatgpt",
|
|
3875
|
+
"claude",
|
|
3876
|
+
"antigravity",
|
|
3877
|
+
"kimi-code",
|
|
3878
|
+
"minimax-code",
|
|
3879
|
+
"command-code",
|
|
3880
|
+
"workbuddy",
|
|
3881
|
+
"ollama"
|
|
3882
|
+
];
|
|
3883
|
+
/** Explicit deployment configuration. Invalid values fail closed at startup. */
|
|
3884
|
+
function readModelRequestLimits(env = process.env) {
|
|
3885
|
+
const raw = env.DSH_PROVIDER_CONCURRENCY;
|
|
3886
|
+
if (!raw) return {};
|
|
3887
|
+
let value;
|
|
3888
|
+
try {
|
|
3889
|
+
value = JSON.parse(raw);
|
|
3890
|
+
} catch {
|
|
3891
|
+
throw new Error("DSH_PROVIDER_CONCURRENCY must be a JSON object");
|
|
3892
|
+
}
|
|
3893
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("DSH_PROVIDER_CONCURRENCY must be a JSON object");
|
|
3894
|
+
const input = value;
|
|
3895
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
3896
|
+
"default",
|
|
3897
|
+
"queueTimeoutMs",
|
|
3898
|
+
...CONTROLLED_PROVIDERS
|
|
3899
|
+
]);
|
|
3900
|
+
for (const key of Object.keys(input)) if (!allowed.has(key)) throw new Error("Unknown DSH_PROVIDER_CONCURRENCY field");
|
|
3901
|
+
const number = (key, max) => {
|
|
3902
|
+
const n = input[key];
|
|
3903
|
+
if (n === void 0) return void 0;
|
|
3904
|
+
if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 0 || n > max) throw new Error("Invalid DSH_PROVIDER_CONCURRENCY numeric value");
|
|
3905
|
+
return n;
|
|
3906
|
+
};
|
|
3907
|
+
const providers = {};
|
|
3908
|
+
for (const provider of CONTROLLED_PROVIDERS) {
|
|
3909
|
+
const limit = number(provider, 1e4);
|
|
3910
|
+
if (limit !== void 0) providers[provider] = limit;
|
|
3911
|
+
}
|
|
3912
|
+
return {
|
|
3913
|
+
default: number("default", 1e4),
|
|
3914
|
+
queueTimeoutMs: number("queueTimeoutMs", 36e5),
|
|
3915
|
+
providers
|
|
3916
|
+
};
|
|
3917
|
+
}
|
|
3918
|
+
/** Only generation POSTs are limited; token refresh/catalog/quota must never wait behind them. */
|
|
3919
|
+
function isModelRequest(input, init) {
|
|
3920
|
+
if ((init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase() !== "POST") return false;
|
|
3921
|
+
let path;
|
|
3922
|
+
try {
|
|
3923
|
+
path = new URL(input instanceof Request ? input.url : String(input)).pathname;
|
|
3924
|
+
} catch {
|
|
3925
|
+
return false;
|
|
3926
|
+
}
|
|
3927
|
+
return [
|
|
3928
|
+
"/responses",
|
|
3929
|
+
"/messages",
|
|
3930
|
+
"/chat/completions",
|
|
3931
|
+
"/api/chat"
|
|
3932
|
+
].some((suffix) => path.endsWith(suffix)) || /:(?:streamGenerateContent|generateContent)$/.test(path);
|
|
3933
|
+
}
|
|
3934
|
+
/** Hold a slot until EOF, cancel, abort or failure, never merely until response headers. */
|
|
3935
|
+
function createControlledModelFetch(baseFetch, options) {
|
|
3936
|
+
const gate = options.gate ?? new ConcurrencyGate();
|
|
3937
|
+
const limit = options.limits.providers?.[options.provider] ?? options.limits.default ?? 0;
|
|
3938
|
+
if (!Number.isSafeInteger(limit) || limit < 0) throw new Error("Invalid provider concurrency limit");
|
|
3939
|
+
if (limit === 0) return baseFetch;
|
|
3940
|
+
return async (input, init) => {
|
|
3941
|
+
if (!isModelRequest(input, init)) return baseFetch(input, init);
|
|
3942
|
+
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
3943
|
+
const credential = headers.get("authorization") ?? headers.get("x-api-key") ?? "";
|
|
3944
|
+
const key = options.provider + ":" + createHash("sha256").update(credential).digest("hex");
|
|
3945
|
+
gate.setLimit(key, limit);
|
|
3946
|
+
const signal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
|
|
3947
|
+
const queued = new AbortController();
|
|
3948
|
+
const abortQueue = () => queued.abort(signal?.reason ?? new DOMException("Aborted", "AbortError"));
|
|
3949
|
+
signal?.addEventListener("abort", abortQueue, { once: true });
|
|
3950
|
+
if (signal?.aborted) abortQueue();
|
|
3951
|
+
const timeout = options.limits.queueTimeoutMs ?? 12e4;
|
|
3952
|
+
const timer = timeout > 0 ? setTimeout(() => queued.abort(new DOMException("Provider concurrency queue timed out", "TimeoutError")), timeout) : void 0;
|
|
3953
|
+
let release;
|
|
3954
|
+
try {
|
|
3955
|
+
release = await gate.acquire(key, queued.signal);
|
|
3956
|
+
} finally {
|
|
3957
|
+
if (timer) clearTimeout(timer);
|
|
3958
|
+
signal?.removeEventListener("abort", abortQueue);
|
|
3959
|
+
if (gate.inFlight(key) === 0) gate.setLimit(key, 0);
|
|
3960
|
+
}
|
|
3961
|
+
let done = false;
|
|
3962
|
+
let reader;
|
|
3963
|
+
let controller;
|
|
3964
|
+
const finish = () => {
|
|
3965
|
+
if (done) return;
|
|
3966
|
+
done = true;
|
|
3967
|
+
signal?.removeEventListener("abort", abort);
|
|
3968
|
+
release();
|
|
3969
|
+
if (gate.inFlight(key) === 0) gate.setLimit(key, 0);
|
|
3970
|
+
};
|
|
3971
|
+
const abort = () => {
|
|
3972
|
+
const reason = signal?.reason ?? new DOMException("Aborted", "AbortError");
|
|
3973
|
+
finish();
|
|
3974
|
+
if (reader) reader.cancel(reason).catch(() => void 0).finally(() => {
|
|
3975
|
+
try {
|
|
3976
|
+
reader?.releaseLock();
|
|
3977
|
+
} catch {}
|
|
3978
|
+
});
|
|
3979
|
+
try {
|
|
3980
|
+
controller?.error(reason);
|
|
3981
|
+
} catch {}
|
|
3982
|
+
};
|
|
3983
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
3984
|
+
try {
|
|
3985
|
+
if (signal?.aborted) {
|
|
3986
|
+
abort();
|
|
3987
|
+
throw signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
3988
|
+
}
|
|
3989
|
+
const response = await baseFetch(input, init);
|
|
3990
|
+
if (done) {
|
|
3991
|
+
response.body?.cancel().catch(() => void 0);
|
|
3992
|
+
throw signal?.reason ?? new DOMException("Aborted", "AbortError");
|
|
3993
|
+
}
|
|
3994
|
+
if (!response.body) {
|
|
3995
|
+
finish();
|
|
3996
|
+
return response;
|
|
3997
|
+
}
|
|
3998
|
+
reader = response.body.getReader();
|
|
3999
|
+
const body = new ReadableStream({
|
|
4000
|
+
start(c) {
|
|
4001
|
+
controller = c;
|
|
4002
|
+
},
|
|
4003
|
+
async pull(c) {
|
|
4004
|
+
try {
|
|
4005
|
+
const chunk = await reader.read();
|
|
4006
|
+
if (done) return;
|
|
4007
|
+
if (chunk.done) {
|
|
4008
|
+
finish();
|
|
4009
|
+
reader.releaseLock();
|
|
4010
|
+
c.close();
|
|
4011
|
+
} else c.enqueue(chunk.value);
|
|
4012
|
+
} catch (error) {
|
|
4013
|
+
finish();
|
|
4014
|
+
try {
|
|
4015
|
+
reader.releaseLock();
|
|
4016
|
+
} catch {}
|
|
4017
|
+
c.error(error);
|
|
4018
|
+
}
|
|
4019
|
+
},
|
|
4020
|
+
async cancel(reason) {
|
|
4021
|
+
finish();
|
|
4022
|
+
try {
|
|
4023
|
+
await reader.cancel(reason);
|
|
4024
|
+
} finally {
|
|
4025
|
+
reader.releaseLock();
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
}, { highWaterMark: 0 });
|
|
4029
|
+
const wrapped = new Response(body, {
|
|
4030
|
+
status: response.status,
|
|
4031
|
+
statusText: response.statusText,
|
|
4032
|
+
headers: response.headers
|
|
4033
|
+
});
|
|
4034
|
+
for (const name of [
|
|
4035
|
+
"url",
|
|
4036
|
+
"redirected",
|
|
4037
|
+
"type"
|
|
4038
|
+
]) Object.defineProperty(wrapped, name, { value: response[name] });
|
|
4039
|
+
return wrapped;
|
|
4040
|
+
} catch (error) {
|
|
4041
|
+
finish();
|
|
4042
|
+
throw error;
|
|
4043
|
+
}
|
|
4044
|
+
};
|
|
4045
|
+
}
|
|
4046
|
+
//#endregion
|
|
4047
|
+
//#region src/host/common/request-diagnostics.ts
|
|
4048
|
+
/**
|
|
4049
|
+
* Reusable request diagnostics wrapper for model provider calls.
|
|
4050
|
+
*
|
|
4051
|
+
* Implements W5 usage, latency, and transport diagnostics:
|
|
4052
|
+
* - First-byte chunk latency (firstByteMs) and total duration
|
|
4053
|
+
* - Request payload bytes (null when unknown stream, not 0)
|
|
4054
|
+
* - Safe runtime-validated enum-only endpoint and provider classification
|
|
4055
|
+
* - Ephemeral keyed-HMAC fingerprints scoped per provider+endpoint
|
|
4056
|
+
* - Disjoint token usage mapping with missing vs zero differentiation and endpoint-awareness
|
|
4057
|
+
* - Complete SSE line and NDJSON streaming parser merging start/delta usage
|
|
4058
|
+
* - Stream proxy with backpressure preservation (highWaterMark: 0) and early cancel/abort
|
|
4059
|
+
* - Isolated failure boundary (diagnostics sink failures never break generation)
|
|
4060
|
+
*
|
|
4061
|
+
* @module dsh-chatgpt-subscription/request-diagnostics
|
|
4062
|
+
*/
|
|
4063
|
+
const DIAGNOSTIC_PROVIDERS = [
|
|
4064
|
+
"codex-chatgpt",
|
|
4065
|
+
"claude-subscription",
|
|
4066
|
+
"antigravity",
|
|
4067
|
+
"kimi-code",
|
|
4068
|
+
"minimax-code",
|
|
4069
|
+
"command-code",
|
|
4070
|
+
"workbuddy-subscription",
|
|
4071
|
+
"ollama",
|
|
4072
|
+
"claude",
|
|
4073
|
+
"workbuddy",
|
|
4074
|
+
"codex"
|
|
4075
|
+
];
|
|
4076
|
+
const KNOWN_WIRE_ENDPOINTS = [
|
|
4077
|
+
"messages",
|
|
4078
|
+
"responses",
|
|
4079
|
+
"chat_completions",
|
|
4080
|
+
"generate_content",
|
|
4081
|
+
"stream_generate_content",
|
|
4082
|
+
"ollama_chat",
|
|
4083
|
+
"ollama_generate",
|
|
4084
|
+
"workbuddy_chat",
|
|
4085
|
+
"unknown"
|
|
4086
|
+
];
|
|
4087
|
+
function isValidProvider(provider) {
|
|
4088
|
+
return typeof provider === "string" && DIAGNOSTIC_PROVIDERS.includes(provider);
|
|
4089
|
+
}
|
|
4090
|
+
function isValidEndpoint(endpoint) {
|
|
4091
|
+
return typeof endpoint === "string" && KNOWN_WIRE_ENDPOINTS.includes(endpoint);
|
|
4092
|
+
}
|
|
4093
|
+
const EPHEMERAL_HMAC_KEY = randomBytes(32);
|
|
4094
|
+
function computeHmacPrefix(data) {
|
|
4095
|
+
return createHmac("sha256", EPHEMERAL_HMAC_KEY).update(data).digest("hex").slice(0, 16);
|
|
4096
|
+
}
|
|
4097
|
+
const MAX_SESSION_CACHE_SIZE = 500;
|
|
4098
|
+
const sessionCache = /* @__PURE__ */ new Map();
|
|
4099
|
+
function updateSessionFingerprints(scopedSessionKey, current) {
|
|
4100
|
+
const prev = sessionCache.get(scopedSessionKey);
|
|
4101
|
+
const comparison = {
|
|
4102
|
+
...current,
|
|
4103
|
+
authChanged: current.authFingerprint !== void 0 && prev?.authFingerprint !== void 0 ? current.authFingerprint !== prev.authFingerprint : void 0,
|
|
4104
|
+
modelChanged: current.modelFingerprint !== void 0 && prev?.modelFingerprint !== void 0 ? current.modelFingerprint !== prev.modelFingerprint : void 0,
|
|
4105
|
+
toolChanged: current.toolFingerprint !== void 0 && prev?.toolFingerprint !== void 0 ? current.toolFingerprint !== prev.toolFingerprint : void 0,
|
|
4106
|
+
prefixChanged: current.prefixFingerprint !== void 0 && prev?.prefixFingerprint !== void 0 ? current.prefixFingerprint !== prev.prefixFingerprint : void 0
|
|
4107
|
+
};
|
|
4108
|
+
if (sessionCache.has(scopedSessionKey)) sessionCache.delete(scopedSessionKey);
|
|
4109
|
+
else if (sessionCache.size >= MAX_SESSION_CACHE_SIZE) {
|
|
4110
|
+
const oldestKey = sessionCache.keys().next().value;
|
|
4111
|
+
if (oldestKey) sessionCache.delete(oldestKey);
|
|
4112
|
+
}
|
|
4113
|
+
sessionCache.set(scopedSessionKey, {
|
|
4114
|
+
authFingerprint: current.authFingerprint ?? prev?.authFingerprint,
|
|
4115
|
+
modelFingerprint: current.modelFingerprint ?? prev?.modelFingerprint,
|
|
4116
|
+
toolFingerprint: current.toolFingerprint ?? prev?.toolFingerprint,
|
|
4117
|
+
prefixFingerprint: current.prefixFingerprint ?? prev?.prefixFingerprint
|
|
4118
|
+
});
|
|
4119
|
+
return comparison;
|
|
4120
|
+
}
|
|
4121
|
+
function isDiagnosticsEnabled(options) {
|
|
4122
|
+
if (options?.enabled !== void 0) return Boolean(options.enabled);
|
|
4123
|
+
return process.env.DSH_PROVIDER_DIAGNOSTICS === "1";
|
|
4124
|
+
}
|
|
4125
|
+
function classifyHttpStatus(statusCode) {
|
|
4126
|
+
if (statusCode === null) return "error";
|
|
4127
|
+
if (statusCode >= 200 && statusCode < 300) return "2xx";
|
|
4128
|
+
if (statusCode >= 300 && statusCode < 400) return "3xx";
|
|
4129
|
+
if (statusCode >= 400 && statusCode < 500) return "4xx";
|
|
4130
|
+
if (statusCode >= 500 && statusCode < 600) return "5xx";
|
|
4131
|
+
return "error";
|
|
4132
|
+
}
|
|
4133
|
+
function isDefaultModelPath(pathname) {
|
|
4134
|
+
const path = pathname.toLowerCase();
|
|
4135
|
+
if (path.endsWith("/messages")) return "messages";
|
|
4136
|
+
if (path.endsWith("/responses")) return "responses";
|
|
4137
|
+
if (path.endsWith("/chat/completions") || path.endsWith("/chatcompletion_v2") || path.endsWith("/conversation") || path.endsWith("/latents/chat")) return "chat_completions";
|
|
4138
|
+
if (path.includes(":streamgeneratecontent")) return "stream_generate_content";
|
|
4139
|
+
if (path.includes(":generatecontent")) return "generate_content";
|
|
4140
|
+
if (path.endsWith("/api/chat")) return "ollama_chat";
|
|
4141
|
+
if (path.endsWith("/api/generate")) return "ollama_generate";
|
|
4142
|
+
return null;
|
|
4143
|
+
}
|
|
4144
|
+
function getUrlAndPathname(input) {
|
|
4145
|
+
let fullUrl = "";
|
|
4146
|
+
if (typeof input === "string") fullUrl = input;
|
|
4147
|
+
else if (input instanceof URL) fullUrl = input.toString();
|
|
4148
|
+
else if (typeof input === "object" && input !== null && "url" in input) fullUrl = String(input.url);
|
|
4149
|
+
try {
|
|
4150
|
+
const parsed = new URL(fullUrl, "http://127.0.0.1");
|
|
4151
|
+
return {
|
|
4152
|
+
fullUrl,
|
|
4153
|
+
pathname: parsed.pathname
|
|
4154
|
+
};
|
|
4155
|
+
} catch {
|
|
4156
|
+
return {
|
|
4157
|
+
fullUrl,
|
|
4158
|
+
pathname: ""
|
|
4159
|
+
};
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
function getHeaderValue(headers, name) {
|
|
4163
|
+
if (!headers) return void 0;
|
|
4164
|
+
const target = name.toLowerCase();
|
|
4165
|
+
if (headers instanceof Headers) return headers.get(target) ?? void 0;
|
|
4166
|
+
if (Array.isArray(headers)) {
|
|
4167
|
+
for (const [key, val] of headers) if (key.toLowerCase() === target) return val;
|
|
4168
|
+
return;
|
|
4169
|
+
}
|
|
4170
|
+
if (typeof headers === "object") {
|
|
4171
|
+
for (const [key, val] of Object.entries(headers)) if (key.toLowerCase() === target) return typeof val === "string" ? val : void 0;
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
function measureRequestBytes(body, input, init) {
|
|
4175
|
+
if (body === null || body === void 0) {
|
|
4176
|
+
if (input instanceof Request && input.body) {
|
|
4177
|
+
const cl = input.headers.get("content-length");
|
|
4178
|
+
if (cl) {
|
|
4179
|
+
const parsed = parseInt(cl, 10);
|
|
4180
|
+
if (!Number.isNaN(parsed) && parsed >= 0) return parsed;
|
|
4181
|
+
}
|
|
4182
|
+
return null;
|
|
4183
|
+
}
|
|
4184
|
+
const cl = getHeaderValue(init?.headers, "content-length");
|
|
4185
|
+
if (cl) {
|
|
4186
|
+
const parsed = parseInt(cl, 10);
|
|
4187
|
+
if (!Number.isNaN(parsed) && parsed >= 0) return parsed;
|
|
4188
|
+
}
|
|
4189
|
+
return 0;
|
|
4190
|
+
}
|
|
4191
|
+
if (typeof body === "string") return Buffer.byteLength(body, "utf-8");
|
|
4192
|
+
if (body instanceof ArrayBuffer) return body.byteLength;
|
|
4193
|
+
if (ArrayBuffer.isView(body)) return body.byteLength;
|
|
4194
|
+
if (typeof Blob !== "undefined" && body instanceof Blob) return body.size;
|
|
4195
|
+
return null;
|
|
4196
|
+
}
|
|
4197
|
+
const MAX_JSON_PARSE_BYTES = 512 * 1024;
|
|
4198
|
+
function extractBodyFingerprints(body) {
|
|
4199
|
+
if (typeof body !== "string" || body.length > MAX_JSON_PARSE_BYTES) return {};
|
|
4200
|
+
const trimmed = body.trim();
|
|
4201
|
+
if (!trimmed.startsWith("{")) return {};
|
|
4202
|
+
try {
|
|
4203
|
+
const parsed = JSON.parse(trimmed);
|
|
4204
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
4205
|
+
let modelFingerprint;
|
|
4206
|
+
if (typeof parsed.model === "string" && parsed.model) modelFingerprint = computeHmacPrefix(parsed.model);
|
|
4207
|
+
let toolFingerprint;
|
|
4208
|
+
if (Array.isArray(parsed.tools)) toolFingerprint = computeHmacPrefix(JSON.stringify(parsed.tools));
|
|
4209
|
+
let prefixFingerprint;
|
|
4210
|
+
let prefixCandidate = "";
|
|
4211
|
+
if (typeof parsed.instructions === "string" && parsed.instructions) prefixCandidate = parsed.instructions;
|
|
4212
|
+
else if (typeof parsed.system === "string" && parsed.system) prefixCandidate = parsed.system;
|
|
4213
|
+
else if (Array.isArray(parsed.system) && parsed.system.length > 0) prefixCandidate = JSON.stringify(parsed.system);
|
|
4214
|
+
else if (Array.isArray(parsed.messages) && parsed.messages.length > 0) {
|
|
4215
|
+
const first = parsed.messages[0];
|
|
4216
|
+
if (first && typeof first === "object") {
|
|
4217
|
+
const f = first;
|
|
4218
|
+
if (f.role === "system" || f.role === "developer") prefixCandidate = typeof f.content === "string" ? f.content : JSON.stringify(f.content);
|
|
4219
|
+
else prefixCandidate = JSON.stringify(f);
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
if (prefixCandidate) prefixFingerprint = computeHmacPrefix(prefixCandidate);
|
|
4223
|
+
return {
|
|
4224
|
+
modelFingerprint,
|
|
4225
|
+
toolFingerprint,
|
|
4226
|
+
prefixFingerprint
|
|
4227
|
+
};
|
|
4228
|
+
} catch {
|
|
4229
|
+
return {};
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
function parseFiniteNonNegativeNumber(val) {
|
|
4233
|
+
if (typeof val === "number") {
|
|
4234
|
+
if (Number.isFinite(val) && val >= 0) return Math.floor(val);
|
|
4235
|
+
return;
|
|
4236
|
+
}
|
|
4237
|
+
if (typeof val === "string" && val.trim() !== "") {
|
|
4238
|
+
const num = Number(val);
|
|
4239
|
+
if (Number.isFinite(num) && num >= 0) return Math.floor(num);
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
function parseUsageObject(raw, endpoint) {
|
|
4243
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
4244
|
+
const r = raw;
|
|
4245
|
+
const totalInput = parseFiniteNonNegativeNumber(r.prompt_tokens) ?? parseFiniteNonNegativeNumber(r.input_tokens);
|
|
4246
|
+
const outputTokens = parseFiniteNonNegativeNumber(r.completion_tokens) ?? parseFiniteNonNegativeNumber(r.output_tokens);
|
|
4247
|
+
const totalTokens = parseFiniteNonNegativeNumber(r.total_tokens);
|
|
4248
|
+
const inputDetails = typeof r.prompt_tokens_details === "object" && r.prompt_tokens_details !== null ? r.prompt_tokens_details : typeof r.input_tokens_details === "object" && r.input_tokens_details !== null ? r.input_tokens_details : null;
|
|
4249
|
+
const outputDetails = typeof r.completion_tokens_details === "object" && r.completion_tokens_details !== null ? r.completion_tokens_details : typeof r.output_tokens_details === "object" && r.output_tokens_details !== null ? r.output_tokens_details : null;
|
|
4250
|
+
const cacheRead = parseFiniteNonNegativeNumber(inputDetails?.cached_tokens) ?? parseFiniteNonNegativeNumber(r.cache_read_input_tokens) ?? parseFiniteNonNegativeNumber(r.cache_read_tokens);
|
|
4251
|
+
const cacheWrite = parseFiniteNonNegativeNumber(inputDetails?.cache_write_tokens) ?? parseFiniteNonNegativeNumber(r.cache_creation_input_tokens) ?? parseFiniteNonNegativeNumber(r.cache_write_tokens);
|
|
4252
|
+
const reasoning = parseFiniteNonNegativeNumber(outputDetails?.reasoning_tokens) ?? parseFiniteNonNegativeNumber(r.reasoning_tokens);
|
|
4253
|
+
if (totalInput === void 0 && outputTokens === void 0 && totalTokens === void 0 && cacheRead === void 0 && cacheWrite === void 0) return;
|
|
4254
|
+
let disjointInput;
|
|
4255
|
+
if (totalInput !== void 0) if (endpoint === "messages") disjointInput = totalInput;
|
|
4256
|
+
else disjointInput = Math.max(0, totalInput - (cacheRead ?? 0) - (cacheWrite ?? 0));
|
|
4257
|
+
return {
|
|
4258
|
+
...disjointInput !== void 0 ? { inputTokens: disjointInput } : {},
|
|
4259
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
4260
|
+
...cacheRead !== void 0 ? { cacheReadTokens: cacheRead } : {},
|
|
4261
|
+
...cacheWrite !== void 0 ? { cacheWriteTokens: cacheWrite } : {},
|
|
4262
|
+
...reasoning !== void 0 ? { reasoningTokens: reasoning } : {},
|
|
4263
|
+
...totalTokens !== void 0 ? { totalTokens } : {}
|
|
4264
|
+
};
|
|
4265
|
+
}
|
|
4266
|
+
function mergeUsage(target, source) {
|
|
4267
|
+
if (!target) return source ? { ...source } : void 0;
|
|
4268
|
+
if (!source) return target ? { ...target } : void 0;
|
|
4269
|
+
return {
|
|
4270
|
+
inputTokens: source.inputTokens ?? target.inputTokens,
|
|
4271
|
+
outputTokens: source.outputTokens ?? target.outputTokens,
|
|
4272
|
+
cacheReadTokens: source.cacheReadTokens ?? target.cacheReadTokens,
|
|
4273
|
+
cacheWriteTokens: source.cacheWriteTokens ?? target.cacheWriteTokens,
|
|
4274
|
+
reasoningTokens: source.reasoningTokens ?? target.reasoningTokens,
|
|
4275
|
+
totalTokens: source.totalTokens ?? target.totalTokens
|
|
4276
|
+
};
|
|
4277
|
+
}
|
|
4278
|
+
const MAX_LINE_BUFFER_BYTES = 64 * 1024;
|
|
4279
|
+
var StreamingUsageParser = class {
|
|
4280
|
+
endpoint;
|
|
4281
|
+
lineBuffer = "";
|
|
4282
|
+
accumulatedUsage;
|
|
4283
|
+
constructor(endpoint) {
|
|
4284
|
+
this.endpoint = endpoint;
|
|
4285
|
+
}
|
|
4286
|
+
droppingLine = false;
|
|
4287
|
+
feed(chunkText) {
|
|
4288
|
+
let start = 0;
|
|
4289
|
+
while (start < chunkText.length) {
|
|
4290
|
+
const newline = chunkText.indexOf("\n", start);
|
|
4291
|
+
const end = newline === -1 ? chunkText.length : newline;
|
|
4292
|
+
if (!this.droppingLine) if (this.lineBuffer.length + end - start > MAX_LINE_BUFFER_BYTES) {
|
|
4293
|
+
this.lineBuffer = "";
|
|
4294
|
+
this.droppingLine = true;
|
|
4295
|
+
} else this.lineBuffer += chunkText.slice(start, end);
|
|
4296
|
+
if (newline === -1) break;
|
|
4297
|
+
if (!this.droppingLine) this.processLine(this.lineBuffer.trim());
|
|
4298
|
+
this.lineBuffer = "";
|
|
4299
|
+
this.droppingLine = false;
|
|
4300
|
+
start = newline + 1;
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
flush() {
|
|
4304
|
+
const trailing = this.lineBuffer.trim();
|
|
4305
|
+
if (trailing) {
|
|
4306
|
+
this.processLine(trailing);
|
|
4307
|
+
this.lineBuffer = "";
|
|
4308
|
+
}
|
|
4309
|
+
return this.accumulatedUsage;
|
|
4310
|
+
}
|
|
4311
|
+
processLine(line) {
|
|
4312
|
+
let payload = line;
|
|
4313
|
+
if (payload.startsWith("data:")) payload = payload.slice(5).trim();
|
|
4314
|
+
if (!payload || payload === "[DONE]") return;
|
|
4315
|
+
if (!payload.startsWith("{")) return;
|
|
4316
|
+
try {
|
|
4317
|
+
const parsed = JSON.parse(payload);
|
|
4318
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return;
|
|
4319
|
+
if (parsed.type === "message_start" && parsed.message && typeof parsed.message === "object") {
|
|
4320
|
+
const msg = parsed.message;
|
|
4321
|
+
if (msg.usage) {
|
|
4322
|
+
const parsedUsage = parseUsageObject(msg.usage, this.endpoint);
|
|
4323
|
+
this.accumulatedUsage = mergeUsage(this.accumulatedUsage, parsedUsage);
|
|
4324
|
+
return;
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
if (parsed.type === "message_delta" && parsed.usage) {
|
|
4328
|
+
const parsedUsage = parseUsageObject(parsed.usage, this.endpoint);
|
|
4329
|
+
this.accumulatedUsage = mergeUsage(this.accumulatedUsage, parsedUsage);
|
|
4330
|
+
return;
|
|
4331
|
+
}
|
|
4332
|
+
if (parsed.done === true) {
|
|
4333
|
+
const promptEval = parseFiniteNonNegativeNumber(parsed.prompt_eval_count);
|
|
4334
|
+
const evalCount = parseFiniteNonNegativeNumber(parsed.eval_count);
|
|
4335
|
+
if (promptEval !== void 0 || evalCount !== void 0) {
|
|
4336
|
+
const ollamaUsage = {
|
|
4337
|
+
...promptEval !== void 0 ? { inputTokens: promptEval } : {},
|
|
4338
|
+
...evalCount !== void 0 ? { outputTokens: evalCount } : {},
|
|
4339
|
+
...promptEval !== void 0 && evalCount !== void 0 ? { totalTokens: promptEval + evalCount } : {}
|
|
4340
|
+
};
|
|
4341
|
+
this.accumulatedUsage = mergeUsage(this.accumulatedUsage, ollamaUsage);
|
|
4342
|
+
return;
|
|
4343
|
+
}
|
|
4344
|
+
}
|
|
4345
|
+
const rawUsage = parsed.usage ?? (parsed.response && typeof parsed.response === "object" ? parsed.response.usage : void 0) ?? (parsed.message && typeof parsed.message === "object" ? parsed.message.usage : void 0);
|
|
4346
|
+
if (rawUsage && typeof rawUsage === "object") {
|
|
4347
|
+
const parsedUsage = parseUsageObject(rawUsage, this.endpoint);
|
|
4348
|
+
this.accumulatedUsage = mergeUsage(this.accumulatedUsage, parsedUsage);
|
|
4349
|
+
}
|
|
4350
|
+
} catch {}
|
|
4351
|
+
}
|
|
4352
|
+
};
|
|
4353
|
+
/**
|
|
4354
|
+
* Creates a diagnostic wrapper around fetch for model requests.
|
|
4355
|
+
*/
|
|
4356
|
+
function createDiagnosticFetch(baseFetch, options) {
|
|
4357
|
+
return async function diagnosticFetch(input, init) {
|
|
4358
|
+
if (!isDiagnosticsEnabled(options)) return baseFetch(input, init);
|
|
4359
|
+
if ((init?.method ?? (input instanceof Request ? input.method : typeof input === "object" && input !== null && "method" in input ? String(input.method) : "GET")).toUpperCase() !== "POST") return baseFetch(input, init);
|
|
4360
|
+
const { fullUrl, pathname } = getUrlAndPathname(input);
|
|
4361
|
+
const rawEndpoint = options?.endpoint ?? (options?.classifyEndpoint ? options.classifyEndpoint(fullUrl, pathname) : isDefaultModelPath(pathname));
|
|
4362
|
+
if (!rawEndpoint || !isValidEndpoint(rawEndpoint)) return baseFetch(input, init);
|
|
4363
|
+
const endpoint = rawEndpoint;
|
|
4364
|
+
const candidateProvider = options?.provider ?? "codex-chatgpt";
|
|
4365
|
+
if (!isValidProvider(candidateProvider)) return baseFetch(input, init);
|
|
4366
|
+
const provider = candidateProvider;
|
|
4367
|
+
const requestBytes = measureRequestBytes(init?.body, input, init);
|
|
4368
|
+
const startTime = performance.now();
|
|
4369
|
+
const authHeader = getHeaderValue(init?.headers, "authorization") ?? getHeaderValue(init?.headers, "x-api-key") ?? (input instanceof Request ? input.headers.get("authorization") ?? input.headers.get("x-api-key") ?? void 0 : void 0);
|
|
4370
|
+
const authFingerprint = authHeader ? computeHmacPrefix(authHeader) : void 0;
|
|
4371
|
+
const sessionRaw = options?.correlation?.sessionId ?? getHeaderValue(init?.headers, "x-session-id") ?? getHeaderValue(init?.headers, "session_id") ?? (input instanceof Request ? input.headers.get("x-session-id") ?? input.headers.get("session_id") ?? void 0 : void 0);
|
|
4372
|
+
const scopedSessionKey = sessionRaw ? computeHmacPrefix(provider + ":" + endpoint + ":" + sessionRaw) : void 0;
|
|
4373
|
+
let retries;
|
|
4374
|
+
if (typeof options?.correlation?.retryCount === "number") retries = options.correlation.retryCount;
|
|
4375
|
+
else {
|
|
4376
|
+
const retryHeader = getHeaderValue(init?.headers, "x-retry-count") ?? getHeaderValue(init?.headers, "retry-count") ?? (input instanceof Request ? input.headers.get("x-retry-count") ?? input.headers.get("retry-count") ?? void 0 : void 0);
|
|
4377
|
+
if (retryHeader) {
|
|
4378
|
+
const parsed = parseInt(retryHeader, 10);
|
|
4379
|
+
if (!Number.isNaN(parsed)) retries = parsed;
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
const currentFingerprints = {
|
|
4383
|
+
authFingerprint,
|
|
4384
|
+
...extractBodyFingerprints(init?.body ?? (input instanceof Request ? void 0 : void 0))
|
|
4385
|
+
};
|
|
4386
|
+
let fingerprints;
|
|
4387
|
+
if (scopedSessionKey) fingerprints = updateSessionFingerprints(scopedSessionKey, currentFingerprints);
|
|
4388
|
+
else if (currentFingerprints.authFingerprint || currentFingerprints.modelFingerprint || currentFingerprints.toolFingerprint || currentFingerprints.prefixFingerprint) fingerprints = { ...currentFingerprints };
|
|
4389
|
+
const signal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
|
|
4390
|
+
let firstByteMs = null;
|
|
4391
|
+
let emitted = false;
|
|
4392
|
+
const usageParser = new StreamingUsageParser(endpoint);
|
|
4393
|
+
const decoder = new TextDecoder("utf-8", { fatal: false });
|
|
4394
|
+
function emitRecord(outcome, statusCode) {
|
|
4395
|
+
if (emitted) return;
|
|
4396
|
+
emitted = true;
|
|
4397
|
+
const durationMs = Math.max(0, Math.round(performance.now() - startTime));
|
|
4398
|
+
const usage = outcome === "completed" ? usageParser.flush() : void 0;
|
|
4399
|
+
const record = {
|
|
4400
|
+
provider,
|
|
4401
|
+
endpoint,
|
|
4402
|
+
requestBytes,
|
|
4403
|
+
firstByteMs,
|
|
4404
|
+
ttftMs: null,
|
|
4405
|
+
durationMs,
|
|
4406
|
+
statusCode,
|
|
4407
|
+
statusClass: classifyHttpStatus(statusCode),
|
|
4408
|
+
outcome,
|
|
4409
|
+
...usage !== void 0 ? { usage } : {},
|
|
4410
|
+
...retries !== void 0 ? { retries } : {},
|
|
4411
|
+
...fingerprints !== void 0 ? { fingerprints } : {}
|
|
4412
|
+
};
|
|
4413
|
+
try {
|
|
4414
|
+
const res = options?.onRecord?.(record);
|
|
4415
|
+
if (res && typeof res.catch === "function") res.catch(() => {});
|
|
4416
|
+
} catch {}
|
|
4417
|
+
}
|
|
4418
|
+
let response;
|
|
4419
|
+
try {
|
|
4420
|
+
response = await baseFetch(input, init);
|
|
4421
|
+
} catch (error) {
|
|
4422
|
+
emitRecord(Boolean(signal?.aborted) || error instanceof Error && error.name === "AbortError" ? "canceled" : "error", null);
|
|
4423
|
+
throw error;
|
|
4424
|
+
}
|
|
4425
|
+
if (signal?.aborted) {
|
|
4426
|
+
const reason = signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
4427
|
+
emitRecord("canceled", response.status);
|
|
4428
|
+
if (response.body) try {
|
|
4429
|
+
const r = response.body.getReader();
|
|
4430
|
+
await r.cancel(reason);
|
|
4431
|
+
r.releaseLock();
|
|
4432
|
+
} catch {}
|
|
4433
|
+
throw reason;
|
|
4434
|
+
}
|
|
4435
|
+
if (response.body === null || response.status === 204 || response.status === 304) {
|
|
4436
|
+
emitRecord(response.ok ? "completed" : "error", response.status);
|
|
4437
|
+
return response;
|
|
4438
|
+
}
|
|
4439
|
+
const reader = response.body.getReader();
|
|
4440
|
+
let streamClosed = false;
|
|
4441
|
+
let controllerRef;
|
|
4442
|
+
const cleanup = () => {
|
|
4443
|
+
streamClosed = true;
|
|
4444
|
+
if (signal) try {
|
|
4445
|
+
signal.removeEventListener("abort", abortListener);
|
|
4446
|
+
} catch {}
|
|
4447
|
+
};
|
|
4448
|
+
const abortListener = () => {
|
|
4449
|
+
if (streamClosed) return;
|
|
4450
|
+
cleanup();
|
|
4451
|
+
const reason = signal?.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
4452
|
+
emitRecord("canceled", response.status);
|
|
4453
|
+
try {
|
|
4454
|
+
controllerRef?.error(reason);
|
|
4455
|
+
} catch {}
|
|
4456
|
+
reader.cancel(reason).catch(() => {}).finally(() => {
|
|
4457
|
+
try {
|
|
4458
|
+
reader.releaseLock();
|
|
4459
|
+
} catch {}
|
|
4460
|
+
});
|
|
4461
|
+
};
|
|
4462
|
+
if (signal) signal.addEventListener("abort", abortListener, { once: true });
|
|
4463
|
+
const wrappedBody = new ReadableStream({
|
|
4464
|
+
start(controller) {
|
|
4465
|
+
controllerRef = controller;
|
|
4466
|
+
},
|
|
4467
|
+
async pull(controller) {
|
|
4468
|
+
try {
|
|
4469
|
+
const { done, value } = await reader.read();
|
|
4470
|
+
if (done) {
|
|
4471
|
+
cleanup();
|
|
4472
|
+
try {
|
|
4473
|
+
reader.releaseLock();
|
|
4474
|
+
} catch {}
|
|
4475
|
+
emitRecord("completed", response.status);
|
|
4476
|
+
controller.close();
|
|
4477
|
+
return;
|
|
4478
|
+
}
|
|
4479
|
+
if (value && value.byteLength > 0) {
|
|
4480
|
+
if (firstByteMs === null) firstByteMs = Math.max(0, Math.round(performance.now() - startTime));
|
|
4481
|
+
const text = decoder.decode(value, { stream: true });
|
|
4482
|
+
if (text) usageParser.feed(text);
|
|
4483
|
+
}
|
|
4484
|
+
controller.enqueue(value);
|
|
4485
|
+
} catch (err) {
|
|
4486
|
+
cleanup();
|
|
4487
|
+
try {
|
|
4488
|
+
reader.releaseLock();
|
|
4489
|
+
} catch {}
|
|
4490
|
+
emitRecord(Boolean(signal?.aborted) || err instanceof Error && err.name === "AbortError" ? "canceled" : "error", response.status);
|
|
4491
|
+
controller.error(err);
|
|
4492
|
+
}
|
|
4493
|
+
},
|
|
4494
|
+
async cancel(reason) {
|
|
4495
|
+
cleanup();
|
|
4496
|
+
emitRecord("canceled", response.status);
|
|
4497
|
+
try {
|
|
4498
|
+
await reader.cancel(reason);
|
|
4499
|
+
} finally {
|
|
4500
|
+
try {
|
|
4501
|
+
reader.releaseLock();
|
|
4502
|
+
} catch {}
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
}, { highWaterMark: 0 });
|
|
4506
|
+
const wrappedResponse = new Response(wrappedBody, {
|
|
4507
|
+
status: response.status,
|
|
4508
|
+
statusText: response.statusText,
|
|
4509
|
+
headers: response.headers
|
|
4510
|
+
});
|
|
4511
|
+
try {
|
|
4512
|
+
Object.defineProperty(wrappedResponse, "url", {
|
|
4513
|
+
value: response.url,
|
|
4514
|
+
writable: false,
|
|
4515
|
+
configurable: true
|
|
4516
|
+
});
|
|
4517
|
+
Object.defineProperty(wrappedResponse, "redirected", {
|
|
4518
|
+
value: response.redirected,
|
|
4519
|
+
writable: false,
|
|
4520
|
+
configurable: true
|
|
4521
|
+
});
|
|
4522
|
+
Object.defineProperty(wrappedResponse, "type", {
|
|
4523
|
+
value: response.type,
|
|
4524
|
+
writable: false,
|
|
4525
|
+
configurable: true
|
|
4526
|
+
});
|
|
4527
|
+
} catch {}
|
|
4528
|
+
return wrappedResponse;
|
|
4529
|
+
};
|
|
4530
|
+
}
|
|
4531
|
+
//#endregion
|
|
3747
4532
|
//#region src/host/antigravity/types.ts
|
|
3748
4533
|
const PROVIDER_NAME$6 = "Antigravity";
|
|
3749
4534
|
const PROVIDER_ID$6 = "antigravity";
|
|
@@ -5265,7 +6050,7 @@ const MAX_VISIBLE_REASONING_CHARS = 12e3;
|
|
|
5265
6050
|
const DEFAULT_POOL_COOLDOWN_MS = 15 * 6e4;
|
|
5266
6051
|
const REASONING_DELTA_FLUSH_CHARS = 768;
|
|
5267
6052
|
const REASONING_TRUNCATED_NOTICE = "\n\n[Reasoning summary truncated to keep the DSH web UI responsive.]";
|
|
5268
|
-
/** Cap on
|
|
6053
|
+
/** Cap on live turns whose routing state is remembered at once. */
|
|
5269
6054
|
const MAX_TRACKED_TURN_STATES = 200;
|
|
5270
6055
|
var ResponsesClient = class {
|
|
5271
6056
|
oauth;
|
|
@@ -5277,13 +6062,20 @@ var ResponsesClient = class {
|
|
|
5277
6062
|
reasoningSummary;
|
|
5278
6063
|
accountPool;
|
|
5279
6064
|
/**
|
|
5280
|
-
* Opaque backend
|
|
6065
|
+
* Opaque backend routing state, keyed by a unique model-request nonce.
|
|
6066
|
+
* The host does not expose a reliable human-turn identity, so this is the
|
|
6067
|
+
* conservative no-cross-request-reuse fallback, not full turn continuity.
|
|
5281
6068
|
*
|
|
5282
6069
|
* The Codex backend hands `x-codex-turn-state` back on each response and
|
|
5283
|
-
* expects it on the next request of the
|
|
5284
|
-
*
|
|
5285
|
-
*
|
|
5286
|
-
*
|
|
6070
|
+
* expects it on the next request of the SAME turn, which lets it resume the
|
|
6071
|
+
* turn instead of re-ingesting the whole history.
|
|
6072
|
+
*
|
|
6073
|
+
* It is a client/server contract with a one-turn scope: the official client
|
|
6074
|
+
* creates a fresh per-turn session and warns that replaying the token across
|
|
6075
|
+
* turns breaks routing. The value is also account-scoped, so each entry
|
|
6076
|
+
* records the auth owner that minted it and is discarded when the signer
|
|
6077
|
+
* changes. It is only ever an optimization, so a miss costs a full resend and
|
|
6078
|
+
* never correctness.
|
|
5287
6079
|
*/
|
|
5288
6080
|
turnStates = /* @__PURE__ */ new Map();
|
|
5289
6081
|
constructor(oauth, attachments, options = {}) {
|
|
@@ -5302,13 +6094,17 @@ var ResponsesClient = class {
|
|
|
5302
6094
|
const options = normalizeGenerateOptions(rawOptions);
|
|
5303
6095
|
const hiddenSandboxControls = hiddenSandboxControlToolNames(options);
|
|
5304
6096
|
const sessionId = stableSessionId(options.sessionId);
|
|
6097
|
+
const turnKey = turnKeyFor(sessionId);
|
|
5305
6098
|
let currentModel = options.model;
|
|
5306
6099
|
let attemptOptions = options;
|
|
5307
6100
|
let response;
|
|
6101
|
+
let release;
|
|
5308
6102
|
while (true) {
|
|
5309
6103
|
const payload = await buildResponsesPayload(attemptOptions, this.attachments, this.localRawImages, this.outputVerbosity(), this.fastMode(), this.reasoningSummary(), promptCacheKeyFor(sessionId));
|
|
5310
6104
|
try {
|
|
5311
|
-
|
|
6105
|
+
const opened = await this.send(payload, sessionId, turnKey, options.signal);
|
|
6106
|
+
response = opened.response;
|
|
6107
|
+
release = opened.release;
|
|
5312
6108
|
break;
|
|
5313
6109
|
} catch (error) {
|
|
5314
6110
|
if (error instanceof LlmError && (error.code === "NOT_FOUND" || error.status === 404)) {
|
|
@@ -5328,42 +6124,47 @@ var ResponsesClient = class {
|
|
|
5328
6124
|
try {
|
|
5329
6125
|
yield* wrapStreamWithWatchdog((watchdogSignal) => parseResponsesStream(response, watchdogSignal, hiddenSandboxControls), options.signal, 3e5, "LLM_STREAM_IDLE_TIMEOUT", "Codex");
|
|
5330
6126
|
} finally {
|
|
6127
|
+
this.turnStates.delete(turnKey);
|
|
6128
|
+
release?.();
|
|
5331
6129
|
this.onGenerationFinished();
|
|
5332
6130
|
}
|
|
5333
6131
|
}
|
|
5334
|
-
async send(payload, sessionId, signal) {
|
|
5335
|
-
if (this.accountPool !== null) return this.sendWithPool(payload, sessionId, signal);
|
|
6132
|
+
async send(payload, sessionId, turnKey, signal) {
|
|
6133
|
+
if (this.accountPool !== null) return this.sendWithPool(payload, sessionId, turnKey, signal);
|
|
5336
6134
|
let credentials = await this.oauth.credentials();
|
|
5337
|
-
let response = await this.request(payload, credentials, sessionId, signal);
|
|
6135
|
+
let response = await this.request(payload, credentials, sessionId, turnKey, signal);
|
|
5338
6136
|
if (response.status === 401) {
|
|
5339
6137
|
await response.body?.cancel().catch(() => void 0);
|
|
5340
6138
|
credentials = await this.oauth.credentials(true);
|
|
5341
|
-
response = await this.request(payload, credentials, sessionId, signal);
|
|
6139
|
+
response = await this.request(payload, credentials, sessionId, turnKey, signal);
|
|
5342
6140
|
}
|
|
5343
6141
|
if (!response.ok) throw await responseError(response);
|
|
5344
|
-
this.rememberTurnState(response,
|
|
5345
|
-
return response;
|
|
6142
|
+
this.rememberTurnState(response, turnKey, credentials);
|
|
6143
|
+
return { response };
|
|
5346
6144
|
}
|
|
5347
6145
|
/**
|
|
5348
|
-
* Record the
|
|
6146
|
+
* Record the routing state a response carried, for this turn's next request.
|
|
5349
6147
|
*
|
|
5350
6148
|
* A response that carries none clears any stored value: a backend that stops
|
|
5351
6149
|
* sending the header has stopped honouring it, and replaying a stale value
|
|
5352
6150
|
* would be guessing. An empty header is treated the same as absent.
|
|
5353
6151
|
*/
|
|
5354
|
-
rememberTurnState(response,
|
|
6152
|
+
rememberTurnState(response, turnKey, credentials) {
|
|
5355
6153
|
const next = response.headers.get(CODEX_TURN_STATE_HEADER)?.trim();
|
|
5356
|
-
if (next === void 0 || next === "") this.turnStates.delete(
|
|
5357
|
-
else this.storeTurnState(
|
|
6154
|
+
if (next === void 0 || next === "") this.turnStates.delete(turnKey);
|
|
6155
|
+
else this.storeTurnState(turnKey, next, credentials);
|
|
5358
6156
|
}
|
|
5359
6157
|
/**
|
|
5360
|
-
* Bounded store: a long-lived host serves many sessions, and nothing
|
|
5361
|
-
* signals that
|
|
5362
|
-
* entry. The value is an optimization, so a miss only costs a
|
|
6158
|
+
* Bounded store: a long-lived host serves many sessions and turns, and nothing
|
|
6159
|
+
* here ever signals that either ended, so the map is capped and evicts the
|
|
6160
|
+
* oldest entry. The value is an optimization, so a miss only costs a resend.
|
|
5363
6161
|
*/
|
|
5364
|
-
storeTurnState(
|
|
5365
|
-
this.turnStates.delete(
|
|
5366
|
-
this.turnStates.set(
|
|
6162
|
+
storeTurnState(turnKey, value, credentials) {
|
|
6163
|
+
this.turnStates.delete(turnKey);
|
|
6164
|
+
this.turnStates.set(turnKey, {
|
|
6165
|
+
value,
|
|
6166
|
+
owner: authOwnerKey(credentials)
|
|
6167
|
+
});
|
|
5367
6168
|
while (this.turnStates.size > MAX_TRACKED_TURN_STATES) {
|
|
5368
6169
|
const oldest = this.turnStates.keys().next();
|
|
5369
6170
|
if (oldest.done === true) break;
|
|
@@ -5371,6 +6172,23 @@ var ResponsesClient = class {
|
|
|
5371
6172
|
}
|
|
5372
6173
|
}
|
|
5373
6174
|
/**
|
|
6175
|
+
* Routing state this turn may replay, or undefined.
|
|
6176
|
+
*
|
|
6177
|
+
* A token minted by a different auth owner is dropped rather than sent: the
|
|
6178
|
+
* value is account-scoped, and sending it to another account is at best a
|
|
6179
|
+
* routing hint for the wrong machine and at worst a replay of state that
|
|
6180
|
+
* account never issued. Dropping it only costs a full resend.
|
|
6181
|
+
*/
|
|
6182
|
+
turnStateFor(turnKey, credentials) {
|
|
6183
|
+
const entry = this.turnStates.get(turnKey);
|
|
6184
|
+
if (entry === void 0) return void 0;
|
|
6185
|
+
if (entry.owner !== authOwnerKey(credentials)) {
|
|
6186
|
+
this.turnStates.delete(turnKey);
|
|
6187
|
+
return;
|
|
6188
|
+
}
|
|
6189
|
+
return entry.value;
|
|
6190
|
+
}
|
|
6191
|
+
/**
|
|
5374
6192
|
* Send one payload with account rotation.
|
|
5375
6193
|
*
|
|
5376
6194
|
* The pool picks the account — skipping cooling, quota-exhausted and failed
|
|
@@ -5380,48 +6198,72 @@ var ResponsesClient = class {
|
|
|
5380
6198
|
* refresh is rejected leaves the rotation instead of invalidating the others.
|
|
5381
6199
|
* The payload is account-independent, so it is never rebuilt between attempts.
|
|
5382
6200
|
*/
|
|
5383
|
-
|
|
6201
|
+
/** What one pool attempt produced, including the account that served it. */
|
|
6202
|
+
async sendWithPool(payload, sessionId, turnKey, signal) {
|
|
5384
6203
|
const pool = this.accountPool;
|
|
5385
6204
|
const tried = /* @__PURE__ */ new Set();
|
|
5386
6205
|
while (true) {
|
|
5387
|
-
const
|
|
6206
|
+
const selected = await pool.getEffectiveAccount(tried, this.fetchFn);
|
|
6207
|
+
const account = selected.account;
|
|
6208
|
+
let credentials = selected.credentials;
|
|
5388
6209
|
tried.add(account.id);
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
6210
|
+
const release = await chatGPTConcurrency().acquire(account.id, signal);
|
|
6211
|
+
let held = true;
|
|
6212
|
+
const free = () => {
|
|
6213
|
+
if (!held) return;
|
|
6214
|
+
held = false;
|
|
6215
|
+
release();
|
|
6216
|
+
};
|
|
6217
|
+
try {
|
|
6218
|
+
let response = await this.request(payload, credentials, sessionId, turnKey, signal);
|
|
6219
|
+
if (response.status === 401) {
|
|
6220
|
+
await response.body?.cancel().catch(() => void 0);
|
|
6221
|
+
try {
|
|
6222
|
+
credentials = await pool.refreshAccountNow(account.id);
|
|
6223
|
+
response = await this.request(payload, credentials, sessionId, turnKey, signal);
|
|
6224
|
+
} catch (error) {
|
|
6225
|
+
await pool.markAuthFailed(account.id, error instanceof Error ? error.message : "ChatGPT sign-in expired.").catch(() => void 0);
|
|
6226
|
+
free();
|
|
6227
|
+
if (await pool.hasAnotherAvailableAccount(tried)) continue;
|
|
6228
|
+
throw new LlmError("ChatGPT sign-in has expired. Sign in again.", "AUTH", {
|
|
6229
|
+
status: 401,
|
|
6230
|
+
cause: error
|
|
6231
|
+
});
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6234
|
+
if (response.status === 429) {
|
|
6235
|
+
const after = retryAfterMs$1(response.headers);
|
|
6236
|
+
await response.body?.cancel().catch(() => void 0);
|
|
6237
|
+
await pool.markCooldown(account.id, after ?? DEFAULT_POOL_COOLDOWN_MS, "Codex 429").catch(() => void 0);
|
|
6238
|
+
free();
|
|
5397
6239
|
if (await pool.hasAnotherAvailableAccount(tried)) continue;
|
|
5398
|
-
throw new LlmError("
|
|
5399
|
-
status:
|
|
5400
|
-
|
|
6240
|
+
throw new LlmError("Codex rate limit reached.", "RATE_LIMIT", {
|
|
6241
|
+
status: 429,
|
|
6242
|
+
...after === void 0 ? {} : { providerRetryAfterMs: after }
|
|
5401
6243
|
});
|
|
5402
6244
|
}
|
|
6245
|
+
if (!response.ok) {
|
|
6246
|
+
const error = await responseError(response);
|
|
6247
|
+
free();
|
|
6248
|
+
throw error;
|
|
6249
|
+
}
|
|
6250
|
+
this.rememberTurnState(response, turnKey, credentials);
|
|
6251
|
+
return {
|
|
6252
|
+
response,
|
|
6253
|
+
release: free
|
|
6254
|
+
};
|
|
6255
|
+
} catch (error) {
|
|
6256
|
+
free();
|
|
6257
|
+
throw error;
|
|
5403
6258
|
}
|
|
5404
|
-
if (response.status === 429) {
|
|
5405
|
-
const after = retryAfterMs$1(response.headers);
|
|
5406
|
-
await response.body?.cancel().catch(() => void 0);
|
|
5407
|
-
await pool.markCooldown(account.id, after ?? DEFAULT_POOL_COOLDOWN_MS, "Codex 429").catch(() => void 0);
|
|
5408
|
-
if (await pool.hasAnotherAvailableAccount(tried)) continue;
|
|
5409
|
-
throw new LlmError("Codex rate limit reached.", "RATE_LIMIT", {
|
|
5410
|
-
status: 429,
|
|
5411
|
-
...after === void 0 ? {} : { providerRetryAfterMs: after }
|
|
5412
|
-
});
|
|
5413
|
-
}
|
|
5414
|
-
if (!response.ok) throw await responseError(response);
|
|
5415
|
-
this.rememberTurnState(response, sessionId);
|
|
5416
|
-
return response;
|
|
5417
6259
|
}
|
|
5418
6260
|
}
|
|
5419
|
-
async request(payload, credentials, sessionId, signal) {
|
|
6261
|
+
async request(payload, credentials, sessionId, turnKey, signal) {
|
|
5420
6262
|
try {
|
|
5421
6263
|
return await this.fetchFn(CODEX_RESPONSES_URL, {
|
|
5422
6264
|
method: "POST",
|
|
5423
6265
|
headers: {
|
|
5424
|
-
...codexHeaders(credentials, sessionId, { turnState: this.
|
|
6266
|
+
...codexHeaders(credentials, sessionId, { turnState: this.turnStateFor(turnKey, credentials) }),
|
|
5425
6267
|
"content-type": "application/json",
|
|
5426
6268
|
accept: "text/event-stream"
|
|
5427
6269
|
},
|
|
@@ -5445,6 +6287,16 @@ var ResponsesClient = class {
|
|
|
5445
6287
|
function promptCacheKeyFor(sessionId) {
|
|
5446
6288
|
return sessionId;
|
|
5447
6289
|
}
|
|
6290
|
+
/** Request-local fallback; never infer a human turn identity from message history. */
|
|
6291
|
+
function turnKeyFor(sessionId) {
|
|
6292
|
+
return `${sessionId}#${createHash("sha256").update(sessionId).update("\0").update(String(perRequestNonce())).digest("hex").slice(0, 24)}`;
|
|
6293
|
+
}
|
|
6294
|
+
/** A value no two requests in this process share. */
|
|
6295
|
+
let requestCounter = 0;
|
|
6296
|
+
function perRequestNonce() {
|
|
6297
|
+
requestCounter += 1;
|
|
6298
|
+
return requestCounter;
|
|
6299
|
+
}
|
|
5448
6300
|
async function* parseResponsesStream(response, signal, hiddenSandboxControls = /* @__PURE__ */ new Set()) {
|
|
5449
6301
|
if (response.body === null) throw new LlmError("Codex returned no response stream.", "PROVIDER_ERROR");
|
|
5450
6302
|
const reader = response.body.getReader();
|
|
@@ -5750,16 +6602,28 @@ function visibleReasoningDelta(delta, currentVisibleChars, alreadyTruncated) {
|
|
|
5750
6602
|
truncated: true
|
|
5751
6603
|
};
|
|
5752
6604
|
}
|
|
6605
|
+
/**
|
|
6606
|
+
* Map one response's usage onto DSH's disjoint accounting.
|
|
6607
|
+
*
|
|
6608
|
+
* `input_tokens` is the whole input, so cached input has to be subtracted out of
|
|
6609
|
+
* it and reported on its own field. Cache WRITES are counted separately from
|
|
6610
|
+
* reads and billed separately: leaving a write inside `inputTokens` would make a
|
|
6611
|
+
* cold prefix look cheaper than it was and hide the cost of a write. A field the
|
|
6612
|
+
* service omits is unknown, not zero, so it is simply not reported.
|
|
6613
|
+
*/
|
|
5753
6614
|
function mapUsage(value) {
|
|
5754
6615
|
if (value === null) return null;
|
|
5755
6616
|
const totalInput = number(value.input_tokens) ?? 0;
|
|
5756
6617
|
const outputTokens = number(value.output_tokens) ?? 0;
|
|
5757
|
-
const
|
|
6618
|
+
const details = record$2(value.input_tokens_details);
|
|
6619
|
+
const cached = number(details?.cached_tokens);
|
|
6620
|
+
const written = number(details?.cache_write_tokens);
|
|
5758
6621
|
const reasoning = number(record$2(value.output_tokens_details)?.reasoning_tokens);
|
|
5759
6622
|
return {
|
|
5760
|
-
inputTokens: Math.max(0, totalInput - cached),
|
|
6623
|
+
inputTokens: Math.max(0, totalInput - (cached ?? 0) - (written ?? 0)),
|
|
5761
6624
|
outputTokens,
|
|
5762
|
-
...cached
|
|
6625
|
+
...cached === void 0 ? {} : { cacheReadTokens: cached },
|
|
6626
|
+
...written === void 0 ? {} : { cacheWriteTokens: written },
|
|
5763
6627
|
...reasoning === void 0 ? {} : { reasoningTokens: reasoning }
|
|
5764
6628
|
};
|
|
5765
6629
|
}
|
|
@@ -6661,7 +7525,7 @@ function field(value, name) {
|
|
|
6661
7525
|
const candidate = value[name];
|
|
6662
7526
|
return typeof candidate === "string" && candidate !== "" ? candidate : null;
|
|
6663
7527
|
}
|
|
6664
|
-
function isRecord$
|
|
7528
|
+
function isRecord$23(value) {
|
|
6665
7529
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6666
7530
|
}
|
|
6667
7531
|
function readPreferencesUpdate(value, current) {
|
|
@@ -6695,7 +7559,7 @@ function readPreferencesUpdate(value, current) {
|
|
|
6695
7559
|
patch.searchProvider = value.searchProvider;
|
|
6696
7560
|
}
|
|
6697
7561
|
if ("contextWindowOverrides" in value) {
|
|
6698
|
-
if (!isRecord$
|
|
7562
|
+
if (!isRecord$23(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
|
|
6699
7563
|
const overrides = {};
|
|
6700
7564
|
for (const [model, contextWindow] of Object.entries(value.contextWindowOverrides)) {
|
|
6701
7565
|
if (!isCodexModelId(model)) throw new PreferenceError("Unknown Codex model for a context window override.");
|
|
@@ -7567,7 +8431,7 @@ const schemaFields = /* @__PURE__ */ new Set([
|
|
|
7567
8431
|
"required",
|
|
7568
8432
|
"propertyOrdering"
|
|
7569
8433
|
]);
|
|
7570
|
-
function isRecord$
|
|
8434
|
+
function isRecord$22(value) {
|
|
7571
8435
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7572
8436
|
}
|
|
7573
8437
|
function mergeSchemas(left, right) {
|
|
@@ -7575,7 +8439,7 @@ function mergeSchemas(left, right) {
|
|
|
7575
8439
|
...left,
|
|
7576
8440
|
...right
|
|
7577
8441
|
};
|
|
7578
|
-
if (isRecord$
|
|
8442
|
+
if (isRecord$22(left.properties) && isRecord$22(right.properties)) merged.properties = {
|
|
7579
8443
|
...left.properties,
|
|
7580
8444
|
...right.properties
|
|
7581
8445
|
};
|
|
@@ -7583,7 +8447,7 @@ function mergeSchemas(left, right) {
|
|
|
7583
8447
|
return merged;
|
|
7584
8448
|
}
|
|
7585
8449
|
function toAntigravityToolSchema(schema) {
|
|
7586
|
-
if (!isRecord$
|
|
8450
|
+
if (!isRecord$22(schema)) return schema;
|
|
7587
8451
|
const root = schema;
|
|
7588
8452
|
function resolveReference(reference) {
|
|
7589
8453
|
let target = root;
|
|
@@ -7591,13 +8455,13 @@ function toAntigravityToolSchema(schema) {
|
|
|
7591
8455
|
const path = reference === "#" ? [] : reference.slice(2).split("/");
|
|
7592
8456
|
for (const segment of path) {
|
|
7593
8457
|
const key = segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
7594
|
-
target = isRecord$
|
|
8458
|
+
target = isRecord$22(target) && Object.hasOwn(target, key) ? target[key] : void 0;
|
|
7595
8459
|
}
|
|
7596
|
-
if (!isRecord$
|
|
8460
|
+
if (!isRecord$22(target)) throw new LlmError(`Antigravity tool schema reference was not found: ${reference}`, "PROVIDER_ERROR");
|
|
7597
8461
|
return target;
|
|
7598
8462
|
}
|
|
7599
8463
|
function convert(node, references) {
|
|
7600
|
-
if (!isRecord$
|
|
8464
|
+
if (!isRecord$22(node)) return {};
|
|
7601
8465
|
let inherited = {};
|
|
7602
8466
|
if (typeof node.$ref === "string") {
|
|
7603
8467
|
if (references.has(node.$ref)) throw new LlmError(`Antigravity tool schema contains a recursive reference: ${node.$ref}`, "PROVIDER_ERROR");
|
|
@@ -7606,8 +8470,8 @@ function toAntigravityToolSchema(schema) {
|
|
|
7606
8470
|
if (Array.isArray(node.allOf)) for (const branch of node.allOf) inherited = mergeSchemas(inherited, convert(branch, references));
|
|
7607
8471
|
const out = {};
|
|
7608
8472
|
for (const [key, value] of Object.entries(node)) if (schemaFields.has(key)) out[key] = value;
|
|
7609
|
-
if (isRecord$
|
|
7610
|
-
if (isRecord$
|
|
8473
|
+
if (isRecord$22(node.properties)) out.properties = Object.fromEntries(Object.entries(node.properties).map(([name, child]) => [name, convert(child, references)]));
|
|
8474
|
+
if (isRecord$22(node.items)) out.items = convert(node.items, references);
|
|
7611
8475
|
const alternatives = Array.isArray(node.anyOf) ? node.anyOf : node.oneOf;
|
|
7612
8476
|
if (Array.isArray(alternatives)) out.anyOf = alternatives.map((child) => convert(child, references));
|
|
7613
8477
|
if (Array.isArray(node.type)) {
|
|
@@ -7635,10 +8499,10 @@ let toolCallCounter = 0;
|
|
|
7635
8499
|
function sanitizeText$5(text) {
|
|
7636
8500
|
return text.replace(/\0/g, "");
|
|
7637
8501
|
}
|
|
7638
|
-
function isRecord$
|
|
8502
|
+
function isRecord$21(value) {
|
|
7639
8503
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7640
8504
|
}
|
|
7641
|
-
function asString$
|
|
8505
|
+
function asString$14(value) {
|
|
7642
8506
|
return typeof value === "string" ? value : void 0;
|
|
7643
8507
|
}
|
|
7644
8508
|
function safeJsonParse$5(text) {
|
|
@@ -7654,26 +8518,26 @@ function sanitizeToolCallId(id, fallbackName) {
|
|
|
7654
8518
|
function toolCallIdNeeded(modelId, runtimeModel) {
|
|
7655
8519
|
return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-") || runtimeModel.startsWith("claude-") || runtimeModel.startsWith("gpt-oss-");
|
|
7656
8520
|
}
|
|
7657
|
-
function parseArguments(raw) {
|
|
7658
|
-
if (isRecord$
|
|
8521
|
+
function parseArguments$1(raw) {
|
|
8522
|
+
if (isRecord$21(raw)) return raw;
|
|
7659
8523
|
if (raw === void 0 || raw === null || raw === "") return {};
|
|
7660
8524
|
const parsed = typeof raw === "string" ? safeJsonParse$5(raw) : raw;
|
|
7661
|
-
return isRecord$
|
|
8525
|
+
return isRecord$21(parsed) ? parsed : {};
|
|
7662
8526
|
}
|
|
7663
8527
|
const NO_RESOLVED_IMAGES$5 = /* @__PURE__ */ new Map();
|
|
7664
8528
|
function attachmentOf$5(block) {
|
|
7665
8529
|
const attachment = block.attachment;
|
|
7666
|
-
if (!isRecord$
|
|
8530
|
+
if (!isRecord$21(attachment)) return void 0;
|
|
7667
8531
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
7668
8532
|
}
|
|
7669
|
-
function attachmentLabel$
|
|
7670
|
-
const attachment = isRecord$
|
|
7671
|
-
return asString$
|
|
8533
|
+
function attachmentLabel$6(block) {
|
|
8534
|
+
const attachment = isRecord$21(block.attachment) ? block.attachment : void 0;
|
|
8535
|
+
return asString$14(attachment?.name) || asString$14(attachment?.attachmentId);
|
|
7672
8536
|
}
|
|
7673
8537
|
function collectImageRefs$4(content, refs) {
|
|
7674
8538
|
if (!Array.isArray(content)) return;
|
|
7675
8539
|
for (const block of content) {
|
|
7676
|
-
if (!isRecord$
|
|
8540
|
+
if (!isRecord$21(block)) continue;
|
|
7677
8541
|
if (block.type === "image") {
|
|
7678
8542
|
const attachment = attachmentOf$5(block);
|
|
7679
8543
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -7682,7 +8546,7 @@ function collectImageRefs$4(content, refs) {
|
|
|
7682
8546
|
if (block.type === "tool-result") collectImageRefs$4(block.content, refs);
|
|
7683
8547
|
}
|
|
7684
8548
|
}
|
|
7685
|
-
function isAbort$
|
|
8549
|
+
function isAbort$4(error, signal) {
|
|
7686
8550
|
return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
|
|
7687
8551
|
}
|
|
7688
8552
|
/**
|
|
@@ -7711,13 +8575,13 @@ function base64Length$4(bytes) {
|
|
|
7711
8575
|
function requestImageBytes$4(block) {
|
|
7712
8576
|
const attachment = attachmentOf$5(block);
|
|
7713
8577
|
if (attachment) return base64Length$4(attachment.bytes);
|
|
7714
|
-
const inline = asString$
|
|
8578
|
+
const inline = asString$14(block.data) || asString$14(block.base64);
|
|
7715
8579
|
return inline ? inline.length : void 0;
|
|
7716
8580
|
}
|
|
7717
8581
|
function collectRequestImageBytes$4(content, lengths) {
|
|
7718
8582
|
if (!Array.isArray(content)) return;
|
|
7719
8583
|
for (const block of content) {
|
|
7720
|
-
if (!isRecord$
|
|
8584
|
+
if (!isRecord$21(block) || block.type !== "image") continue;
|
|
7721
8585
|
const bytes = requestImageBytes$4(block);
|
|
7722
8586
|
if (bytes !== void 0) lengths.push(bytes);
|
|
7723
8587
|
}
|
|
@@ -7752,7 +8616,7 @@ function offloadOldestRequestImages$4(options) {
|
|
|
7752
8616
|
if (remaining.count === 0 || !Array.isArray(message.content)) return message;
|
|
7753
8617
|
let replaced = false;
|
|
7754
8618
|
const content = message.content.map((block) => {
|
|
7755
|
-
if (remaining.count === 0 || !isRecord$
|
|
8619
|
+
if (remaining.count === 0 || !isRecord$21(block) || block.type !== "image") return block;
|
|
7756
8620
|
if (requestImageBytes$4(block) === void 0) return block;
|
|
7757
8621
|
remaining.count -= 1;
|
|
7758
8622
|
replaced = true;
|
|
@@ -7804,21 +8668,21 @@ async function resolveRequestImages$4(options, attachments, signal) {
|
|
|
7804
8668
|
data: Buffer.from(stored.data).toString("base64")
|
|
7805
8669
|
});
|
|
7806
8670
|
} catch (error) {
|
|
7807
|
-
if (isAbort$
|
|
8671
|
+
if (isAbort$4(error, signal)) throw error;
|
|
7808
8672
|
resolved.set(attachmentId, { kind: "unavailable" });
|
|
7809
8673
|
}
|
|
7810
8674
|
}));
|
|
7811
8675
|
return resolved;
|
|
7812
8676
|
}
|
|
7813
|
-
function unavailableImageText$
|
|
7814
|
-
const label = attachmentLabel$
|
|
8677
|
+
function unavailableImageText$6(block) {
|
|
8678
|
+
const label = attachmentLabel$6(block);
|
|
7815
8679
|
return `[image unavailable: ${label ? `${label} could not be read` : "the image could not be read"}; ask the user to attach it again if the image is needed]`;
|
|
7816
8680
|
}
|
|
7817
8681
|
function imageBlockToPart(block, images) {
|
|
7818
|
-
let data = asString$
|
|
7819
|
-
const source = isRecord$
|
|
7820
|
-
if (!data && source) data = asString$
|
|
7821
|
-
let mimeType = asString$
|
|
8682
|
+
let data = asString$14(block.data) || asString$14(block.base64);
|
|
8683
|
+
const source = isRecord$21(block.source) ? block.source : void 0;
|
|
8684
|
+
if (!data && source) data = asString$14(source.data) || asString$14(source.base64);
|
|
8685
|
+
let mimeType = asString$14(block.mimeType) || asString$14(block.mediaType) || (source ? asString$14(source.mimeType) || asString$14(source.mediaType) : void 0) || "image/png";
|
|
7822
8686
|
if (data?.startsWith("data:")) {
|
|
7823
8687
|
const match = data.match(/^data:([^;,]+);base64,(.*)$/s);
|
|
7824
8688
|
if (match) {
|
|
@@ -7841,10 +8705,10 @@ function contentToUserParts(content, images) {
|
|
|
7841
8705
|
if (typeof content === "string") return [{ text: sanitizeText$5(content) }];
|
|
7842
8706
|
if (!Array.isArray(content)) return [];
|
|
7843
8707
|
const parts = [];
|
|
7844
|
-
for (const block of content) if (isRecord$
|
|
7845
|
-
else if (isRecord$
|
|
8708
|
+
for (const block of content) if (isRecord$21(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText$5(block.text) });
|
|
8709
|
+
else if (isRecord$21(block) && block.type === "image") {
|
|
7846
8710
|
const img = imageBlockToPart(block, images);
|
|
7847
|
-
parts.push(img ?? { text: unavailableImageText$
|
|
8711
|
+
parts.push(img ?? { text: unavailableImageText$6(block) });
|
|
7848
8712
|
}
|
|
7849
8713
|
return parts;
|
|
7850
8714
|
}
|
|
@@ -7857,9 +8721,9 @@ function toolResultImageParts(blocks, images) {
|
|
|
7857
8721
|
if (!Array.isArray(blocks)) return [];
|
|
7858
8722
|
const parts = [];
|
|
7859
8723
|
for (const block of blocks) {
|
|
7860
|
-
if (!isRecord$
|
|
8724
|
+
if (!isRecord$21(block)) continue;
|
|
7861
8725
|
if (block.type === "image") {
|
|
7862
|
-
parts.push(imageBlockToPart(block, images) ?? { text: unavailableImageText$
|
|
8726
|
+
parts.push(imageBlockToPart(block, images) ?? { text: unavailableImageText$6(block) });
|
|
7863
8727
|
continue;
|
|
7864
8728
|
}
|
|
7865
8729
|
if (block.type === "tool-result") parts.push(...toolResultImageParts(block.content, images));
|
|
@@ -7869,11 +8733,11 @@ function toolResultImageParts(blocks, images) {
|
|
|
7869
8733
|
function toolResultText$5(blocks) {
|
|
7870
8734
|
if (!Array.isArray(blocks)) return "";
|
|
7871
8735
|
return blocks.map((block) => {
|
|
7872
|
-
if (!isRecord$
|
|
8736
|
+
if (!isRecord$21(block)) return "";
|
|
7873
8737
|
if (block.type === "text" && typeof block.text === "string") return sanitizeText$5(block.text);
|
|
7874
8738
|
if (block.type === "tool-result") return toolResultText$5(block.content);
|
|
7875
8739
|
if (block.type === "image") {
|
|
7876
|
-
const label = attachmentLabel$
|
|
8740
|
+
const label = attachmentLabel$6(block);
|
|
7877
8741
|
return label ? `[image: ${label}]` : "[image]";
|
|
7878
8742
|
}
|
|
7879
8743
|
return "";
|
|
@@ -7883,16 +8747,16 @@ function replayBlockFor$1(message, index) {
|
|
|
7883
8747
|
const source = message.source;
|
|
7884
8748
|
if (!source || source.kind !== "model" || source.provider !== "antigravity") return void 0;
|
|
7885
8749
|
const state = source.replayState;
|
|
7886
|
-
if (!isRecord$
|
|
8750
|
+
if (!isRecord$21(state)) return void 0;
|
|
7887
8751
|
if (Array.isArray(state.blocks)) return state.blocks[index];
|
|
7888
|
-
const resp = isRecord$
|
|
8752
|
+
const resp = isRecord$21(state.response) ? state.response : void 0;
|
|
7889
8753
|
if (resp) {
|
|
7890
8754
|
if (Array.isArray(resp.outputItems)) return resp.outputItems[index];
|
|
7891
8755
|
if (Array.isArray(resp.blocks)) return resp.blocks[index];
|
|
7892
8756
|
}
|
|
7893
8757
|
}
|
|
7894
8758
|
function thoughtSignature(part) {
|
|
7895
|
-
return asString$
|
|
8759
|
+
return asString$14(part?.thoughtSignature) || asString$14(part?.thought_signature) || asString$14(part?.thinkingSignature) || asString$14(part?.textSignature);
|
|
7896
8760
|
}
|
|
7897
8761
|
function replayPart(part) {
|
|
7898
8762
|
const copy = { ...part };
|
|
@@ -7908,10 +8772,10 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
7908
8772
|
if (!Array.isArray(message.content)) return parts;
|
|
7909
8773
|
for (let index = 0; index < message.content.length; index++) {
|
|
7910
8774
|
const block = message.content[index];
|
|
7911
|
-
if (!isRecord$
|
|
8775
|
+
if (!isRecord$21(block)) continue;
|
|
7912
8776
|
const replay = replayBlockFor$1(message, index);
|
|
7913
|
-
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$
|
|
7914
|
-
if ((block.type === "text" || block.type === "reasoning") && originalParts.length > 0 && originalParts.every((part) => !part.functionCall) && originalParts.map((part) => asString$
|
|
8777
|
+
const originalParts = Array.isArray(replay?.parts) ? replay.parts.filter(isRecord$21) : [];
|
|
8778
|
+
if ((block.type === "text" || block.type === "reasoning") && originalParts.length > 0 && originalParts.every((part) => !part.functionCall) && originalParts.map((part) => asString$14(part.text) || "").join("") === sanitizeText$5(String(block.text || ""))) {
|
|
7915
8779
|
parts.push(...originalParts.map(replayPart));
|
|
7916
8780
|
continue;
|
|
7917
8781
|
}
|
|
@@ -7931,8 +8795,8 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
7931
8795
|
} else if (block.type === "tool-call") {
|
|
7932
8796
|
const toolId = String(block.id || "");
|
|
7933
8797
|
const toolName = String(block.name || "");
|
|
7934
|
-
const originalCall = originalParts.find((part) => isRecord$
|
|
7935
|
-
const wireId = asString$
|
|
8798
|
+
const originalCall = originalParts.find((part) => isRecord$21(part.functionCall));
|
|
8799
|
+
const wireId = asString$14((isRecord$21(originalCall?.functionCall) ? originalCall.functionCall : void 0)?.id) || (toolCallIdNeeded(model.id, runtimeModel) ? sanitizeToolCallId(toolId, toolName) : originalCall ? void 0 : toolId || void 0);
|
|
7936
8800
|
toolCalls.set(toolId, {
|
|
7937
8801
|
name: toolName,
|
|
7938
8802
|
id: wireId
|
|
@@ -7941,7 +8805,7 @@ function assistantParts(message, model, runtimeModel, toolCalls) {
|
|
|
7941
8805
|
parts.push({
|
|
7942
8806
|
functionCall: {
|
|
7943
8807
|
name: toolName,
|
|
7944
|
-
args: parseArguments(block.arguments),
|
|
8808
|
+
args: parseArguments$1(block.arguments),
|
|
7945
8809
|
...wireId ? { id: wireId } : {}
|
|
7946
8810
|
},
|
|
7947
8811
|
...effectiveSignature ? { thoughtSignature: effectiveSignature } : {}
|
|
@@ -7984,7 +8848,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
7984
8848
|
continue;
|
|
7985
8849
|
}
|
|
7986
8850
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
7987
|
-
const userParts = contentToUserParts(content.filter((b) => !isRecord$
|
|
8851
|
+
const userParts = contentToUserParts(content.filter((b) => !isRecord$21(b) || b.type !== "tool-result"), images);
|
|
7988
8852
|
if (role === "system") {
|
|
7989
8853
|
if (userParts.length) contents.push({
|
|
7990
8854
|
role: GEMINI_ROLE.user,
|
|
@@ -7996,7 +8860,7 @@ function convertMessages(options, model, runtimeModel, images = NO_RESOLVED_IMAG
|
|
|
7996
8860
|
role: GEMINI_ROLE.user,
|
|
7997
8861
|
parts: userParts
|
|
7998
8862
|
});
|
|
7999
|
-
for (const b of content) if (isRecord$
|
|
8863
|
+
for (const b of content) if (isRecord$21(b) && b.type === "tool-result") pushToolResult(contents, b, toolCalls, model, runtimeModel, images);
|
|
8000
8864
|
}
|
|
8001
8865
|
return contents;
|
|
8002
8866
|
}
|
|
@@ -8119,7 +8983,7 @@ const USAGE_FIELDS = [
|
|
|
8119
8983
|
"totalTokenCount"
|
|
8120
8984
|
];
|
|
8121
8985
|
function collectUsage(value, state) {
|
|
8122
|
-
if (!isRecord$
|
|
8986
|
+
if (!isRecord$21(value)) return;
|
|
8123
8987
|
for (const key of USAGE_FIELDS) {
|
|
8124
8988
|
const count = value[key];
|
|
8125
8989
|
if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0) continue;
|
|
@@ -8149,15 +9013,15 @@ function processStreamLine$2(line, state) {
|
|
|
8149
9013
|
}
|
|
8150
9014
|
if (!json) return [];
|
|
8151
9015
|
const chunk = safeJsonParse$5(json);
|
|
8152
|
-
if (!isRecord$
|
|
8153
|
-
const responseData = isRecord$
|
|
9016
|
+
if (!isRecord$21(chunk)) return [];
|
|
9017
|
+
const responseData = isRecord$21(chunk.response) ? chunk.response : chunk;
|
|
8154
9018
|
const candidates = Array.isArray(responseData.candidates) ? responseData.candidates : [];
|
|
8155
|
-
const candidate = isRecord$
|
|
8156
|
-
const content = isRecord$
|
|
9019
|
+
const candidate = isRecord$21(candidates[0]) ? candidates[0] : void 0;
|
|
9020
|
+
const content = isRecord$21(candidate?.content) ? candidate.content : void 0;
|
|
8157
9021
|
const parts = Array.isArray(content?.parts) ? content.parts : [];
|
|
8158
9022
|
const out = [];
|
|
8159
9023
|
for (const part of parts) {
|
|
8160
|
-
if (!isRecord$
|
|
9024
|
+
if (!isRecord$21(part)) continue;
|
|
8161
9025
|
if (typeof part.text === "string" && part.text !== "") {
|
|
8162
9026
|
const isThinking = Boolean(part.thought);
|
|
8163
9027
|
const blockType = isThinking ? "reasoning" : "text";
|
|
@@ -8189,7 +9053,7 @@ function processStreamLine$2(line, state) {
|
|
|
8189
9053
|
index: state.currentBlock.index,
|
|
8190
9054
|
text: delta
|
|
8191
9055
|
});
|
|
8192
|
-
} else if (!isRecord$
|
|
9056
|
+
} else if (!isRecord$21(part.functionCall) && thoughtSignature(part)) {
|
|
8193
9057
|
if (state.replayBlocks.length === 0) {
|
|
8194
9058
|
const type = part.thought ? "reasoning" : "text";
|
|
8195
9059
|
state.blocks.push({
|
|
@@ -8213,12 +9077,12 @@ function processStreamLine$2(line, state) {
|
|
|
8213
9077
|
}
|
|
8214
9078
|
state.replayBlocks[state.replayBlocks.length - 1].parts.push(replayPart(part));
|
|
8215
9079
|
}
|
|
8216
|
-
if (isRecord$
|
|
9080
|
+
if (isRecord$21(part.functionCall)) {
|
|
8217
9081
|
out.push(...closeCurrentBlock(state));
|
|
8218
9082
|
const fc = part.functionCall;
|
|
8219
|
-
const toolName = asString$
|
|
8220
|
-
const toolId = asString$
|
|
8221
|
-
const argsText = JSON.stringify(isRecord$
|
|
9083
|
+
const toolName = asString$14(fc.name) || "";
|
|
9084
|
+
const toolId = asString$14(fc.id) || sanitizeToolCallId("", toolName);
|
|
9085
|
+
const argsText = JSON.stringify(isRecord$21(fc.args) ? fc.args : {});
|
|
8222
9086
|
const index = state.blocks.length;
|
|
8223
9087
|
const block = {
|
|
8224
9088
|
type: "tool-call",
|
|
@@ -8255,7 +9119,7 @@ function processStreamLine$2(line, state) {
|
|
|
8255
9119
|
}
|
|
8256
9120
|
collectUsage(chunk.usageMetadata, state);
|
|
8257
9121
|
if (responseData !== chunk) collectUsage(responseData.usageMetadata, state);
|
|
8258
|
-
const finishReason = asString$
|
|
9122
|
+
const finishReason = asString$14(candidate?.finishReason) || asString$14(responseData.finishReason);
|
|
8259
9123
|
if (finishReason) {
|
|
8260
9124
|
state.finishReason = finishReason;
|
|
8261
9125
|
out.push(...closeCurrentBlock(state));
|
|
@@ -9909,6 +10773,7 @@ const CALLBACK_ALLOWED_ORIGINS = [
|
|
|
9909
10773
|
"http://localhost:3000"
|
|
9910
10774
|
];
|
|
9911
10775
|
const DISCOVERY_TIMEOUT_MS$3 = 15e3;
|
|
10776
|
+
const CATALOG_CACHE_TTL_MS$3 = 1800 * 1e3;
|
|
9912
10777
|
const STREAM_IDLE_TIMEOUT_MS$4 = 3e5;
|
|
9913
10778
|
const STREAM_IDLE_TIMEOUT_CODE$4 = "LLM_STREAM_IDLE_TIMEOUT";
|
|
9914
10779
|
/**
|
|
@@ -9925,6 +10790,8 @@ const HEADER_PROJECT_SLUG = "x-command-code-project-slug";
|
|
|
9925
10790
|
const HEADER_TASTE_LEARNING = "x-command-code-taste-learning";
|
|
9926
10791
|
const HEADER_SESSION_ID = "x-command-code-session-id";
|
|
9927
10792
|
const HEADER_OSS_PRIMARY_PROVIDER = "x-command-code-oss-primary-provider";
|
|
10793
|
+
/** Official header that enforces Zero Data Retention (ZDR) routing. */
|
|
10794
|
+
const HEADER_ZDR = "x-cmd-zdr";
|
|
9928
10795
|
function resolveApiEnv(raw = process.env.DSH_COMMAND_CODE_ENV) {
|
|
9929
10796
|
const value = (raw || "").trim().toLowerCase();
|
|
9930
10797
|
return value === "staging" || value === "local" ? value : "prod";
|
|
@@ -9952,6 +10819,21 @@ function wireForModel$2(modelId) {
|
|
|
9952
10819
|
return /^claude[-/]/i.test(modelId.trim()) ? "anthropic" : "openai";
|
|
9953
10820
|
}
|
|
9954
10821
|
/**
|
|
10822
|
+
* Wire one catalog entry says it is served on, when the catalog said so.
|
|
10823
|
+
*
|
|
10824
|
+
* The provider publishes `supported_endpoints` per model, and that is the
|
|
10825
|
+
* authority on which route answers for it. A model the catalog does not describe
|
|
10826
|
+
* returns undefined so the caller falls back to {@link wireForModel} instead of
|
|
10827
|
+
* being sent to a route nobody vouched for.
|
|
10828
|
+
*/
|
|
10829
|
+
function wireForCatalogEntry$1(entry) {
|
|
10830
|
+
for (const endpoint of entry.supportedEndpoints ?? []) {
|
|
10831
|
+
if (/\/v1\/messages\b|\bmessages\b/i.test(endpoint)) return "anthropic";
|
|
10832
|
+
if (/\/v1\/responses\b|\bresponses\b/i.test(endpoint)) return "responses";
|
|
10833
|
+
if (/chat\/completions|\bcompletions\b/i.test(endpoint)) return "openai";
|
|
10834
|
+
}
|
|
10835
|
+
}
|
|
10836
|
+
/**
|
|
9955
10837
|
* Reasoning levels one model advertises, in DSH's vocabulary.
|
|
9956
10838
|
*
|
|
9957
10839
|
* The registry is the authority: a model it describes but gives no
|
|
@@ -10457,13 +11339,13 @@ function commandCodeHeaders(apiKey, extra = {}) {
|
|
|
10457
11339
|
function timeoutSignal$3(signal, ms) {
|
|
10458
11340
|
return signal ? AbortSignal.any([signal, AbortSignal.timeout(ms)]) : AbortSignal.timeout(ms);
|
|
10459
11341
|
}
|
|
10460
|
-
function isRecord$
|
|
11342
|
+
function isRecord$20(value) {
|
|
10461
11343
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10462
11344
|
}
|
|
10463
11345
|
function asRecord$9(value) {
|
|
10464
|
-
return isRecord$
|
|
11346
|
+
return isRecord$20(value) ? value : void 0;
|
|
10465
11347
|
}
|
|
10466
|
-
function asString$
|
|
11348
|
+
function asString$13(value) {
|
|
10467
11349
|
if (typeof value === "string" && value.trim() !== "") return value;
|
|
10468
11350
|
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
10469
11351
|
}
|
|
@@ -10476,10 +11358,19 @@ function asNumber$3(value) {
|
|
|
10476
11358
|
}
|
|
10477
11359
|
function firstString$1(record, keys) {
|
|
10478
11360
|
for (const key of keys) {
|
|
10479
|
-
const value = asString$
|
|
11361
|
+
const value = asString$13(record[key]);
|
|
10480
11362
|
if (value !== void 0) return value;
|
|
10481
11363
|
}
|
|
10482
11364
|
}
|
|
11365
|
+
/** First key that holds a non-empty array of strings. */
|
|
11366
|
+
function firstStringArray(record, keys) {
|
|
11367
|
+
for (const key of keys) {
|
|
11368
|
+
const value = record[key];
|
|
11369
|
+
if (!Array.isArray(value)) continue;
|
|
11370
|
+
const entries = value.filter((entry) => typeof entry === "string" && entry !== "");
|
|
11371
|
+
if (entries.length > 0) return entries;
|
|
11372
|
+
}
|
|
11373
|
+
}
|
|
10483
11374
|
function firstNumber$2(record, keys) {
|
|
10484
11375
|
for (const key of keys) {
|
|
10485
11376
|
const value = asNumber$3(record[key]);
|
|
@@ -10496,7 +11387,7 @@ function deepValue(value, keys, depth = 0) {
|
|
|
10496
11387
|
}
|
|
10497
11388
|
return;
|
|
10498
11389
|
}
|
|
10499
|
-
if (!isRecord$
|
|
11390
|
+
if (!isRecord$20(value)) return void 0;
|
|
10500
11391
|
for (const key of keys) if (value[key] !== void 0 && value[key] !== null) return value[key];
|
|
10501
11392
|
for (const nested of Object.values(value)) {
|
|
10502
11393
|
const hit = deepValue(nested, keys, depth + 1);
|
|
@@ -10510,7 +11401,7 @@ function collectRecords(value, keys, depth = 0, out = []) {
|
|
|
10510
11401
|
for (const entry of value) collectRecords(entry, keys, depth + 1, out);
|
|
10511
11402
|
return out;
|
|
10512
11403
|
}
|
|
10513
|
-
if (!isRecord$
|
|
11404
|
+
if (!isRecord$20(value)) return out;
|
|
10514
11405
|
if (keys.some((key) => value[key] !== void 0)) out.push(value);
|
|
10515
11406
|
for (const nested of Object.values(value)) collectRecords(nested, keys, depth + 1, out);
|
|
10516
11407
|
return out;
|
|
@@ -10714,7 +11605,7 @@ function parseCreditBalances(payload, consumed = []) {
|
|
|
10714
11605
|
/** Resolve the plan id the subscription or credits payload reports, when either does. */
|
|
10715
11606
|
function parsePlanId(payloads) {
|
|
10716
11607
|
for (const payload of payloads) {
|
|
10717
|
-
const id = asString$
|
|
11608
|
+
const id = asString$13(deepValue(payload, [
|
|
10718
11609
|
"planId",
|
|
10719
11610
|
"plan_id",
|
|
10720
11611
|
"priceId",
|
|
@@ -10733,7 +11624,7 @@ function parsePlanId(payloads) {
|
|
|
10733
11624
|
*/
|
|
10734
11625
|
function parseSubscriptionStatus(payload) {
|
|
10735
11626
|
const root = asRecord$9(payload) ?? {};
|
|
10736
|
-
return asString$
|
|
11627
|
+
return asString$13(deepValue(asRecord$9(root.data) ?? root, ["status"])) ?? null;
|
|
10737
11628
|
}
|
|
10738
11629
|
/** End of the current billing period, in Unix milliseconds. */
|
|
10739
11630
|
function parseSubscriptionPeriodEnd(payload) {
|
|
@@ -10986,6 +11877,7 @@ function extractUnlimited(payload) {
|
|
|
10986
11877
|
return deepValue(payload, ["unlimited", "isUnlimited"]) === true;
|
|
10987
11878
|
}
|
|
10988
11879
|
let cachedCatalog$1;
|
|
11880
|
+
let catalogIsRehydrated = false;
|
|
10989
11881
|
/** Bumped by every cache clear so a fetch already in flight cannot publish. */
|
|
10990
11882
|
let catalogCacheEpoch$2 = 0;
|
|
10991
11883
|
let catalogInFlight$2 = null;
|
|
@@ -11020,7 +11912,8 @@ function parseProviderModels(payload) {
|
|
|
11020
11912
|
"contextLength",
|
|
11021
11913
|
"context_window",
|
|
11022
11914
|
"contextWindow"
|
|
11023
|
-
])
|
|
11915
|
+
]),
|
|
11916
|
+
...firstStringArray(record, ["supported_endpoints", "supportedEndpoints"]) === void 0 ? {} : { supportedEndpoints: firstStringArray(record, ["supported_endpoints", "supportedEndpoints"]) }
|
|
11024
11917
|
});
|
|
11025
11918
|
}
|
|
11026
11919
|
return models;
|
|
@@ -11067,6 +11960,7 @@ async function loadProviderModels(options = {}) {
|
|
|
11067
11960
|
models,
|
|
11068
11961
|
fetchedAt
|
|
11069
11962
|
};
|
|
11963
|
+
catalogIsRehydrated = true;
|
|
11070
11964
|
}, 0);
|
|
11071
11965
|
const warm = cachedCatalog$1;
|
|
11072
11966
|
if (rehydratedAt > 0 && warm !== void 0) {
|
|
@@ -11088,6 +11982,7 @@ function refreshProviderModels$1(options = {}) {
|
|
|
11088
11982
|
models,
|
|
11089
11983
|
fetchedAt
|
|
11090
11984
|
};
|
|
11985
|
+
catalogIsRehydrated = false;
|
|
11091
11986
|
writeCatalogSnapshot(catalogSnapshotName("command-code", options.apiEnv ?? resolveApiEnv()), models, fetchedAt);
|
|
11092
11987
|
}
|
|
11093
11988
|
return models.length > 0 ? models : cachedCatalog$1?.models ?? [];
|
|
@@ -11097,10 +11992,19 @@ function refreshProviderModels$1(options = {}) {
|
|
|
11097
11992
|
if (catalogInFlight$2 === request) catalogInFlight$2 = null;
|
|
11098
11993
|
});
|
|
11099
11994
|
}
|
|
11995
|
+
function isCatalogStale() {
|
|
11996
|
+
if (cachedCatalog$1 === void 0) return true;
|
|
11997
|
+
if (catalogIsRehydrated) return true;
|
|
11998
|
+
return Date.now() - cachedCatalog$1.fetchedAt >= CATALOG_CACHE_TTL_MS$3;
|
|
11999
|
+
}
|
|
12000
|
+
function getCachedCatalogFetchedAt() {
|
|
12001
|
+
return cachedCatalog$1?.fetchedAt ?? null;
|
|
12002
|
+
}
|
|
11100
12003
|
function clearCachedCatalog$3() {
|
|
11101
12004
|
catalogCacheEpoch$2 += 1;
|
|
11102
12005
|
cachedCatalog$1 = void 0;
|
|
11103
12006
|
catalogInFlight$2 = null;
|
|
12007
|
+
catalogIsRehydrated = false;
|
|
11104
12008
|
catalogSnapshotScopesLoaded$2.clear();
|
|
11105
12009
|
}
|
|
11106
12010
|
async function getJson(path, apiKey, options = {}) {
|
|
@@ -11256,7 +12160,7 @@ function buildModelOptions$2(catalog, enabledModelIds, overrides) {
|
|
|
11256
12160
|
contextWindow: overrides[model.id] && overrides[model.id] > 0 ? overrides[model.id] : contextWindow,
|
|
11257
12161
|
defaultMaxTokens: maxOutputTokensFor$4(model.id),
|
|
11258
12162
|
...efforts.length > 0 ? { reasoningEfforts: efforts } : {},
|
|
11259
|
-
wire: wireForModel$2(model.id)
|
|
12163
|
+
wire: wireForCatalogEntry$1(model) ?? wireForModel$2(model.id)
|
|
11260
12164
|
};
|
|
11261
12165
|
});
|
|
11262
12166
|
}
|
|
@@ -11272,10 +12176,10 @@ function buildModelOptions$2(catalog, enabledModelIds, overrides) {
|
|
|
11272
12176
|
*
|
|
11273
12177
|
* https://commandcode.ai/blog/command-code-provider-api
|
|
11274
12178
|
*/
|
|
11275
|
-
function isRecord$
|
|
12179
|
+
function isRecord$19(value) {
|
|
11276
12180
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11277
12181
|
}
|
|
11278
|
-
function asString$
|
|
12182
|
+
function asString$12(value) {
|
|
11279
12183
|
return typeof value === "string" ? value : void 0;
|
|
11280
12184
|
}
|
|
11281
12185
|
function safeJsonParse$4(text) {
|
|
@@ -11288,7 +12192,7 @@ function safeJsonParse$4(text) {
|
|
|
11288
12192
|
function sanitizeText$4(text) {
|
|
11289
12193
|
return text.replace(/\0/g, "");
|
|
11290
12194
|
}
|
|
11291
|
-
function isAbort$
|
|
12195
|
+
function isAbort$3(error, signal) {
|
|
11292
12196
|
return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
|
|
11293
12197
|
}
|
|
11294
12198
|
const NO_RESOLVED_IMAGES$4 = /* @__PURE__ */ new Map();
|
|
@@ -11312,17 +12216,17 @@ const SUPPORTED_IMAGE_MEDIA_TYPES$4 = /* @__PURE__ */ new Set([
|
|
|
11312
12216
|
]);
|
|
11313
12217
|
function attachmentOf$4(block) {
|
|
11314
12218
|
const attachment = block.attachment;
|
|
11315
|
-
if (!isRecord$
|
|
12219
|
+
if (!isRecord$19(attachment)) return void 0;
|
|
11316
12220
|
return typeof attachment.attachmentId === "string" ? attachment : void 0;
|
|
11317
12221
|
}
|
|
11318
|
-
function attachmentLabel$
|
|
11319
|
-
const attachment = isRecord$
|
|
11320
|
-
return asString$
|
|
12222
|
+
function attachmentLabel$5(block) {
|
|
12223
|
+
const attachment = isRecord$19(block.attachment) ? block.attachment : void 0;
|
|
12224
|
+
return asString$12(attachment?.name) || asString$12(attachment?.attachmentId);
|
|
11321
12225
|
}
|
|
11322
12226
|
function collectImageRefs$3(content, refs) {
|
|
11323
12227
|
if (!Array.isArray(content)) return;
|
|
11324
12228
|
for (const block of content) {
|
|
11325
|
-
if (!isRecord$
|
|
12229
|
+
if (!isRecord$19(block)) continue;
|
|
11326
12230
|
if (block.type === "image") {
|
|
11327
12231
|
const attachment = attachmentOf$4(block);
|
|
11328
12232
|
if (attachment) refs.set(attachment.attachmentId, attachment);
|
|
@@ -11337,13 +12241,13 @@ function base64Length$3(bytes) {
|
|
|
11337
12241
|
function requestImageBytes$3(block) {
|
|
11338
12242
|
const attachment = attachmentOf$4(block);
|
|
11339
12243
|
if (attachment) return base64Length$3(attachment.bytes);
|
|
11340
|
-
const inline = asString$
|
|
12244
|
+
const inline = asString$12(block.data) || asString$12(block.base64);
|
|
11341
12245
|
return inline ? inline.length : void 0;
|
|
11342
12246
|
}
|
|
11343
12247
|
function collectRequestImageBytes$3(content, lengths) {
|
|
11344
12248
|
if (!Array.isArray(content)) return;
|
|
11345
12249
|
for (const block of content) {
|
|
11346
|
-
if (!isRecord$
|
|
12250
|
+
if (!isRecord$19(block) || block.type !== "image") continue;
|
|
11347
12251
|
const bytes = requestImageBytes$3(block);
|
|
11348
12252
|
if (bytes !== void 0) lengths.push(bytes);
|
|
11349
12253
|
}
|
|
@@ -11370,7 +12274,7 @@ function offloadOldestRequestImages$3(options) {
|
|
|
11370
12274
|
if (remaining.count === 0 || !Array.isArray(message.content)) return message;
|
|
11371
12275
|
let replaced = false;
|
|
11372
12276
|
const content = message.content.map((block) => {
|
|
11373
|
-
if (remaining.count === 0 || !isRecord$
|
|
12277
|
+
if (remaining.count === 0 || !isRecord$19(block) || block.type !== "image") return block;
|
|
11374
12278
|
if (requestImageBytes$3(block) === void 0) return block;
|
|
11375
12279
|
remaining.count -= 1;
|
|
11376
12280
|
replaced = true;
|
|
@@ -11412,21 +12316,21 @@ async function resolveRequestImages$3(options, attachments, signal) {
|
|
|
11412
12316
|
data: Buffer.from(stored.data).toString("base64")
|
|
11413
12317
|
});
|
|
11414
12318
|
} catch (error) {
|
|
11415
|
-
if (isAbort$
|
|
12319
|
+
if (isAbort$3(error, signal)) throw error;
|
|
11416
12320
|
resolved.set(attachmentId, { kind: "unavailable" });
|
|
11417
12321
|
}
|
|
11418
12322
|
}));
|
|
11419
12323
|
return resolved;
|
|
11420
12324
|
}
|
|
11421
|
-
function unavailableImageText$
|
|
11422
|
-
const label = attachmentLabel$
|
|
12325
|
+
function unavailableImageText$5(block) {
|
|
12326
|
+
const label = attachmentLabel$5(block);
|
|
11423
12327
|
return `[image unavailable: ${label ? `${label} could not be read` : "the image could not be read"}; ask the user to attach it again if the image is needed]`;
|
|
11424
12328
|
}
|
|
11425
12329
|
function imageBlockToInline$3(block, images) {
|
|
11426
|
-
let data = asString$
|
|
11427
|
-
const source = isRecord$
|
|
11428
|
-
if (!data && source) data = asString$
|
|
11429
|
-
let mediaType = asString$
|
|
12330
|
+
let data = asString$12(block.data) || asString$12(block.base64);
|
|
12331
|
+
const source = isRecord$19(block.source) ? block.source : void 0;
|
|
12332
|
+
if (!data && source) data = asString$12(source.data) || asString$12(source.base64);
|
|
12333
|
+
let mediaType = asString$12(block.mimeType) || asString$12(block.mediaType) || (source ? asString$12(source.mimeType) || asString$12(source.mediaType) : void 0) || "image/png";
|
|
11430
12334
|
if (data?.startsWith("data:")) {
|
|
11431
12335
|
const matched = data.match(/^data:([^;,]+);base64,(.*)$/s);
|
|
11432
12336
|
if (matched) {
|
|
@@ -11450,7 +12354,7 @@ function textOf$5(content) {
|
|
|
11450
12354
|
if (!Array.isArray(content)) return "";
|
|
11451
12355
|
const parts = [];
|
|
11452
12356
|
for (const block of content) {
|
|
11453
|
-
if (!isRecord$
|
|
12357
|
+
if (!isRecord$19(block)) continue;
|
|
11454
12358
|
if (block.type === "text" && typeof block.text === "string") parts.push(sanitizeText$4(block.text));
|
|
11455
12359
|
else if (block.type === "tool-result") parts.push(textOf$5(block.content));
|
|
11456
12360
|
}
|
|
@@ -11459,10 +12363,10 @@ function textOf$5(content) {
|
|
|
11459
12363
|
function toolResultText$4(blocks) {
|
|
11460
12364
|
if (!Array.isArray(blocks)) return "";
|
|
11461
12365
|
return blocks.map((block) => {
|
|
11462
|
-
if (!isRecord$
|
|
12366
|
+
if (!isRecord$19(block)) return "";
|
|
11463
12367
|
if (block.type === "text" && typeof block.text === "string") return sanitizeText$4(block.text);
|
|
11464
12368
|
if (block.type === "tool-result") return toolResultText$4(block.content);
|
|
11465
|
-
if (block.type === "image") return `[image: ${attachmentLabel$
|
|
12369
|
+
if (block.type === "image") return `[image: ${attachmentLabel$5(block) ?? "attached image"}]`;
|
|
11466
12370
|
return "";
|
|
11467
12371
|
}).join("");
|
|
11468
12372
|
}
|
|
@@ -11477,7 +12381,7 @@ function toolResultBlocks$2(blocks, images) {
|
|
|
11477
12381
|
const out = [];
|
|
11478
12382
|
let hasImage = false;
|
|
11479
12383
|
for (const block of blocks) {
|
|
11480
|
-
if (!isRecord$
|
|
12384
|
+
if (!isRecord$19(block)) continue;
|
|
11481
12385
|
if (block.type === "text" && typeof block.text === "string") {
|
|
11482
12386
|
out.push({
|
|
11483
12387
|
type: "text",
|
|
@@ -11498,7 +12402,7 @@ function toolResultBlocks$2(blocks, images) {
|
|
|
11498
12402
|
});
|
|
11499
12403
|
else out.push({
|
|
11500
12404
|
type: "text",
|
|
11501
|
-
text: unavailableImageText$
|
|
12405
|
+
text: unavailableImageText$5(block)
|
|
11502
12406
|
});
|
|
11503
12407
|
continue;
|
|
11504
12408
|
}
|
|
@@ -11526,7 +12430,7 @@ function toolResultImageBlocks$2(blocks, images) {
|
|
|
11526
12430
|
if (!Array.isArray(blocks)) return [];
|
|
11527
12431
|
const out = [];
|
|
11528
12432
|
for (const block of blocks) {
|
|
11529
|
-
if (!isRecord$
|
|
12433
|
+
if (!isRecord$19(block)) continue;
|
|
11530
12434
|
if (block.type === "image") {
|
|
11531
12435
|
const inline = imageBlockToInline$3(block, images);
|
|
11532
12436
|
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) out.push({
|
|
@@ -11535,7 +12439,7 @@ function toolResultImageBlocks$2(blocks, images) {
|
|
|
11535
12439
|
});
|
|
11536
12440
|
else out.push({
|
|
11537
12441
|
type: "text",
|
|
11538
|
-
text: unavailableImageText$
|
|
12442
|
+
text: unavailableImageText$5(block)
|
|
11539
12443
|
});
|
|
11540
12444
|
continue;
|
|
11541
12445
|
}
|
|
@@ -11570,7 +12474,7 @@ function nonSystemMessages$4(options) {
|
|
|
11570
12474
|
}
|
|
11571
12475
|
/** Drop the JSON-Schema keywords provider gateways reject or ignore. */
|
|
11572
12476
|
function stripMetaSchema$2(schema) {
|
|
11573
|
-
if (!isRecord$
|
|
12477
|
+
if (!isRecord$19(schema)) return {
|
|
11574
12478
|
type: "object",
|
|
11575
12479
|
properties: {}
|
|
11576
12480
|
};
|
|
@@ -11587,7 +12491,7 @@ function openAIUserContent$2(message, images) {
|
|
|
11587
12491
|
const parts = [];
|
|
11588
12492
|
let hasImage = false;
|
|
11589
12493
|
for (const block of message.content) {
|
|
11590
|
-
if (!isRecord$
|
|
12494
|
+
if (!isRecord$19(block)) continue;
|
|
11591
12495
|
if (block.type === "text" && typeof block.text === "string") {
|
|
11592
12496
|
const text = sanitizeText$4(block.text);
|
|
11593
12497
|
if (text !== "") parts.push({
|
|
@@ -11604,7 +12508,7 @@ function openAIUserContent$2(message, images) {
|
|
|
11604
12508
|
});
|
|
11605
12509
|
} else parts.push({
|
|
11606
12510
|
type: "text",
|
|
11607
|
-
text: unavailableImageText$
|
|
12511
|
+
text: unavailableImageText$5(block)
|
|
11608
12512
|
});
|
|
11609
12513
|
}
|
|
11610
12514
|
}
|
|
@@ -11615,7 +12519,7 @@ function openAIAssistantContent$2(message) {
|
|
|
11615
12519
|
const textParts = [];
|
|
11616
12520
|
const toolCalls = [];
|
|
11617
12521
|
for (const block of message.content) {
|
|
11618
|
-
if (!isRecord$
|
|
12522
|
+
if (!isRecord$19(block)) continue;
|
|
11619
12523
|
if (block.type === "text" && typeof block.text === "string") textParts.push(sanitizeText$4(block.text));
|
|
11620
12524
|
else if (block.type === "tool-call" && typeof block.name === "string") toolCalls.push({
|
|
11621
12525
|
id: typeof block.id === "string" && block.id !== "" ? block.id : `call_${toolCalls.length}`,
|
|
@@ -11647,7 +12551,7 @@ function buildOpenAIRequest$1(options, images = NO_RESOLVED_IMAGES$4) {
|
|
|
11647
12551
|
while (index < conversation.length && isToolResultMessage$4(conversation[index])) {
|
|
11648
12552
|
const current = conversation[index];
|
|
11649
12553
|
const block = current.content[0];
|
|
11650
|
-
const callId = isRecord$
|
|
12554
|
+
const callId = isRecord$19(block) && typeof block.toolCallId === "string" ? block.toolCallId : "";
|
|
11651
12555
|
messages.push({
|
|
11652
12556
|
role: "tool",
|
|
11653
12557
|
tool_call_id: callId,
|
|
@@ -11708,7 +12612,7 @@ function anthropicUserContent$2(message, images) {
|
|
|
11708
12612
|
if (!Array.isArray(message.content)) return [];
|
|
11709
12613
|
const blocks = [];
|
|
11710
12614
|
for (const block of message.content) {
|
|
11711
|
-
if (!isRecord$
|
|
12615
|
+
if (!isRecord$19(block)) continue;
|
|
11712
12616
|
if (block.type === "text" && typeof block.text === "string") {
|
|
11713
12617
|
const text = sanitizeText$4(block.text);
|
|
11714
12618
|
if (text !== "") blocks.push({
|
|
@@ -11727,7 +12631,7 @@ function anthropicUserContent$2(message, images) {
|
|
|
11727
12631
|
});
|
|
11728
12632
|
else blocks.push({
|
|
11729
12633
|
type: "text",
|
|
11730
|
-
text: unavailableImageText$
|
|
12634
|
+
text: unavailableImageText$5(block)
|
|
11731
12635
|
});
|
|
11732
12636
|
} else if (block.type === "tool-result") {
|
|
11733
12637
|
const callId = typeof block.toolCallId === "string" ? block.toolCallId : "";
|
|
@@ -11745,7 +12649,7 @@ function anthropicUserContent$2(message, images) {
|
|
|
11745
12649
|
function anthropicAssistantContent$2(message) {
|
|
11746
12650
|
const blocks = [];
|
|
11747
12651
|
for (const block of message.content) {
|
|
11748
|
-
if (!isRecord$
|
|
12652
|
+
if (!isRecord$19(block)) continue;
|
|
11749
12653
|
if (block.type === "text" && typeof block.text === "string") {
|
|
11750
12654
|
const text = sanitizeText$4(block.text);
|
|
11751
12655
|
if (text !== "") blocks.push({
|
|
@@ -11758,7 +12662,7 @@ function anthropicAssistantContent$2(message) {
|
|
|
11758
12662
|
type: "tool_use",
|
|
11759
12663
|
id: typeof block.id === "string" && block.id !== "" ? block.id : `toolu_${blocks.length}`,
|
|
11760
12664
|
name: block.name,
|
|
11761
|
-
input: isRecord$
|
|
12665
|
+
input: isRecord$19(parsed) ? parsed : {}
|
|
11762
12666
|
});
|
|
11763
12667
|
}
|
|
11764
12668
|
}
|
|
@@ -11835,7 +12739,136 @@ function buildAnthropicRequest$1(options, images = NO_RESOLVED_IMAGES$4) {
|
|
|
11835
12739
|
}
|
|
11836
12740
|
/** Build the body for whichever endpoint serves `modelId`. */
|
|
11837
12741
|
function buildRequest$1(options, wire, images = NO_RESOLVED_IMAGES$4) {
|
|
11838
|
-
|
|
12742
|
+
if (wire === "anthropic") return buildAnthropicRequest$1(options, images);
|
|
12743
|
+
if (wire === "responses") return buildResponsesRequest(options, images);
|
|
12744
|
+
return buildOpenAIRequest$1(options, images);
|
|
12745
|
+
}
|
|
12746
|
+
/**
|
|
12747
|
+
* Build one `/responses` body.
|
|
12748
|
+
*
|
|
12749
|
+
* The Responses API takes a flat `input` list instead of `messages`, and its
|
|
12750
|
+
* reasoning and tool calls are item types rather than message fields. This is a
|
|
12751
|
+
* separate builder rather than a reshaping of the Chat Completions one: the two
|
|
12752
|
+
* APIs differ in more than field names, and a translated body is a request the
|
|
12753
|
+
* route rejects.
|
|
12754
|
+
*/
|
|
12755
|
+
function responsesUserContent(message, images) {
|
|
12756
|
+
if (!Array.isArray(message.content)) return [];
|
|
12757
|
+
const parts = [];
|
|
12758
|
+
for (const block of message.content) {
|
|
12759
|
+
if (!isRecord$19(block)) continue;
|
|
12760
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
12761
|
+
const text = sanitizeText$4(block.text);
|
|
12762
|
+
if (text !== "") parts.push({
|
|
12763
|
+
type: "input_text",
|
|
12764
|
+
text
|
|
12765
|
+
});
|
|
12766
|
+
} else if (block.type === "image") {
|
|
12767
|
+
const inline = imageBlockToInline$3(block, images);
|
|
12768
|
+
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) parts.push({
|
|
12769
|
+
type: "input_image",
|
|
12770
|
+
image_url: "data:" + inline.mediaType + ";base64," + inline.data
|
|
12771
|
+
});
|
|
12772
|
+
else parts.push({
|
|
12773
|
+
type: "input_text",
|
|
12774
|
+
text: unavailableImageText$5(block)
|
|
12775
|
+
});
|
|
12776
|
+
}
|
|
12777
|
+
}
|
|
12778
|
+
return parts;
|
|
12779
|
+
}
|
|
12780
|
+
function responsesToolResultImageBlocks(blocks, images) {
|
|
12781
|
+
if (!Array.isArray(blocks)) return [];
|
|
12782
|
+
const out = [];
|
|
12783
|
+
for (const block of blocks) {
|
|
12784
|
+
if (!isRecord$19(block)) continue;
|
|
12785
|
+
if (block.type === "image") {
|
|
12786
|
+
const inline = imageBlockToInline$3(block, images);
|
|
12787
|
+
if (inline && SUPPORTED_IMAGE_MEDIA_TYPES$4.has(inline.mediaType)) out.push({
|
|
12788
|
+
type: "input_image",
|
|
12789
|
+
image_url: "data:" + inline.mediaType + ";base64," + inline.data
|
|
12790
|
+
});
|
|
12791
|
+
else out.push({
|
|
12792
|
+
type: "input_text",
|
|
12793
|
+
text: unavailableImageText$5(block)
|
|
12794
|
+
});
|
|
12795
|
+
continue;
|
|
12796
|
+
}
|
|
12797
|
+
if (block.type === "tool-result") out.push(...responsesToolResultImageBlocks(block.content, images));
|
|
12798
|
+
}
|
|
12799
|
+
return out;
|
|
12800
|
+
}
|
|
12801
|
+
function buildResponsesRequest(options, images = NO_RESOLVED_IMAGES$4) {
|
|
12802
|
+
const input = [];
|
|
12803
|
+
const system = leadingSystemText$4(options);
|
|
12804
|
+
const conversation = nonSystemMessages$4(options);
|
|
12805
|
+
for (let index = 0; index < conversation.length; index++) {
|
|
12806
|
+
const message = conversation[index];
|
|
12807
|
+
if (isToolResultMessage$4(message)) {
|
|
12808
|
+
const imageBlocks = [];
|
|
12809
|
+
while (index < conversation.length && isToolResultMessage$4(conversation[index])) {
|
|
12810
|
+
const current = conversation[index];
|
|
12811
|
+
const toolResult = Array.isArray(current.content) ? current.content.find((b) => isRecord$19(b) && b.type === "tool-result") : void 0;
|
|
12812
|
+
const callId = (isRecord$19(toolResult) && typeof toolResult.toolCallId === "string" ? toolResult.toolCallId : "") || (typeof current.source?.callId === "string" ? current.source.callId : "");
|
|
12813
|
+
input.push({
|
|
12814
|
+
type: "function_call_output",
|
|
12815
|
+
call_id: callId,
|
|
12816
|
+
output: toolResultText$4(current.content)
|
|
12817
|
+
});
|
|
12818
|
+
imageBlocks.push(...responsesToolResultImageBlocks(current.content, images));
|
|
12819
|
+
index += 1;
|
|
12820
|
+
}
|
|
12821
|
+
index -= 1;
|
|
12822
|
+
if (imageBlocks.length > 0) input.push({
|
|
12823
|
+
role: "user",
|
|
12824
|
+
content: imageBlocks
|
|
12825
|
+
});
|
|
12826
|
+
continue;
|
|
12827
|
+
}
|
|
12828
|
+
if (message.role === "assistant") {
|
|
12829
|
+
const { content, toolCalls } = openAIAssistantContent$2(message);
|
|
12830
|
+
if (content !== "") input.push({
|
|
12831
|
+
role: "assistant",
|
|
12832
|
+
content
|
|
12833
|
+
});
|
|
12834
|
+
for (const call of toolCalls) {
|
|
12835
|
+
const fn = isRecord$19(call.function) ? call.function : void 0;
|
|
12836
|
+
input.push({
|
|
12837
|
+
type: "function_call",
|
|
12838
|
+
call_id: call.id,
|
|
12839
|
+
name: typeof fn?.name === "string" ? fn.name : "",
|
|
12840
|
+
arguments: typeof fn?.arguments === "string" ? fn.arguments : "{}"
|
|
12841
|
+
});
|
|
12842
|
+
}
|
|
12843
|
+
continue;
|
|
12844
|
+
}
|
|
12845
|
+
const content = responsesUserContent(message, images);
|
|
12846
|
+
if (content.length === 0) continue;
|
|
12847
|
+
input.push({
|
|
12848
|
+
role: "user",
|
|
12849
|
+
content
|
|
12850
|
+
});
|
|
12851
|
+
}
|
|
12852
|
+
const effort = wireReasoningEffort(options.reasoningEffort === void 0 ? void 0 : String(options.reasoningEffort));
|
|
12853
|
+
const maxTokens = options.maxTokens ?? maxOutputTokensFor$4(options.model);
|
|
12854
|
+
return {
|
|
12855
|
+
model: options.model,
|
|
12856
|
+
input,
|
|
12857
|
+
stream: true,
|
|
12858
|
+
max_output_tokens: maxTokens,
|
|
12859
|
+
...system === void 0 ? {} : { instructions: system },
|
|
12860
|
+
...options.temperature === void 0 ? {} : { temperature: options.temperature },
|
|
12861
|
+
...options.tools && options.tools.length > 0 ? {
|
|
12862
|
+
tools: options.tools.map((tool) => ({
|
|
12863
|
+
type: "function",
|
|
12864
|
+
name: tool.name,
|
|
12865
|
+
description: tool.description,
|
|
12866
|
+
parameters: stripMetaSchema$2(tool.parameters)
|
|
12867
|
+
})),
|
|
12868
|
+
tool_choice: "auto"
|
|
12869
|
+
} : {},
|
|
12870
|
+
...effort === void 0 || effort === "" ? {} : { reasoning: { effort } }
|
|
12871
|
+
};
|
|
11839
12872
|
}
|
|
11840
12873
|
function createStreamState$5(wire) {
|
|
11841
12874
|
return {
|
|
@@ -11922,24 +12955,24 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
11922
12955
|
}
|
|
11923
12956
|
if (payload === "") return [];
|
|
11924
12957
|
const chunk = safeJsonParse$4(payload);
|
|
11925
|
-
if (!isRecord$
|
|
12958
|
+
if (!isRecord$19(chunk)) return [];
|
|
11926
12959
|
const out = [];
|
|
11927
|
-
const usage = isRecord$
|
|
12960
|
+
const usage = isRecord$19(chunk.usage) ? chunk.usage : void 0;
|
|
11928
12961
|
if (usage) {
|
|
11929
12962
|
state.sawUsage = true;
|
|
11930
12963
|
const prompt = numberOr$5(usage.prompt_tokens, 0);
|
|
11931
|
-
const details = isRecord$
|
|
12964
|
+
const details = isRecord$19(usage.prompt_tokens_details) ? usage.prompt_tokens_details : void 0;
|
|
11932
12965
|
const cached = details ? numberOr$5(details.cached_tokens, 0) : 0;
|
|
11933
12966
|
state.inputTokens = Math.max(0, prompt - cached);
|
|
11934
12967
|
state.cacheReadTokens = cached;
|
|
11935
12968
|
state.outputTokens = numberOr$5(usage.completion_tokens, state.outputTokens);
|
|
11936
|
-
if (isRecord$
|
|
12969
|
+
if (isRecord$19(usage.completion_tokens_details)) state.reasoningTokens = numberOr$5(usage.completion_tokens_details.reasoning_tokens, state.reasoningTokens);
|
|
11937
12970
|
}
|
|
11938
12971
|
const choices = Array.isArray(chunk.choices) ? chunk.choices : [];
|
|
11939
|
-
const choice = isRecord$
|
|
11940
|
-
const delta = isRecord$
|
|
12972
|
+
const choice = isRecord$19(choices[0]) ? choices[0] : void 0;
|
|
12973
|
+
const delta = isRecord$19(choice?.delta) ? choice.delta : void 0;
|
|
11941
12974
|
if (delta) {
|
|
11942
|
-
const reasoning = asString$
|
|
12975
|
+
const reasoning = asString$12(delta.reasoning_content) ?? asString$12(delta.reasoning);
|
|
11943
12976
|
if (reasoning !== void 0 && reasoning !== "") {
|
|
11944
12977
|
out.push(...closeToolCalls$4(state));
|
|
11945
12978
|
if (state.current === null || state.current.type !== "reasoning") out.push(...openTextBlock$3(state, "reasoning"));
|
|
@@ -11951,7 +12984,7 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
11951
12984
|
text: sanitizeText$4(reasoning)
|
|
11952
12985
|
});
|
|
11953
12986
|
}
|
|
11954
|
-
const content = asString$
|
|
12987
|
+
const content = asString$12(delta.content);
|
|
11955
12988
|
if (content !== void 0 && content !== "") {
|
|
11956
12989
|
out.push(...closeToolCalls$4(state));
|
|
11957
12990
|
if (state.current === null || state.current.type !== "text") out.push(...openTextBlock$3(state, "text"));
|
|
@@ -11965,11 +12998,11 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
11965
12998
|
}
|
|
11966
12999
|
const toolDeltas = Array.isArray(delta.tool_calls) ? delta.tool_calls : [];
|
|
11967
13000
|
for (const entry of toolDeltas) {
|
|
11968
|
-
if (!isRecord$
|
|
13001
|
+
if (!isRecord$19(entry)) continue;
|
|
11969
13002
|
out.push(...applyOpenAIToolDelta$1(entry, state));
|
|
11970
13003
|
}
|
|
11971
13004
|
}
|
|
11972
|
-
const finish = asString$
|
|
13005
|
+
const finish = asString$12(choice?.finish_reason);
|
|
11973
13006
|
if (finish !== void 0 && finish !== "") {
|
|
11974
13007
|
state.finishReason = finish;
|
|
11975
13008
|
out.push(...closeCurrent$3(state));
|
|
@@ -11979,15 +13012,15 @@ function processOpenAIStreamLine$1(line, state) {
|
|
|
11979
13012
|
}
|
|
11980
13013
|
function applyOpenAIToolDelta$1(entry, state) {
|
|
11981
13014
|
const wireIndex = typeof entry.index === "number" ? entry.index : 0;
|
|
11982
|
-
const fn = isRecord$
|
|
13015
|
+
const fn = isRecord$19(entry.function) ? entry.function : {};
|
|
11983
13016
|
const out = [];
|
|
11984
13017
|
let call = state.toolCalls.get(wireIndex);
|
|
11985
13018
|
if (call === void 0) {
|
|
11986
13019
|
out.push(...closeCurrent$3(state));
|
|
11987
13020
|
call = {
|
|
11988
13021
|
blockIndex: state.blocks.length,
|
|
11989
|
-
id: asString$
|
|
11990
|
-
name: asString$
|
|
13022
|
+
id: asString$12(entry.id) ?? `call_${wireIndex}`,
|
|
13023
|
+
name: asString$12(fn.name) ?? "",
|
|
11991
13024
|
arguments: "",
|
|
11992
13025
|
started: false
|
|
11993
13026
|
};
|
|
@@ -12000,13 +13033,13 @@ function applyOpenAIToolDelta$1(entry, state) {
|
|
|
12000
13033
|
state.toolCalls.set(wireIndex, call);
|
|
12001
13034
|
} else {
|
|
12002
13035
|
if (call.id === `call_${wireIndex}`) {
|
|
12003
|
-
const id = asString$
|
|
13036
|
+
const id = asString$12(entry.id);
|
|
12004
13037
|
if (id !== void 0) call.id = id;
|
|
12005
13038
|
}
|
|
12006
|
-
const name = asString$
|
|
13039
|
+
const name = asString$12(fn.name);
|
|
12007
13040
|
if (name !== void 0 && name !== "") call.name = name;
|
|
12008
13041
|
}
|
|
12009
|
-
const argsDelta = asString$
|
|
13042
|
+
const argsDelta = asString$12(fn.arguments) ?? "";
|
|
12010
13043
|
if (argsDelta !== "") call.arguments += argsDelta;
|
|
12011
13044
|
if (!call.started) {
|
|
12012
13045
|
call.started = true;
|
|
@@ -12037,12 +13070,12 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
12037
13070
|
const payload = trimmed.slice(5).trim();
|
|
12038
13071
|
if (payload === "" || payload === "[DONE]") return [];
|
|
12039
13072
|
const event = safeJsonParse$4(payload);
|
|
12040
|
-
if (!isRecord$
|
|
12041
|
-
const type = asString$
|
|
13073
|
+
if (!isRecord$19(event)) return [];
|
|
13074
|
+
const type = asString$12(event.type);
|
|
12042
13075
|
const out = [];
|
|
12043
13076
|
if (type === "message_start") {
|
|
12044
|
-
const message = isRecord$
|
|
12045
|
-
const usage = message && isRecord$
|
|
13077
|
+
const message = isRecord$19(event.message) ? event.message : void 0;
|
|
13078
|
+
const usage = message && isRecord$19(message.usage) ? message.usage : void 0;
|
|
12046
13079
|
if (usage) {
|
|
12047
13080
|
state.sawUsage = true;
|
|
12048
13081
|
state.inputTokens = numberOr$5(usage.input_tokens, 0);
|
|
@@ -12050,21 +13083,21 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
12050
13083
|
state.cacheWriteTokens = numberOr$5(usage.cache_creation_input_tokens, 0);
|
|
12051
13084
|
state.outputTokens = numberOr$5(usage.output_tokens, 0);
|
|
12052
13085
|
}
|
|
12053
|
-
const stop = message ? asString$
|
|
13086
|
+
const stop = message ? asString$12(message.stop_reason) : void 0;
|
|
12054
13087
|
if (stop !== void 0 && stop !== null) state.finishReason = stop;
|
|
12055
13088
|
return out;
|
|
12056
13089
|
}
|
|
12057
13090
|
if (type === "content_block_start") {
|
|
12058
13091
|
const contentIndex = numberOr$5(event.index, 0);
|
|
12059
|
-
const block = isRecord$
|
|
12060
|
-
const blockType = asString$
|
|
13092
|
+
const block = isRecord$19(event.content_block) ? event.content_block : {};
|
|
13093
|
+
const blockType = asString$12(block.type);
|
|
12061
13094
|
out.push(...closeCurrent$3(state));
|
|
12062
13095
|
if (blockType === "tool_use") {
|
|
12063
13096
|
const index = state.blocks.length;
|
|
12064
13097
|
const pending = {
|
|
12065
13098
|
blockIndex: index,
|
|
12066
|
-
id: asString$
|
|
12067
|
-
name: asString$
|
|
13099
|
+
id: asString$12(block.id) ?? `toolu_${contentIndex}`,
|
|
13100
|
+
name: asString$12(block.name) ?? "",
|
|
12068
13101
|
arguments: "",
|
|
12069
13102
|
started: true
|
|
12070
13103
|
};
|
|
@@ -12106,11 +13139,11 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
12106
13139
|
}
|
|
12107
13140
|
if (type === "content_block_delta") {
|
|
12108
13141
|
const contentIndex = numberOr$5(event.index, 0);
|
|
12109
|
-
const delta = isRecord$
|
|
12110
|
-
const deltaType = asString$
|
|
13142
|
+
const delta = isRecord$19(event.delta) ? event.delta : {};
|
|
13143
|
+
const deltaType = asString$12(delta.type);
|
|
12111
13144
|
if (deltaType === "input_json_delta") {
|
|
12112
13145
|
const pending = state.toolCalls.get(contentIndex);
|
|
12113
|
-
const partial = asString$
|
|
13146
|
+
const partial = asString$12(delta.partial_json) ?? "";
|
|
12114
13147
|
if (pending !== void 0) {
|
|
12115
13148
|
pending.arguments += partial;
|
|
12116
13149
|
out.push({
|
|
@@ -12123,7 +13156,7 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
12123
13156
|
}
|
|
12124
13157
|
return out;
|
|
12125
13158
|
}
|
|
12126
|
-
const text = deltaType === "thinking_delta" ? asString$
|
|
13159
|
+
const text = deltaType === "thinking_delta" ? asString$12(delta.thinking) : asString$12(delta.text);
|
|
12127
13160
|
if (text !== void 0 && text !== "") {
|
|
12128
13161
|
const index = state.contentIndexes.get(contentIndex) ?? state.current?.index;
|
|
12129
13162
|
const kind = deltaType === "thinking_delta" ? "reasoning" : "text";
|
|
@@ -12179,10 +13212,10 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
12179
13212
|
return out;
|
|
12180
13213
|
}
|
|
12181
13214
|
if (type === "message_delta") {
|
|
12182
|
-
const delta = isRecord$
|
|
12183
|
-
const stop = delta ? asString$
|
|
13215
|
+
const delta = isRecord$19(event.delta) ? event.delta : void 0;
|
|
13216
|
+
const stop = delta ? asString$12(delta.stop_reason) : void 0;
|
|
12184
13217
|
if (stop !== void 0 && stop !== "") state.finishReason = stop;
|
|
12185
|
-
const usage = isRecord$
|
|
13218
|
+
const usage = isRecord$19(event.usage) ? event.usage : void 0;
|
|
12186
13219
|
if (usage) {
|
|
12187
13220
|
state.sawUsage = true;
|
|
12188
13221
|
state.outputTokens = numberOr$5(usage.output_tokens, state.outputTokens);
|
|
@@ -12193,7 +13226,7 @@ function processAnthropicStreamLine$1(line, state) {
|
|
|
12193
13226
|
state.done = true;
|
|
12194
13227
|
return closeStream$4(state);
|
|
12195
13228
|
}
|
|
12196
|
-
if (type === "error") throw new LlmError$1(`Command Code stream error: ${asString$
|
|
13229
|
+
if (type === "error") throw new LlmError$1(`Command Code stream error: ${asString$12((isRecord$19(event.error) ? event.error : {}).message) ?? "unknown error"}`, "PROVIDER_ERROR");
|
|
12197
13230
|
return out;
|
|
12198
13231
|
}
|
|
12199
13232
|
function tokenUsage$4(state) {
|
|
@@ -12207,10 +13240,200 @@ function tokenUsage$4(state) {
|
|
|
12207
13240
|
}
|
|
12208
13241
|
function finishReasonFor$4(state) {
|
|
12209
13242
|
const reason = state.finishReason ?? "";
|
|
12210
|
-
if (reason === "length" || reason === "max_tokens") return { kind: "max-tokens" };
|
|
13243
|
+
if (reason === "length" || reason === "max_tokens" || reason === "max_output_tokens") return { kind: "max-tokens" };
|
|
12211
13244
|
if (state.hasToolCall || reason === "tool_calls" || reason === "tool_use") return { kind: "tool-calls" };
|
|
12212
13245
|
return { kind: "stop" };
|
|
12213
13246
|
}
|
|
13247
|
+
/**
|
|
13248
|
+
* Fold one SSE line from `/responses`.
|
|
13249
|
+
*
|
|
13250
|
+
* The Responses stream is event-typed rather than delta-shaped: text arrives as
|
|
13251
|
+
* `response.output_text.delta`, tool calls as `function_call_arguments.delta`
|
|
13252
|
+
* keyed by an item id, and the turn ends on `response.completed` or
|
|
13253
|
+
* `response.failed`. Those are read as written; there is no Chat Completions
|
|
13254
|
+
* shape to reinterpret.
|
|
13255
|
+
*/
|
|
13256
|
+
function processResponsesStreamLine(line, state) {
|
|
13257
|
+
const trimmed = line.trim();
|
|
13258
|
+
if (trimmed === "" || !trimmed.startsWith("data:")) return [];
|
|
13259
|
+
const payload = trimmed.slice(5).trim();
|
|
13260
|
+
if (payload === "" || payload === "[DONE]") return [];
|
|
13261
|
+
let parsed;
|
|
13262
|
+
try {
|
|
13263
|
+
parsed = JSON.parse(payload);
|
|
13264
|
+
} catch {
|
|
13265
|
+
return [];
|
|
13266
|
+
}
|
|
13267
|
+
if (!isRecord$19(parsed)) return [];
|
|
13268
|
+
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
13269
|
+
if (type === "response.output_text.delta") {
|
|
13270
|
+
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
13271
|
+
if (delta === "") return [];
|
|
13272
|
+
state.hasContent = true;
|
|
13273
|
+
const opened = openBlock(state, "text");
|
|
13274
|
+
state.current.text += delta;
|
|
13275
|
+
return [...opened, {
|
|
13276
|
+
type: "text-delta",
|
|
13277
|
+
index: state.current.index,
|
|
13278
|
+
text: delta
|
|
13279
|
+
}];
|
|
13280
|
+
}
|
|
13281
|
+
if (type === "response.reasoning_summary_text.delta") {
|
|
13282
|
+
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
13283
|
+
if (delta === "") return [];
|
|
13284
|
+
state.hasContent = true;
|
|
13285
|
+
const opened = openBlock(state, "reasoning");
|
|
13286
|
+
state.current.text += delta;
|
|
13287
|
+
return [...opened, {
|
|
13288
|
+
type: "reasoning-delta",
|
|
13289
|
+
index: state.current.index,
|
|
13290
|
+
text: delta
|
|
13291
|
+
}];
|
|
13292
|
+
}
|
|
13293
|
+
if (type === "response.output_item.added") {
|
|
13294
|
+
const item = isRecord$19(parsed.item) ? parsed.item : null;
|
|
13295
|
+
if (item === null || item.type !== "function_call") return [];
|
|
13296
|
+
return startResponsesToolCall(state, parsed, item);
|
|
13297
|
+
}
|
|
13298
|
+
if (type === "response.function_call_arguments.delta") {
|
|
13299
|
+
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
13300
|
+
const call = responsesToolCall(state, parsed.item_id ?? parsed.call_id);
|
|
13301
|
+
if (call === void 0 || delta === "") return [];
|
|
13302
|
+
call.arguments += delta;
|
|
13303
|
+
return [{
|
|
13304
|
+
type: "tool-call-delta",
|
|
13305
|
+
index: call.blockIndex,
|
|
13306
|
+
id: toToolCallId(call.id),
|
|
13307
|
+
name: call.name,
|
|
13308
|
+
argumentsDelta: delta
|
|
13309
|
+
}];
|
|
13310
|
+
}
|
|
13311
|
+
if (type === "response.function_call_arguments.done") {
|
|
13312
|
+
const call = responsesToolCall(state, parsed.item_id ?? parsed.call_id);
|
|
13313
|
+
if (call === void 0) return [];
|
|
13314
|
+
if (typeof parsed.arguments === "string") call.arguments = parsed.arguments;
|
|
13315
|
+
return [];
|
|
13316
|
+
}
|
|
13317
|
+
if (type === "response.completed" || type === "response.incomplete") {
|
|
13318
|
+
const response = isRecord$19(parsed.response) ? parsed.response : null;
|
|
13319
|
+
if (response !== null) {
|
|
13320
|
+
if (response.status === "failed") {
|
|
13321
|
+
state.done = true;
|
|
13322
|
+
const errorObj = isRecord$19(response.error) ? response.error : null;
|
|
13323
|
+
const detail = errorObj && typeof errorObj.message === "string" ? errorObj.message : typeof response.error === "string" ? response.error : "";
|
|
13324
|
+
throw new LlmError$1(detail === "" ? "Command Code responses stream failed" : `Command Code responses stream failed: ${detail}`, "PROVIDER_ERROR");
|
|
13325
|
+
}
|
|
13326
|
+
readResponsesUsage(response, state);
|
|
13327
|
+
}
|
|
13328
|
+
state.done = true;
|
|
13329
|
+
if (type === "response.incomplete") {
|
|
13330
|
+
const details = response && isRecord$19(response.incomplete_details) ? response.incomplete_details : null;
|
|
13331
|
+
const reason = details && typeof details.reason === "string" ? details.reason : "";
|
|
13332
|
+
state.finishReason = reason === "max_output_tokens" || reason === "length" || reason === "max_tokens" ? "length" : reason || "length";
|
|
13333
|
+
} else state.finishReason = "stop";
|
|
13334
|
+
return [];
|
|
13335
|
+
}
|
|
13336
|
+
if (type === "response.failed" || type === "error") {
|
|
13337
|
+
state.done = true;
|
|
13338
|
+
const errorObj = isRecord$19(parsed.error) ? parsed.error : isRecord$19(parsed.response) && isRecord$19(parsed.response.error) ? parsed.response.error : null;
|
|
13339
|
+
const detail = (typeof parsed.message === "string" ? parsed.message : "") || (errorObj && typeof errorObj.message === "string" ? errorObj.message : "") || (isRecord$19(parsed.response) && typeof parsed.response.error === "string" ? parsed.response.error : typeof parsed.error === "string" ? parsed.error : "");
|
|
13340
|
+
throw new LlmError$1(detail === "" ? "Command Code responses stream failed" : `Command Code responses stream failed: ${detail}`, "PROVIDER_ERROR");
|
|
13341
|
+
}
|
|
13342
|
+
return [];
|
|
13343
|
+
}
|
|
13344
|
+
function openBlock(state, type) {
|
|
13345
|
+
if (state.current !== null && state.current.type === type) return [];
|
|
13346
|
+
const out = closeCurrent$3(state);
|
|
13347
|
+
state.current = {
|
|
13348
|
+
index: state.blocks.length,
|
|
13349
|
+
type,
|
|
13350
|
+
text: ""
|
|
13351
|
+
};
|
|
13352
|
+
state.blocks.push({
|
|
13353
|
+
type,
|
|
13354
|
+
text: ""
|
|
13355
|
+
});
|
|
13356
|
+
out.push({
|
|
13357
|
+
type: "block-start",
|
|
13358
|
+
index: state.current.index,
|
|
13359
|
+
blockType: type
|
|
13360
|
+
});
|
|
13361
|
+
return out;
|
|
13362
|
+
}
|
|
13363
|
+
function startResponsesToolCall(state, event, item) {
|
|
13364
|
+
const out = closeCurrent$3(state);
|
|
13365
|
+
const index = state.blocks.length;
|
|
13366
|
+
const name = typeof item.name === "string" ? item.name : "";
|
|
13367
|
+
const itemId = typeof item.id === "string" ? item.id : typeof event.item_id === "string" ? event.item_id : null;
|
|
13368
|
+
const call = {
|
|
13369
|
+
blockIndex: index,
|
|
13370
|
+
id: typeof item.call_id === "string" ? item.call_id : `call_${String(itemId ?? index)}`,
|
|
13371
|
+
name,
|
|
13372
|
+
arguments: "",
|
|
13373
|
+
started: true
|
|
13374
|
+
};
|
|
13375
|
+
state.blocks.push({
|
|
13376
|
+
type: "tool-call",
|
|
13377
|
+
id: toToolCallId(call.id),
|
|
13378
|
+
name,
|
|
13379
|
+
arguments: ""
|
|
13380
|
+
});
|
|
13381
|
+
state.toolCalls.set(index, call);
|
|
13382
|
+
if (itemId !== null) responsesItemIndexOf(state).set(itemId, call);
|
|
13383
|
+
state.hasToolCall = true;
|
|
13384
|
+
out.push({
|
|
13385
|
+
type: "block-start",
|
|
13386
|
+
index,
|
|
13387
|
+
blockType: "tool-call"
|
|
13388
|
+
});
|
|
13389
|
+
out.push({
|
|
13390
|
+
type: "tool-call-delta",
|
|
13391
|
+
index,
|
|
13392
|
+
id: toToolCallId(call.id),
|
|
13393
|
+
name,
|
|
13394
|
+
argumentsDelta: ""
|
|
13395
|
+
});
|
|
13396
|
+
return out;
|
|
13397
|
+
}
|
|
13398
|
+
/** Item id -> call, held beside the stream state that produced it. */
|
|
13399
|
+
const responsesItemIndex = /* @__PURE__ */ new WeakMap();
|
|
13400
|
+
function responsesItemIndexOf(state) {
|
|
13401
|
+
let index = responsesItemIndex.get(state);
|
|
13402
|
+
if (index === void 0) {
|
|
13403
|
+
index = /* @__PURE__ */ new Map();
|
|
13404
|
+
responsesItemIndex.set(state, index);
|
|
13405
|
+
}
|
|
13406
|
+
return index;
|
|
13407
|
+
}
|
|
13408
|
+
/**
|
|
13409
|
+
* The call a function-arguments event refers to.
|
|
13410
|
+
*
|
|
13411
|
+
* The event carries `item_id` while the recorded call carries `call_id`, so
|
|
13412
|
+
* matching on the call id alone silently dropped every argument delta. The
|
|
13413
|
+
* call-id fallback stays for a provider that only sends one of the two.
|
|
13414
|
+
*/
|
|
13415
|
+
function responsesToolCall(state, key) {
|
|
13416
|
+
if (typeof key !== "string" || key === "") return void 0;
|
|
13417
|
+
const match = responsesItemIndex.get(state)?.get(key);
|
|
13418
|
+
if (match !== void 0) return match;
|
|
13419
|
+
for (const call of state.toolCalls.values()) if (call.id === toToolCallId(key)) return call;
|
|
13420
|
+
}
|
|
13421
|
+
function readResponsesUsage(response, state) {
|
|
13422
|
+
const usage = isRecord$19(response.usage) ? response.usage : null;
|
|
13423
|
+
if (usage === null) return;
|
|
13424
|
+
const cached = isRecord$19(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cached_tokens) : 0;
|
|
13425
|
+
const written = isRecord$19(usage.input_tokens_details) ? numberOrZero(usage.input_tokens_details.cache_write_tokens) : 0;
|
|
13426
|
+
state.inputTokens = Math.max(0, numberOrZero(usage.input_tokens) - cached - written);
|
|
13427
|
+
state.cacheReadTokens = cached;
|
|
13428
|
+
state.cacheWriteTokens = written;
|
|
13429
|
+
state.outputTokens = numberOrZero(usage.output_tokens);
|
|
13430
|
+
const outputDetails = isRecord$19(usage.output_tokens_details) ? usage.output_tokens_details : null;
|
|
13431
|
+
if (outputDetails !== null) state.reasoningTokens = numberOrZero(outputDetails.reasoning_tokens);
|
|
13432
|
+
state.sawUsage = true;
|
|
13433
|
+
}
|
|
13434
|
+
function numberOrZero(value) {
|
|
13435
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
|
|
13436
|
+
}
|
|
12214
13437
|
/** Flush every open block, then emit usage and the terminal finish. */
|
|
12215
13438
|
function closeStream$4(state) {
|
|
12216
13439
|
if (state.finished) return [];
|
|
@@ -12231,6 +13454,213 @@ function assertStreamComplete$4(state) {
|
|
|
12231
13454
|
if (!state.done && state.finishReason === null) throw new LlmError$1("Command Code stream ended before its terminal event", "PROVIDER_ERROR");
|
|
12232
13455
|
}
|
|
12233
13456
|
//#endregion
|
|
13457
|
+
//#region src/host/common/capabilities.ts
|
|
13458
|
+
/** Error thrown by requireCapability when a capability contract fails. */
|
|
13459
|
+
var CapabilityError = class extends Error {
|
|
13460
|
+
query;
|
|
13461
|
+
evaluation;
|
|
13462
|
+
constructor(message, query, evaluation) {
|
|
13463
|
+
super(message);
|
|
13464
|
+
this.name = "CapabilityError";
|
|
13465
|
+
this.query = query;
|
|
13466
|
+
this.evaluation = evaluation;
|
|
13467
|
+
}
|
|
13468
|
+
};
|
|
13469
|
+
/** Known Ollama vision model prefixes per official model documentation. */
|
|
13470
|
+
const KNOWN_OLLAMA_VISION_PREFIXES = [
|
|
13471
|
+
"llava",
|
|
13472
|
+
"bakllava",
|
|
13473
|
+
"llama3.2-vision",
|
|
13474
|
+
"moondream",
|
|
13475
|
+
"minicpm-v",
|
|
13476
|
+
"qwen2.5-vl",
|
|
13477
|
+
"qwen2-vl"
|
|
13478
|
+
];
|
|
13479
|
+
/**
|
|
13480
|
+
* Matcher for Ollama vision models, aligned with src/host/ollama/types.ts.
|
|
13481
|
+
* Checks exact base name before ':' or '/' to prevent partial hijacking (e.g. 'llava-evil').
|
|
13482
|
+
*/
|
|
13483
|
+
function isKnownOllamaVisionModel(modelId) {
|
|
13484
|
+
const base = modelId.toLowerCase().trim().split(":")[0]?.trim() ?? "";
|
|
13485
|
+
return KNOWN_OLLAMA_VISION_PREFIXES.includes(base);
|
|
13486
|
+
}
|
|
13487
|
+
/** Ollama thinking models with level control ('low'|'medium'|'high'). */
|
|
13488
|
+
const KNOWN_OLLAMA_THINKING_LEVELS_PREFIXES = ["gpt-oss"];
|
|
13489
|
+
/** Ollama thinking models with boolean control (true|false). */
|
|
13490
|
+
const KNOWN_OLLAMA_THINKING_BOOLEAN_PREFIXES = [
|
|
13491
|
+
"deepseek-r1",
|
|
13492
|
+
"qwq",
|
|
13493
|
+
"qwen3"
|
|
13494
|
+
];
|
|
13495
|
+
/** Matcher for Ollama thinking models, aligned with src/host/ollama/types.ts. */
|
|
13496
|
+
function isKnownOllamaThinkingModel(modelId) {
|
|
13497
|
+
const base = modelId.toLowerCase().trim().split(":")[0]?.trim() ?? "";
|
|
13498
|
+
return KNOWN_OLLAMA_THINKING_LEVELS_PREFIXES.includes(base) || KNOWN_OLLAMA_THINKING_BOOLEAN_PREFIXES.includes(base);
|
|
13499
|
+
}
|
|
13500
|
+
/** Immutable validated capability evidence registry. */
|
|
13501
|
+
const CAPABILITY_EVIDENCE_REGISTRY = Object.freeze([
|
|
13502
|
+
{
|
|
13503
|
+
id: "ollama-native-image",
|
|
13504
|
+
provider: "ollama",
|
|
13505
|
+
wire: ["openai", "native"],
|
|
13506
|
+
modelSelector: isKnownOllamaVisionModel,
|
|
13507
|
+
authMode: "api-key",
|
|
13508
|
+
capability: "native-image",
|
|
13509
|
+
status: "supported",
|
|
13510
|
+
evidenceLevel: "offline-contract",
|
|
13511
|
+
implemented: true,
|
|
13512
|
+
date: "2026-10-03",
|
|
13513
|
+
testPath: "test/ollama-projection.test.ts",
|
|
13514
|
+
versionTag: "0.2.0-rc.2",
|
|
13515
|
+
notes: "Supported strictly for verified vision model prefixes (llava, bakllava, llama3.2-vision, moondream, minicpm-v, qwen2.5-vl, qwen2-vl). Exact boundary prevents llava-evil."
|
|
13516
|
+
},
|
|
13517
|
+
{
|
|
13518
|
+
id: "ollama-thinking-control",
|
|
13519
|
+
provider: "ollama",
|
|
13520
|
+
wire: ["openai", "native"],
|
|
13521
|
+
modelSelector: isKnownOllamaThinkingModel,
|
|
13522
|
+
authMode: "api-key",
|
|
13523
|
+
capability: "thinking-control",
|
|
13524
|
+
status: "supported",
|
|
13525
|
+
evidenceLevel: "offline-contract",
|
|
13526
|
+
implemented: true,
|
|
13527
|
+
date: "2026-10-03",
|
|
13528
|
+
testPath: "test/ollama-projection.test.ts",
|
|
13529
|
+
link: "https://github.com/ollama/ollama/blob/main/docs/capabilities/thinking.mdx",
|
|
13530
|
+
versionTag: "0.2.0-rc.2",
|
|
13531
|
+
notes: "Model-level think control for gpt-oss (levels) and deepseek-r1/qwq/qwen3 (boolean). Replay across turns remains unsupported."
|
|
13532
|
+
},
|
|
13533
|
+
{
|
|
13534
|
+
id: "command-code-zdr",
|
|
13535
|
+
provider: "command-code",
|
|
13536
|
+
wire: [
|
|
13537
|
+
"responses",
|
|
13538
|
+
"anthropic",
|
|
13539
|
+
"openai",
|
|
13540
|
+
"chat-completions",
|
|
13541
|
+
"messages"
|
|
13542
|
+
],
|
|
13543
|
+
modelSelector: "*",
|
|
13544
|
+
authMode: [
|
|
13545
|
+
"api-key",
|
|
13546
|
+
"token",
|
|
13547
|
+
"oauth"
|
|
13548
|
+
],
|
|
13549
|
+
capability: "zdr",
|
|
13550
|
+
status: "supported",
|
|
13551
|
+
evidenceLevel: "offline-contract",
|
|
13552
|
+
implemented: true,
|
|
13553
|
+
date: "2026-10-03",
|
|
13554
|
+
testPath: "test/command-code-adapter.test.ts",
|
|
13555
|
+
link: "https://github.com/CommandCodeAI/pi-commandcode-provider/blob/main/index.ts",
|
|
13556
|
+
versionTag: "0.2.0-rc.2",
|
|
13557
|
+
notes: "Official contract: x-cmd-zdr: 1 header active across provider wires. 422 cmd_zdr_no_providers cannot silently degrade."
|
|
13558
|
+
}
|
|
13559
|
+
].map((r) => Object.freeze({
|
|
13560
|
+
...r,
|
|
13561
|
+
wire: Array.isArray(r.wire) ? Object.freeze([...r.wire]) : r.wire,
|
|
13562
|
+
authMode: Array.isArray(r.authMode) ? Object.freeze([...r.authMode]) : r.authMode,
|
|
13563
|
+
modelSelector: Array.isArray(r.modelSelector) ? Object.freeze([...r.modelSelector]) : r.modelSelector
|
|
13564
|
+
})));
|
|
13565
|
+
function matchesWire(expected, actual) {
|
|
13566
|
+
const normActual = actual.toLowerCase().trim();
|
|
13567
|
+
if (typeof expected === "string") {
|
|
13568
|
+
if (expected === "*") return true;
|
|
13569
|
+
return expected.toLowerCase().trim() === normActual;
|
|
13570
|
+
}
|
|
13571
|
+
return expected.some((w) => w.toLowerCase().trim() === normActual);
|
|
13572
|
+
}
|
|
13573
|
+
function matchesAuth(expected, actual) {
|
|
13574
|
+
const normActual = actual.toLowerCase().trim();
|
|
13575
|
+
if (typeof expected === "string") {
|
|
13576
|
+
if (expected === "*") return true;
|
|
13577
|
+
return expected.toLowerCase().trim() === normActual;
|
|
13578
|
+
}
|
|
13579
|
+
return expected.some((a) => a.toLowerCase().trim() === normActual);
|
|
13580
|
+
}
|
|
13581
|
+
function matchesModel(selector, model) {
|
|
13582
|
+
const normModel = model.toLowerCase().trim();
|
|
13583
|
+
if (typeof selector === "function") return selector(model);
|
|
13584
|
+
if (typeof selector === "string") {
|
|
13585
|
+
if (selector === "*") return true;
|
|
13586
|
+
const normSelector = selector.toLowerCase().trim();
|
|
13587
|
+
return normModel === normSelector || normModel.startsWith(normSelector + ":") || normModel.startsWith(normSelector + "/");
|
|
13588
|
+
}
|
|
13589
|
+
return selector.some((s) => {
|
|
13590
|
+
const normS = s.toLowerCase().trim();
|
|
13591
|
+
return normModel === normS || normModel.startsWith(normS + ":") || normModel.startsWith(normS + "/");
|
|
13592
|
+
});
|
|
13593
|
+
}
|
|
13594
|
+
/** Query evidence records matching the given criteria. Supports optional pure registry argument for tests. */
|
|
13595
|
+
function queryCapabilityEvidence(query, registry = CAPABILITY_EVIDENCE_REGISTRY) {
|
|
13596
|
+
return registry.filter((rec) => {
|
|
13597
|
+
if (query.provider && rec.provider.toLowerCase() !== query.provider.toLowerCase().trim()) return false;
|
|
13598
|
+
if (query.capability && rec.capability !== query.capability) return false;
|
|
13599
|
+
if (query.wire && !matchesWire(rec.wire, query.wire)) return false;
|
|
13600
|
+
if (query.authMode && !matchesAuth(rec.authMode, query.authMode)) return false;
|
|
13601
|
+
if (query.model && !matchesModel(rec.modelSelector, query.model)) return false;
|
|
13602
|
+
return true;
|
|
13603
|
+
});
|
|
13604
|
+
}
|
|
13605
|
+
/**
|
|
13606
|
+
* Evaluate whether a capability is permitted for a concrete request target.
|
|
13607
|
+
* Fails closed if query is missing explicit fields, or lacks matching verified evidence.
|
|
13608
|
+
*/
|
|
13609
|
+
function evaluateCapability(query, registry = CAPABILITY_EVIDENCE_REGISTRY) {
|
|
13610
|
+
if (!query || typeof query.provider !== "string" || !query.provider.trim() || typeof query.wire !== "string" || !query.wire.trim() || typeof query.model !== "string" || !query.model.trim() || typeof query.authMode !== "string" || !query.authMode.trim() || typeof query.capability !== "string" || !query.capability.trim()) return {
|
|
13611
|
+
allowed: false,
|
|
13612
|
+
state: "unknown",
|
|
13613
|
+
implemented: false,
|
|
13614
|
+
reason: "Capability query requires non-empty provider, wire, model, authMode, and capability"
|
|
13615
|
+
};
|
|
13616
|
+
const matching = queryCapabilityEvidence(query, registry);
|
|
13617
|
+
if (matching.length === 0) return {
|
|
13618
|
+
allowed: false,
|
|
13619
|
+
state: "unknown",
|
|
13620
|
+
implemented: false,
|
|
13621
|
+
reason: `No matching verified evidence for ${query.provider}/${query.wire}/${query.model} (auth: ${query.authMode}, capability: ${query.capability}).`
|
|
13622
|
+
};
|
|
13623
|
+
const record = matching[0];
|
|
13624
|
+
const isSupportedLevel = record.evidenceLevel === "live-validated" || record.evidenceLevel === "offline-contract";
|
|
13625
|
+
const isAllowed = record.implemented && record.status === "supported" && isSupportedLevel;
|
|
13626
|
+
return {
|
|
13627
|
+
allowed: isAllowed,
|
|
13628
|
+
state: record.status,
|
|
13629
|
+
implemented: record.implemented,
|
|
13630
|
+
evidenceLevel: record.evidenceLevel,
|
|
13631
|
+
evidence: record,
|
|
13632
|
+
reason: isAllowed ? void 0 : record.notes ?? `Capability '${query.capability}' is not permitted.`
|
|
13633
|
+
};
|
|
13634
|
+
}
|
|
13635
|
+
/**
|
|
13636
|
+
* Require that a capability is supported and allowed. Throws CapabilityError otherwise.
|
|
13637
|
+
*/
|
|
13638
|
+
function requireCapability(query, registry = CAPABILITY_EVIDENCE_REGISTRY) {
|
|
13639
|
+
const result = evaluateCapability(query, registry);
|
|
13640
|
+
if (!result.allowed) throw new CapabilityError(`Capability requirement rejected: '${query.capability}' on ${query.provider} (${query.wire}/${query.model}, auth: ${query.authMode}): ${result.reason ?? "not permitted"}`, query, result);
|
|
13641
|
+
return result;
|
|
13642
|
+
}
|
|
13643
|
+
/** Whether Ollama image input is supported for the target model. */
|
|
13644
|
+
function supportsOllamaImage(model, wire = "openai", authMode = "api-key") {
|
|
13645
|
+
return evaluateCapability({
|
|
13646
|
+
provider: "ollama",
|
|
13647
|
+
wire,
|
|
13648
|
+
model,
|
|
13649
|
+
authMode,
|
|
13650
|
+
capability: "native-image"
|
|
13651
|
+
}).allowed;
|
|
13652
|
+
}
|
|
13653
|
+
/** Whether Ollama thinking control is supported for the target model. */
|
|
13654
|
+
function supportsOllamaThinkingControl(model, wire = "openai", authMode = "api-key") {
|
|
13655
|
+
return evaluateCapability({
|
|
13656
|
+
provider: "ollama",
|
|
13657
|
+
wire,
|
|
13658
|
+
model,
|
|
13659
|
+
authMode,
|
|
13660
|
+
capability: "thinking-control"
|
|
13661
|
+
}).allowed;
|
|
13662
|
+
}
|
|
13663
|
+
//#endregion
|
|
12234
13664
|
//#region src/host/command-code/adapter.ts
|
|
12235
13665
|
/**
|
|
12236
13666
|
* Transient-failure retry policy for the `command-code` route.
|
|
@@ -12264,6 +13694,41 @@ const RETRY_POLICY$5 = resolveRetryPolicy({
|
|
|
12264
13694
|
function resolveDefaultReasoningEffort$3(efforts, configuredEffort) {
|
|
12265
13695
|
if (configuredEffort && efforts.includes(configuredEffort)) return ReasoningEffortId(configuredEffort);
|
|
12266
13696
|
}
|
|
13697
|
+
/**
|
|
13698
|
+
* Resolves whether Zero Data Retention (ZDR) routing is active for Command Code.
|
|
13699
|
+
* An explicit boolean on adapter options takes precedence over host environment variables.
|
|
13700
|
+
* When not specified in options, DSH_COMMAND_CODE_ZDR is consulted first, then CMD_ZDR.
|
|
13701
|
+
*/
|
|
13702
|
+
function resolveZeroDataRetention(optionsZdr) {
|
|
13703
|
+
if (optionsZdr !== void 0) return optionsZdr;
|
|
13704
|
+
if (process.env.DSH_COMMAND_CODE_ZDR !== void 0) return process.env.DSH_COMMAND_CODE_ZDR === "1";
|
|
13705
|
+
return process.env.CMD_ZDR === "1";
|
|
13706
|
+
}
|
|
13707
|
+
/**
|
|
13708
|
+
* Classifies model-specific access or plan entitlement denials.
|
|
13709
|
+
* These are failures of permissions/entitlement for this model/account,
|
|
13710
|
+
* not invalid API credentials, so the account must not be marked invalid
|
|
13711
|
+
* and whole-account rotation must not occur.
|
|
13712
|
+
*/
|
|
13713
|
+
function isCommandCodeModelAccessDenied(status, detail) {
|
|
13714
|
+
if (status === 403 || status === 404 || status === 400) {
|
|
13715
|
+
const lower = detail.toLowerCase();
|
|
13716
|
+
if (lower.includes("upgrade_required") || lower.includes("not entitled") || lower.includes("entitlement") || lower.includes("model_not_found") || lower.includes("model_not_allowed") || lower.includes("model_access_denied") || lower.includes("permission_denied") || lower.includes("access_denied") || lower.includes("insufficient_permissions") || lower.includes("model") && (lower.includes("access") || lower.includes("permission") || lower.includes("entitled") || lower.includes("forbidden") || lower.includes("not allowed") || lower.includes("unauthorized") || lower.includes("not supported") || lower.includes("plan") || lower.includes("tier")) || lower.includes("plan") && (lower.includes("upgrade") || lower.includes("tier") || lower.includes("include") || lower.includes("support"))) return true;
|
|
13717
|
+
if (status === 403 && !isCommandCodeCredentialInvalid(status, detail)) return true;
|
|
13718
|
+
}
|
|
13719
|
+
return false;
|
|
13720
|
+
}
|
|
13721
|
+
/**
|
|
13722
|
+
* Classifies actual credential invalidity (e.g. 401 Unauthorized, or 403 with explicit key errors).
|
|
13723
|
+
*/
|
|
13724
|
+
function isCommandCodeCredentialInvalid(status, detail) {
|
|
13725
|
+
if (status === 401) return true;
|
|
13726
|
+
if (status === 403) {
|
|
13727
|
+
const lower = detail.toLowerCase();
|
|
13728
|
+
if (lower.includes("invalid_api_key") || lower.includes("invalid_token") || lower.includes("bad_api_key") || lower.includes("invalid key") || lower.includes("bad api key") || lower.includes("api key invalid") || lower.includes("key revoked") || lower.includes("revoked_key") || lower.includes("expired_key") || lower.includes("key expired") || lower.includes("token expired") || lower.includes("incorrect api key") || lower.includes("authentication failed")) return true;
|
|
13729
|
+
}
|
|
13730
|
+
return false;
|
|
13731
|
+
}
|
|
12267
13732
|
/** Cooldown one rate-limited key takes when the provider states no delay. */
|
|
12268
13733
|
const POOL_COOLDOWN_MS$4 = 15 * 6e4;
|
|
12269
13734
|
var CommandCodeAdapter = class extends LlmAdapter {
|
|
@@ -12380,7 +13845,16 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
12380
13845
|
}
|
|
12381
13846
|
async *requestStream(options, signal) {
|
|
12382
13847
|
const fetchFn = this.options.fetchFn ?? fetch;
|
|
12383
|
-
const
|
|
13848
|
+
const catalogEntry = (await this.catalog()).find((entry) => entry.id === options.model);
|
|
13849
|
+
const wire = (catalogEntry === void 0 ? void 0 : wireForCatalogEntry$1(catalogEntry)) ?? wireForModel$2(options.model);
|
|
13850
|
+
const isZdr = resolveZeroDataRetention(this.options.zeroDataRetention);
|
|
13851
|
+
if (isZdr) requireCapability({
|
|
13852
|
+
provider: PROVIDER_ID$5,
|
|
13853
|
+
wire,
|
|
13854
|
+
model: options.model,
|
|
13855
|
+
authMode: "api-key",
|
|
13856
|
+
capability: "zdr"
|
|
13857
|
+
});
|
|
12384
13858
|
const requestOptions = offloadOldestRequestImages$3(normalizeGenerateOptions(options));
|
|
12385
13859
|
const images = await resolveRequestImages$3(requestOptions, this.options.attachments, signal);
|
|
12386
13860
|
const body = JSON.stringify(buildRequest$1(requestOptions, wire, images));
|
|
@@ -12404,17 +13878,17 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
12404
13878
|
apiKey = effective.credentials.apiKey;
|
|
12405
13879
|
apiEnv = effective.apiEnv;
|
|
12406
13880
|
}
|
|
12407
|
-
const endpoint = `${providerUrl(apiEnv)}${wire
|
|
12408
|
-
const
|
|
13881
|
+
const endpoint = `${providerUrl(apiEnv)}${endpointPathFor(wire)}`;
|
|
13882
|
+
const baseHeaders = {
|
|
12409
13883
|
...commandCodeHeaders(apiKey),
|
|
12410
13884
|
"user-agent": PLUGIN_USER_AGENT,
|
|
12411
13885
|
accept: "text/event-stream",
|
|
12412
|
-
"
|
|
12413
|
-
} : {
|
|
12414
|
-
...commandCodeHeaders(apiKey),
|
|
12415
|
-
"user-agent": PLUGIN_USER_AGENT,
|
|
12416
|
-
accept: "text/event-stream"
|
|
13886
|
+
...isZdr ? { [HEADER_ZDR]: "1" } : {}
|
|
12417
13887
|
};
|
|
13888
|
+
const headers = wire === "anthropic" ? {
|
|
13889
|
+
...baseHeaders,
|
|
13890
|
+
"anthropic-version": "2023-06-01"
|
|
13891
|
+
} : baseHeaders;
|
|
12418
13892
|
try {
|
|
12419
13893
|
response = await fetchFn(endpoint, {
|
|
12420
13894
|
method: "POST",
|
|
@@ -12428,8 +13902,13 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
12428
13902
|
}
|
|
12429
13903
|
if (response.ok) break;
|
|
12430
13904
|
detail = (await response.text().catch(() => "")).slice(0, 600);
|
|
13905
|
+
if (response.status === 422) {
|
|
13906
|
+
if (detail.includes("cmd_zdr_no_providers")) throw new LlmError(`${PROVIDER_NAME$5} rejected request under Zero Data Retention: no ZDR-capable upstream is available for this model (${detail || "cmd_zdr_no_providers"}).`, "PROVIDER_ERROR", { status: 422 });
|
|
13907
|
+
break;
|
|
13908
|
+
}
|
|
12431
13909
|
if (pool === null || accountId === void 0) break;
|
|
12432
|
-
if (response.status
|
|
13910
|
+
if (isCommandCodeModelAccessDenied(response.status, detail)) break;
|
|
13911
|
+
if (isCommandCodeCredentialInvalid(response.status, detail)) {
|
|
12433
13912
|
await pool.markAuthFailed(accountId, `${PROVIDER_NAME$5} rejected the stored API key (${response.status}).`, "invalid").catch(() => void 0);
|
|
12434
13913
|
if (await pool.hasAnotherAvailableAccount(tried)) continue;
|
|
12435
13914
|
break;
|
|
@@ -12444,7 +13923,12 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
12444
13923
|
}
|
|
12445
13924
|
if (response === void 0 || !response.ok) {
|
|
12446
13925
|
const status = response?.status ?? 500;
|
|
12447
|
-
if (status ===
|
|
13926
|
+
if (status === 422) {
|
|
13927
|
+
if (detail.includes("cmd_zdr_no_providers")) throw new LlmError(`${PROVIDER_NAME$5} rejected request under Zero Data Retention: no ZDR-capable upstream is available for this model (${detail || "cmd_zdr_no_providers"}).`, "PROVIDER_ERROR", { status: 422 });
|
|
13928
|
+
throw new LlmError(`${PROVIDER_NAME$5} validation error (422): ${detail || "Unprocessable Entity"}`, "PROVIDER_ERROR", { status: 422 });
|
|
13929
|
+
}
|
|
13930
|
+
if (isCommandCodeModelAccessDenied(status, detail)) throw new LlmError(`${PROVIDER_NAME$5} access denied for model ${options.model}: this model is not included in the plan or requires higher entitlement (${status}).${detail ? ` ${detail}` : ""}`, "PROVIDER_ERROR", { status });
|
|
13931
|
+
if (isCommandCodeCredentialInvalid(status, detail) || status === 401) throw new LlmError(`${PROVIDER_NAME$5} rejected the stored API key (${status}). Sign in again from Settings > Command Code.${detail ? ` ${detail}` : ""}`, "INVALID_CREDENTIAL", { status });
|
|
12448
13932
|
if (status === 429) {
|
|
12449
13933
|
const after = response === void 0 ? void 0 : retryAfterMs$1(response.headers);
|
|
12450
13934
|
throw new LlmError(`${PROVIDER_NAME$5} rate limit or plan quota reached (429). Check the quota card in Settings > Command Code.${detail ? ` ${detail}` : ""}`, "RATE_LIMIT", {
|
|
@@ -12482,8 +13966,16 @@ var CommandCodeAdapter = class extends LlmAdapter {
|
|
|
12482
13966
|
}
|
|
12483
13967
|
}
|
|
12484
13968
|
};
|
|
13969
|
+
/** Path suffix the chosen route answers on. */
|
|
13970
|
+
function endpointPathFor(wire) {
|
|
13971
|
+
if (wire === "anthropic") return "/messages";
|
|
13972
|
+
if (wire === "responses") return "/responses";
|
|
13973
|
+
return "/chat/completions";
|
|
13974
|
+
}
|
|
12485
13975
|
function processLine$1(line, state, wire) {
|
|
12486
|
-
|
|
13976
|
+
if (wire === "anthropic") return processAnthropicStreamLine$1(line, state);
|
|
13977
|
+
if (wire === "responses") return processResponsesStreamLine(line, state);
|
|
13978
|
+
return processOpenAIStreamLine$1(line, state);
|
|
12487
13979
|
}
|
|
12488
13980
|
//#endregion
|
|
12489
13981
|
//#region src/host/command-code/account-pool.ts
|
|
@@ -13211,6 +14703,9 @@ async function getCommandCodeWebStatus(store, modelSettings, preferences, option
|
|
|
13211
14703
|
}),
|
|
13212
14704
|
quota: quota ?? null,
|
|
13213
14705
|
lastFetchedAt: quota?.fetchedAt ?? null,
|
|
14706
|
+
catalogFetchedAt: getCachedCatalogFetchedAt(),
|
|
14707
|
+
catalogStale: isCatalogStale(),
|
|
14708
|
+
...resolveZeroDataRetention(options.zeroDataRetention) ? { zeroDataRetention: true } : options.zeroDataRetention !== void 0 ? { zeroDataRetention: false } : {},
|
|
13214
14709
|
models,
|
|
13215
14710
|
contextWindowOverrides: settings.contextWindowOverrides,
|
|
13216
14711
|
defaultReasoningEffort: settings.defaultReasoningEffort,
|
|
@@ -13571,6 +15066,48 @@ function ollamaHeaders(apiKey) {
|
|
|
13571
15066
|
"content-type": "application/json"
|
|
13572
15067
|
};
|
|
13573
15068
|
}
|
|
15069
|
+
/**
|
|
15070
|
+
* Tightly-scoped check for known vision-capable models in Ollama.
|
|
15071
|
+
* Conservative allowlist of known vision families; unknown models default to false.
|
|
15072
|
+
*/
|
|
15073
|
+
function ollamaModelSupportsImage(modelId) {
|
|
15074
|
+
return supportsOllamaImage(modelId, "openai", "api-key");
|
|
15075
|
+
}
|
|
15076
|
+
/**
|
|
15077
|
+
* Resolve model-specific thinking support in Ollama.
|
|
15078
|
+
* Source: official https://github.com/ollama/ollama/blob/main/docs/capabilities/thinking.mdx
|
|
15079
|
+
* - 'levels': gpt-oss family (values low, medium, high; cannot disable thinking)
|
|
15080
|
+
* - 'boolean': deepseek-r1, qwq, qwen3 families (values true, false)
|
|
15081
|
+
* - 'none': all other / unknown models (omit thinking control entirely)
|
|
15082
|
+
*/
|
|
15083
|
+
function thinkingModeForModel(modelId) {
|
|
15084
|
+
if (!supportsOllamaThinkingControl(modelId, "native", "api-key")) return "none";
|
|
15085
|
+
const base = modelId.toLowerCase().split(":")[0] ?? "";
|
|
15086
|
+
if (base === "gpt-oss") return "levels";
|
|
15087
|
+
if (base === "deepseek-r1" || base === "qwq" || base === "qwen3") return "boolean";
|
|
15088
|
+
return "none";
|
|
15089
|
+
}
|
|
15090
|
+
/**
|
|
15091
|
+
* Map reasoning effort to wire think parameter for the target model.
|
|
15092
|
+
* Per Ollama docs:
|
|
15093
|
+
* - gpt-oss: only 'low' | 'medium' | 'high' are valid. gpt-oss cannot disable thinking, so 'none'
|
|
15094
|
+
* as well as 'xhigh'/'max'/garbage MUST be omitted (undefined).
|
|
15095
|
+
* - boolean models: 'none' -> false; 'low'|'medium'|'high'|'auto'|'true' -> true; garbage -> undefined.
|
|
15096
|
+
* - all other models: omit (undefined).
|
|
15097
|
+
*/
|
|
15098
|
+
function thinkForModel(modelId, reasoningEffort) {
|
|
15099
|
+
if (reasoningEffort === void 0 || reasoningEffort === null) return void 0;
|
|
15100
|
+
const mode = thinkingModeForModel(modelId);
|
|
15101
|
+
if (mode === "none") return void 0;
|
|
15102
|
+
const effort = String(reasoningEffort).toLowerCase();
|
|
15103
|
+
if (mode === "boolean") {
|
|
15104
|
+
if (effort === "none") return false;
|
|
15105
|
+
if (effort === "low" || effort === "medium" || effort === "high" || effort === "auto" || effort === "true") return true;
|
|
15106
|
+
return;
|
|
15107
|
+
}
|
|
15108
|
+
if (effort === "low" || effort === "medium" || effort === "high") return effort;
|
|
15109
|
+
if (effort === "auto" || effort === "true") return "medium";
|
|
15110
|
+
}
|
|
13574
15111
|
//#endregion
|
|
13575
15112
|
//#region src/host/ollama/token-store.ts
|
|
13576
15113
|
function credentialPath$4() {
|
|
@@ -13742,36 +15279,79 @@ function buildBody(wire, request) {
|
|
|
13742
15279
|
if (wire === "openai") return buildOpenAIBody(request);
|
|
13743
15280
|
return buildNativeBody(request);
|
|
13744
15281
|
}
|
|
15282
|
+
function toBareBase64(image) {
|
|
15283
|
+
const match = /^data:[^;]+;base64,(.+)$/.exec(image);
|
|
15284
|
+
return match ? match[1] : image;
|
|
15285
|
+
}
|
|
15286
|
+
function toDataUrl(image, defaultMediaType = "image/png") {
|
|
15287
|
+
if (image.startsWith("data:") || image.startsWith("http://") || image.startsWith("https://")) return image;
|
|
15288
|
+
return `data:${defaultMediaType};base64,${image}`;
|
|
15289
|
+
}
|
|
15290
|
+
function parseArguments(raw) {
|
|
15291
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) return raw;
|
|
15292
|
+
if (raw === void 0 || raw === null || raw === "") return {};
|
|
15293
|
+
if (typeof raw === "string") {
|
|
15294
|
+
const trimmed = raw.trim();
|
|
15295
|
+
if (trimmed === "" || trimmed === "{}") return {};
|
|
15296
|
+
const parsed = JSON.parse(trimmed);
|
|
15297
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed;
|
|
15298
|
+
throw new Error(`Tool call arguments must be a JSON object, got ${typeof parsed}`);
|
|
15299
|
+
}
|
|
15300
|
+
throw new Error(`Invalid tool call arguments: ${String(raw)}`);
|
|
15301
|
+
}
|
|
13745
15302
|
function buildOpenAIBody(request) {
|
|
13746
|
-
const messages =
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
|
|
13751
|
-
|
|
13752
|
-
|
|
13753
|
-
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
|
|
13757
|
-
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
15303
|
+
const messages = [];
|
|
15304
|
+
let pendingToolImages = [];
|
|
15305
|
+
const flushToolImages = () => {
|
|
15306
|
+
if (pendingToolImages.length > 0) {
|
|
15307
|
+
messages.push({
|
|
15308
|
+
role: "user",
|
|
15309
|
+
content: pendingToolImages.map((image) => ({
|
|
15310
|
+
type: "image_url",
|
|
15311
|
+
image_url: { url: toDataUrl(image) }
|
|
15312
|
+
}))
|
|
15313
|
+
});
|
|
15314
|
+
pendingToolImages = [];
|
|
15315
|
+
}
|
|
15316
|
+
};
|
|
15317
|
+
for (const message of request.messages) {
|
|
15318
|
+
if (message.role === "tool") {
|
|
15319
|
+
messages.push({
|
|
15320
|
+
role: "tool",
|
|
15321
|
+
tool_call_id: message.toolCallId ?? "",
|
|
15322
|
+
content: message.content
|
|
15323
|
+
});
|
|
15324
|
+
if (message.images !== void 0 && message.images.length > 0) pendingToolImages.push(...message.images);
|
|
15325
|
+
continue;
|
|
15326
|
+
}
|
|
15327
|
+
flushToolImages();
|
|
15328
|
+
if (message.role === "assistant" && message.toolCalls !== void 0 && message.toolCalls.length > 0) {
|
|
15329
|
+
messages.push({
|
|
15330
|
+
role: "assistant",
|
|
15331
|
+
content: message.content,
|
|
15332
|
+
tool_calls: message.toolCalls.map((call) => ({
|
|
15333
|
+
id: call.id,
|
|
15334
|
+
type: "function",
|
|
15335
|
+
function: {
|
|
15336
|
+
name: call.name,
|
|
15337
|
+
arguments: typeof call.arguments === "string" ? call.arguments : JSON.stringify(call.arguments ?? {})
|
|
15338
|
+
}
|
|
15339
|
+
}))
|
|
15340
|
+
});
|
|
15341
|
+
continue;
|
|
15342
|
+
}
|
|
15343
|
+
messages.push(openAIContent(message));
|
|
15344
|
+
}
|
|
15345
|
+
flushToolImages();
|
|
13769
15346
|
const body = {
|
|
13770
15347
|
model: request.model,
|
|
13771
15348
|
messages,
|
|
13772
15349
|
stream: true,
|
|
13773
15350
|
max_tokens: request.maxOutputTokens ?? 8192
|
|
13774
15351
|
};
|
|
15352
|
+
if (typeof request.think === "string") body.reasoning_effort = request.think;
|
|
15353
|
+
else if (request.think === true) body.reasoning_effort = "auto";
|
|
15354
|
+
else if (request.think === false) body.reasoning_effort = "none";
|
|
13775
15355
|
if (request.tools !== void 0 && request.tools.length > 0) body.tools = request.tools.map((tool) => ({
|
|
13776
15356
|
type: "function",
|
|
13777
15357
|
function: {
|
|
@@ -13785,28 +15365,30 @@ function buildOpenAIBody(request) {
|
|
|
13785
15365
|
}
|
|
13786
15366
|
function buildNativeBody(request) {
|
|
13787
15367
|
const messages = request.messages.map((message) => {
|
|
13788
|
-
if (message.role === "tool")
|
|
13789
|
-
|
|
13790
|
-
|
|
13791
|
-
|
|
15368
|
+
if (message.role === "tool") {
|
|
15369
|
+
const item = {
|
|
15370
|
+
role: "tool",
|
|
15371
|
+
content: message.content
|
|
15372
|
+
};
|
|
15373
|
+
if (message.images !== void 0 && message.images.length > 0) item.images = message.images.map(toBareBase64);
|
|
15374
|
+
return item;
|
|
15375
|
+
}
|
|
13792
15376
|
if (message.role === "assistant" && message.toolCalls !== void 0 && message.toolCalls.length > 0) return {
|
|
13793
15377
|
role: "assistant",
|
|
13794
15378
|
content: message.content,
|
|
13795
15379
|
tool_calls: message.toolCalls.map((call) => ({ function: {
|
|
13796
15380
|
name: call.name,
|
|
13797
|
-
arguments: call.arguments
|
|
15381
|
+
arguments: parseArguments(call.arguments)
|
|
13798
15382
|
} }))
|
|
13799
15383
|
};
|
|
13800
|
-
return
|
|
13801
|
-
role: message.role,
|
|
13802
|
-
content: message.content
|
|
13803
|
-
};
|
|
15384
|
+
return nativeContent(message);
|
|
13804
15385
|
});
|
|
13805
15386
|
const body = {
|
|
13806
15387
|
model: request.model,
|
|
13807
15388
|
messages,
|
|
13808
15389
|
stream: true
|
|
13809
15390
|
};
|
|
15391
|
+
if (request.think !== void 0) body.think = request.think;
|
|
13810
15392
|
const options = { num_predict: request.maxOutputTokens ?? 8192 };
|
|
13811
15393
|
if (request.temperature !== void 0) options.temperature = request.temperature;
|
|
13812
15394
|
body.options = options;
|
|
@@ -13820,6 +15402,42 @@ function buildNativeBody(request) {
|
|
|
13820
15402
|
}));
|
|
13821
15403
|
return body;
|
|
13822
15404
|
}
|
|
15405
|
+
/**
|
|
15406
|
+
* A user turn on the OpenAI surface: text stays text, images become parts.
|
|
15407
|
+
*
|
|
15408
|
+
* Ollama's OpenAI compatibility takes the standard `image_url` part with a data URL.
|
|
15409
|
+
*/
|
|
15410
|
+
function openAIContent(message) {
|
|
15411
|
+
if (message.images === void 0 || message.images.length === 0) return {
|
|
15412
|
+
role: message.role,
|
|
15413
|
+
content: message.content
|
|
15414
|
+
};
|
|
15415
|
+
return {
|
|
15416
|
+
role: message.role,
|
|
15417
|
+
content: [{
|
|
15418
|
+
type: "text",
|
|
15419
|
+
text: message.content
|
|
15420
|
+
}, ...message.images.map((image) => ({
|
|
15421
|
+
type: "image_url",
|
|
15422
|
+
image_url: { url: toDataUrl(image) }
|
|
15423
|
+
}))]
|
|
15424
|
+
};
|
|
15425
|
+
}
|
|
15426
|
+
/**
|
|
15427
|
+
* A user turn on the native surface, where images are a sibling array of raw
|
|
15428
|
+
* base64 strings rather than data URLs or interleaved content parts.
|
|
15429
|
+
*/
|
|
15430
|
+
function nativeContent(message) {
|
|
15431
|
+
if (message.images === void 0 || message.images.length === 0) return {
|
|
15432
|
+
role: message.role,
|
|
15433
|
+
content: message.content
|
|
15434
|
+
};
|
|
15435
|
+
return {
|
|
15436
|
+
role: message.role,
|
|
15437
|
+
content: message.content,
|
|
15438
|
+
images: message.images.map(toBareBase64)
|
|
15439
|
+
};
|
|
15440
|
+
}
|
|
13823
15441
|
/** The endpoint one request goes to. */
|
|
13824
15442
|
function chatUrl(wire) {
|
|
13825
15443
|
return wire === "openai" ? `${CLOUD_BASE_URL}${OPENAI_CHAT_PATH}` : `${CLOUD_BASE_URL}${NATIVE_CHAT_PATH}`;
|
|
@@ -13936,6 +15554,11 @@ function parseOpenAIChunk(record, pending) {
|
|
|
13936
15554
|
type: "text",
|
|
13937
15555
|
text: fields.content
|
|
13938
15556
|
});
|
|
15557
|
+
const thinking = typeof fields.thinking === "string" ? fields.thinking : typeof fields.reasoning === "string" ? fields.reasoning : typeof fields.reasoning_content === "string" ? fields.reasoning_content : "";
|
|
15558
|
+
if (thinking !== "") events.push({
|
|
15559
|
+
type: "thinking",
|
|
15560
|
+
text: thinking
|
|
15561
|
+
});
|
|
13939
15562
|
if (Array.isArray(fields.tool_calls)) for (const raw of fields.tool_calls) accumulateToolCall(raw, pending, true);
|
|
13940
15563
|
}
|
|
13941
15564
|
if (typeof entry.finish_reason === "string" && entry.finish_reason !== "") events.push({
|
|
@@ -13959,6 +15582,11 @@ function parseNativeChunk(record, pending) {
|
|
|
13959
15582
|
type: "text",
|
|
13960
15583
|
text: fields.content
|
|
13961
15584
|
});
|
|
15585
|
+
const thinking = typeof fields.thinking === "string" ? fields.thinking : typeof fields.reasoning === "string" ? fields.reasoning : typeof fields.reasoning_content === "string" ? fields.reasoning_content : "";
|
|
15586
|
+
if (thinking !== "") events.push({
|
|
15587
|
+
type: "thinking",
|
|
15588
|
+
text: thinking
|
|
15589
|
+
});
|
|
13962
15590
|
if (Array.isArray(fields.tool_calls)) for (const raw of fields.tool_calls) accumulateToolCall(raw, pending, false);
|
|
13963
15591
|
}
|
|
13964
15592
|
if (record.done === true) events.push({
|
|
@@ -14094,6 +15722,7 @@ function createStreamState$4() {
|
|
|
14094
15722
|
return {
|
|
14095
15723
|
nextBlockIndex: 0,
|
|
14096
15724
|
textBlock: null,
|
|
15725
|
+
thinkingBlock: null,
|
|
14097
15726
|
toolCalls: /* @__PURE__ */ new Map(),
|
|
14098
15727
|
hasContent: false
|
|
14099
15728
|
};
|
|
@@ -14101,30 +15730,85 @@ function createStreamState$4() {
|
|
|
14101
15730
|
/** Fold one event into chunks, opening and closing blocks as the stream needs. */
|
|
14102
15731
|
function applyEvent(state, event) {
|
|
14103
15732
|
if (event.type === "text") return applyText(state, event.text);
|
|
15733
|
+
if (event.type === "thinking") return applyThinking(state, event.text);
|
|
14104
15734
|
if (event.type === "tool_call") return applyToolCall(state, event.call);
|
|
14105
15735
|
return [];
|
|
14106
15736
|
}
|
|
15737
|
+
/**
|
|
15738
|
+
* Fold a thinking delta into its own block.
|
|
15739
|
+
*
|
|
15740
|
+
* The trace and the answer interleave, so a text delta or a tool call closes
|
|
15741
|
+
* the reasoning block: two open blocks at once would hand the caller chunks in
|
|
15742
|
+
* an order the stream never produced.
|
|
15743
|
+
*/
|
|
15744
|
+
function applyThinking(state, text) {
|
|
15745
|
+
state.hasContent = true;
|
|
15746
|
+
if (state.thinkingBlock === null) {
|
|
15747
|
+
const out = closeText(state);
|
|
15748
|
+
const index = state.nextBlockIndex;
|
|
15749
|
+
state.nextBlockIndex += 1;
|
|
15750
|
+
state.thinkingBlock = {
|
|
15751
|
+
index,
|
|
15752
|
+
text
|
|
15753
|
+
};
|
|
15754
|
+
return [
|
|
15755
|
+
...out,
|
|
15756
|
+
{
|
|
15757
|
+
type: "block-start",
|
|
15758
|
+
index,
|
|
15759
|
+
blockType: "reasoning"
|
|
15760
|
+
},
|
|
15761
|
+
{
|
|
15762
|
+
type: "reasoning-delta",
|
|
15763
|
+
index,
|
|
15764
|
+
text
|
|
15765
|
+
}
|
|
15766
|
+
];
|
|
15767
|
+
}
|
|
15768
|
+
state.thinkingBlock.text += text;
|
|
15769
|
+
return [{
|
|
15770
|
+
type: "reasoning-delta",
|
|
15771
|
+
index: state.thinkingBlock.index,
|
|
15772
|
+
text
|
|
15773
|
+
}];
|
|
15774
|
+
}
|
|
15775
|
+
function closeThinking(state) {
|
|
15776
|
+
const block = state.thinkingBlock;
|
|
15777
|
+
if (block === null) return [];
|
|
15778
|
+
state.thinkingBlock = null;
|
|
15779
|
+
return [{
|
|
15780
|
+
type: "block-end",
|
|
15781
|
+
index: block.index,
|
|
15782
|
+
block: {
|
|
15783
|
+
type: "reasoning",
|
|
15784
|
+
text: block.text
|
|
15785
|
+
}
|
|
15786
|
+
}];
|
|
15787
|
+
}
|
|
14107
15788
|
function applyText(state, text) {
|
|
14108
15789
|
state.hasContent = true;
|
|
15790
|
+
const closed = closeThinking(state);
|
|
14109
15791
|
if (state.textBlock === null) {
|
|
15792
|
+
closed.push({
|
|
15793
|
+
type: "block-start",
|
|
15794
|
+
index: state.nextBlockIndex,
|
|
15795
|
+
blockType: "text"
|
|
15796
|
+
});
|
|
14110
15797
|
const index = state.nextBlockIndex;
|
|
14111
15798
|
state.nextBlockIndex += 1;
|
|
14112
15799
|
state.textBlock = {
|
|
14113
15800
|
index,
|
|
14114
15801
|
text
|
|
14115
15802
|
};
|
|
14116
|
-
|
|
14117
|
-
type: "block-start",
|
|
14118
|
-
index,
|
|
14119
|
-
blockType: "text"
|
|
14120
|
-
}, {
|
|
15803
|
+
closed.push({
|
|
14121
15804
|
type: "text-delta",
|
|
14122
15805
|
index,
|
|
14123
15806
|
text
|
|
14124
|
-
}
|
|
15807
|
+
});
|
|
15808
|
+
return closed;
|
|
14125
15809
|
}
|
|
14126
15810
|
state.textBlock.text += text;
|
|
14127
|
-
return [{
|
|
15811
|
+
return [...closed, {
|
|
14128
15812
|
type: "text-delta",
|
|
14129
15813
|
index: state.textBlock.index,
|
|
14130
15814
|
text
|
|
@@ -14133,6 +15817,7 @@ function applyText(state, text) {
|
|
|
14133
15817
|
function applyToolCall(state, call) {
|
|
14134
15818
|
state.hasContent = true;
|
|
14135
15819
|
const out = [];
|
|
15820
|
+
out.push(...closeThinking(state));
|
|
14136
15821
|
out.push(...closeText(state));
|
|
14137
15822
|
const index = state.nextBlockIndex;
|
|
14138
15823
|
state.nextBlockIndex += 1;
|
|
@@ -14195,7 +15880,11 @@ function closeToolCalls$3(state) {
|
|
|
14195
15880
|
*/
|
|
14196
15881
|
function closeStream$3(state) {
|
|
14197
15882
|
const sawToolCall = state.toolCalls.size > 0;
|
|
14198
|
-
const out = [
|
|
15883
|
+
const out = [
|
|
15884
|
+
...closeThinking(state),
|
|
15885
|
+
...closeText(state),
|
|
15886
|
+
...closeToolCalls$3(state)
|
|
15887
|
+
];
|
|
14199
15888
|
out.push({
|
|
14200
15889
|
type: "finish",
|
|
14201
15890
|
reason: sawToolCall ? { kind: "tool-calls" } : { kind: "stop" }
|
|
@@ -14297,7 +15986,7 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
14297
15986
|
provider: prov,
|
|
14298
15987
|
id: model.id,
|
|
14299
15988
|
name: model.name ?? model.id,
|
|
14300
|
-
inputModalities: ["text", "image"]
|
|
15989
|
+
inputModalities: ollamaModelSupportsImage(model.id) ? ["text", "image"] : ["text"]
|
|
14301
15990
|
}));
|
|
14302
15991
|
}
|
|
14303
15992
|
async resolveModel(provider, modelId, signal) {
|
|
@@ -14307,7 +15996,7 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
14307
15996
|
provider,
|
|
14308
15997
|
id: modelId,
|
|
14309
15998
|
name: entry?.name ?? modelId,
|
|
14310
|
-
inputModalities: ["text", "image"],
|
|
15999
|
+
inputModalities: ollamaModelSupportsImage(modelId) ? ["text", "image"] : ["text"],
|
|
14311
16000
|
context: { contextWindow: contextWindowFor(entry) },
|
|
14312
16001
|
...outputReservation(contextWindowFor(entry), DEFAULT_MAX_OUTPUT_TOKENS)
|
|
14313
16002
|
};
|
|
@@ -14321,10 +16010,11 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
14321
16010
|
async *stream(options) {
|
|
14322
16011
|
yield* wrapStreamWithWatchdog((watchdogSignal) => this.requestStream(options, watchdogSignal), options.signal, STREAM_IDLE_TIMEOUT_MS$4, STREAM_IDLE_TIMEOUT_CODE$4, PROVIDER_NAME$4);
|
|
14323
16012
|
}
|
|
14324
|
-
async *requestStream(
|
|
16013
|
+
async *requestStream(rawOptions, signal) {
|
|
14325
16014
|
const fetchFn = this.options.fetchFn ?? fetch;
|
|
16015
|
+
const options = normalizeGenerateOptions(rawOptions);
|
|
14326
16016
|
const wire = wireForModel$1(options.model);
|
|
14327
|
-
const request = toOllamaRequest(options);
|
|
16017
|
+
const request = await toOllamaRequest(options, this.options.attachments, signal);
|
|
14328
16018
|
const pool = this.accountPool;
|
|
14329
16019
|
const tried = /* @__PURE__ */ new Set();
|
|
14330
16020
|
let lastStatus;
|
|
@@ -14364,7 +16054,7 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
14364
16054
|
spent = event.usage;
|
|
14365
16055
|
continue;
|
|
14366
16056
|
}
|
|
14367
|
-
if (event.type === "text" || event.type === "tool_call") {
|
|
16057
|
+
if (event.type === "text" || event.type === "thinking" || event.type === "tool_call") {
|
|
14368
16058
|
for (const chunk of applyEvent(state, event)) yield chunk;
|
|
14369
16059
|
continue;
|
|
14370
16060
|
}
|
|
@@ -14393,18 +16083,134 @@ var OllamaAdapter = class extends LlmAdapter {
|
|
|
14393
16083
|
throw new LlmError(`${PROVIDER_NAME$4} request failed${status === void 0 ? "" : ` (${status})`}.${lastDetail}`, status !== void 0 && status >= 500 ? "SERVER" : "INVALID_REQUEST", status === void 0 ? {} : { status });
|
|
14394
16084
|
}
|
|
14395
16085
|
};
|
|
14396
|
-
|
|
14397
|
-
|
|
14398
|
-
|
|
14399
|
-
|
|
14400
|
-
|
|
14401
|
-
|
|
14402
|
-
|
|
14403
|
-
|
|
14404
|
-
|
|
14405
|
-
|
|
16086
|
+
function isAbort$2(error, signal) {
|
|
16087
|
+
return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
|
|
16088
|
+
}
|
|
16089
|
+
function isRecord$18(value) {
|
|
16090
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16091
|
+
}
|
|
16092
|
+
function asString$11(value) {
|
|
16093
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
16094
|
+
}
|
|
16095
|
+
function attachmentLabel$4(block) {
|
|
16096
|
+
const attachment = isRecord$18(block.attachment) ? block.attachment : void 0;
|
|
16097
|
+
return asString$11(attachment?.name) || asString$11(attachment?.attachmentId);
|
|
16098
|
+
}
|
|
16099
|
+
function unavailableImageText$4(block) {
|
|
16100
|
+
const label = attachmentLabel$4(block);
|
|
16101
|
+
return `[image unavailable: ${label ? `${label} could not be read` : "the image could not be read"}; ask the user to attach it again if the image is needed]`;
|
|
16102
|
+
}
|
|
16103
|
+
function abortException(signal) {
|
|
16104
|
+
if (signal?.reason !== void 0) return signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason));
|
|
16105
|
+
return new DOMException("The operation was aborted", "AbortError");
|
|
16106
|
+
}
|
|
16107
|
+
function unsupportedImageText$1(model, block) {
|
|
16108
|
+
const label = attachmentLabel$4(block);
|
|
16109
|
+
return `[image unsupported: ${label ? `${label}: ` : ""}model ${model} is not known to declare image support; remove the attachment or switch to a vision model]`;
|
|
16110
|
+
}
|
|
16111
|
+
function combineTextParts(parts) {
|
|
16112
|
+
let result = "";
|
|
16113
|
+
for (const part of parts) {
|
|
16114
|
+
if (!part) continue;
|
|
16115
|
+
if (!result) result = part;
|
|
16116
|
+
else if (part.startsWith("[image ") || result.endsWith("\n")) result = result.trimEnd() + "\n\n" + part;
|
|
16117
|
+
else result += part;
|
|
16118
|
+
}
|
|
16119
|
+
return result;
|
|
16120
|
+
}
|
|
16121
|
+
async function processMessageContent(content, model, attachments, signal) {
|
|
16122
|
+
if (signal?.aborted) throw abortException(signal);
|
|
16123
|
+
if (typeof content === "string") return { text: content };
|
|
16124
|
+
if (!Array.isArray(content)) return { text: "" };
|
|
16125
|
+
const textParts = [];
|
|
16126
|
+
const images = [];
|
|
16127
|
+
for (const part of content) {
|
|
16128
|
+
if (typeof part === "string") {
|
|
16129
|
+
textParts.push(part);
|
|
14406
16130
|
continue;
|
|
14407
16131
|
}
|
|
16132
|
+
if (!isRecord$18(part)) continue;
|
|
16133
|
+
if (part.type === "text") {
|
|
16134
|
+
if (typeof part.text === "string" && part.text !== "") textParts.push(part.text);
|
|
16135
|
+
continue;
|
|
16136
|
+
}
|
|
16137
|
+
if (part.type === "image") {
|
|
16138
|
+
if (!ollamaModelSupportsImage(model)) {
|
|
16139
|
+
textParts.push(unsupportedImageText$1(model, part));
|
|
16140
|
+
continue;
|
|
16141
|
+
}
|
|
16142
|
+
const reference = isImageRef(part.attachment) ? part.attachment : isImageRef(part.source) ? part.source : null;
|
|
16143
|
+
if (reference !== null) {
|
|
16144
|
+
if (!attachments) {
|
|
16145
|
+
textParts.push(unavailableImageText$4(part));
|
|
16146
|
+
continue;
|
|
16147
|
+
}
|
|
16148
|
+
try {
|
|
16149
|
+
if (signal?.aborted) throw abortException(signal);
|
|
16150
|
+
const loaded = await attachments.readImage(reference, signal);
|
|
16151
|
+
if (signal?.aborted) throw abortException(signal);
|
|
16152
|
+
if (loaded === null) textParts.push(unavailableImageText$4(part));
|
|
16153
|
+
else images.push(encodeImage(loaded.data, loaded.ref.mediaType));
|
|
16154
|
+
} catch (error) {
|
|
16155
|
+
if (isAbort$2(error, signal)) throw error;
|
|
16156
|
+
textParts.push(unavailableImageText$4(part));
|
|
16157
|
+
}
|
|
16158
|
+
continue;
|
|
16159
|
+
}
|
|
16160
|
+
const inline = part.source;
|
|
16161
|
+
if (isRecord$18(inline)) {
|
|
16162
|
+
const data = inline.data;
|
|
16163
|
+
if (typeof data === "string" && data !== "") {
|
|
16164
|
+
images.push(encodeImage(data, asMediaType(inline.mediaType)));
|
|
16165
|
+
continue;
|
|
16166
|
+
}
|
|
16167
|
+
}
|
|
16168
|
+
if (typeof part.image === "string" && part.image !== "") {
|
|
16169
|
+
images.push(part.image);
|
|
16170
|
+
continue;
|
|
16171
|
+
}
|
|
16172
|
+
textParts.push(unavailableImageText$4(part));
|
|
16173
|
+
continue;
|
|
16174
|
+
}
|
|
16175
|
+
}
|
|
16176
|
+
return {
|
|
16177
|
+
text: combineTextParts(textParts),
|
|
16178
|
+
...images.length > 0 ? { images } : {}
|
|
16179
|
+
};
|
|
16180
|
+
}
|
|
16181
|
+
/**
|
|
16182
|
+
* Project DSH's request shape onto Ollama's, per surface.
|
|
16183
|
+
* Consumes the canonical Message vocabulary produced by normalizeGenerateOptions.
|
|
16184
|
+
*/
|
|
16185
|
+
async function toOllamaRequest(options, attachments, signal) {
|
|
16186
|
+
if (signal?.aborted) throw abortException(signal);
|
|
16187
|
+
const messages = [];
|
|
16188
|
+
const trimmedSystem = typeof options.system === "string" && options.system.trim() !== "" ? options.system.trim() : void 0;
|
|
16189
|
+
if (trimmedSystem !== void 0) messages.push({
|
|
16190
|
+
role: "system",
|
|
16191
|
+
content: trimmedSystem
|
|
16192
|
+
});
|
|
16193
|
+
for (let index = 0; index < options.messages.length; index++) {
|
|
16194
|
+
const message = options.messages[index];
|
|
16195
|
+
if (index === 0 && trimmedSystem !== void 0 && (message.role === "system" || message.role === "developer")) {
|
|
16196
|
+
if (textOf$4(message.content).trim() === trimmedSystem) continue;
|
|
16197
|
+
}
|
|
16198
|
+
if ((message.source?.kind === "tool" || Array.isArray(message.content) && message.content.some((b) => b.type === "tool-result")) && Array.isArray(message.content)) {
|
|
16199
|
+
const toolResultBlocks = message.content.filter((b) => b.type === "tool-result");
|
|
16200
|
+
if (toolResultBlocks.length > 0) {
|
|
16201
|
+
for (const block of toolResultBlocks) {
|
|
16202
|
+
const toolCallId = String(block.toolCallId ?? (isRecord$18(message.source) ? message.source.callId : "") ?? "");
|
|
16203
|
+
const processed = await processMessageContent(block.content, options.model, attachments, signal);
|
|
16204
|
+
messages.push({
|
|
16205
|
+
role: "tool",
|
|
16206
|
+
content: processed.text,
|
|
16207
|
+
...toolCallId !== "" ? { toolCallId } : {},
|
|
16208
|
+
...processed.images ? { images: processed.images } : {}
|
|
16209
|
+
});
|
|
16210
|
+
}
|
|
16211
|
+
continue;
|
|
16212
|
+
}
|
|
16213
|
+
}
|
|
14408
16214
|
const toolCalls = assistantToolCalls(message.content);
|
|
14409
16215
|
if (toolCalls.length > 0) {
|
|
14410
16216
|
messages.push({
|
|
@@ -14414,9 +16220,26 @@ function toOllamaRequest(options) {
|
|
|
14414
16220
|
});
|
|
14415
16221
|
continue;
|
|
14416
16222
|
}
|
|
16223
|
+
if (message.role === "assistant") {
|
|
16224
|
+
messages.push({
|
|
16225
|
+
role: "assistant",
|
|
16226
|
+
content: textOf$4(message.content)
|
|
16227
|
+
});
|
|
16228
|
+
continue;
|
|
16229
|
+
}
|
|
16230
|
+
if (message.role === "system" || message.role === "developer") {
|
|
16231
|
+
const text = textOf$4(message.content).trim();
|
|
16232
|
+
if (text !== "") messages.push({
|
|
16233
|
+
role: "system",
|
|
16234
|
+
content: text
|
|
16235
|
+
});
|
|
16236
|
+
continue;
|
|
16237
|
+
}
|
|
16238
|
+
const processed = await processMessageContent(message.content, options.model, attachments, signal);
|
|
14417
16239
|
messages.push({
|
|
14418
|
-
role:
|
|
14419
|
-
content:
|
|
16240
|
+
role: "user",
|
|
16241
|
+
content: processed.text,
|
|
16242
|
+
...processed.images ? { images: processed.images } : {}
|
|
14420
16243
|
});
|
|
14421
16244
|
}
|
|
14422
16245
|
const request = {
|
|
@@ -14425,6 +16248,8 @@ function toOllamaRequest(options) {
|
|
|
14425
16248
|
};
|
|
14426
16249
|
if (options.maxTokens !== void 0) request.maxOutputTokens = options.maxTokens;
|
|
14427
16250
|
if (options.temperature !== void 0) request.temperature = options.temperature;
|
|
16251
|
+
const think = thinkForModel(options.model, options.reasoningEffort);
|
|
16252
|
+
if (think !== void 0) request.think = think;
|
|
14428
16253
|
const tools = options.tools;
|
|
14429
16254
|
if (Array.isArray(tools) && tools.length > 0) request.tools = tools.map((tool) => ({
|
|
14430
16255
|
name: tool.name,
|
|
@@ -14455,6 +16280,23 @@ function assistantToolCalls(content) {
|
|
|
14455
16280
|
}
|
|
14456
16281
|
return calls;
|
|
14457
16282
|
}
|
|
16283
|
+
function isImageRef(value) {
|
|
16284
|
+
return typeof value === "object" && value !== null && typeof value.attachmentId === "string";
|
|
16285
|
+
}
|
|
16286
|
+
function asMediaType(value) {
|
|
16287
|
+
return typeof value === "string" && value !== "" ? value : "image/png";
|
|
16288
|
+
}
|
|
16289
|
+
/**
|
|
16290
|
+
* The base64 payload Ollama wants, with or without a data URL prefix.
|
|
16291
|
+
*
|
|
16292
|
+
* The native surface takes the bare payload; a `data:` URL is accepted by both
|
|
16293
|
+
* surfaces once, so one encoding serves either and avoids a route-specific
|
|
16294
|
+
* guess about what a given model accepts.
|
|
16295
|
+
*/
|
|
16296
|
+
function encodeImage(data, mediaType) {
|
|
16297
|
+
const base64 = typeof data === "string" ? data : Buffer.from(data).toString("base64");
|
|
16298
|
+
return base64.startsWith("data:") ? base64 : `data:${mediaType};base64,${base64}`;
|
|
16299
|
+
}
|
|
14458
16300
|
function textOf$4(content) {
|
|
14459
16301
|
if (typeof content === "string") return content;
|
|
14460
16302
|
if (Array.isArray(content)) return content.map((part) => {
|
|
@@ -20368,6 +22210,22 @@ function redactToken(token) {
|
|
|
20368
22210
|
if (token === void 0 || token === null || token === "") return "<none>";
|
|
20369
22211
|
return "sha256:" + createHash("sha256").update(token).digest("hex").slice(0, 12) + "/len:" + token.length;
|
|
20370
22212
|
}
|
|
22213
|
+
/**
|
|
22214
|
+
* Adapter-selected auth owner (nonsecret hash).
|
|
22215
|
+
*
|
|
22216
|
+
* Scoped to the specific account and credential token/key selected by the adapter.
|
|
22217
|
+
* Any account change or token/key replacement produces a different hash,
|
|
22218
|
+
* preventing thinking/signature replay across different authentication boundaries.
|
|
22219
|
+
*/
|
|
22220
|
+
function computeMinimaxAuthOwner(credentials, accountId) {
|
|
22221
|
+
const token = (credentials?.apiKey || credentials?.accessToken || "").trim();
|
|
22222
|
+
if (!token) return "";
|
|
22223
|
+
const hash = createHash("sha256");
|
|
22224
|
+
if (accountId && accountId.trim() !== "") hash.update(`acc:${accountId}\0`);
|
|
22225
|
+
if (credentials?.region && credentials.region.trim() !== "") hash.update(`reg:${credentials.region}\0`);
|
|
22226
|
+
hash.update(`tok:${token}`);
|
|
22227
|
+
return hash.digest("hex").slice(0, 32);
|
|
22228
|
+
}
|
|
20371
22229
|
//#endregion
|
|
20372
22230
|
//#region src/host/minimax-code/mapper.ts
|
|
20373
22231
|
/**
|
|
@@ -20457,6 +22315,31 @@ function safeJsonParse$2(text) {
|
|
|
20457
22315
|
function sanitizeText$2(text) {
|
|
20458
22316
|
return text.replace(/\0/g, "");
|
|
20459
22317
|
}
|
|
22318
|
+
function jsonSafeValue$1(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
22319
|
+
if (value === null) return null;
|
|
22320
|
+
switch (typeof value) {
|
|
22321
|
+
case "string":
|
|
22322
|
+
case "boolean": return value;
|
|
22323
|
+
case "number":
|
|
22324
|
+
if (!Number.isFinite(value)) return void 0;
|
|
22325
|
+
return Object.is(value, -0) ? 0 : value;
|
|
22326
|
+
case "object": break;
|
|
22327
|
+
default: return;
|
|
22328
|
+
}
|
|
22329
|
+
if (seen.has(value)) return void 0;
|
|
22330
|
+
seen.add(value);
|
|
22331
|
+
try {
|
|
22332
|
+
if (Array.isArray(value)) return Array.from(value, (item) => jsonSafeValue$1(item, seen) ?? null);
|
|
22333
|
+
const record = {};
|
|
22334
|
+
for (const key of Object.keys(value)) {
|
|
22335
|
+
const child = jsonSafeValue$1(value[key], seen);
|
|
22336
|
+
if (child !== void 0) record[key] = child;
|
|
22337
|
+
}
|
|
22338
|
+
return record;
|
|
22339
|
+
} finally {
|
|
22340
|
+
seen.delete(value);
|
|
22341
|
+
}
|
|
22342
|
+
}
|
|
20460
22343
|
/** Media types the wire accepts as inline base64. */
|
|
20461
22344
|
const SUPPORTED_IMAGE_MEDIA_TYPES$2 = /* @__PURE__ */ new Set([
|
|
20462
22345
|
"image/png",
|
|
@@ -20640,9 +22523,92 @@ function anthropicUserContent(message, images) {
|
|
|
20640
22523
|
}
|
|
20641
22524
|
return blocks;
|
|
20642
22525
|
}
|
|
20643
|
-
|
|
22526
|
+
/**
|
|
22527
|
+
* Native blocks to replay for one assistant turn, as the service issued them.
|
|
22528
|
+
*
|
|
22529
|
+
* Read from the message's stored replay state rather than rebuilt from the
|
|
22530
|
+
* visible reasoning text: a rebuilt block is not the block the model signed, and
|
|
22531
|
+
* the difference is exactly what the provider validates. Falls back to nothing
|
|
22532
|
+
* when a turn carries no stored state, which is the current behaviour for
|
|
22533
|
+
* history this route did not produce.
|
|
22534
|
+
*/
|
|
22535
|
+
/** Fingerprint the entire array of emitted visible blocks in an assistant message. */
|
|
22536
|
+
function computeVisibleContentHash(blocks) {
|
|
22537
|
+
if (!Array.isArray(blocks)) return "";
|
|
22538
|
+
const canonical = blocks.map((b) => {
|
|
22539
|
+
if (!isRecord$15(b)) return null;
|
|
22540
|
+
const type = asString$8(b.type) ?? "";
|
|
22541
|
+
if (type === "text" || type === "reasoning") return [type, sanitizeText$2(asString$8(b.text) ?? "")];
|
|
22542
|
+
if (type === "tool-call") return [
|
|
22543
|
+
type,
|
|
22544
|
+
asString$8(b.id) ?? "",
|
|
22545
|
+
asString$8(b.name) ?? "",
|
|
22546
|
+
toolCallArguments$2(b.arguments)
|
|
22547
|
+
];
|
|
22548
|
+
return [type, jsonSafeValue$1(b)];
|
|
22549
|
+
});
|
|
22550
|
+
return createHash("sha256").update(JSON.stringify(canonical)).digest("hex").slice(0, 32);
|
|
22551
|
+
}
|
|
22552
|
+
/**
|
|
22553
|
+
* Verbatim wire block to replay for one reasoning block.
|
|
22554
|
+
*
|
|
22555
|
+
* Verifies route, model, and auth owner provenance. Fails safely on legacy unscoped state
|
|
22556
|
+
* or any mismatch, dropping the replay block rather than submitting invalid state.
|
|
22557
|
+
* Preserves raw native text and signature binding.
|
|
22558
|
+
*/
|
|
22559
|
+
function replayedThinkingBlock$1(message, index, expectedScope, block) {
|
|
22560
|
+
const source = message.source;
|
|
22561
|
+
if (source === void 0 || source === null || !isRecord$15(source)) return void 0;
|
|
22562
|
+
if (source.provider && source.provider !== PROVIDER_ID$2 && source.provider !== "dsh-chatgpt-subscription") return;
|
|
22563
|
+
if (source.model && expectedScope && source.model !== expectedScope.model) return;
|
|
22564
|
+
const state = source.replayState;
|
|
22565
|
+
if (!isRecord$15(state)) return void 0;
|
|
22566
|
+
const resp = isRecord$15(state.response) ? state.response : state;
|
|
22567
|
+
const stateRoute = asString$8(resp.route) ?? asString$8(resp.provider);
|
|
22568
|
+
const stateModel = asString$8(resp.model);
|
|
22569
|
+
const stateAuthOwner = asString$8(resp.authOwner);
|
|
22570
|
+
if (!stateRoute || !stateModel || !stateAuthOwner || stateAuthOwner.trim() === "") return;
|
|
22571
|
+
if (expectedScope === void 0) return void 0;
|
|
22572
|
+
if (expectedScope.route && stateRoute !== expectedScope.route) return void 0;
|
|
22573
|
+
if (expectedScope.model && stateModel !== expectedScope.model) return void 0;
|
|
22574
|
+
if (!expectedScope.authOwner || expectedScope.authOwner.trim() === "" || stateAuthOwner !== expectedScope.authOwner) return;
|
|
22575
|
+
let candidate;
|
|
22576
|
+
if (Array.isArray(state.blocks)) {
|
|
22577
|
+
const entry = state.blocks[index];
|
|
22578
|
+
if (isRecord$15(entry)) candidate = entry;
|
|
22579
|
+
}
|
|
22580
|
+
if (candidate === void 0 && Array.isArray(resp.blocks)) {
|
|
22581
|
+
const entry = resp.blocks[index];
|
|
22582
|
+
if (isRecord$15(entry)) candidate = entry;
|
|
22583
|
+
}
|
|
22584
|
+
if (candidate === void 0 && Array.isArray(resp.nativeBlocks)) {
|
|
22585
|
+
const entry = resp.nativeBlocks[index];
|
|
22586
|
+
if (isRecord$15(entry)) candidate = entry;
|
|
22587
|
+
}
|
|
22588
|
+
if (candidate === void 0) return void 0;
|
|
22589
|
+
if (Array.isArray(state.blocks) && state.blocks.length !== message.content.length) return void 0;
|
|
22590
|
+
if (Array.isArray(resp.blocks) && resp.blocks.length !== message.content.length) return void 0;
|
|
22591
|
+
const stateVisibleHash = asString$8(resp.visibleContentHash);
|
|
22592
|
+
if (!stateVisibleHash || stateVisibleHash.trim() === "") return void 0;
|
|
22593
|
+
if (computeVisibleContentHash(message.content) !== stateVisibleHash) return void 0;
|
|
22594
|
+
if (candidate.type !== "thinking" && candidate.type !== "redacted_thinking") return;
|
|
22595
|
+
const visibleText = asString$8(block?.text) ?? "";
|
|
22596
|
+
if (candidate.type === "redacted_thinking") {
|
|
22597
|
+
if (visibleText !== "") return void 0;
|
|
22598
|
+
const data = asString$8(candidate.data);
|
|
22599
|
+
if (!data || data.trim() === "") return void 0;
|
|
22600
|
+
return structuredClone(candidate);
|
|
22601
|
+
}
|
|
22602
|
+
if (sanitizeText$2(asString$8(candidate.thinking) ?? "") !== sanitizeText$2(visibleText)) return;
|
|
22603
|
+
const signature = asString$8(candidate.signature);
|
|
22604
|
+
if (!signature || signature.trim() === "") return void 0;
|
|
22605
|
+
return structuredClone(candidate);
|
|
22606
|
+
}
|
|
22607
|
+
function anthropicAssistantContent(message, expectedScope) {
|
|
20644
22608
|
const blocks = [];
|
|
20645
|
-
|
|
22609
|
+
if (!Array.isArray(message.content)) return blocks;
|
|
22610
|
+
for (let index = 0; index < message.content.length; index++) {
|
|
22611
|
+
const block = message.content[index];
|
|
20646
22612
|
if (!isRecord$15(block)) continue;
|
|
20647
22613
|
if (block.type === "text" && typeof block.text === "string") {
|
|
20648
22614
|
const text = sanitizeText$2(block.text);
|
|
@@ -20660,6 +22626,12 @@ function anthropicAssistantContent(message) {
|
|
|
20660
22626
|
name: block.name,
|
|
20661
22627
|
input: isRecord$15(parsed) ? parsed : {}
|
|
20662
22628
|
});
|
|
22629
|
+
continue;
|
|
22630
|
+
}
|
|
22631
|
+
if (block.type === "reasoning") {
|
|
22632
|
+
const replay = replayedThinkingBlock$1(message, index, expectedScope, block);
|
|
22633
|
+
if (replay !== void 0) blocks.push(replay);
|
|
22634
|
+
continue;
|
|
20663
22635
|
}
|
|
20664
22636
|
}
|
|
20665
22637
|
return blocks;
|
|
@@ -20808,6 +22780,12 @@ function markMinimaxCacheBreakpoints(body) {
|
|
|
20808
22780
|
}
|
|
20809
22781
|
/** Build one Anthropic Messages body for this subscription. */
|
|
20810
22782
|
function buildMinimaxRequest(options, images = NO_RESOLVED_IMAGES$2, request = {}) {
|
|
22783
|
+
const expectedScope = request.authOwner !== void 0 ? {
|
|
22784
|
+
route: request.route ?? PROVIDER_ID$2,
|
|
22785
|
+
model: options.model,
|
|
22786
|
+
authOwner: request.authOwner,
|
|
22787
|
+
provider: PROVIDER_ID$2
|
|
22788
|
+
} : void 0;
|
|
20811
22789
|
const entries = [];
|
|
20812
22790
|
for (const message of nonSystemMessages$2(options)) {
|
|
20813
22791
|
if (isToolResultMessage$2(message)) {
|
|
@@ -20819,7 +22797,7 @@ function buildMinimaxRequest(options, images = NO_RESOLVED_IMAGES$2, request = {
|
|
|
20819
22797
|
}
|
|
20820
22798
|
entries.push({
|
|
20821
22799
|
role: message.role === "assistant" ? "assistant" : "user",
|
|
20822
|
-
content: message.role === "assistant" ? anthropicAssistantContent(message) : anthropicUserContent(message, images)
|
|
22800
|
+
content: message.role === "assistant" ? anthropicAssistantContent(message, expectedScope) : anthropicUserContent(message, images)
|
|
20823
22801
|
});
|
|
20824
22802
|
}
|
|
20825
22803
|
const system = leadingSystemText$2(options);
|
|
@@ -20867,8 +22845,16 @@ function assertRequestBodyFits(body, limit = maxMessageBodyBytes()) {
|
|
|
20867
22845
|
if (bytes <= limit) return serialized;
|
|
20868
22846
|
throw new LlmError$1("MiniMax Code(编程订阅) request was not sent: the serialized body is " + bytes + " bytes, above the " + limit + "-byte ceiling this route enforces. Compact the conversation or start a new session, and check for large tool results or attached media.", "PROVIDER_ERROR");
|
|
20869
22847
|
}
|
|
20870
|
-
function createStreamState() {
|
|
22848
|
+
function createStreamState(options) {
|
|
20871
22849
|
return {
|
|
22850
|
+
provenance: options?.model && options?.authOwner ? {
|
|
22851
|
+
route: options.route ?? PROVIDER_ID$2,
|
|
22852
|
+
model: options.model,
|
|
22853
|
+
authOwner: options.authOwner,
|
|
22854
|
+
provider: options.provider ?? PROVIDER_ID$2
|
|
22855
|
+
} : void 0,
|
|
22856
|
+
replayBlocks: [],
|
|
22857
|
+
pendingThinking: /* @__PURE__ */ new Map(),
|
|
20872
22858
|
blocks: [],
|
|
20873
22859
|
current: null,
|
|
20874
22860
|
toolCalls: /* @__PURE__ */ new Map(),
|
|
@@ -20912,6 +22898,7 @@ function openTextBlock$1(state, type) {
|
|
|
20912
22898
|
type,
|
|
20913
22899
|
text: ""
|
|
20914
22900
|
});
|
|
22901
|
+
state.replayBlocks[index] = null;
|
|
20915
22902
|
out.push({
|
|
20916
22903
|
type: "block-start",
|
|
20917
22904
|
index,
|
|
@@ -20929,6 +22916,7 @@ function closeToolCalls$1(state) {
|
|
|
20929
22916
|
arguments: call.arguments === "" ? "{}" : call.arguments
|
|
20930
22917
|
};
|
|
20931
22918
|
state.blocks[call.blockIndex] = block;
|
|
22919
|
+
state.replayBlocks[call.blockIndex] = null;
|
|
20932
22920
|
out.push({
|
|
20933
22921
|
type: "block-end",
|
|
20934
22922
|
index: call.blockIndex,
|
|
@@ -20961,9 +22949,34 @@ function closeMinimaxStream(state) {
|
|
|
20961
22949
|
type: "usage",
|
|
20962
22950
|
usage: tokenUsage$2(state)
|
|
20963
22951
|
});
|
|
22952
|
+
const paddedBlocks = state.blocks.map((_, i) => {
|
|
22953
|
+
const b = state.replayBlocks[i];
|
|
22954
|
+
return b !== void 0 && b !== null ? jsonSafeValue$1(b) : null;
|
|
22955
|
+
});
|
|
22956
|
+
const replayThinking = paddedBlocks.filter((b) => b !== null);
|
|
22957
|
+
const hasReplay = replayThinking.length > 0;
|
|
22958
|
+
const visibleContentHash = computeVisibleContentHash(state.blocks);
|
|
22959
|
+
const replayState = hasReplay ? state.provenance ? {
|
|
22960
|
+
response: {
|
|
22961
|
+
provider: state.provenance.provider ?? PROVIDER_ID$2,
|
|
22962
|
+
route: state.provenance.route ?? PROVIDER_ID$2,
|
|
22963
|
+
model: state.provenance.model,
|
|
22964
|
+
authOwner: state.provenance.authOwner,
|
|
22965
|
+
visibleContentHash,
|
|
22966
|
+
minimaxThinking: replayThinking
|
|
22967
|
+
},
|
|
22968
|
+
blocks: paddedBlocks
|
|
22969
|
+
} : {
|
|
22970
|
+
response: {
|
|
22971
|
+
visibleContentHash,
|
|
22972
|
+
minimaxThinking: replayThinking
|
|
22973
|
+
},
|
|
22974
|
+
blocks: paddedBlocks
|
|
22975
|
+
} : void 0;
|
|
20964
22976
|
out.push({
|
|
20965
22977
|
type: "finish",
|
|
20966
|
-
reason: finishReasonFor$2(state)
|
|
22978
|
+
reason: finishReasonFor$2(state),
|
|
22979
|
+
...replayState !== void 0 ? { replayState } : {}
|
|
20967
22980
|
});
|
|
20968
22981
|
return out;
|
|
20969
22982
|
}
|
|
@@ -21034,8 +23047,40 @@ function processMinimaxStreamLine(line, state) {
|
|
|
21034
23047
|
return out;
|
|
21035
23048
|
}
|
|
21036
23049
|
if (blockType === "thinking" || blockType === "redacted_thinking") {
|
|
21037
|
-
|
|
21038
|
-
|
|
23050
|
+
const index = state.blocks.length;
|
|
23051
|
+
const kind = blockType === "redacted_thinking" ? "redacted_thinking" : "thinking";
|
|
23052
|
+
const data = asString$8(block.data) ?? "";
|
|
23053
|
+
const signature = asString$8(block.signature) ?? "";
|
|
23054
|
+
const thinking = asString$8(block.thinking) ?? "";
|
|
23055
|
+
state.current = {
|
|
23056
|
+
index,
|
|
23057
|
+
type: "reasoning",
|
|
23058
|
+
text: kind === "thinking" ? thinking : ""
|
|
23059
|
+
};
|
|
23060
|
+
state.blocks.push({
|
|
23061
|
+
type: "reasoning",
|
|
23062
|
+
text: state.current.text
|
|
23063
|
+
});
|
|
23064
|
+
const initialReplay = isRecord$15(event.content_block) ? structuredClone(event.content_block) : { type: blockType };
|
|
23065
|
+
state.replayBlocks[index] = initialReplay;
|
|
23066
|
+
state.pendingThinking.set(contentIndex, {
|
|
23067
|
+
dshIndex: index,
|
|
23068
|
+
kind,
|
|
23069
|
+
text: thinking,
|
|
23070
|
+
signature,
|
|
23071
|
+
data
|
|
23072
|
+
});
|
|
23073
|
+
state.contentIndexes.set(contentIndex, index);
|
|
23074
|
+
out.push({
|
|
23075
|
+
type: "block-start",
|
|
23076
|
+
index,
|
|
23077
|
+
blockType: "reasoning"
|
|
23078
|
+
});
|
|
23079
|
+
if (state.current.text !== "") out.push({
|
|
23080
|
+
type: "reasoning-delta",
|
|
23081
|
+
index,
|
|
23082
|
+
text: state.current.text
|
|
23083
|
+
});
|
|
21039
23084
|
return out;
|
|
21040
23085
|
}
|
|
21041
23086
|
out.push(...openTextBlock$1(state, "text"));
|
|
@@ -21061,8 +23106,37 @@ function processMinimaxStreamLine(line, state) {
|
|
|
21061
23106
|
}
|
|
21062
23107
|
return out;
|
|
21063
23108
|
}
|
|
23109
|
+
if (deltaType === "signature_delta") {
|
|
23110
|
+
const signature = asString$8(delta.signature) ?? "";
|
|
23111
|
+
const pending = state.pendingThinking.get(contentIndex);
|
|
23112
|
+
if (pending !== void 0 && signature !== "") {
|
|
23113
|
+
pending.signature += signature;
|
|
23114
|
+
const replay = state.replayBlocks[pending.dshIndex];
|
|
23115
|
+
if (replay !== void 0 && replay !== null) replay.signature = (asString$8(replay.signature) ?? "") + signature;
|
|
23116
|
+
}
|
|
23117
|
+
return out;
|
|
23118
|
+
}
|
|
23119
|
+
if (deltaType === "data_delta") {
|
|
23120
|
+
const data = asString$8(delta.data) ?? "";
|
|
23121
|
+
const pending = state.pendingThinking.get(contentIndex);
|
|
23122
|
+
if (pending !== void 0 && data !== "") {
|
|
23123
|
+
pending.data = (pending.data ?? "") + data;
|
|
23124
|
+
const replay = state.replayBlocks[pending.dshIndex];
|
|
23125
|
+
if (replay !== void 0 && replay !== null) replay.data = (asString$8(replay.data) ?? "") + data;
|
|
23126
|
+
}
|
|
23127
|
+
return out;
|
|
23128
|
+
}
|
|
21064
23129
|
const text = deltaType === "thinking_delta" ? asString$8(delta.thinking) : asString$8(delta.text);
|
|
21065
23130
|
if (text !== void 0 && text !== "") {
|
|
23131
|
+
const pending = state.pendingThinking.get(contentIndex);
|
|
23132
|
+
if (pending !== void 0) {
|
|
23133
|
+
pending.text += text;
|
|
23134
|
+
const replay = state.replayBlocks[pending.dshIndex];
|
|
23135
|
+
if (replay !== void 0 && replay !== null) {
|
|
23136
|
+
const field = replay.type === "redacted_thinking" ? "data" : "thinking";
|
|
23137
|
+
replay[field] = (asString$8(replay[field]) ?? "") + text;
|
|
23138
|
+
}
|
|
23139
|
+
}
|
|
21066
23140
|
const index = state.contentIndexes.get(contentIndex) ?? state.current?.index;
|
|
21067
23141
|
const kind = deltaType === "thinking_delta" ? "reasoning" : "text";
|
|
21068
23142
|
if (state.current === null || state.current.index !== index) {
|
|
@@ -21077,6 +23151,7 @@ function processMinimaxStreamLine(line, state) {
|
|
|
21077
23151
|
type: kind,
|
|
21078
23152
|
text: ""
|
|
21079
23153
|
});
|
|
23154
|
+
state.replayBlocks[next] = null;
|
|
21080
23155
|
state.contentIndexes.set(contentIndex, next);
|
|
21081
23156
|
out.push({
|
|
21082
23157
|
type: "block-start",
|
|
@@ -21159,7 +23234,7 @@ const MINIMAX_CODE_MODELS = [
|
|
|
21159
23234
|
maxAttachments: null,
|
|
21160
23235
|
maxImageBytes: null,
|
|
21161
23236
|
maxVideoBytes: null,
|
|
21162
|
-
|
|
23237
|
+
filesApiDocumented: false,
|
|
21163
23238
|
description: "Thinking is always on and has no selectable level. Text input only."
|
|
21164
23239
|
},
|
|
21165
23240
|
{
|
|
@@ -21175,7 +23250,7 @@ const MINIMAX_CODE_MODELS = [
|
|
|
21175
23250
|
maxAttachments: null,
|
|
21176
23251
|
maxImageBytes: null,
|
|
21177
23252
|
maxVideoBytes: null,
|
|
21178
|
-
|
|
23253
|
+
filesApiDocumented: false,
|
|
21179
23254
|
description: "The high-speed M2.7. Thinking is always on with no selectable level. Text input only."
|
|
21180
23255
|
},
|
|
21181
23256
|
{
|
|
@@ -21191,7 +23266,7 @@ const MINIMAX_CODE_MODELS = [
|
|
|
21191
23266
|
maxAttachments: 9,
|
|
21192
23267
|
maxImageBytes: 10 * 1024 * 1024,
|
|
21193
23268
|
maxVideoBytes: 50 * 1024 * 1024,
|
|
21194
|
-
|
|
23269
|
+
filesApiDocumented: true,
|
|
21195
23270
|
description: "Flagship subscription model. Thinking is on by default and can be disabled. Accepts text, image and video; up to 9 attachments, 10MB per image and 50MB per video."
|
|
21196
23271
|
},
|
|
21197
23272
|
{
|
|
@@ -21214,7 +23289,7 @@ const MINIMAX_CODE_MODELS = [
|
|
|
21214
23289
|
maxAttachments: 4,
|
|
21215
23290
|
maxImageBytes: 10 * 1024 * 1024,
|
|
21216
23291
|
maxVideoBytes: 50 * 1024 * 1024,
|
|
21217
|
-
|
|
23292
|
+
filesApiDocumented: true,
|
|
21218
23293
|
description: "Preview Flash model with a 1M context window. Thinking is forced on with a selectable effort level. Accepts up to 4 attachments."
|
|
21219
23294
|
}
|
|
21220
23295
|
];
|
|
@@ -23399,11 +25474,6 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
23399
25474
|
const contextWindow = contextWindowForModel(options.model, settings.contextWindowOverrides);
|
|
23400
25475
|
const requestOptions = offloadOldestRequestImages$2(normalizeGenerateOptions(options), maxRequestImageBytes());
|
|
23401
25476
|
const images = await resolveRequestImages$2(requestOptions, this.options.attachments, signal);
|
|
23402
|
-
const requestedMax = requestOptions.maxTokens ?? maxOutputTokensFor$1(options.model, contextWindow);
|
|
23403
|
-
const body = assertRequestBodyFits(buildMinimaxRequest({
|
|
23404
|
-
...requestOptions,
|
|
23405
|
-
maxTokens: clampOutputToContext(requestedMax, contextWindow, estimatedInputTokens(requestOptions))
|
|
23406
|
-
}, images, { cacheControl: true }));
|
|
23407
25477
|
let credentials;
|
|
23408
25478
|
let accountId;
|
|
23409
25479
|
try {
|
|
@@ -23414,6 +25484,19 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
23414
25484
|
if (error instanceof MinimaxCodeUnauthorizedError) throw new LlmError(error.message, "INVALID_CREDENTIAL", { cause: error });
|
|
23415
25485
|
throw new LlmError("MiniMax Code(编程订阅) credential could not be prepared: " + (error instanceof Error ? error.message : String(error)), "MISSING_CREDENTIAL", { cause: error });
|
|
23416
25486
|
}
|
|
25487
|
+
let authOwner = computeMinimaxAuthOwner(credentials, accountId);
|
|
25488
|
+
const effectiveMax = clampOutputToContext(requestOptions.maxTokens ?? maxOutputTokensFor$1(options.model, contextWindow), contextWindow, estimatedInputTokens(requestOptions));
|
|
25489
|
+
const buildBodyForOwner = (owner) => {
|
|
25490
|
+
return assertRequestBodyFits(buildMinimaxRequest({
|
|
25491
|
+
...requestOptions,
|
|
25492
|
+
maxTokens: effectiveMax
|
|
25493
|
+
}, images, {
|
|
25494
|
+
cacheControl: true,
|
|
25495
|
+
authOwner: owner,
|
|
25496
|
+
route: PROVIDER_ID$2
|
|
25497
|
+
}));
|
|
25498
|
+
};
|
|
25499
|
+
let body = buildBodyForOwner(authOwner);
|
|
23417
25500
|
let response;
|
|
23418
25501
|
for (let attempt = 0;; attempt += 1) {
|
|
23419
25502
|
try {
|
|
@@ -23433,6 +25516,8 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
23433
25516
|
const refused = credentials.accessToken;
|
|
23434
25517
|
try {
|
|
23435
25518
|
credentials = await this.renewCredential(accountId, fetchFn, signal, refused);
|
|
25519
|
+
authOwner = computeMinimaxAuthOwner(credentials, accountId);
|
|
25520
|
+
body = buildBodyForOwner(authOwner);
|
|
23436
25521
|
} catch (error) {
|
|
23437
25522
|
if (error instanceof MinimaxCodeUnauthorizedError) throw new LlmError(error.message, "INVALID_CREDENTIAL", { cause: error });
|
|
23438
25523
|
throw new LlmError("MiniMax Code(编程订阅) credential could not be renewed after the service refused " + redactToken(refused) + ": " + (error instanceof Error ? error.message : String(error)), "INVALID_CREDENTIAL", { cause: error });
|
|
@@ -23451,7 +25536,12 @@ var MinimaxCodeAdapter = class extends LlmAdapter {
|
|
|
23451
25536
|
if (response.body === null) throw new LlmError("MiniMax Code(编程订阅) returned an empty response body", "PROVIDER_ERROR");
|
|
23452
25537
|
const reader = response.body.getReader();
|
|
23453
25538
|
const decoder = new TextDecoder();
|
|
23454
|
-
const state = createStreamState(
|
|
25539
|
+
const state = createStreamState({
|
|
25540
|
+
model: options.model,
|
|
25541
|
+
authOwner,
|
|
25542
|
+
route: PROVIDER_ID$2,
|
|
25543
|
+
provider: PROVIDER_ID$2
|
|
25544
|
+
});
|
|
23455
25545
|
let buffer = "";
|
|
23456
25546
|
try {
|
|
23457
25547
|
while (true) {
|
|
@@ -27717,6 +29807,7 @@ function createStreamState$2() {
|
|
|
27717
29807
|
inputTokens: 0,
|
|
27718
29808
|
outputTokens: 0,
|
|
27719
29809
|
cacheReadTokens: 0,
|
|
29810
|
+
cacheWriteTokens: 0,
|
|
27720
29811
|
reasoningTokens: 0,
|
|
27721
29812
|
sawUsage: false
|
|
27722
29813
|
};
|
|
@@ -27798,8 +29889,10 @@ function processStreamLine$1(line, state) {
|
|
|
27798
29889
|
const prompt = numberOr$1(usage.prompt_tokens, 0);
|
|
27799
29890
|
const details = isRecord$8(usage.prompt_tokens_details) ? usage.prompt_tokens_details : void 0;
|
|
27800
29891
|
const cached = details ? numberOr$1(details.cached_tokens, 0) : 0;
|
|
27801
|
-
|
|
29892
|
+
const written = details ? numberOr$1(details.cache_write_tokens, 0) : 0;
|
|
29893
|
+
state.inputTokens = Math.max(0, prompt - cached - written);
|
|
27802
29894
|
state.cacheReadTokens = cached;
|
|
29895
|
+
state.cacheWriteTokens = written;
|
|
27803
29896
|
state.outputTokens = numberOr$1(usage.completion_tokens, state.outputTokens);
|
|
27804
29897
|
if (isRecord$8(usage.completion_tokens_details)) state.reasoningTokens = numberOr$1(usage.completion_tokens_details.reasoning_tokens, state.reasoningTokens);
|
|
27805
29898
|
}
|
|
@@ -27900,6 +29993,7 @@ function tokenUsage$1(state) {
|
|
|
27900
29993
|
inputTokens: state.inputTokens,
|
|
27901
29994
|
outputTokens: state.outputTokens,
|
|
27902
29995
|
...state.cacheReadTokens > 0 ? { cacheReadTokens: state.cacheReadTokens } : {},
|
|
29996
|
+
...state.cacheWriteTokens > 0 ? { cacheWriteTokens: state.cacheWriteTokens } : {},
|
|
27903
29997
|
...state.reasoningTokens > 0 ? { reasoningTokens: state.reasoningTokens } : {}
|
|
27904
29998
|
};
|
|
27905
29999
|
}
|
|
@@ -39752,8 +41846,16 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
39752
41846
|
logger: ctx.logger
|
|
39753
41847
|
});
|
|
39754
41848
|
const proxyFetch = proxyManager.createFetch();
|
|
41849
|
+
const providerLimits = readModelRequestLimits();
|
|
41850
|
+
const modelFetch = Object.fromEntries(CONTROLLED_PROVIDERS.map((provider) => [provider, createControlledModelFetch(createDiagnosticFetch(proxyFetch, {
|
|
41851
|
+
provider,
|
|
41852
|
+
onRecord: (record) => ctx.logger.info("[provider-diagnostics] " + JSON.stringify(record))
|
|
41853
|
+
}), {
|
|
41854
|
+
provider,
|
|
41855
|
+
limits: providerLimits
|
|
41856
|
+
})]));
|
|
39755
41857
|
const antigravityAdapter = new AntigravityAdapter(antigravityStore, antigravityModelSettings, antigravityPreferences, {
|
|
39756
|
-
fetchFn:
|
|
41858
|
+
fetchFn: modelFetch.antigravity,
|
|
39757
41859
|
attachments: ctx.attachments
|
|
39758
41860
|
}, antigravityAccountPool);
|
|
39759
41861
|
let antigravityRegistration;
|
|
@@ -39780,7 +41882,7 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
39780
41882
|
conflict: () => commandCodeConflict
|
|
39781
41883
|
}, commandCodeAccountPool);
|
|
39782
41884
|
const commandCodeAdapter = new CommandCodeAdapter(commandCodeStore, commandCodeModelSettings, commandCodePreferences, {
|
|
39783
|
-
fetchFn:
|
|
41885
|
+
fetchFn: modelFetch["command-code"],
|
|
39784
41886
|
attachments: ctx.attachments
|
|
39785
41887
|
}, commandCodeAccountPool);
|
|
39786
41888
|
let commandCodeRegistration;
|
|
@@ -39788,11 +41890,14 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
39788
41890
|
const ollamaStore = new FileCredentialStore$5();
|
|
39789
41891
|
const ollamaModelSettings = new FileModelSettingsStore$5();
|
|
39790
41892
|
const ollamaAccountPool = new OllamaAccountPool({ store: ollamaStore });
|
|
39791
|
-
const ollamaAdapter = new OllamaAdapter(ollamaStore, ollamaModelSettings, {
|
|
41893
|
+
const ollamaAdapter = new OllamaAdapter(ollamaStore, ollamaModelSettings, {
|
|
41894
|
+
fetchFn: modelFetch.ollama,
|
|
41895
|
+
attachments: ctx.attachments
|
|
41896
|
+
}, ollamaAccountPool);
|
|
39792
41897
|
let ollamaRegistration;
|
|
39793
41898
|
let ollamaConflict = null;
|
|
39794
41899
|
const kimiCodeAdapter = new KimiCodeAdapter(kimiCodeStore, kimiCodeModelSettings, kimiCodePreferences, {
|
|
39795
|
-
fetchFn:
|
|
41900
|
+
fetchFn: modelFetch["kimi-code"],
|
|
39796
41901
|
attachments: ctx.attachments,
|
|
39797
41902
|
videos: { readVideo: async (ref) => ({
|
|
39798
41903
|
data: await readVideoBytes(ref),
|
|
@@ -39854,7 +41959,7 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
39854
41959
|
conflict: () => kimiCodeConflict
|
|
39855
41960
|
}, kimiCodeAccountPool);
|
|
39856
41961
|
const minimaxCodeAdapter = new MinimaxCodeAdapter(minimaxCodeStore, {
|
|
39857
|
-
fetchFn:
|
|
41962
|
+
fetchFn: modelFetch["minimax-code"],
|
|
39858
41963
|
attachments: ctx.attachments,
|
|
39859
41964
|
accountPool: minimaxCodeAccountPool
|
|
39860
41965
|
}, minimaxCodeModelSettings, minimaxCodePreferences);
|
|
@@ -39904,7 +42009,7 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
39904
42009
|
checkin: minimaxCodeCheckin
|
|
39905
42010
|
}, minimaxCodeModelSettings, minimaxCodePreferences);
|
|
39906
42011
|
const workBuddyAdapter = new WorkBuddyAdapter(workBuddyStore, workBuddyModelSettings, workBuddyPreferences, {
|
|
39907
|
-
fetchFn:
|
|
42012
|
+
fetchFn: modelFetch.workbuddy,
|
|
39908
42013
|
attachments: ctx.attachments,
|
|
39909
42014
|
accountPool: workBuddyAccountPool
|
|
39910
42015
|
});
|
|
@@ -39946,7 +42051,7 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
39946
42051
|
checkin: workBuddyCheckin
|
|
39947
42052
|
});
|
|
39948
42053
|
const claudeAdapter = new ClaudeAdapter(claudeStore, claudeModelSettings, claudePreferences, {
|
|
39949
|
-
fetchFn:
|
|
42054
|
+
fetchFn: modelFetch.claude,
|
|
39950
42055
|
attachments: ctx.attachments,
|
|
39951
42056
|
accountPool: claudeAccountPool
|
|
39952
42057
|
});
|
|
@@ -39983,7 +42088,7 @@ function apply(ctx, pluginConfig = {}) {
|
|
|
39983
42088
|
const usage = new UsageService(oauth, { fetchFn: proxyFetch });
|
|
39984
42089
|
codexAccountPool.setQuotaBlockedUntil((account, now) => usage.blockedUntilFor(account.credentials, now));
|
|
39985
42090
|
const adapter = new CodexChatGptAdapter(new ResponsesClient(oauth, ctx.attachments, {
|
|
39986
|
-
fetchFn:
|
|
42091
|
+
fetchFn: modelFetch["codex-chatgpt"],
|
|
39987
42092
|
accountPool: codexAccountPool,
|
|
39988
42093
|
localRawImages: { baseUrl: localWebServerBaseUrl(ctx.webServer.host, ctx.webServer.port) },
|
|
39989
42094
|
onGenerationFinished: () => usage.invalidate(),
|