@xiaoso/dsh-tool-plus 0.1.16 → 0.1.17-beta.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/lib/{abort-DZ54CaKU.mjs → abort-BTblp0Rt.mjs} +2 -2
- package/lib/{alibaba-coding-plan-DODpkcqi.mjs → alibaba-coding-plan-t3R4YKCB.mjs} +3 -3
- package/lib/{alibaba-token-plan-C6lpKCda.mjs → alibaba-token-plan-CO4djG1y.mjs} +4 -4
- package/lib/{alibaba-token-plan-CHGyWOfX.mjs → alibaba-token-plan-CUjYJX0-.mjs} +1 -1
- package/lib/{anthropic-Dv_GGX6g.mjs → anthropic-nf7w6so1.mjs} +48 -21
- package/lib/{api-key-validation-MnMf_ihX.mjs → api-key-validation-C_tZ7Y9a.mjs} +2 -2
- package/lib/behavior-BwvTUtag.mjs +111 -0
- package/lib/{browser-render-BZXt548F.mjs → browser-render-BQaEVK_n.mjs} +4 -4
- package/lib/{reference-CtvM4zLU.mjs → bundled-references-CrQ4dQcT.mjs} +57 -4
- package/lib/cascade-BWjRTD1q.mjs +30265 -0
- package/lib/{cline-pass-BT9CBEOj.mjs → cline-pass-Bjm-O8GG.mjs} +2 -2
- package/lib/cloudflare-CKXRJ0l9.mjs +37 -0
- package/lib/{cloudflare-ai-gateway-DReN6ohJ.mjs → cloudflare-ai-gateway-BpnBSjwV.mjs} +8 -4
- package/lib/cloudflare-workers-ai-odj9XswL.mjs +32 -0
- package/lib/collapse-_nkb2dn7.mjs +1945 -0
- package/lib/{coreweave-C82HrLpb.mjs → coreweave-CGDZOD8-.mjs} +1 -1
- package/lib/{coreweave-C68UgRQO.mjs → coreweave-DwffcgW5.mjs} +4 -4
- package/lib/cursor-BgOuV7Qf.mjs +794 -0
- package/lib/{cursor-_gHHq-Dq.mjs → cursor-CF1iHHbv.mjs} +68 -19
- package/lib/{cursor-proto-Cr_WEMuv.mjs → cursor-proto-BuNTjX3L.mjs} +823 -23
- package/lib/{devin-QpfTSl9H.mjs → devin-7OEeEb80.mjs} +112 -31
- package/lib/{devin-proto-DYhp4Ty6.mjs → devin-proto-C-QreeAv.mjs} +71 -5
- package/lib/{dom-DG3sPESc.mjs → dom-BmbZv4v9.mjs} +598 -182
- package/lib/{error-O63pC8zg.mjs → error-8LVo7tug.mjs} +725 -104
- package/lib/factory-droid-ChhmsGWb.mjs +71 -0
- package/lib/{fs-promises-shim-B2YFWkcd.mjs → fs-promises-shim-CANAxFWh.mjs} +10 -1
- package/lib/{github-copilot-BMEcw8N1.mjs → github-copilot-QwevdT30.mjs} +4 -4
- package/lib/{github-copilot-headers-DnjGEbko.mjs → github-copilot-headers-CAUZ0rUD.mjs} +138 -26
- package/lib/{gitlab-duo-DnHqJjFY.mjs → gitlab-duo-DigV7k_o.mjs} +2 -2
- package/lib/{google-antigravity-B1mSckrK.mjs → google-antigravity-D-oe0r8A.mjs} +6 -7
- package/lib/{google-gemini-cli-DJwThCCn.mjs → google-gemini-cli-CQBHgGJT.mjs} +5 -6
- package/lib/{google-oauth-shared-BctLpVUN.mjs → google-oauth-shared-CvgVIUwJ.mjs} +2 -2
- package/lib/google-validation-CdvRj4ec.mjs +126 -0
- package/lib/id-EJwVLDHE.mjs +2548 -0
- package/lib/index.mjs +248 -258
- package/lib/{kilo-BEgEwMJQ.mjs → kilo-5r6OffCa.mjs} +2 -2
- package/lib/{kimi-T9NldEIJ.mjs → kimi-DOOtbXmm.mjs} +9 -10
- package/lib/{launch-BIWNyrBB.mjs → launch-DkAjib7h.mjs} +1 -1
- package/lib/{markit-Dvp2JPdT.mjs → markit-CCcyeAds.mjs} +359 -140
- package/lib/{models-DnXMYGcJ.mjs → models-DCpDT_im.mjs} +199203 -112275
- package/lib/{muse-code-CNvqqAp6.mjs → muse-code-CYf8zj_B.mjs} +5 -5
- package/lib/{openai-codex-D5-CyTxa.mjs → openai-codex-Cj-TvSFh.mjs} +13 -10
- package/lib/{perplexity-A2PEoGaC.mjs → perplexity-Dh_Qgsmx.mjs} +68 -25
- package/lib/{pkce-BYjmugmw.mjs → pkce-DO-jBJLV.mjs} +1 -1
- package/lib/pricing-DusBEhwm.mjs +593 -0
- package/lib/{readability-uGhqb4GC.mjs → readability-XHK0o3bv.mjs} +287 -27
- package/lib/{scrapers-C1ujjV8k.mjs → scrapers-B6PADOVm.mjs} +13 -13
- package/lib/slugify-ynYWBIA5.mjs +11 -0
- package/lib/snowflake-B_jap6lz.mjs +666 -0
- package/lib/snowflake-Cu1ZyOp6.mjs +151 -0
- package/lib/{src-AELUUb5U.mjs → src-BhinCSpi.mjs} +1172 -458
- package/lib/{stream-AVOoe5Hj.mjs → stream-D2ivg3GH.mjs} +3740 -5311
- package/lib/{turndown-D6fof1OX.mjs → turndown-Vya8SavL.mjs} +82 -30
- package/lib/types/tools/hashline/native/index.d.ts +2 -2
- package/lib/types/tools/hashline/native/policy.d.ts +19 -21
- package/lib/types/tools/omp/session/agent-storage.d.ts +3 -3
- package/lib/types/tools/shared/bun-named-shim.d.ts +5 -0
- package/lib/{types-BLrLohwK.mjs → types-tH1v9uRm.mjs} +2 -2
- package/lib/{utils-cZX3f_Ls.mjs → utils-C_Ncya1r.mjs} +1737 -509
- package/lib/utils-D2tK38xq.mjs +34006 -0
- package/lib/{utils-I4I8azHN.mjs → utils-DrDwmNtu.mjs} +8 -3
- package/lib/{xai-oauth-CbeldUSV.mjs → xai-oauth-DLLzX6my.mjs} +4 -4
- package/lib/{xiaomi-Dl3VSs04.mjs → xiaomi-CqK-KivK.mjs} +2 -2
- package/lib/{zai-FyjGSypr.mjs → zai-DoJ2xPIH.mjs} +2 -2
- package/lib/{zip-DRrycScC.mjs → zip-DG1mUV6X.mjs} +1 -1
- package/package.json +8 -8
- package/lib/amazon-bedrock-Dx0rr9dY.mjs +0 -996
- package/lib/anthropic-ClCw0PV_.mjs +0 -3624
- package/lib/anthropic-wire-lbGAFzYb.mjs +0 -927
- package/lib/azure-openai-responses-WE_bgFWp.mjs +0 -285
- package/lib/bundled-references-DY3EJxZV.mjs +0 -56
- package/lib/cloudflare-ai-gateway-CrMa97Ap.mjs +0 -44
- package/lib/collapse-CkMNRNr7.mjs +0 -2304
- package/lib/cursor-C6jaIJDg.mjs +0 -283
- package/lib/cursor-D-XZYDf_.mjs +0 -4544
- package/lib/deterministic-id-CUEIMSqR.mjs +0 -17
- package/lib/devin-ExGIghmx.mjs +0 -69
- package/lib/devin-u0ljw5P6.mjs +0 -628
- package/lib/effort-BdtQMN-_.mjs +0 -23
- package/lib/event-stream-BKEtb45Y.mjs +0 -167
- package/lib/gemini-headers-Co9cVvex.mjs +0 -54
- package/lib/google-auth-qAN-6DMW.mjs +0 -232
- package/lib/google-gemini-cli-B2Aw1S5w.mjs +0 -844
- package/lib/google-lTxuq28n.mjs +0 -38
- package/lib/google-shared-Y01j9xOr.mjs +0 -856
- package/lib/google-validation-BEkNs-Bx.mjs +0 -18
- package/lib/google-vertex-DcM-FRC5.mjs +0 -99
- package/lib/hosts-00rjRzUD.mjs +0 -203
- package/lib/id-AL5obQzR.mjs +0 -71
- package/lib/idle-iterator-D0ZXEcUq.mjs +0 -366
- package/lib/ollama-6R0oXZks.mjs +0 -574
- package/lib/openai-codex-responses-C8bnBWOe.mjs +0 -3232
- package/lib/openai-completions-DNr3mUsn.mjs +0 -1430
- package/lib/openai-configuration-update-xMYUjaK_.mjs +0 -150
- package/lib/openai-reasoning-fallback-Ju-5pSR8.mjs +0 -380
- package/lib/openai-responses-CjpafAEr.mjs +0 -861
- package/lib/protobuf-BMgDYu_U.mjs +0 -717
- package/lib/provider-response-SqKSkkUc.mjs +0 -22
- package/lib/proxy-DxvCQOCA.mjs +0 -230
- package/lib/request-debug-BwPOqN_j.mjs +0 -183
- package/lib/sanitize-text-BCf5KmCf.mjs +0 -33
- package/lib/sse-debug-B-tkLCPW.mjs +0 -11
- package/lib/stream-markup-healing-CN3RsqG9.mjs +0 -267
- package/lib/strict-tool-validation-CBTz1dAA.mjs +0 -105
- package/lib/taxonomy-DSIM99rS.mjs +0 -15392
- package/lib/thinking-ngMwf_1Y.mjs +0 -336
- package/lib/type-guards-CttAANEx.mjs +0 -11
- package/lib/utils-CE3DSebh.mjs +0 -11763
- package/lib/vision-guard-BCpXWUj3.mjs +0 -77
|
@@ -0,0 +1,2548 @@
|
|
|
1
|
+
import { installBunShim as __installBunShim } from "./bun-shim.mjs";
|
|
2
|
+
__installBunShim();
|
|
3
|
+
import { c as compareRevision, h as isRecord, n as hasModelScopedEffortsRule, r as resolveCascade, u as parseRevision } from "./cascade-BWjRTD1q.mjs";
|
|
4
|
+
import "./utils-DrDwmNtu.mjs";
|
|
5
|
+
import { l as classifyModel, p as stripThinkingVariantSuffix } from "./pricing-DusBEhwm.mjs";
|
|
6
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/effort.ts
|
|
7
|
+
/** User-facing thinking levels, ordered least to most intensive. */
|
|
8
|
+
let Effort = /* @__PURE__ */ function(Effort) {
|
|
9
|
+
Effort["Minimal"] = "minimal";
|
|
10
|
+
Effort["Low"] = "low";
|
|
11
|
+
Effort["Medium"] = "medium";
|
|
12
|
+
Effort["High"] = "high";
|
|
13
|
+
Effort["XHigh"] = "xhigh";
|
|
14
|
+
Effort["Max"] = "max";
|
|
15
|
+
return Effort;
|
|
16
|
+
}({});
|
|
17
|
+
const THINKING_EFFORTS = [
|
|
18
|
+
"minimal",
|
|
19
|
+
"low",
|
|
20
|
+
"medium",
|
|
21
|
+
"high",
|
|
22
|
+
"xhigh",
|
|
23
|
+
"max"
|
|
24
|
+
];
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/hosts.ts
|
|
27
|
+
const KNOWN_HOSTS = {
|
|
28
|
+
openai: {
|
|
29
|
+
providers: ["openai"],
|
|
30
|
+
urlMarkers: ["api.openai.com"]
|
|
31
|
+
},
|
|
32
|
+
/** URL-only: a Codex provider rerouted through a proxy does not imply the subscription backend's capabilities. */
|
|
33
|
+
openaiCodex: { urlMarkers: ["chatgpt.com/backend-api", "chat.openai.com/backend-api"] },
|
|
34
|
+
azureOpenAI: {
|
|
35
|
+
providers: ["azure"],
|
|
36
|
+
urlMarkers: [
|
|
37
|
+
".openai.azure.com",
|
|
38
|
+
"azure.com/openai",
|
|
39
|
+
"models.inference.ai.azure.com"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
openrouter: {
|
|
43
|
+
providers: ["openrouter"],
|
|
44
|
+
urlMarkers: ["openrouter.ai"]
|
|
45
|
+
},
|
|
46
|
+
vercelAIGateway: {
|
|
47
|
+
providers: ["vercel-ai-gateway"],
|
|
48
|
+
urlMarkers: ["ai-gateway.vercel.sh"]
|
|
49
|
+
},
|
|
50
|
+
githubCopilot: {
|
|
51
|
+
providers: ["github-copilot"],
|
|
52
|
+
urlMarkers: ["githubcopilot.com", "copilot-api."]
|
|
53
|
+
},
|
|
54
|
+
anthropic: {
|
|
55
|
+
providers: ["anthropic"],
|
|
56
|
+
urlMarkers: ["api.anthropic.com"]
|
|
57
|
+
},
|
|
58
|
+
/** DeepSeek's first-party API only — gates direct-API quirks (max_tokens field, thinking extraBody). */
|
|
59
|
+
deepseekDirect: {
|
|
60
|
+
providers: ["deepseek"],
|
|
61
|
+
urlMarkers: ["api.deepseek.com"]
|
|
62
|
+
},
|
|
63
|
+
/** Any DeepSeek-operated host (first-party API, web-chat fronts). Wider than `deepseekDirect` on purpose. */
|
|
64
|
+
deepseekFamily: {
|
|
65
|
+
providers: ["deepseek"],
|
|
66
|
+
urlMarkers: ["deepseek.com"]
|
|
67
|
+
},
|
|
68
|
+
cerebras: {
|
|
69
|
+
providers: ["cerebras"],
|
|
70
|
+
urlMarkers: ["cerebras.ai"]
|
|
71
|
+
},
|
|
72
|
+
zai: {
|
|
73
|
+
providers: ["zai"],
|
|
74
|
+
urlMarkers: ["api.z.ai"]
|
|
75
|
+
},
|
|
76
|
+
zhipu: {
|
|
77
|
+
providers: ["zhipu-coding-plan"],
|
|
78
|
+
urlMarkers: ["open.bigmodel.cn"]
|
|
79
|
+
},
|
|
80
|
+
kilo: {
|
|
81
|
+
providers: ["kilo"],
|
|
82
|
+
urlMarkers: ["api.kilo.ai"]
|
|
83
|
+
},
|
|
84
|
+
alibabaDashscope: {
|
|
85
|
+
providers: ["alibaba-coding-plan", "alibaba-token-plan"],
|
|
86
|
+
urlMarkers: ["dashscope", "token-plan."]
|
|
87
|
+
},
|
|
88
|
+
umans: {
|
|
89
|
+
providers: ["umans"],
|
|
90
|
+
urlMarkers: ["api.code.umans.ai"]
|
|
91
|
+
},
|
|
92
|
+
xiaomi: {
|
|
93
|
+
providers: ["xiaomi"],
|
|
94
|
+
providerPrefixes: ["xiaomi-token-plan-"],
|
|
95
|
+
urlMarkers: ["xiaomimimo.com"]
|
|
96
|
+
},
|
|
97
|
+
xai: {
|
|
98
|
+
providers: ["xai", "xai-oauth"],
|
|
99
|
+
urlMarkers: ["api.x.ai"]
|
|
100
|
+
},
|
|
101
|
+
mistral: {
|
|
102
|
+
providers: ["mistral"],
|
|
103
|
+
urlMarkers: ["mistral.ai"]
|
|
104
|
+
},
|
|
105
|
+
together: {
|
|
106
|
+
providers: ["together"],
|
|
107
|
+
urlMarkers: ["api.together.xyz"]
|
|
108
|
+
},
|
|
109
|
+
baseten: {
|
|
110
|
+
providers: ["baseten"],
|
|
111
|
+
urlMarkers: ["baseten.co"]
|
|
112
|
+
},
|
|
113
|
+
/** URL-only on purpose: the `fireworks`/`firepass` providers route per-model and not every model is Fireworks-shaped. */
|
|
114
|
+
fireworks: { urlMarkers: ["fireworks.ai"] },
|
|
115
|
+
groq: {
|
|
116
|
+
providers: ["groq"],
|
|
117
|
+
urlMarkers: ["api.groq.com"]
|
|
118
|
+
},
|
|
119
|
+
minimax: {
|
|
120
|
+
providers: [
|
|
121
|
+
"minimax",
|
|
122
|
+
"minimax-code",
|
|
123
|
+
"minimax-code-cn"
|
|
124
|
+
],
|
|
125
|
+
urlMarkers: ["api.minimax.io", "api.minimaxi.com"]
|
|
126
|
+
},
|
|
127
|
+
qwenPortal: {
|
|
128
|
+
providers: ["qwen-portal"],
|
|
129
|
+
urlMarkers: ["portal.qwen.ai"]
|
|
130
|
+
},
|
|
131
|
+
/** NVIDIA NIM (`integrate.api.nvidia.com`). Qwen NIM endpoints take `chat_template_kwargs.enable_thinking`, never top-level `enable_thinking`. */
|
|
132
|
+
nvidia: {
|
|
133
|
+
providers: ["nvidia"],
|
|
134
|
+
urlMarkers: ["integrate.api.nvidia.com"]
|
|
135
|
+
},
|
|
136
|
+
/** Venice AI (`api.venice.ai`). OpenAI-compatible; drives reasoning via top-level `reasoning_effort` (and `venice_parameters.disable_thinking`), and rejects DashScope's top-level `enable_thinking` with a 400 (`additionalProperties: false` request schema). */
|
|
137
|
+
venice: {
|
|
138
|
+
providers: ["venice"],
|
|
139
|
+
urlMarkers: ["api.venice.ai"]
|
|
140
|
+
},
|
|
141
|
+
moonshotNative: {
|
|
142
|
+
providers: ["moonshot", "kimi-code"],
|
|
143
|
+
urlMarkers: ["api.moonshot.ai", "api.kimi.com"]
|
|
144
|
+
},
|
|
145
|
+
/** Google AI Studio's OpenAI-compatible shim (`/v1beta/openai`) — a subset of chat-completions; rejects `store` with a 400. Native Gemini uses `google-generative-ai` api instead. */
|
|
146
|
+
googleAistudio: {
|
|
147
|
+
providers: [],
|
|
148
|
+
urlMarkers: ["generativelanguage.googleapis.com"]
|
|
149
|
+
},
|
|
150
|
+
opencode: {
|
|
151
|
+
providers: ["opencode-go", "opencode-zen"],
|
|
152
|
+
urlMarkers: ["opencode.ai"]
|
|
153
|
+
},
|
|
154
|
+
/** ZenMux's Anthropic-compatible proxy (`zenmux.ai/api/anthropic`) forwards to signature-enforcing Anthropic. */
|
|
155
|
+
zenmux: {
|
|
156
|
+
providers: ["zenmux"],
|
|
157
|
+
urlMarkers: ["zenmux.ai"]
|
|
158
|
+
},
|
|
159
|
+
chutes: { urlMarkers: ["chutes.ai"] }
|
|
160
|
+
};
|
|
161
|
+
const MAX_URL_HOST_MATCHES = 512;
|
|
162
|
+
const urlHostMatches = /* @__PURE__ */ new Map();
|
|
163
|
+
function getUrlHostMatches(baseUrl) {
|
|
164
|
+
let matches = urlHostMatches.get(baseUrl);
|
|
165
|
+
if (matches !== void 0) return matches;
|
|
166
|
+
if (urlHostMatches.size === MAX_URL_HOST_MATCHES) urlHostMatches.clear();
|
|
167
|
+
matches = /* @__PURE__ */ new Map();
|
|
168
|
+
urlHostMatches.set(baseUrl, matches);
|
|
169
|
+
return matches;
|
|
170
|
+
}
|
|
171
|
+
/** URL-only host check (for call sites that have no provider id, e.g. raw env config). */
|
|
172
|
+
function hostMatchesUrl(baseUrl, host) {
|
|
173
|
+
if (!baseUrl) return false;
|
|
174
|
+
const matches = getUrlHostMatches(baseUrl);
|
|
175
|
+
const cached = matches.get(host);
|
|
176
|
+
if (cached !== void 0) return cached;
|
|
177
|
+
const spec = KNOWN_HOSTS[host];
|
|
178
|
+
for (const marker of spec.urlMarkers) if (includesAsciiCaseInsensitive(baseUrl, marker)) {
|
|
179
|
+
matches.set(host, true);
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
matches.set(host, false);
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
/** Provider-or-URL host check — the canonical `provider === id || baseUrl.includes(marker)` idiom. */
|
|
186
|
+
function modelMatchesHost(model, host) {
|
|
187
|
+
const spec = KNOWN_HOSTS[host];
|
|
188
|
+
if (spec.providers) {
|
|
189
|
+
for (const provider of spec.providers) if (model.provider === provider) return true;
|
|
190
|
+
}
|
|
191
|
+
if (spec.providerPrefixes) {
|
|
192
|
+
for (const prefix of spec.providerPrefixes) if (model.provider.startsWith(prefix)) return true;
|
|
193
|
+
}
|
|
194
|
+
return hostMatchesUrl(model.baseUrl, host);
|
|
195
|
+
}
|
|
196
|
+
function includesAsciiCaseInsensitive(value, lowerNeedle) {
|
|
197
|
+
const needleLength = lowerNeedle.length;
|
|
198
|
+
const end = value.length - needleLength;
|
|
199
|
+
for (let start = 0; start <= end; start++) {
|
|
200
|
+
let offset = 0;
|
|
201
|
+
for (; offset < needleLength; offset++) if ((value.charCodeAt(start + offset) | 32) !== lowerNeedle.charCodeAt(offset)) break;
|
|
202
|
+
if (offset === needleLength) return true;
|
|
203
|
+
}
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Hostname for a Vertex AI GenerateContent / rawPredict / OpenAI-compat
|
|
208
|
+
* request for the given location.
|
|
209
|
+
*
|
|
210
|
+
* - `global` → global endpoint (`aiplatform.googleapis.com`)
|
|
211
|
+
* - `eu` / `us` multi-regions → REP endpoints (`aiplatform.{eu|us}.rep.googleapis.com`)
|
|
212
|
+
* - every other location → regional (`{location}-aiplatform.googleapis.com`)
|
|
213
|
+
*
|
|
214
|
+
* Multi-region codes do NOT follow the regional `{location}-aiplatform` pattern;
|
|
215
|
+
* interpolating them that way yields hosts like `eu-aiplatform.googleapis.com`
|
|
216
|
+
* that 404.
|
|
217
|
+
*/
|
|
218
|
+
function resolveVertexEndpointHost(location) {
|
|
219
|
+
if (location === "global") return "aiplatform.googleapis.com";
|
|
220
|
+
if (location === "eu" || location === "us") return `aiplatform.${location}.rep.googleapis.com`;
|
|
221
|
+
return `${location}-aiplatform.googleapis.com`;
|
|
222
|
+
}
|
|
223
|
+
/** Vertex AI Anthropic raw-predict endpoints (`:streamRawPredict` / `:rawPredict`). */
|
|
224
|
+
function isVertexRawPredictUrl(baseUrl) {
|
|
225
|
+
return baseUrl.includes(":streamRawPredict") || baseUrl.includes(":rawPredict");
|
|
226
|
+
}
|
|
227
|
+
const BEDROCK_PUBLIC_HOST = /^(?:(?<runtime>bedrock-runtime(?:-fips)?)\.[a-z0-9-]+\.amazonaws\.com|bedrock-mantle\.(?:[a-z0-9-]+|\{region\})\.api\.aws)$/;
|
|
228
|
+
const BEDROCK_PRIVATELINK_HOST = /^vpce-[a-z0-9-]+\.(?:(?<runtime>bedrock-runtime(?:-fips)?)|bedrock-mantle)\.[a-z0-9-]+\.vpce\.amazonaws\.com$/;
|
|
229
|
+
function hasPathPrefix(pathname, prefix) {
|
|
230
|
+
return pathname === prefix || pathname.startsWith(`${prefix}/`);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Amazon Bedrock API route on a bedrock-runtime or bedrock-mantle endpoint
|
|
234
|
+
* (public, FIPS, or PrivateLink hostname):
|
|
235
|
+
* - `anthropic`: the Anthropic Messages API under `/anthropic`.
|
|
236
|
+
* - `openai`: the OpenAI-compatible APIs under `/openai`, plus Mantle's
|
|
237
|
+
* documented `/v1` base (`https://bedrock-mantle.<region>.api.aws/v1`).
|
|
238
|
+
* - `runtime`: the bedrock-runtime origin itself, where Converse and
|
|
239
|
+
* InvokeModel live under `/model/<id>/…`.
|
|
240
|
+
*
|
|
241
|
+
* Hostnames are parsed strictly so proxies that embed these hosts in a path
|
|
242
|
+
* do not match.
|
|
243
|
+
*/
|
|
244
|
+
function isBedrockRouteUrl(baseUrl, route) {
|
|
245
|
+
if (!baseUrl) return false;
|
|
246
|
+
let url;
|
|
247
|
+
try {
|
|
248
|
+
url = new URL(baseUrl);
|
|
249
|
+
} catch {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
if (url.protocol !== "https:") return false;
|
|
253
|
+
const host = BEDROCK_PUBLIC_HOST.exec(url.hostname) ?? BEDROCK_PRIVATELINK_HOST.exec(url.hostname);
|
|
254
|
+
if (!host) return false;
|
|
255
|
+
const isMantle = host.groups?.runtime === void 0;
|
|
256
|
+
if (route === "runtime") return !isMantle && url.pathname === "/";
|
|
257
|
+
if (route === "anthropic") return hasPathPrefix(url.pathname, "/anthropic");
|
|
258
|
+
return hasPathPrefix(url.pathname, "/openai") || isMantle && hasPathPrefix(url.pathname, "/v1");
|
|
259
|
+
}
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/model-thinking.ts
|
|
262
|
+
/**
|
|
263
|
+
* Returns the supported thinking efforts declared on the model metadata.
|
|
264
|
+
* Empty for non-reasoning models and for reasoning models without a
|
|
265
|
+
* controllable effort surface (`thinking: undefined`).
|
|
266
|
+
*/
|
|
267
|
+
function getSupportedEfforts(model) {
|
|
268
|
+
if (!model.reasoning) return [];
|
|
269
|
+
return model.thinking?.efforts ?? [];
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Neutral view of a model whose thinking ladder is unverified: `reasoning: false`
|
|
273
|
+
* and no ladder. Re-entering policy resolution starts from it (the ladder is
|
|
274
|
+
* derived output, not authored input), and requests without an effort send it so
|
|
275
|
+
* no reasoning or disable fields reach an upstream that may reject them.
|
|
276
|
+
*/
|
|
277
|
+
function withoutUnverifiedThinking(model) {
|
|
278
|
+
if (!model.thinking?.unverified) return model;
|
|
279
|
+
const { thinking: _derived, ...rest } = model;
|
|
280
|
+
return {
|
|
281
|
+
...rest,
|
|
282
|
+
reasoning: false
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function requireSupportedEffort(model, effort) {
|
|
286
|
+
if (!model.reasoning) throw new Error(`Model ${model.provider}/${model.id} does not support thinking`);
|
|
287
|
+
const levels = getSupportedEfforts(model);
|
|
288
|
+
if (!levels.includes(effort)) throw new Error(`Thinking effort ${effort} is not supported by ${model.provider}/${model.id}. Supported efforts: ${levels.join(", ")}`);
|
|
289
|
+
return effort;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Maps a normalized thinking effort to Anthropic adaptive effort values via
|
|
293
|
+
* the model's baked `thinking.effortMap` (identity for unmapped efforts).
|
|
294
|
+
*
|
|
295
|
+
* The Anthropic adaptive wire vocabulary has no `minimal` tier (valid values:
|
|
296
|
+
* `low`, `medium`, `high`, `xhigh`, `max`); a model ladder that exposes
|
|
297
|
+
* `minimal` — custom `anthropic-messages` providers do, unlike the built-in
|
|
298
|
+
* Claude ladders — would otherwise serialize `output_config.effort: "minimal"`
|
|
299
|
+
* and 400 (`level "minimal" not supported`). Clamp it to the lowest real tier,
|
|
300
|
+
* mirroring {@link mapEffortToGoogleThinkingLevel}'s `minimal` handling.
|
|
301
|
+
*/
|
|
302
|
+
function mapEffortToAnthropicAdaptiveEffort(model, effort) {
|
|
303
|
+
const supported = requireSupportedEffort(model, effort);
|
|
304
|
+
const mapped = model.thinking?.effortMap?.[supported] ?? supported;
|
|
305
|
+
if (mapped === "minimal") return "low";
|
|
306
|
+
return mapped;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Resolves the upstream wire model id for a request at the given effort
|
|
310
|
+
* (`undefined` = thinking off). Collapsed effort-tier variants route through
|
|
311
|
+
* `thinking.effortRouting`; everything else falls back to
|
|
312
|
+
* `requestModelId ?? id`.
|
|
313
|
+
*/
|
|
314
|
+
function resolveWireModelId(model, effort) {
|
|
315
|
+
return model.thinking?.effortRouting?.[effort ?? "off"] ?? model.requestModelId ?? model.id;
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/wire/codex.ts
|
|
319
|
+
/**
|
|
320
|
+
* Constants for OpenAI Codex (ChatGPT OAuth) backend
|
|
321
|
+
*/
|
|
322
|
+
const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
323
|
+
const OFFICIAL_CODEX_URL = new URL(CODEX_BASE_URL);
|
|
324
|
+
/** Strict official-Codex endpoint check; exact origin or a path boundary after {@link CODEX_BASE_URL}. */
|
|
325
|
+
function isOfficialCodexApiUrl(baseUrl) {
|
|
326
|
+
if (!baseUrl) return true;
|
|
327
|
+
try {
|
|
328
|
+
const candidate = new URL(baseUrl);
|
|
329
|
+
const candidatePath = candidate.pathname.replace(/\/+$/, "");
|
|
330
|
+
return candidate.origin === OFFICIAL_CODEX_URL.origin && (candidatePath === OFFICIAL_CODEX_URL.pathname || candidatePath.startsWith(`${OFFICIAL_CODEX_URL.pathname}/`));
|
|
331
|
+
} catch {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Pinned OpenAI Codex client version (corresponds to @openai/codex package version).
|
|
337
|
+
* Updated from npm by `bun run check-spoofed-versions --update`.
|
|
338
|
+
*
|
|
339
|
+
* The backend version-gates model availability against this value on both
|
|
340
|
+
* `/models?client_version=` and `/responses`, independently of a model's
|
|
341
|
+
* declared `minimal_client_version`: GPT-6.1 Sol declares 0.153.0 yet is
|
|
342
|
+
* omitted from `/models` at 0.155.1 and listed at 0.159.0. An older pin
|
|
343
|
+
* silently hides newer SKUs from discovery.
|
|
344
|
+
*/
|
|
345
|
+
const CODEX_CLIENT_VERSION = "0.159.0";
|
|
346
|
+
const OPENAI_HEADERS = {
|
|
347
|
+
BETA: "OpenAI-Beta",
|
|
348
|
+
/** Codex feature-negotiation header; values identify opt-in wire protocols. */
|
|
349
|
+
CODEX_BETA_FEATURES: "x-codex-beta-features",
|
|
350
|
+
ACCOUNT_ID: "chatgpt-account-id",
|
|
351
|
+
ORIGINATOR: "originator",
|
|
352
|
+
VERSION: "version",
|
|
353
|
+
SESSION_ID: "session_id",
|
|
354
|
+
CONVERSATION_ID: "conversation_id",
|
|
355
|
+
SCOPED_SESSION_ID: "session-id",
|
|
356
|
+
THREAD_ID: "thread-id",
|
|
357
|
+
INSTALLATION_ID: "x-codex-installation-id",
|
|
358
|
+
WINDOW_ID: "x-codex-window-id",
|
|
359
|
+
TURN_METADATA: "x-codex-turn-metadata",
|
|
360
|
+
PARENT_THREAD_ID: "x-codex-parent-thread-id",
|
|
361
|
+
SUBAGENT: "x-openai-subagent",
|
|
362
|
+
/** Responses Lite transport marker (codex-rs `add_responses_lite_header`); value is always `"true"`. */
|
|
363
|
+
RESPONSES_LITE: "x-openai-internal-codex-responses-lite",
|
|
364
|
+
/** DeviceCheck attestation envelope (codex-rs `X_OAI_ATTESTATION_HEADER`); sent on ChatGPT-OAuth requests. */
|
|
365
|
+
ATTESTATION: "x-oai-attestation",
|
|
366
|
+
/** Client-declared data residency for region-pinned enterprise workspaces. */
|
|
367
|
+
RESIDENCY: "x-openai-internal-codex-residency",
|
|
368
|
+
/**
|
|
369
|
+
* Model routing hint (codex-rs `X_CODEX_ROUTING_HINT_HEADER`): `model=<slug>`
|
|
370
|
+
* or `model=<slug>;tier=<service_tier>`; sent on every ChatGPT-OAuth
|
|
371
|
+
* Responses, compaction, and WebSocket handshake request. Built by
|
|
372
|
+
* {@link codexRoutingHint}.
|
|
373
|
+
*/
|
|
374
|
+
ROUTING_HINT: "x-codex-routing-hint"
|
|
375
|
+
};
|
|
376
|
+
const OPENAI_HEADER_VALUES = {
|
|
377
|
+
BETA_RESPONSES: "responses=experimental",
|
|
378
|
+
BETA_RESPONSES_WEBSOCKETS_V2: "responses_websockets=2026-02-06",
|
|
379
|
+
REMOTE_COMPACTION_V2: "remote_compaction_v2",
|
|
380
|
+
ORIGINATOR_CODEX: "omp"
|
|
381
|
+
};
|
|
382
|
+
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
383
|
+
/**
|
|
384
|
+
* Build the `x-codex-routing-hint` value for a request (codex-rs
|
|
385
|
+
* `build_routing_hint_header`): the requested model slug plus the explicit
|
|
386
|
+
* service tier when one is set. Callers set it only on ChatGPT-OAuth requests
|
|
387
|
+
* to the Codex backend; API-key OpenAI traffic never carries it.
|
|
388
|
+
*/
|
|
389
|
+
function codexRoutingHint(model, serviceTier) {
|
|
390
|
+
return serviceTier ? `model=${model};tier=${serviceTier}` : `model=${model}`;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Extract account ID from a Codex JWT access token.
|
|
394
|
+
* Returns undefined if the token is not a valid Codex JWT.
|
|
395
|
+
*/
|
|
396
|
+
function getCodexAccountId(accessToken) {
|
|
397
|
+
try {
|
|
398
|
+
const parts = accessToken.split(".");
|
|
399
|
+
if (parts.length !== 3) return void 0;
|
|
400
|
+
const decoded = Buffer.from(parts[1] ?? "", "base64").toString("utf-8");
|
|
401
|
+
return JSON.parse(decoded)["https://api.openai.com/auth"]?.chatgpt_account_id ?? void 0;
|
|
402
|
+
} catch {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/** Residency claim value of an account that is not region-pinned. */
|
|
407
|
+
const CODEX_UNCONSTRAINED_RESIDENCY = "no_constraint";
|
|
408
|
+
/**
|
|
409
|
+
* Extract the account's data residency from a Codex JWT access token.
|
|
410
|
+
*
|
|
411
|
+
* Enterprise ChatGPT workspaces can be pinned to a region. Such a workspace
|
|
412
|
+
* rejects a Codex request whose egress does not match it — HTTP 401
|
|
413
|
+
* `Workspace is not authorized in this region.` — unless the client declares
|
|
414
|
+
* the residency itself. The token already carries it, so no configuration is
|
|
415
|
+
* needed: `chatgpt_data_residency` is the authoritative claim, with
|
|
416
|
+
* `chatgpt_compute_residency` as the fallback for tokens that only carry that.
|
|
417
|
+
*
|
|
418
|
+
* Returns undefined for a non-JWT token, or for the (common) accounts that are
|
|
419
|
+
* not region-pinned: their claims omit residency or carry the
|
|
420
|
+
* {@link CODEX_UNCONSTRAINED_RESIDENCY} sentinel. The backend answers 404 to a
|
|
421
|
+
* declared `no_constraint`, and codex-rs never sends it.
|
|
422
|
+
*/
|
|
423
|
+
function getCodexResidency(accessToken) {
|
|
424
|
+
try {
|
|
425
|
+
const parts = accessToken.split(".");
|
|
426
|
+
if (parts.length !== 3) return void 0;
|
|
427
|
+
const decoded = Buffer.from(parts[1] ?? "", "base64").toString("utf-8");
|
|
428
|
+
const auth = JSON.parse(decoded)[JWT_CLAIM_PATH];
|
|
429
|
+
for (const claim of [auth?.chatgpt_data_residency, auth?.chatgpt_compute_residency]) {
|
|
430
|
+
if (typeof claim !== "string") continue;
|
|
431
|
+
const residency = claim.trim();
|
|
432
|
+
if (residency.length === 0) continue;
|
|
433
|
+
return residency.toLowerCase() === CODEX_UNCONSTRAINED_RESIDENCY ? void 0 : residency;
|
|
434
|
+
}
|
|
435
|
+
return;
|
|
436
|
+
} catch {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Adds the token's workspace residency to Codex request headers without
|
|
442
|
+
* replacing a caller-supplied value.
|
|
443
|
+
*/
|
|
444
|
+
function applyCodexResidencyHeader(headers, accessToken) {
|
|
445
|
+
const headerName = OPENAI_HEADERS.RESIDENCY;
|
|
446
|
+
if (headers instanceof Headers) {
|
|
447
|
+
if (headers.has(headerName)) return;
|
|
448
|
+
const residency = getCodexResidency(accessToken);
|
|
449
|
+
if (residency) headers.set(headerName, residency);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
for (const configuredName in headers) if (configuredName.toLowerCase() === headerName) return;
|
|
453
|
+
const residency = getCodexResidency(accessToken);
|
|
454
|
+
if (residency) headers[headerName] = residency;
|
|
455
|
+
}
|
|
456
|
+
//#endregion
|
|
457
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/compat/anthropic.ts
|
|
458
|
+
/**
|
|
459
|
+
* Anthropic endpoint-shape predicates: which URLs are the official first-party
|
|
460
|
+
* API, and which non-official hosts enforce Anthropic's thinking-signature
|
|
461
|
+
* protocol on replay. Compat resolution (`./resolve`) and runtime routing in
|
|
462
|
+
* pi-ai both consume these; URL matching is the one detection surface that
|
|
463
|
+
* stays in code.
|
|
464
|
+
*/
|
|
465
|
+
const OFFICIAL_ANTHROPIC_URL = "https://api.anthropic.com";
|
|
466
|
+
/**
|
|
467
|
+
* Official first-party Anthropic API. A missing baseUrl is official on purpose:
|
|
468
|
+
* request dispatch falls back to `https://api.anthropic.com`. This is the one
|
|
469
|
+
* auth-sensitive host check — OAuth credentials are attached based on it — so
|
|
470
|
+
* it requires the exact origin or a path boundary (`/`) after it; a bare
|
|
471
|
+
* prefix check would accept lookalikes like `https://api.anthropic.com.evil.com`.
|
|
472
|
+
*/
|
|
473
|
+
function isOfficialAnthropicApiUrl(baseUrl) {
|
|
474
|
+
if (!baseUrl) return true;
|
|
475
|
+
const lower = baseUrl.toLowerCase();
|
|
476
|
+
return lower === OFFICIAL_ANTHROPIC_URL || lower.startsWith(`${OFFICIAL_ANTHROPIC_URL}/`);
|
|
477
|
+
}
|
|
478
|
+
const CLOUDFLARE_ANTHROPIC_GATEWAY_URL_MARKER = /gateway\.ai\.cloudflare\.com\/.+\/anthropic(?:\/|$)/i;
|
|
479
|
+
const VERTEX_ANTHROPIC_URL_MARKER = /aiplatform\.googleapis\.com\/.+\/publishers\/anthropic\//i;
|
|
480
|
+
const BEDROCK_ANTHROPIC_URL_MARKER = /(?:^|\/\/|\.)bedrock-runtime\.[a-z0-9-]+\.amazonaws\.com/i;
|
|
481
|
+
const AZURE_ANTHROPIC_URL_MARKER = /(?:^|\/\/|\.)[a-z0-9-]+\.(?:inference|services)\.ai\.azure\.com/i;
|
|
482
|
+
/**
|
|
483
|
+
* Amazon Bedrock's Anthropic Messages API: the `/anthropic` path on the
|
|
484
|
+
* bedrock-runtime or bedrock-mantle endpoint. It rejects the tool `strict`
|
|
485
|
+
* field and enforces Anthropic signatures on replay.
|
|
486
|
+
*/
|
|
487
|
+
function isBedrockAnthropicRoute(baseUrl) {
|
|
488
|
+
return isBedrockRouteUrl(baseUrl, "anthropic");
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Azure AI Inference / Foundry Anthropic route
|
|
492
|
+
* (`<resource>.inference.ai.azure.com`, `<resource>.services.ai.azure.com`).
|
|
493
|
+
* Fronts Claude behind Azure identity and enforces Anthropic signatures on
|
|
494
|
+
* replay; it also rejects the top-level `strict` tool field.
|
|
495
|
+
*/
|
|
496
|
+
function isAzureAnthropicRoute(baseUrl) {
|
|
497
|
+
return baseUrl !== void 0 && AZURE_ANTHROPIC_URL_MARKER.test(baseUrl);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Known non-official URLs that enforce Anthropic thinking signatures on replay
|
|
501
|
+
* (GitHub Copilot, ZenMux, Cloudflare AI Gateway `/anthropic`, Google Vertex
|
|
502
|
+
* `publishers/anthropic`, AWS Bedrock, Azure Foundry).
|
|
503
|
+
*
|
|
504
|
+
* Runtime routing calls this with the effective URL because a model's resolved
|
|
505
|
+
* compat can be stale after Foundry or a provider base-URL override reroutes it.
|
|
506
|
+
*/
|
|
507
|
+
function isAnthropicSigningProxyUrl(baseUrl) {
|
|
508
|
+
return hostMatchesUrl(baseUrl, "githubCopilot") || hostMatchesUrl(baseUrl, "zenmux") || baseUrl !== void 0 && (CLOUDFLARE_ANTHROPIC_GATEWAY_URL_MARKER.test(baseUrl) || VERTEX_ANTHROPIC_URL_MARKER.test(baseUrl) || BEDROCK_ANTHROPIC_URL_MARKER.test(baseUrl)) || isBedrockAnthropicRoute(baseUrl) || isAzureAnthropicRoute(baseUrl);
|
|
509
|
+
}
|
|
510
|
+
//#endregion
|
|
511
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/compat/apply.ts
|
|
512
|
+
/**
|
|
513
|
+
* Assign defined override values onto a freshly-built resolved compat record,
|
|
514
|
+
* in place. Keys the record doesn't declare are ignored (loosely-typed config
|
|
515
|
+
* may carry junk). `buildModel` is the only intended caller — the record being
|
|
516
|
+
* mutated is the single per-model allocation; nothing here runs per request.
|
|
517
|
+
*/
|
|
518
|
+
function applyCompatOverrides(compat, overrides) {
|
|
519
|
+
if (!overrides) return;
|
|
520
|
+
for (const key in overrides) {
|
|
521
|
+
const value = overrides[key];
|
|
522
|
+
if (value !== void 0 && key in compat) compat[key] = value;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
//#endregion
|
|
526
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/types.ts
|
|
527
|
+
/** Catalog kinds used to isolate role-specific runners from session chat models. */
|
|
528
|
+
const MODEL_KINDS = [
|
|
529
|
+
"chat",
|
|
530
|
+
"tiny",
|
|
531
|
+
"image",
|
|
532
|
+
"tts",
|
|
533
|
+
"stt",
|
|
534
|
+
"search",
|
|
535
|
+
"judge",
|
|
536
|
+
"embedding",
|
|
537
|
+
"rerank",
|
|
538
|
+
"video"
|
|
539
|
+
];
|
|
540
|
+
/** Kinds a provider maps to a runner transport through `kind-apis` in its KDL; discovery drops rows of these kinds when the provider declares no API. */
|
|
541
|
+
const KIND_API_KINDS = [
|
|
542
|
+
"image",
|
|
543
|
+
"tts",
|
|
544
|
+
"stt",
|
|
545
|
+
"embedding",
|
|
546
|
+
"rerank",
|
|
547
|
+
"video"
|
|
548
|
+
];
|
|
549
|
+
new Map(Object.entries({
|
|
550
|
+
"web-search": "search",
|
|
551
|
+
typesafe: "judge",
|
|
552
|
+
"openrouter-decisions": "judge",
|
|
553
|
+
"openai-decisions": "judge",
|
|
554
|
+
"workers-ai-systemone": "judge",
|
|
555
|
+
"openai-images": "image",
|
|
556
|
+
"openrouter-images": "image",
|
|
557
|
+
"xai-tts": "tts",
|
|
558
|
+
"openai-speech": "tts",
|
|
559
|
+
"openai-embeddings": "embedding",
|
|
560
|
+
"openrouter-rerank": "rerank",
|
|
561
|
+
"openrouter-video": "video",
|
|
562
|
+
"openai-transcriptions": "stt"
|
|
563
|
+
}));
|
|
564
|
+
//#endregion
|
|
565
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/wire/factory-droid.ts
|
|
566
|
+
/**
|
|
567
|
+
* Factory Droid wire constants shared by catalog discovery, the pi-ai
|
|
568
|
+
* provider, OAuth login and account usage: client identity, upstream/region
|
|
569
|
+
* vocabulary, API hosts and the account-scoped model-cache namespace.
|
|
570
|
+
*/
|
|
571
|
+
/**
|
|
572
|
+
* Client version reported to Factory's API. When bumping it, recapture the
|
|
573
|
+
* native request corpus with `packages/ai/scripts/capture-factory-droid-native.ts`
|
|
574
|
+
* against the matching CLI binary and make `factory-droid-native-parity.test.ts` pass.
|
|
575
|
+
*/
|
|
576
|
+
const FACTORY_DROID_CLIENT_VERSION = "0.237.0";
|
|
577
|
+
/** Wire protocols the Factory proxy multiplexes; `api-routes provider="factory-droid"` picks one per model. */
|
|
578
|
+
const FACTORY_DROID_WIRES = [
|
|
579
|
+
"openai-completions",
|
|
580
|
+
"openai-responses",
|
|
581
|
+
"anthropic-messages",
|
|
582
|
+
"google-generate"
|
|
583
|
+
];
|
|
584
|
+
/**
|
|
585
|
+
* Upstream routers the proxy dispatches to, sent as the `x-api-provider`
|
|
586
|
+
* header. The KDL compiler validates the rotation and region axes against it.
|
|
587
|
+
*/
|
|
588
|
+
const FACTORY_DROID_UPSTREAMS = [
|
|
589
|
+
"fireworks",
|
|
590
|
+
"baseten",
|
|
591
|
+
"mistral",
|
|
592
|
+
"anthropic",
|
|
593
|
+
"azure_anthropic",
|
|
594
|
+
"vertex_anthropic",
|
|
595
|
+
"bedrock_anthropic",
|
|
596
|
+
"openai",
|
|
597
|
+
"azure_openai",
|
|
598
|
+
"bedrock_openai",
|
|
599
|
+
"google",
|
|
600
|
+
"xai",
|
|
601
|
+
"snowflake",
|
|
602
|
+
"databricks"
|
|
603
|
+
];
|
|
604
|
+
/** Inference region for an account: explicit scope wins, else EU residency infers EU, else global. */
|
|
605
|
+
function resolveFactoryDroidInferenceRegion(scope) {
|
|
606
|
+
return scope.inferenceRegion ?? (scope.region === "eu" ? "eu" : "global");
|
|
607
|
+
}
|
|
608
|
+
/** Factory API host per residency region; EU accounts are served from the EU region. */
|
|
609
|
+
function factoryDroidApiBaseUrl(region) {
|
|
610
|
+
return region === "eu" ? "https://api.eu.factory.ai" : "https://api.factory.ai";
|
|
611
|
+
}
|
|
612
|
+
/** Per-wire base URL for an account region; the stream layer appends the path suffix. */
|
|
613
|
+
function factoryDroidWireBaseUrl(wire, region) {
|
|
614
|
+
const host = factoryDroidApiBaseUrl(region);
|
|
615
|
+
switch (wire) {
|
|
616
|
+
case "openai-completions":
|
|
617
|
+
case "openai-responses": return `${host}/api/llm/o/v1`;
|
|
618
|
+
case "anthropic-messages": return `${host}/api/llm/a`;
|
|
619
|
+
case "google-generate": return `${host}/api/llm/g/v1`;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Client identity headers shared by discovery, usage and inference requests.
|
|
624
|
+
* Callers add their own auth, `Accept`, routing and SDK headers.
|
|
625
|
+
*/
|
|
626
|
+
function factoryDroidClientHeaders(orgId) {
|
|
627
|
+
return {
|
|
628
|
+
"X-Client-Version": FACTORY_DROID_CLIENT_VERSION,
|
|
629
|
+
"X-Factory-Client": "cli",
|
|
630
|
+
...orgId ? { "X-Factory-Org-Id": orgId } : {}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
/** Model-cache namespace for a Factory credential and its account scope. */
|
|
634
|
+
function factoryDroidModelCacheProviderId(options) {
|
|
635
|
+
const token = options.apiKey ?? "";
|
|
636
|
+
let credentialScope = `bearer\u0000${token}`;
|
|
637
|
+
const parts = token.split(".");
|
|
638
|
+
if (parts.length === 3 && parts.every(Boolean)) try {
|
|
639
|
+
const claims = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
640
|
+
if (isRecord(claims) && !Array.isArray(claims) && typeof claims.external_org_id === "string" && claims.external_org_id.trim() && typeof claims.sub === "string" && claims.sub.trim()) credentialScope = `account\u0000${claims.external_org_id}\u0000${claims.sub}`;
|
|
641
|
+
} catch {}
|
|
642
|
+
const region = options.region === "eu" ? "eu" : "global";
|
|
643
|
+
const scope = `${credentialScope}\u0000${options.orgId ?? ""}\u0000${region}\u0000${options.inferenceRegion ?? region}`;
|
|
644
|
+
return `factory-droid:models-v4:${Bun.hash(scope).toString(36)}`;
|
|
645
|
+
}
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/compat/axes.ts
|
|
648
|
+
const OAI = ["openai", "openai-responses"];
|
|
649
|
+
const EFFORTS = [
|
|
650
|
+
"minimal",
|
|
651
|
+
"low",
|
|
652
|
+
"medium",
|
|
653
|
+
"high",
|
|
654
|
+
"xhigh",
|
|
655
|
+
"max"
|
|
656
|
+
];
|
|
657
|
+
[...EFFORTS];
|
|
658
|
+
/**
|
|
659
|
+
* How hard agent prompts push subagent delegation for a model lineage
|
|
660
|
+
* (`delegation-bias` axis). `eager` is the unassigned default: the prompt
|
|
661
|
+
* actively pushes fan-out. `restrained` keeps fan-out for genuinely parallel
|
|
662
|
+
* slices but forbids reflexive scouts and single-agent babysitting. `gated`
|
|
663
|
+
* forbids subagents unless the user or a skill asks.
|
|
664
|
+
*/
|
|
665
|
+
const DELEGATION_BIASES = [
|
|
666
|
+
"eager",
|
|
667
|
+
"restrained",
|
|
668
|
+
"gated"
|
|
669
|
+
];
|
|
670
|
+
const THINKING_MODES = [
|
|
671
|
+
"effort",
|
|
672
|
+
"budget",
|
|
673
|
+
"google-level",
|
|
674
|
+
"anthropic-adaptive",
|
|
675
|
+
"anthropic-budget-effort"
|
|
676
|
+
];
|
|
677
|
+
function wire(key, records, shape = "scalar", values) {
|
|
678
|
+
return {
|
|
679
|
+
key,
|
|
680
|
+
set: "wire",
|
|
681
|
+
shape,
|
|
682
|
+
records,
|
|
683
|
+
values
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* KDL directive → axis definition. Keys are the kebab-case directive
|
|
688
|
+
* spellings accepted inside `classes/*.kdl` and `providers/*.kdl` rule
|
|
689
|
+
* blocks.
|
|
690
|
+
*/
|
|
691
|
+
const AXES = {
|
|
692
|
+
"completions-reasoning-mode": wire("completionsReasoningMode", ["request"], "scalar", [
|
|
693
|
+
"none",
|
|
694
|
+
"effort",
|
|
695
|
+
"opt-in",
|
|
696
|
+
"forced-on"
|
|
697
|
+
]),
|
|
698
|
+
"completions-reasoning-history": wire("completionsReasoningHistory", ["request"], "scalar", [
|
|
699
|
+
"omit",
|
|
700
|
+
"preserved",
|
|
701
|
+
"interleaved"
|
|
702
|
+
]),
|
|
703
|
+
"anthropic-thinking": wire("anthropicThinking", ["request"], "scalar", [
|
|
704
|
+
"adaptive",
|
|
705
|
+
"adaptive-summarized",
|
|
706
|
+
"budget-interleaved",
|
|
707
|
+
"budget-effort"
|
|
708
|
+
]),
|
|
709
|
+
"anthropic-tool-streaming-beta": wire("anthropicToolStreamingBeta", ["request"]),
|
|
710
|
+
"openai-platform-header": wire("openaiPlatformHeader", ["request"]),
|
|
711
|
+
"responses-cache-retention": wire("responsesCacheRetention", ["request"]),
|
|
712
|
+
"responses-verbosity": wire("responsesVerbosity", ["request"], "scalar", ["low"]),
|
|
713
|
+
"responses-service-tier": wire("responsesServiceTier", ["request"], "scalar", ["priority"]),
|
|
714
|
+
"responses-parallel-tool-calls": wire("responsesParallelToolCalls", ["request"]),
|
|
715
|
+
"responses-safety-identifier": wire("responsesSafetyIdentifier", ["request"]),
|
|
716
|
+
"responses-tool-choice-auto": wire("responsesToolChoiceAuto", ["request"]),
|
|
717
|
+
"routing-session-lock": wire("routingSessionLock", ["request"]),
|
|
718
|
+
"google-thinking": wire("googleThinking", ["request"], "scalar", ["level", "level-medium"]),
|
|
719
|
+
"allows-synthetic-reasoning-content-for-tool-calls": wire("allowsSyntheticReasoningContentForToolCalls", OAI),
|
|
720
|
+
"always-send-max-tokens": wire("alwaysSendMaxTokens", OAI),
|
|
721
|
+
"cache-control-format": wire("cacheControlFormat", OAI, "scalar", ["anthropic"]),
|
|
722
|
+
"clamp-output-to-model-max": wire("clampOutputToModelMax", OAI),
|
|
723
|
+
"disable-reasoning-on-forced-tool-choice": wire("disableReasoningOnForcedToolChoice", OAI),
|
|
724
|
+
"disable-reasoning-on-tool-choice": wire("disableReasoningOnToolChoice", OAI),
|
|
725
|
+
"disable-reasoning-with-tools": wire("disableReasoningWithTools", ["openai"]),
|
|
726
|
+
"drop-thinking-when-reasoning-effort": wire("dropThinkingWhenReasoningEffort", ["openai"]),
|
|
727
|
+
"empty-length-finish-is-context-error": wire("emptyLengthFinishIsContextError", OAI),
|
|
728
|
+
"extra-body": {
|
|
729
|
+
...wire("extraBody", ["openai"], "object"),
|
|
730
|
+
verbatimKeys: true
|
|
731
|
+
},
|
|
732
|
+
"filter-reasoning-history": wire("filterReasoningHistory", OAI),
|
|
733
|
+
"include-encrypted-reasoning": wire("includeEncryptedReasoning", OAI),
|
|
734
|
+
"kimi-api-format": wire("kimiApiFormat", ["openai"], "scalar", ["openai", "anthropic"]),
|
|
735
|
+
"max-tokens-field": wire("maxTokensField", ["openai"], "scalar", ["max_completion_tokens", "max_tokens"]),
|
|
736
|
+
"mistral-reasoning-content-parts": wire("mistralReasoningContentParts", ["openai"]),
|
|
737
|
+
"native-kimi-k3-reasoning": wire("nativeKimiK3Reasoning", ["openai"]),
|
|
738
|
+
"omit-reasoning-effort": wire("omitReasoningEffort", OAI),
|
|
739
|
+
"prompt-cache-breakpoint-ttl": wire("promptCacheBreakpointTtl", OAI, "scalar", ["30m"]),
|
|
740
|
+
"prompt-cache-session-header": wire("promptCacheSessionHeader", OAI, "scalar", ["x-grok-conv-id"]),
|
|
741
|
+
"qwen-preserve-thinking": wire("qwenPreserveThinking", ["openai"]),
|
|
742
|
+
"reject-root-object-union": wire("rejectRootObjectUnion", OAI),
|
|
743
|
+
"retry-without-strict-on-grammar-error": wire("retryWithoutStrictOnGrammarError", OAI),
|
|
744
|
+
"session-header": wire("sessionHeader", OAI, "scalar", ["x-litellm-session-id"]),
|
|
745
|
+
"reasoning-content-field": wire("reasoningContentField", OAI, "scalar", [
|
|
746
|
+
"reasoning_content",
|
|
747
|
+
"reasoning",
|
|
748
|
+
"reasoning_text"
|
|
749
|
+
]),
|
|
750
|
+
"reasoning-deltas-may-be-cumulative": wire("reasoningDeltasMayBeCumulative", OAI),
|
|
751
|
+
"reasoning-disable-mode": wire("reasoningDisableMode", OAI, "scalar", [
|
|
752
|
+
"omit",
|
|
753
|
+
"lowest-effort",
|
|
754
|
+
"none-effort",
|
|
755
|
+
"openrouter-enabled-false",
|
|
756
|
+
"cline-enabled-false",
|
|
757
|
+
"venice-disable-thinking",
|
|
758
|
+
"zai-thinking-disabled",
|
|
759
|
+
"qwen-enable-thinking-false",
|
|
760
|
+
"qwen-template-false",
|
|
761
|
+
"chat-template-thinking-false"
|
|
762
|
+
]),
|
|
763
|
+
"reasoning-effort-map": wire("reasoningEffortMap", OAI, "object"),
|
|
764
|
+
"replay-reasoning-content": wire("replayReasoningContent", ["openai"]),
|
|
765
|
+
"synthetic-reasoning-content-fallback": wire("syntheticReasoningContentFallback", ["openai"]),
|
|
766
|
+
"requires-assistant-after-tool-result": wire("requiresAssistantAfterToolResult", ["openai"]),
|
|
767
|
+
"requires-assistant-content-for-tool-calls": wire("requiresAssistantContentForToolCalls", OAI),
|
|
768
|
+
"requires-mistral-tool-ids": wire("requiresMistralToolIds", ["openai"]),
|
|
769
|
+
"requires-reasoning-content-for-all-assistant-turns": wire("requiresReasoningContentForAllAssistantTurns", OAI),
|
|
770
|
+
"requires-reasoning-content-for-tool-calls": wire("requiresReasoningContentForToolCalls", OAI),
|
|
771
|
+
"requires-thinking-as-text": wire("requiresThinkingAsText", ["openai"]),
|
|
772
|
+
"requires-tool-result-name": wire("requiresToolResultName", ["openai"]),
|
|
773
|
+
"strict-responses-pairing": wire("strictResponsesPairing", ["openai-responses"]),
|
|
774
|
+
"connection-bound-native-history": wire("connectionBoundNativeHistory", ["openai-responses"]),
|
|
775
|
+
"stateful-responses": wire("statefulResponses", ["openai-responses"]),
|
|
776
|
+
"requires-reasoning-off-juice-instruction": wire("requiresReasoningOffJuiceInstruction", ["openai-responses"]),
|
|
777
|
+
"supports-all-turns-reasoning-context": wire("supportsAllTurnsReasoningContext", ["openai-responses"]),
|
|
778
|
+
"supports-configuration-update": wire("supportsConfigurationUpdate", ["openai-responses"]),
|
|
779
|
+
"supports-steering": wire("supportsSteering", ["openai-responses"]),
|
|
780
|
+
"strip-deepseek-special-tokens": wire("stripDeepseekSpecialTokens", OAI),
|
|
781
|
+
"stream-markup-healing-pattern": wire("streamMarkupHealingPattern", OAI, "scalar", [
|
|
782
|
+
"kimi",
|
|
783
|
+
"dsml",
|
|
784
|
+
"qwen",
|
|
785
|
+
"thinking",
|
|
786
|
+
"harmony"
|
|
787
|
+
]),
|
|
788
|
+
"supports-developer-role": wire("supportsDeveloperRole", OAI),
|
|
789
|
+
"supports-image-detail-original": wire("supportsImageDetailOriginal", ["openai-responses"]),
|
|
790
|
+
"supports-long-prompt-cache-retention": wire("supportsLongPromptCacheRetention", [...OAI, "bedrock"]),
|
|
791
|
+
"supports-multiple-system-messages": wire("supportsMultipleSystemMessages", ["openai"]),
|
|
792
|
+
"supports-named-tool-choice": wire("supportsNamedToolChoice", OAI),
|
|
793
|
+
"supports-obfuscation-opt-out": wire("supportsObfuscationOptOut", ["openai-responses"]),
|
|
794
|
+
"harmony-leak-mitigation": wire("harmonyLeakMitigation", ["openai-responses"]),
|
|
795
|
+
"supports-penalty-and-stop-params": wire("supportsPenaltyAndStopParams", OAI),
|
|
796
|
+
"supports-prompt-cache-breakpoints": wire("supportsPromptCacheBreakpoints", OAI),
|
|
797
|
+
"supports-prompt-cache-key": wire("supportsPromptCacheKey", ["openai"]),
|
|
798
|
+
"supports-reasoning-effort": wire("supportsReasoningEffort", OAI),
|
|
799
|
+
"supports-reasoning-params": wire("supportsReasoningParams", OAI),
|
|
800
|
+
"supports-reasoning-summary": wire("supportsReasoningSummary", ["openai-responses"]),
|
|
801
|
+
"store-responses": wire("storeResponses", ["openai-responses"]),
|
|
802
|
+
"supports-store": wire("supportsStore", ["openai"]),
|
|
803
|
+
"supports-strict-mode": wire("supportsStrictMode", OAI),
|
|
804
|
+
"supports-tool-choice": wire("supportsToolChoice", OAI),
|
|
805
|
+
"supports-usage-in-streaming": wire("supportsUsageInStreaming", ["openai"]),
|
|
806
|
+
"template-reasoning-effort": wire("qwenTemplateReasoningEffort", ["openai"]),
|
|
807
|
+
"thinking-format": wire("thinkingFormat", OAI, "scalar", [
|
|
808
|
+
"openai",
|
|
809
|
+
"openrouter",
|
|
810
|
+
"zai",
|
|
811
|
+
"kimi",
|
|
812
|
+
"qwen",
|
|
813
|
+
"qwen-chat-template",
|
|
814
|
+
"chat-template"
|
|
815
|
+
]),
|
|
816
|
+
"thinking-keep": wire("thinkingKeep", ["openai"]),
|
|
817
|
+
"tool-schema-flavor": wire("toolSchemaFlavor", OAI, "scalar", [
|
|
818
|
+
"moonshot-mfjs",
|
|
819
|
+
"grammar",
|
|
820
|
+
"none"
|
|
821
|
+
]),
|
|
822
|
+
"tool-strict-mode": wire("toolStrictMode", ["openai"], "scalar", [
|
|
823
|
+
"all_strict",
|
|
824
|
+
"none",
|
|
825
|
+
"mixed"
|
|
826
|
+
]),
|
|
827
|
+
"uses-openai-tool-call-id-limit": wire("usesOpenAIToolCallIdLimit", OAI),
|
|
828
|
+
"when-thinking": wire("whenThinking", ["openai"], "object"),
|
|
829
|
+
"wire-model-id-mode": wire("wireModelIdMode", OAI, "scalar", [
|
|
830
|
+
"raw",
|
|
831
|
+
"cline-pass",
|
|
832
|
+
"firepass",
|
|
833
|
+
"fireworks",
|
|
834
|
+
"openrouter"
|
|
835
|
+
]),
|
|
836
|
+
"zai-reasoning-effort-dialect": wire("zaiReasoningEffortDialect", ["openai"]),
|
|
837
|
+
"bedrock-messages-api": wire("bedrockMessagesApi", ["anthropic"]),
|
|
838
|
+
"allow-anthropic-header-overrides": wire("allowAnthropicHeaderOverrides", ["anthropic"]),
|
|
839
|
+
"disable-adaptive-thinking": wire("disableAdaptiveThinking", ["anthropic"]),
|
|
840
|
+
"disable-strict-tools": wire("disableStrictTools", ["anthropic"]),
|
|
841
|
+
"between-tools-effort": wire("betweenToolsEffort", ["anthropic"], "scalar", [
|
|
842
|
+
"low",
|
|
843
|
+
"medium",
|
|
844
|
+
"high"
|
|
845
|
+
]),
|
|
846
|
+
"disabled-thinking": wire("disabledThinking", ["anthropic", "bedrock"], "scalar", [
|
|
847
|
+
"omit",
|
|
848
|
+
"disabled",
|
|
849
|
+
"adaptive",
|
|
850
|
+
"between-tools"
|
|
851
|
+
]),
|
|
852
|
+
"effort-beta": wire("effortBeta", ["anthropic"]),
|
|
853
|
+
"escape-builtin-tool-names": wire("escapeBuiltinToolNames", ["anthropic"]),
|
|
854
|
+
"fast-mode": wire("fastMode", ["anthropic"]),
|
|
855
|
+
"first-party-provider": wire("firstPartyProvider", ["anthropic"]),
|
|
856
|
+
"honors-claude-code-beta-kill-switch": wire("honorsClaudeCodeBetaKillSwitch", ["anthropic"]),
|
|
857
|
+
"inject-claude-code-instruction": wire("injectClaudeCodeInstruction", ["anthropic"]),
|
|
858
|
+
"max-image-dimension": wire("maxImageDimension", ["anthropic", "google"]),
|
|
859
|
+
"max-image-payload-bytes": wire("maxImagePayloadBytes", ["anthropic", "google"]),
|
|
860
|
+
"official-endpoint": wire("officialEndpoint", ["anthropic", "openai-responses"]),
|
|
861
|
+
"replay-unsigned-thinking": wire("replayUnsignedThinking", ["anthropic"]),
|
|
862
|
+
"requires-thinking-enabled": wire("requiresThinkingEnabled", ["anthropic"]),
|
|
863
|
+
"requires-tool-result-id": wire("requiresToolResultId", ["anthropic"]),
|
|
864
|
+
"signing-endpoint": wire("signingEndpoint", ["anthropic"]),
|
|
865
|
+
"strip-thinking-history": wire("stripThinkingHistory", [
|
|
866
|
+
"openai",
|
|
867
|
+
"anthropic",
|
|
868
|
+
"ollama"
|
|
869
|
+
]),
|
|
870
|
+
"supports-context-management": wire("supportsContextManagement", ["anthropic"]),
|
|
871
|
+
"supports-output-effort": wire("supportsOutputEffort", ["anthropic"]),
|
|
872
|
+
"supports-eager-tool-input-streaming": wire("supportsEagerToolInputStreaming", ["anthropic"]),
|
|
873
|
+
"supports-long-cache-retention": wire("supportsLongCacheRetention", ["anthropic"]),
|
|
874
|
+
"supports-mid-conversation-system": wire("supportsMidConversationSystem", ["anthropic"]),
|
|
875
|
+
"supports-mid-conversation-tool-changes": wire("supportsMidConversationToolChanges", ["anthropic"]),
|
|
876
|
+
"supports-per-message-effort": wire("supportsPerMessageEffort", ["anthropic"]),
|
|
877
|
+
"supports-server-compaction": wire("supportsServerCompaction", ["anthropic", "bedrock"]),
|
|
878
|
+
"supports-between-tools-thinking": wire("supportsBetweenToolsThinking", ["anthropic"]),
|
|
879
|
+
"supports-thinking-binding-controls": wire("supportsThinkingBindingControls", ["anthropic", "bedrock"]),
|
|
880
|
+
"supports-turn-scoped-system": wire("supportsTurnScopedSystem", ["anthropic"]),
|
|
881
|
+
"prompt-cache-maximum-checkpoints": wire("promptCacheMaximumCheckpoints", ["bedrock"]),
|
|
882
|
+
"prompt-cache-minimum-tokens": wire("promptCacheMinimumTokens", ["bedrock"]),
|
|
883
|
+
"prompt-cache-mode": wire("promptCacheMode", ["bedrock"], "scalar", [
|
|
884
|
+
"none",
|
|
885
|
+
"automatic",
|
|
886
|
+
"explicit"
|
|
887
|
+
]),
|
|
888
|
+
"model-router": wire("modelRouter", ["devin"]),
|
|
889
|
+
"supports-parallel-tool-calls": wire("supportsParallelToolCalls", ["devin"]),
|
|
890
|
+
"trust-explicit-thinking-only": wire("trustExplicitThinkingOnly", [...OAI, "devin"]),
|
|
891
|
+
"antigravity-claude-tool-mode": wire("antigravityClaudeToolMode", ["google"]),
|
|
892
|
+
"antigravity-usage-label": wire("antigravityUsageLabel", ["google"]),
|
|
893
|
+
"cca-legacy-parameters-schema": wire("ccaLegacyParametersSchema", ["google"]),
|
|
894
|
+
"claude-thinking-beta-header": wire("claudeThinkingBetaHeader", ["google"]),
|
|
895
|
+
"drop-unsigned-thinking": wire("dropUnsignedThinking", ["google"]),
|
|
896
|
+
"flash-stream-leak-workaround": wire("flashStreamLeakWorkaround", ["google"]),
|
|
897
|
+
"multimodal-function-response": wire("multimodalFunctionResponse", ["google"]),
|
|
898
|
+
"requires-skip-thought-signature": wire("requiresSkipThoughtSignature", ["google"]),
|
|
899
|
+
"requires-skip-thought-signature-on-first-function-call": wire("requiresSkipThoughtSignatureOnFirstFunctionCall", ["google"]),
|
|
900
|
+
"supports-function-part-id": wire("supportsFunctionPartId", ["google"]),
|
|
901
|
+
/**
|
|
902
|
+
* Whether this wire may revise text it has already streamed: bytes
|
|
903
|
+
* reclassified out of the visible channel (a leaned-on thinking opener),
|
|
904
|
+
* carved into a tool call, reordered by content-block index, or replaced
|
|
905
|
+
* wholesale by an authoritative final payload. Unassigned means the wire
|
|
906
|
+
* only appends, so the transcript may retire finished lines into native
|
|
907
|
+
* scrollback while the turn is still streaming (see
|
|
908
|
+
* `AssistantMessageComponent`). Declare `possible` only with a citable
|
|
909
|
+
* mechanism: the renderer also verifies published rows every frame and stops
|
|
910
|
+
* retiring the block on the first mismatch, so this axis decides where
|
|
911
|
+
* mid-stream retirement is attempted, not whether it is safe.
|
|
912
|
+
*/
|
|
913
|
+
"stream-revision": wire("streamRevision", [...OAI, "bedrock"], "scalar", ["none", "possible"]),
|
|
914
|
+
"stream-first-event-timeout-ms": wire("streamFirstEventTimeoutMs", [...OAI, "google"]),
|
|
915
|
+
"stream-idle-timeout-ms": wire("streamIdleTimeoutMs", [
|
|
916
|
+
...OAI,
|
|
917
|
+
"anthropic",
|
|
918
|
+
"bedrock",
|
|
919
|
+
"google"
|
|
920
|
+
]),
|
|
921
|
+
"strip-image-input": wire("stripImageInput", [
|
|
922
|
+
...OAI,
|
|
923
|
+
"anthropic",
|
|
924
|
+
"google"
|
|
925
|
+
]),
|
|
926
|
+
"supports-forced-tool-choice": wire("supportsForcedToolChoice", [
|
|
927
|
+
...OAI,
|
|
928
|
+
"anthropic",
|
|
929
|
+
"bedrock"
|
|
930
|
+
]),
|
|
931
|
+
"supports-sampling-params": wire("supportsSamplingParams", [
|
|
932
|
+
...OAI,
|
|
933
|
+
"anthropic",
|
|
934
|
+
"bedrock",
|
|
935
|
+
"devin",
|
|
936
|
+
"google"
|
|
937
|
+
]),
|
|
938
|
+
"thinking-loop-guard": wire("thinkingLoopGuard", [
|
|
939
|
+
...OAI,
|
|
940
|
+
"anthropic",
|
|
941
|
+
"google"
|
|
942
|
+
], "scalar", [
|
|
943
|
+
"gemini",
|
|
944
|
+
"deepseek",
|
|
945
|
+
"xai"
|
|
946
|
+
]),
|
|
947
|
+
"thinking-default-level": {
|
|
948
|
+
key: "defaultLevel",
|
|
949
|
+
set: "thinking",
|
|
950
|
+
shape: "scalar",
|
|
951
|
+
values: EFFORTS
|
|
952
|
+
},
|
|
953
|
+
"thinking-effort-budgets": {
|
|
954
|
+
key: "effortBudgets",
|
|
955
|
+
set: "thinking",
|
|
956
|
+
shape: "object"
|
|
957
|
+
},
|
|
958
|
+
"thinking-effort-map": {
|
|
959
|
+
key: "effortMap",
|
|
960
|
+
set: "thinking",
|
|
961
|
+
shape: "object"
|
|
962
|
+
},
|
|
963
|
+
"thinking-efforts": {
|
|
964
|
+
key: "efforts",
|
|
965
|
+
set: "thinking",
|
|
966
|
+
shape: "array",
|
|
967
|
+
values: EFFORTS,
|
|
968
|
+
emptyArray: true
|
|
969
|
+
},
|
|
970
|
+
"thinking-mode": {
|
|
971
|
+
key: "mode",
|
|
972
|
+
set: "thinking",
|
|
973
|
+
shape: "scalar",
|
|
974
|
+
values: THINKING_MODES
|
|
975
|
+
},
|
|
976
|
+
"thinking-requires-effort": {
|
|
977
|
+
key: "requiresEffort",
|
|
978
|
+
set: "thinking",
|
|
979
|
+
shape: "scalar"
|
|
980
|
+
},
|
|
981
|
+
"thinking-prefix-binding": {
|
|
982
|
+
key: "prefixBinding",
|
|
983
|
+
set: "thinking",
|
|
984
|
+
shape: "scalar"
|
|
985
|
+
},
|
|
986
|
+
"thinking-suppress-when-off": {
|
|
987
|
+
key: "suppressWhenOff",
|
|
988
|
+
set: "thinking",
|
|
989
|
+
shape: "scalar"
|
|
990
|
+
},
|
|
991
|
+
"thinking-supports-display": {
|
|
992
|
+
key: "supportsDisplay",
|
|
993
|
+
set: "thinking",
|
|
994
|
+
shape: "scalar"
|
|
995
|
+
},
|
|
996
|
+
"thinking-upgrade-neutral": {
|
|
997
|
+
key: "upgradeNeutral",
|
|
998
|
+
set: "thinking",
|
|
999
|
+
shape: "scalar"
|
|
1000
|
+
},
|
|
1001
|
+
/** Unclassified neutral ids here are capability-unknown: offer the ladder as opt-in. */
|
|
1002
|
+
"thinking-unverified": {
|
|
1003
|
+
key: "unverified",
|
|
1004
|
+
set: "thinking",
|
|
1005
|
+
shape: "scalar"
|
|
1006
|
+
},
|
|
1007
|
+
"apply-patch-tool-type": {
|
|
1008
|
+
key: "applyPatchToolType",
|
|
1009
|
+
set: "catalog",
|
|
1010
|
+
shape: "scalar",
|
|
1011
|
+
values: ["freeform", "function"]
|
|
1012
|
+
},
|
|
1013
|
+
"requires-native-tools": {
|
|
1014
|
+
key: "requiresNativeTools",
|
|
1015
|
+
set: "catalog",
|
|
1016
|
+
shape: "scalar",
|
|
1017
|
+
values: [true, false]
|
|
1018
|
+
},
|
|
1019
|
+
"tool-schemas-in-context": {
|
|
1020
|
+
key: "toolSchemasInContext",
|
|
1021
|
+
set: "catalog",
|
|
1022
|
+
shape: "scalar",
|
|
1023
|
+
values: [true, false]
|
|
1024
|
+
},
|
|
1025
|
+
"requires-tool-free-history-for-tool-opt-out": {
|
|
1026
|
+
key: "requiresToolFreeHistoryForToolOptOut",
|
|
1027
|
+
set: "catalog",
|
|
1028
|
+
shape: "scalar",
|
|
1029
|
+
values: [true, false]
|
|
1030
|
+
},
|
|
1031
|
+
/** Replayed tool calls must satisfy the target wire's declaration charset and length. */
|
|
1032
|
+
"tool-call-name-charset": {
|
|
1033
|
+
key: "toolCallNameCharset",
|
|
1034
|
+
set: "catalog",
|
|
1035
|
+
shape: "scalar",
|
|
1036
|
+
values: ["ascii", "google"]
|
|
1037
|
+
},
|
|
1038
|
+
"tool-call-name-max-length": {
|
|
1039
|
+
key: "toolCallNameMaxLength",
|
|
1040
|
+
set: "catalog",
|
|
1041
|
+
shape: "scalar"
|
|
1042
|
+
},
|
|
1043
|
+
"preserves-max-output-tokens": {
|
|
1044
|
+
key: "preservesMaxOutputTokens",
|
|
1045
|
+
set: "catalog",
|
|
1046
|
+
shape: "scalar",
|
|
1047
|
+
values: [true, false]
|
|
1048
|
+
},
|
|
1049
|
+
"omit-max-output-tokens": {
|
|
1050
|
+
key: "omitMaxOutputTokens",
|
|
1051
|
+
set: "catalog",
|
|
1052
|
+
shape: "scalar",
|
|
1053
|
+
values: [true, false]
|
|
1054
|
+
},
|
|
1055
|
+
/**
|
|
1056
|
+
* The host accepts a prompt plus `max_tokens` beyond the context window and
|
|
1057
|
+
* ends generation at the window (Anthropic `model_context_window_exceeded`)
|
|
1058
|
+
* instead of rejecting the request, so callers must not lower the output cap
|
|
1059
|
+
* to fit the window.
|
|
1060
|
+
*/
|
|
1061
|
+
"stops-output-at-context-window": {
|
|
1062
|
+
key: "stopsOutputAtContextWindow",
|
|
1063
|
+
set: "catalog",
|
|
1064
|
+
shape: "scalar",
|
|
1065
|
+
values: [true, false]
|
|
1066
|
+
},
|
|
1067
|
+
/**
|
|
1068
|
+
* The model's input classifier refuses requests that reproduce assistant
|
|
1069
|
+
* reasoning as quoted text (`reasoning_extraction`), so reasoning must not
|
|
1070
|
+
* be replayed as text into its input: compaction archives, summarizer
|
|
1071
|
+
* transcripts and demoted interrupted turns leave it out. Declared per class
|
|
1072
|
+
* in `classes/*.kdl`.
|
|
1073
|
+
*/
|
|
1074
|
+
"classifier-cot-extraction": {
|
|
1075
|
+
key: "classifierCotExtraction",
|
|
1076
|
+
set: "catalog",
|
|
1077
|
+
shape: "scalar",
|
|
1078
|
+
values: [true, false]
|
|
1079
|
+
},
|
|
1080
|
+
"clamp-context-override": {
|
|
1081
|
+
key: "clampContextOverride",
|
|
1082
|
+
set: "catalog",
|
|
1083
|
+
shape: "scalar"
|
|
1084
|
+
},
|
|
1085
|
+
"context-promotion-target": {
|
|
1086
|
+
key: "contextPromotionTarget",
|
|
1087
|
+
set: "catalog",
|
|
1088
|
+
shape: "scalar"
|
|
1089
|
+
},
|
|
1090
|
+
"context-window-floor": {
|
|
1091
|
+
key: "contextWindowFloor",
|
|
1092
|
+
set: "catalog",
|
|
1093
|
+
shape: "scalar"
|
|
1094
|
+
},
|
|
1095
|
+
"context-window-authoritative": {
|
|
1096
|
+
key: "contextWindowAuthoritative",
|
|
1097
|
+
set: "catalog",
|
|
1098
|
+
shape: "scalar",
|
|
1099
|
+
values: [true, false]
|
|
1100
|
+
},
|
|
1101
|
+
/**
|
|
1102
|
+
* The model's long-context price tier does not bound its runtime context
|
|
1103
|
+
* window: with extended context off, the window is not capped at the
|
|
1104
|
+
* tier's input threshold (subscription hosts carrying API-equivalent tier
|
|
1105
|
+
* prices, or a model served at its full window). Explicit user context
|
|
1106
|
+
* overrides still apply.
|
|
1107
|
+
*/
|
|
1108
|
+
"long-context-window-uncapped": {
|
|
1109
|
+
key: "longContextWindowUncapped",
|
|
1110
|
+
set: "catalog",
|
|
1111
|
+
shape: "scalar",
|
|
1112
|
+
values: [true, false]
|
|
1113
|
+
},
|
|
1114
|
+
"cost-patch": {
|
|
1115
|
+
key: "costPatch",
|
|
1116
|
+
set: "catalog",
|
|
1117
|
+
shape: "object"
|
|
1118
|
+
},
|
|
1119
|
+
"cost-fallback": {
|
|
1120
|
+
key: "costFallback",
|
|
1121
|
+
set: "catalog",
|
|
1122
|
+
shape: "object"
|
|
1123
|
+
},
|
|
1124
|
+
/**
|
|
1125
|
+
* The host bills cache-hit input tokens at the full input rate (no cache
|
|
1126
|
+
* discount), so the built row's `cacheRead` tracks its live `input` price.
|
|
1127
|
+
*/
|
|
1128
|
+
"cache-read-at-input-rate": {
|
|
1129
|
+
key: "cacheReadAtInputRate",
|
|
1130
|
+
set: "catalog",
|
|
1131
|
+
shape: "scalar",
|
|
1132
|
+
values: [true, false]
|
|
1133
|
+
},
|
|
1134
|
+
/**
|
|
1135
|
+
* The host bills cache writes at this multiple of the row's live `input`
|
|
1136
|
+
* price, for hosts whose discovery reports cache-write tokens but no
|
|
1137
|
+
* cache-write rate. It replaces any `cacheWrite` the row carries, including
|
|
1138
|
+
* a `cost-patch`/`cost-fallback` value and an absolute long-context tier's
|
|
1139
|
+
* `cacheWrite` (rebased on the tier's `input`).
|
|
1140
|
+
*/
|
|
1141
|
+
"cache-write-rate": {
|
|
1142
|
+
key: "cacheWriteRate",
|
|
1143
|
+
set: "catalog",
|
|
1144
|
+
shape: "scalar"
|
|
1145
|
+
},
|
|
1146
|
+
/**
|
|
1147
|
+
* How the model line bills a replayed signed thinking block as input:
|
|
1148
|
+
* `output` bills the reasoning its signature carries, at the size the turn
|
|
1149
|
+
* billed as output, and not the signature's bytes. Unassigned: estimated
|
|
1150
|
+
* from the signature's bytes. Read through `./thinking-replay`.
|
|
1151
|
+
*/
|
|
1152
|
+
"thinking-replay-tokens": {
|
|
1153
|
+
key: "thinkingReplayTokens",
|
|
1154
|
+
set: "catalog",
|
|
1155
|
+
shape: "scalar",
|
|
1156
|
+
values: ["output", "signature"]
|
|
1157
|
+
},
|
|
1158
|
+
"delegation-bias": {
|
|
1159
|
+
key: "delegationBias",
|
|
1160
|
+
set: "catalog",
|
|
1161
|
+
shape: "scalar",
|
|
1162
|
+
values: DELEGATION_BIASES
|
|
1163
|
+
},
|
|
1164
|
+
"discovery-api": {
|
|
1165
|
+
key: "discoveryApi",
|
|
1166
|
+
set: "catalog",
|
|
1167
|
+
shape: "scalar"
|
|
1168
|
+
},
|
|
1169
|
+
"edit-prompt-variant": {
|
|
1170
|
+
key: "editPromptVariant",
|
|
1171
|
+
set: "catalog",
|
|
1172
|
+
shape: "scalar",
|
|
1173
|
+
values: ["full", "compact"]
|
|
1174
|
+
},
|
|
1175
|
+
"edit-revision": {
|
|
1176
|
+
key: "editRevision",
|
|
1177
|
+
set: "catalog",
|
|
1178
|
+
shape: "scalar"
|
|
1179
|
+
},
|
|
1180
|
+
"input-modalities": {
|
|
1181
|
+
key: "inputModalities",
|
|
1182
|
+
set: "catalog",
|
|
1183
|
+
shape: "array",
|
|
1184
|
+
values: ["text", "image"]
|
|
1185
|
+
},
|
|
1186
|
+
kind: {
|
|
1187
|
+
key: "kind",
|
|
1188
|
+
set: "catalog",
|
|
1189
|
+
shape: "scalar",
|
|
1190
|
+
values: MODEL_KINDS
|
|
1191
|
+
},
|
|
1192
|
+
"web-search": {
|
|
1193
|
+
key: "webSearch",
|
|
1194
|
+
set: "catalog",
|
|
1195
|
+
shape: "scalar",
|
|
1196
|
+
values: [
|
|
1197
|
+
"gemini",
|
|
1198
|
+
"anthropic",
|
|
1199
|
+
"codex",
|
|
1200
|
+
"xai",
|
|
1201
|
+
"openrouter",
|
|
1202
|
+
"openai"
|
|
1203
|
+
]
|
|
1204
|
+
},
|
|
1205
|
+
"web-search-model": {
|
|
1206
|
+
key: "webSearchModel",
|
|
1207
|
+
set: "catalog",
|
|
1208
|
+
shape: "scalar"
|
|
1209
|
+
},
|
|
1210
|
+
"hosted-image": {
|
|
1211
|
+
key: "hostedImage",
|
|
1212
|
+
set: "catalog",
|
|
1213
|
+
shape: "scalar",
|
|
1214
|
+
values: [true, false]
|
|
1215
|
+
},
|
|
1216
|
+
/** How the model line bills an input image; shape and formulas in `./image-tokenization`. */
|
|
1217
|
+
"image-tokenization": {
|
|
1218
|
+
key: "imageTokenization",
|
|
1219
|
+
set: "catalog",
|
|
1220
|
+
shape: "object"
|
|
1221
|
+
},
|
|
1222
|
+
/**
|
|
1223
|
+
* Most images one request may carry, documents included where the host
|
|
1224
|
+
* counts them. Unassigned: unknown host, held to a conservative floor.
|
|
1225
|
+
*/
|
|
1226
|
+
"image-count-budget": {
|
|
1227
|
+
key: "imageCountBudget",
|
|
1228
|
+
set: "catalog",
|
|
1229
|
+
shape: "scalar"
|
|
1230
|
+
},
|
|
1231
|
+
/**
|
|
1232
|
+
* Replaces `image-count-budget` while the request reaches the official
|
|
1233
|
+
* endpoint (pi-ai `reachesBudgetedHost`): a limit measured or documented
|
|
1234
|
+
* for the host's own endpoint, which a custom `baseUrl` or an
|
|
1235
|
+
* `ANTHROPIC_BASE_URL`/`FOUNDRY_BASE_URL` reroute (proxy, gateway) need not share.
|
|
1236
|
+
*/
|
|
1237
|
+
"official-image-count-budget": {
|
|
1238
|
+
key: "officialImageCountBudget",
|
|
1239
|
+
set: "catalog",
|
|
1240
|
+
shape: "scalar"
|
|
1241
|
+
},
|
|
1242
|
+
/**
|
|
1243
|
+
* Opt-in: the reader may get a fresh `read` result as snapcompact frames
|
|
1244
|
+
* (`snapcompact.toolResults`), because it edits as exactly from frames as
|
|
1245
|
+
* from text. Unassigned: `read` results stay text, since a reader that
|
|
1246
|
+
* misreads indentation in frames makes inexact edits.
|
|
1247
|
+
*/
|
|
1248
|
+
"snapcompact-image-reads": {
|
|
1249
|
+
key: "snapcompactImageReads",
|
|
1250
|
+
set: "catalog",
|
|
1251
|
+
shape: "scalar",
|
|
1252
|
+
values: [true, false]
|
|
1253
|
+
},
|
|
1254
|
+
"image-model": {
|
|
1255
|
+
key: "imageModel",
|
|
1256
|
+
set: "catalog",
|
|
1257
|
+
shape: "scalar"
|
|
1258
|
+
},
|
|
1259
|
+
/** Longest image edge the host passes to the model: it downscales larger images to fit first. */
|
|
1260
|
+
"image-max-edge": {
|
|
1261
|
+
key: "imageMaxEdge",
|
|
1262
|
+
set: "catalog",
|
|
1263
|
+
shape: "scalar"
|
|
1264
|
+
},
|
|
1265
|
+
"inline-image-byte-budget": {
|
|
1266
|
+
key: "inlineImageByteBudget",
|
|
1267
|
+
set: "catalog",
|
|
1268
|
+
shape: "scalar"
|
|
1269
|
+
},
|
|
1270
|
+
/**
|
|
1271
|
+
* Largest request body the host accepts. Where set, the inline-image byte clamp keeps the newest user message's
|
|
1272
|
+
* and newest tool result's images past `inline-image-byte-budget` while they alone fit this limit.
|
|
1273
|
+
*/
|
|
1274
|
+
"request-body-byte-limit": {
|
|
1275
|
+
key: "requestBodyByteLimit",
|
|
1276
|
+
set: "catalog",
|
|
1277
|
+
shape: "scalar"
|
|
1278
|
+
},
|
|
1279
|
+
/**
|
|
1280
|
+
* Opt-in: the host budgets above apply whenever the request reaches the provider's own catalog endpoint
|
|
1281
|
+
* (`endpoint-hosts`, or an unset `baseUrl`), for a hosted gateway whose compat names no official endpoint.
|
|
1282
|
+
* Unassigned: they apply only on the official endpoint (pi-ai `reachesOfficialEndpoint`).
|
|
1283
|
+
*/
|
|
1284
|
+
"catalog-endpoint-budgets": {
|
|
1285
|
+
key: "catalogEndpointBudgets",
|
|
1286
|
+
set: "catalog",
|
|
1287
|
+
shape: "scalar",
|
|
1288
|
+
values: [true, false]
|
|
1289
|
+
},
|
|
1290
|
+
/**
|
|
1291
|
+
* Supported formats and size bounds for judgment-request images; `mime-types`
|
|
1292
|
+
* is comma-separated because object payloads have no array shape.
|
|
1293
|
+
*/
|
|
1294
|
+
"judgment-image-input": {
|
|
1295
|
+
key: "judgmentImageInput",
|
|
1296
|
+
set: "catalog",
|
|
1297
|
+
shape: "object"
|
|
1298
|
+
},
|
|
1299
|
+
/**
|
|
1300
|
+
* `Content-Encoding`s the host decodes on request bodies, most preferred
|
|
1301
|
+
* first. Applied only while the request reaches the provider's own endpoint
|
|
1302
|
+
* (see `endpoint-hosts`), so a proxy or rerouted endpoint never receives
|
|
1303
|
+
* bytes it cannot read. Unassigned: bodies are sent uncompressed.
|
|
1304
|
+
*/
|
|
1305
|
+
"request-encodings": {
|
|
1306
|
+
key: "requestEncodings",
|
|
1307
|
+
set: "catalog",
|
|
1308
|
+
shape: "array",
|
|
1309
|
+
values: ["zstd", "gzip"]
|
|
1310
|
+
},
|
|
1311
|
+
/**
|
|
1312
|
+
* HTTPS hostnames the provider itself serves besides its bundled base URL's
|
|
1313
|
+
* host — per-account endpoints such as Copilot's plan hosts. Together with
|
|
1314
|
+
* that host they define the provider's own endpoint, which tells first-party
|
|
1315
|
+
* traffic from a custom gateway that reuses the provider id.
|
|
1316
|
+
*/
|
|
1317
|
+
"endpoint-hosts": {
|
|
1318
|
+
key: "endpointHosts",
|
|
1319
|
+
set: "catalog",
|
|
1320
|
+
shape: "array"
|
|
1321
|
+
},
|
|
1322
|
+
/**
|
|
1323
|
+
* Pattern of the JSON 400/415 error body the host returns when it cannot
|
|
1324
|
+
* decode a compressed request body. Unassigned: every JSON 400/415 is a
|
|
1325
|
+
* possible rejection and is re-checked with an uncompressed resend. Either
|
|
1326
|
+
* way, a 400/403/415 whose body is not JSON (presumed a proxy's answer, not
|
|
1327
|
+
* the host's) is re-checked too.
|
|
1328
|
+
*/
|
|
1329
|
+
"request-encoding-rejection": {
|
|
1330
|
+
key: "requestEncodingRejection",
|
|
1331
|
+
set: "catalog",
|
|
1332
|
+
shape: "scalar"
|
|
1333
|
+
},
|
|
1334
|
+
/**
|
|
1335
|
+
* Most questions one System One judgment request may carry; the judge
|
|
1336
|
+
* splits larger requests. Unassigned: the endpoint takes any batch size.
|
|
1337
|
+
*/
|
|
1338
|
+
"judgment-questions-max": {
|
|
1339
|
+
key: "judgmentQuestionsMax",
|
|
1340
|
+
set: "catalog",
|
|
1341
|
+
shape: "scalar"
|
|
1342
|
+
},
|
|
1343
|
+
"limits-patch": {
|
|
1344
|
+
key: "limitsPatch",
|
|
1345
|
+
set: "catalog",
|
|
1346
|
+
shape: "object"
|
|
1347
|
+
},
|
|
1348
|
+
/** Seeds `maxTokens` only when the row reports none; applied after `limits-patch`. */
|
|
1349
|
+
"max-tokens-fallback": {
|
|
1350
|
+
key: "maxTokensFallback",
|
|
1351
|
+
set: "catalog",
|
|
1352
|
+
shape: "scalar"
|
|
1353
|
+
},
|
|
1354
|
+
/** Most output tokens the host serves: a larger reported `maxTokens` is lowered to it; an unknown one stays unknown. */
|
|
1355
|
+
"max-tokens-ceiling": {
|
|
1356
|
+
key: "maxTokensCeiling",
|
|
1357
|
+
set: "catalog",
|
|
1358
|
+
shape: "scalar"
|
|
1359
|
+
},
|
|
1360
|
+
"long-context-cost": {
|
|
1361
|
+
key: "longContext",
|
|
1362
|
+
set: "catalog",
|
|
1363
|
+
shape: "object"
|
|
1364
|
+
},
|
|
1365
|
+
"prompt-cache": {
|
|
1366
|
+
key: "promptCache",
|
|
1367
|
+
set: "catalog",
|
|
1368
|
+
shape: "object"
|
|
1369
|
+
},
|
|
1370
|
+
"task-sibling-prompt-cache": {
|
|
1371
|
+
key: "taskSiblingPromptCache",
|
|
1372
|
+
set: "catalog",
|
|
1373
|
+
shape: "scalar",
|
|
1374
|
+
values: [true, false]
|
|
1375
|
+
},
|
|
1376
|
+
/**
|
|
1377
|
+
* Prompt-cache lookback in block positions: how far back from a cache
|
|
1378
|
+
* breakpoint the provider looks for an earlier request's cache entry.
|
|
1379
|
+
* Unassigned: no known lookback bound.
|
|
1380
|
+
*/
|
|
1381
|
+
"prompt-cache-lookback": {
|
|
1382
|
+
key: "promptCacheLookback",
|
|
1383
|
+
set: "catalog",
|
|
1384
|
+
shape: "scalar"
|
|
1385
|
+
},
|
|
1386
|
+
/**
|
|
1387
|
+
* The provider's own endpoint keeps prompt-cache entries indefinitely
|
|
1388
|
+
* (logged-in Codex subscriptions): an idle gap never cools the prefix, so
|
|
1389
|
+
* rewriting it while idle only discards a warm cache. Read through
|
|
1390
|
+
* `compat/prompt-cache-persistent.ts`. Unassigned: entries expire.
|
|
1391
|
+
*/
|
|
1392
|
+
"prompt-cache-persistent": {
|
|
1393
|
+
key: "promptCachePersistent",
|
|
1394
|
+
set: "catalog",
|
|
1395
|
+
shape: "scalar",
|
|
1396
|
+
values: [true, false]
|
|
1397
|
+
},
|
|
1398
|
+
"long-usage-limit-fallback": {
|
|
1399
|
+
key: "longUsageLimitFallback",
|
|
1400
|
+
set: "catalog",
|
|
1401
|
+
shape: "scalar"
|
|
1402
|
+
},
|
|
1403
|
+
"max-context-window": {
|
|
1404
|
+
key: "maxContextWindow",
|
|
1405
|
+
set: "catalog",
|
|
1406
|
+
shape: "scalar"
|
|
1407
|
+
},
|
|
1408
|
+
/**
|
|
1409
|
+
* Every host of the model returns its full plaintext trace wherever the
|
|
1410
|
+
* wire does not mark reasoning as a summary (`ThinkingContent.summary ===
|
|
1411
|
+
* false` vouches for nothing more), and its identity (class + family +
|
|
1412
|
+
* revision) pins one set of weights, so a turn's reasoning may replay
|
|
1413
|
+
* natively on another host serving the same model. Marked summaries never
|
|
1414
|
+
* carry; do not declare it where a host serves summaries unmarked.
|
|
1415
|
+
* Declared per family in `classes/*.kdl`; a host whose reasoning must not be
|
|
1416
|
+
* carried in or out sets `#false` in its `providers/*.kdl`. Read through
|
|
1417
|
+
* `compat/reasoning-carry.ts`.
|
|
1418
|
+
*/
|
|
1419
|
+
"portable-reasoning": {
|
|
1420
|
+
key: "portableReasoning",
|
|
1421
|
+
set: "catalog",
|
|
1422
|
+
shape: "scalar",
|
|
1423
|
+
values: [true, false]
|
|
1424
|
+
},
|
|
1425
|
+
"pricing-status": {
|
|
1426
|
+
key: "pricingStatus",
|
|
1427
|
+
set: "catalog",
|
|
1428
|
+
shape: "scalar",
|
|
1429
|
+
values: [
|
|
1430
|
+
"free",
|
|
1431
|
+
"included",
|
|
1432
|
+
"variable",
|
|
1433
|
+
"unknown"
|
|
1434
|
+
]
|
|
1435
|
+
},
|
|
1436
|
+
"requires-cursor-tool-schema-projection": {
|
|
1437
|
+
key: "requiresCursorToolSchemaProjection",
|
|
1438
|
+
set: "catalog",
|
|
1439
|
+
shape: "scalar"
|
|
1440
|
+
},
|
|
1441
|
+
"requires-tool-result-image-hoisting": {
|
|
1442
|
+
key: "requiresToolResultImageHoisting",
|
|
1443
|
+
set: "catalog",
|
|
1444
|
+
shape: "scalar"
|
|
1445
|
+
},
|
|
1446
|
+
"supports-assistant-prefill": {
|
|
1447
|
+
key: "supportsAssistantPrefill",
|
|
1448
|
+
set: "catalog",
|
|
1449
|
+
shape: "scalar"
|
|
1450
|
+
},
|
|
1451
|
+
/**
|
|
1452
|
+
* Ordered Anthropic model ids forwarded as the server-side `fallbacks`
|
|
1453
|
+
* chain when the user opts in. Each must appear in the requested model's
|
|
1454
|
+
* `allowed_fallback_models` (GET /v1/models/{id}); anything else is a 400.
|
|
1455
|
+
*/
|
|
1456
|
+
"server-side-fallback-models": {
|
|
1457
|
+
key: "serverSideFallbackModels",
|
|
1458
|
+
set: "catalog",
|
|
1459
|
+
shape: "array"
|
|
1460
|
+
},
|
|
1461
|
+
/**
|
|
1462
|
+
* Anthropic model ids a refusal's `fallback_credit_token` may be redeemed
|
|
1463
|
+
* on (the refused model's permitted fallback targets). Unordered; a retry
|
|
1464
|
+
* on any other model cannot redeem the credit.
|
|
1465
|
+
*/
|
|
1466
|
+
"fallback-credit-targets": {
|
|
1467
|
+
key: "fallbackCreditTargets",
|
|
1468
|
+
set: "catalog",
|
|
1469
|
+
shape: "array"
|
|
1470
|
+
},
|
|
1471
|
+
priority: {
|
|
1472
|
+
key: "priority",
|
|
1473
|
+
set: "catalog",
|
|
1474
|
+
shape: "scalar"
|
|
1475
|
+
},
|
|
1476
|
+
"service-tier-cost": {
|
|
1477
|
+
key: "serviceTierCost",
|
|
1478
|
+
set: "catalog",
|
|
1479
|
+
shape: "object"
|
|
1480
|
+
},
|
|
1481
|
+
"time-based-cost": {
|
|
1482
|
+
key: "timeBased",
|
|
1483
|
+
set: "catalog",
|
|
1484
|
+
shape: "object"
|
|
1485
|
+
},
|
|
1486
|
+
/** Ordered upstream rotation; the first entry is the default `x-api-provider`. */
|
|
1487
|
+
"upstream-rotation": {
|
|
1488
|
+
key: "upstreamRotation",
|
|
1489
|
+
set: "catalog",
|
|
1490
|
+
shape: "array",
|
|
1491
|
+
values: FACTORY_DROID_UPSTREAMS
|
|
1492
|
+
},
|
|
1493
|
+
/**
|
|
1494
|
+
* Upstreams eligible to serve one inference region. The provider-wide rule
|
|
1495
|
+
* is the upstream serving table; a model rule replaces it (the native
|
|
1496
|
+
* region override), and a bare directive means the region never serves it.
|
|
1497
|
+
*/
|
|
1498
|
+
"region-upstreams-global": {
|
|
1499
|
+
key: "regionUpstreamsGlobal",
|
|
1500
|
+
set: "catalog",
|
|
1501
|
+
shape: "array",
|
|
1502
|
+
values: FACTORY_DROID_UPSTREAMS,
|
|
1503
|
+
emptyArray: true
|
|
1504
|
+
},
|
|
1505
|
+
"region-upstreams-us": {
|
|
1506
|
+
key: "regionUpstreamsUs",
|
|
1507
|
+
set: "catalog",
|
|
1508
|
+
shape: "array",
|
|
1509
|
+
values: FACTORY_DROID_UPSTREAMS,
|
|
1510
|
+
emptyArray: true
|
|
1511
|
+
},
|
|
1512
|
+
"region-upstreams-eu": {
|
|
1513
|
+
key: "regionUpstreamsEu",
|
|
1514
|
+
set: "catalog",
|
|
1515
|
+
shape: "array",
|
|
1516
|
+
values: FACTORY_DROID_UPSTREAMS,
|
|
1517
|
+
emptyArray: true
|
|
1518
|
+
},
|
|
1519
|
+
/** EU inference limits (`context-window`, `max-tokens`) where the region narrows the default. */
|
|
1520
|
+
"region-limits-eu": {
|
|
1521
|
+
key: "regionLimitsEu",
|
|
1522
|
+
set: "catalog",
|
|
1523
|
+
shape: "object"
|
|
1524
|
+
},
|
|
1525
|
+
/**
|
|
1526
|
+
* Subscription credit rates: `input` is the per-token credit weight shown
|
|
1527
|
+
* as the model's multiplier; `output` and `cache-read` multiply it.
|
|
1528
|
+
*/
|
|
1529
|
+
"credit-rates": {
|
|
1530
|
+
key: "creditRates",
|
|
1531
|
+
set: "catalog",
|
|
1532
|
+
shape: "object"
|
|
1533
|
+
},
|
|
1534
|
+
/**
|
|
1535
|
+
* List price borrowed from a bundled catalog row: provider id, then the
|
|
1536
|
+
* row id when it differs from the model's own. Absent means no list price.
|
|
1537
|
+
*/
|
|
1538
|
+
"list-price-from": {
|
|
1539
|
+
key: "listPriceFrom",
|
|
1540
|
+
set: "catalog",
|
|
1541
|
+
shape: "array"
|
|
1542
|
+
},
|
|
1543
|
+
/** Provider family whose live routing defaults apply to the model. */
|
|
1544
|
+
"routing-family": {
|
|
1545
|
+
key: "routingFamily",
|
|
1546
|
+
set: "catalog",
|
|
1547
|
+
shape: "scalar",
|
|
1548
|
+
values: [
|
|
1549
|
+
"anthropic",
|
|
1550
|
+
"openai",
|
|
1551
|
+
"google",
|
|
1552
|
+
"factory",
|
|
1553
|
+
"xai"
|
|
1554
|
+
]
|
|
1555
|
+
},
|
|
1556
|
+
/** Other ids organization policy may use for the model. */
|
|
1557
|
+
"policy-aliases": {
|
|
1558
|
+
key: "policyAliases",
|
|
1559
|
+
set: "catalog",
|
|
1560
|
+
shape: "array"
|
|
1561
|
+
},
|
|
1562
|
+
/**
|
|
1563
|
+
* Account gates: `feature-flag` must be on to list the model,
|
|
1564
|
+
* `deprecation-flag` hides it once on, `requires-explicit-opt-in` hides it
|
|
1565
|
+
* without an org policy, and `base-variant` marks a fast tier of that model.
|
|
1566
|
+
*/
|
|
1567
|
+
entitlement: {
|
|
1568
|
+
key: "entitlement",
|
|
1569
|
+
set: "catalog",
|
|
1570
|
+
shape: "object"
|
|
1571
|
+
},
|
|
1572
|
+
/** The native default when the caller picks no effort is thinking off. */
|
|
1573
|
+
"default-reasoning-off": {
|
|
1574
|
+
key: "defaultReasoningOff",
|
|
1575
|
+
set: "catalog",
|
|
1576
|
+
shape: "scalar",
|
|
1577
|
+
values: [true, false]
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
/** Records applicable to each API family; used by `resolve.ts` when applying wire axes. */
|
|
1581
|
+
const API_COMPAT_RECORDS = {
|
|
1582
|
+
"ollama-chat": ["ollama"],
|
|
1583
|
+
"openai-completions": ["openai"],
|
|
1584
|
+
openrouter: ["openai", "openai-responses"],
|
|
1585
|
+
"openai-responses": ["openai-responses"],
|
|
1586
|
+
"azure-openai-responses": ["openai-responses"],
|
|
1587
|
+
"openai-codex-responses": ["openai-responses"],
|
|
1588
|
+
"anthropic-messages": ["anthropic"],
|
|
1589
|
+
"bedrock-converse-stream": ["bedrock"],
|
|
1590
|
+
"devin-agent": ["devin"],
|
|
1591
|
+
"google-generative-ai": ["google"],
|
|
1592
|
+
"google-vertex": ["google"],
|
|
1593
|
+
"google-gemini-cli": ["google"]
|
|
1594
|
+
};
|
|
1595
|
+
//#endregion
|
|
1596
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/compat/resolve.ts
|
|
1597
|
+
/**
|
|
1598
|
+
* `resolveModelPolicy` — the single model-policy resolver. Layering, later
|
|
1599
|
+
* layers win:
|
|
1600
|
+
*
|
|
1601
|
+
* 1. unconditional per-API compat defaults;
|
|
1602
|
+
* 2. host-derived flags: URL/provider detection via `hosts.ts`, including
|
|
1603
|
+
* compound host×identity branches (keyed on {@link ModelIdentity} fields,
|
|
1604
|
+
* never on model-name matching);
|
|
1605
|
+
* 3. compat-cascade axes compiled from `rules/` (pure identity- and
|
|
1606
|
+
* provider-keyed policy lives there, not here);
|
|
1607
|
+
* 4. spec-authored sparse overrides (`applyCompatOverrides`), then the
|
|
1608
|
+
* spec-undefined fixups the legacy builders applied.
|
|
1609
|
+
*
|
|
1610
|
+
* Thinking metadata resolves after compat: explicit spec thinking wins,
|
|
1611
|
+
* cascade thinking axes drive the ladder/mode/wire facts, and identity-generic
|
|
1612
|
+
* fallbacks cover unmatched targets.
|
|
1613
|
+
*/
|
|
1614
|
+
const REQUEST_AXIS_KEYS = Object.values(AXES).filter((axis) => axis.records?.includes("request")).map((axis) => axis.key);
|
|
1615
|
+
function resolveRequestPolicy(axes) {
|
|
1616
|
+
const policy = {};
|
|
1617
|
+
for (const key of REQUEST_AXIS_KEYS) if (key in axes.wire) policy[key] = axes.wire[key];
|
|
1618
|
+
return policy;
|
|
1619
|
+
}
|
|
1620
|
+
/** Structured identity plus revision comparison helpers. */
|
|
1621
|
+
var IdentityFacts = class {
|
|
1622
|
+
identity;
|
|
1623
|
+
revision;
|
|
1624
|
+
constructor(identity) {
|
|
1625
|
+
this.identity = identity;
|
|
1626
|
+
this.revision = identity.revision === void 0 ? void 0 : parseRevision(identity.revision);
|
|
1627
|
+
}
|
|
1628
|
+
is(cls) {
|
|
1629
|
+
return this.identity.class === cls;
|
|
1630
|
+
}
|
|
1631
|
+
family(...families) {
|
|
1632
|
+
return this.identity.family !== void 0 && families.includes(this.identity.family);
|
|
1633
|
+
}
|
|
1634
|
+
revGte(min) {
|
|
1635
|
+
if (!this.revision) return false;
|
|
1636
|
+
const bound = parseRevision(min);
|
|
1637
|
+
return bound !== void 0 && compareRevision(this.revision, bound) >= 0;
|
|
1638
|
+
}
|
|
1639
|
+
revMajor() {
|
|
1640
|
+
return this.revision?.[0];
|
|
1641
|
+
}
|
|
1642
|
+
/** Kimi K2.7-Code / K3 lines whose native hosts mandate enabled thinking. */
|
|
1643
|
+
get kimiMandatoryThinking() {
|
|
1644
|
+
return this.is("kimi") && this.family("k2.7-code", "k3");
|
|
1645
|
+
}
|
|
1646
|
+
/** Adaptive-thinking Claude generation floor (Opus ≥ min; Sonnet/Fable/Mythos ≥ 5; Haiku ≥ 5.5). */
|
|
1647
|
+
anthropicAdaptiveGenAtLeast(opusMin) {
|
|
1648
|
+
if (!this.is("anthropic")) return false;
|
|
1649
|
+
if (this.family("opus")) return this.revGte(opusMin);
|
|
1650
|
+
if (this.family("sonnet", "fable", "mythos")) return this.revGte("5");
|
|
1651
|
+
if (this.family("haiku")) return this.revGte("5.5");
|
|
1652
|
+
return false;
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
/**
|
|
1656
|
+
* The id a spec resolves identity and rules under: its own, unless that names
|
|
1657
|
+
* no model and the row sends a model that does on the wire. A Devin Fusion
|
|
1658
|
+
* pairing runs only its lead, so the lead's reviewed rules (cache-write rate,
|
|
1659
|
+
* long-context tier) apply to it at build time.
|
|
1660
|
+
*/
|
|
1661
|
+
function policyModelId(spec) {
|
|
1662
|
+
const wireId = spec.requestModelId;
|
|
1663
|
+
if (wireId === void 0 || wireId === spec.id) return spec.id;
|
|
1664
|
+
if (classifyModel(spec.provider, spec.id).class !== "unknown") return spec.id;
|
|
1665
|
+
return classifyModel(spec.provider, wireId).class === "unknown" ? spec.id : wireId;
|
|
1666
|
+
}
|
|
1667
|
+
function resolveIdentity(spec) {
|
|
1668
|
+
return classifyModel(spec.provider, policyModelId(spec));
|
|
1669
|
+
}
|
|
1670
|
+
let keyRecords;
|
|
1671
|
+
function getKeyRecords() {
|
|
1672
|
+
if (!keyRecords) {
|
|
1673
|
+
keyRecords = {};
|
|
1674
|
+
for (const directive in AXES) {
|
|
1675
|
+
const axis = AXES[directive];
|
|
1676
|
+
if (axis.set === "wire" && axis.records) keyRecords[axis.key] = { records: axis.records };
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
return keyRecords;
|
|
1680
|
+
}
|
|
1681
|
+
/**
|
|
1682
|
+
* Assigns resolved wire axes onto a freshly-detected compat record. An axis
|
|
1683
|
+
* lands only when the model's API maps to one of the axis's declared records
|
|
1684
|
+
* — the axes table is the single applicability authority, so optional keys
|
|
1685
|
+
* absent from the detected baseline still land.
|
|
1686
|
+
*/
|
|
1687
|
+
function applyWireAxes(compat, wire, api) {
|
|
1688
|
+
const records = API_COMPAT_RECORDS[api];
|
|
1689
|
+
if (!records) return;
|
|
1690
|
+
const info = getKeyRecords();
|
|
1691
|
+
for (const key in wire) {
|
|
1692
|
+
if (!info[key]?.records.some((record) => records.includes(record))) continue;
|
|
1693
|
+
Reflect.set(compat, key, wire[key]);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Preserves legacy per-key merge semantics for map-valued effort remaps: a
|
|
1698
|
+
* cascade-provided `reasoningEffortMap` is the detected base and sparse spec
|
|
1699
|
+
* overrides overlay it key by key (spec keys win, unseen keys survive).
|
|
1700
|
+
*/
|
|
1701
|
+
function overlayEffortMapAxis(compat, axes, specCompat) {
|
|
1702
|
+
const axisMap = effortRecord(axes.wire.reasoningEffortMap);
|
|
1703
|
+
if (axisMap && specCompat?.reasoningEffortMap) compat.reasoningEffortMap = {
|
|
1704
|
+
...axisMap,
|
|
1705
|
+
...specCompat.reasoningEffortMap
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
function effortList(value) {
|
|
1709
|
+
if (!Array.isArray(value)) return void 0;
|
|
1710
|
+
const out = [];
|
|
1711
|
+
for (const entry of value) {
|
|
1712
|
+
const effort = THINKING_EFFORTS.find((candidate) => candidate === entry);
|
|
1713
|
+
if (effort === void 0) return void 0;
|
|
1714
|
+
out.push(effort);
|
|
1715
|
+
}
|
|
1716
|
+
return out;
|
|
1717
|
+
}
|
|
1718
|
+
function effortValue(value) {
|
|
1719
|
+
return THINKING_EFFORTS.find((candidate) => candidate === value);
|
|
1720
|
+
}
|
|
1721
|
+
function objectPayload(value) {
|
|
1722
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1723
|
+
}
|
|
1724
|
+
function effortRecord(value) {
|
|
1725
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
1726
|
+
const out = {};
|
|
1727
|
+
let any = false;
|
|
1728
|
+
for (const key in value) {
|
|
1729
|
+
const effort = effortValue(key);
|
|
1730
|
+
const mapped = Reflect.get(value, key);
|
|
1731
|
+
if (effort === void 0 || typeof mapped !== "string") continue;
|
|
1732
|
+
out[effort] = mapped;
|
|
1733
|
+
any = true;
|
|
1734
|
+
}
|
|
1735
|
+
return any ? out : void 0;
|
|
1736
|
+
}
|
|
1737
|
+
function effortNumberRecord(value) {
|
|
1738
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
1739
|
+
const out = {};
|
|
1740
|
+
let any = false;
|
|
1741
|
+
for (const key in value) {
|
|
1742
|
+
const effort = effortValue(key);
|
|
1743
|
+
const mapped = Reflect.get(value, key);
|
|
1744
|
+
if (effort === void 0 || typeof mapped !== "number") continue;
|
|
1745
|
+
out[effort] = mapped;
|
|
1746
|
+
any = true;
|
|
1747
|
+
}
|
|
1748
|
+
return any ? out : void 0;
|
|
1749
|
+
}
|
|
1750
|
+
function thinkingMode(value) {
|
|
1751
|
+
switch (value) {
|
|
1752
|
+
case "effort":
|
|
1753
|
+
case "budget":
|
|
1754
|
+
case "google-level":
|
|
1755
|
+
case "anthropic-adaptive":
|
|
1756
|
+
case "anthropic-budget-effort": return value;
|
|
1757
|
+
default: return;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
const GLM_CODING_PLAN_STREAM_IDLE_TIMEOUT_MS = 6e5;
|
|
1761
|
+
const LOCAL_OPENAI_COMPAT_STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
1762
|
+
const LOCAL_OPENAI_COMPAT_PROVIDERS = {
|
|
1763
|
+
"llama.cpp": true,
|
|
1764
|
+
"lm-studio": true,
|
|
1765
|
+
vllm: true,
|
|
1766
|
+
ollama: true
|
|
1767
|
+
};
|
|
1768
|
+
const PROXY_OPENAI_COMPAT_PROVIDERS = { litellm: true };
|
|
1769
|
+
function hasLocalLoopbackBaseUrl(baseUrl) {
|
|
1770
|
+
if (!baseUrl) return false;
|
|
1771
|
+
let hostname;
|
|
1772
|
+
try {
|
|
1773
|
+
hostname = new URL(baseUrl).hostname.toLowerCase();
|
|
1774
|
+
if (hostname.endsWith(".")) hostname = hostname.slice(0, -1);
|
|
1775
|
+
} catch {
|
|
1776
|
+
return false;
|
|
1777
|
+
}
|
|
1778
|
+
if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "0.0.0.0" || hostname === "::1" || hostname === "[::1]") return true;
|
|
1779
|
+
if (hostname.startsWith("10.")) return true;
|
|
1780
|
+
if (hostname.startsWith("192.168.")) return true;
|
|
1781
|
+
if (/^172\.(1[6-9]|2[0-9]|3[01])\./.test(hostname)) return true;
|
|
1782
|
+
if (hostname.endsWith(".local")) return true;
|
|
1783
|
+
return false;
|
|
1784
|
+
}
|
|
1785
|
+
function resolveReasoningDisableMode(thinkingFormat) {
|
|
1786
|
+
switch (thinkingFormat) {
|
|
1787
|
+
case "openrouter": return "openrouter-enabled-false";
|
|
1788
|
+
case "zai":
|
|
1789
|
+
case "kimi": return "zai-thinking-disabled";
|
|
1790
|
+
case "qwen": return "qwen-enable-thinking-false";
|
|
1791
|
+
case "qwen-chat-template": return "qwen-template-false";
|
|
1792
|
+
case "chat-template": return "chat-template-thinking-false";
|
|
1793
|
+
default: return "lowest-effort";
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
/** Strict official-OpenAI check: provider id `openai` and an `api.openai.com` host. */
|
|
1797
|
+
function isOfficialOpenAIEndpoint(provider, baseUrl) {
|
|
1798
|
+
if (provider !== "openai") return false;
|
|
1799
|
+
if (!baseUrl) return true;
|
|
1800
|
+
try {
|
|
1801
|
+
return new URL(baseUrl).hostname === "api.openai.com";
|
|
1802
|
+
} catch {
|
|
1803
|
+
return false;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
function detectOpenAI(spec, facts, reasoningCapable) {
|
|
1807
|
+
const provider = spec.provider;
|
|
1808
|
+
const backendProvider = spec.providerType ?? provider;
|
|
1809
|
+
const baseUrl = spec.baseUrl;
|
|
1810
|
+
const hostModel = {
|
|
1811
|
+
provider,
|
|
1812
|
+
baseUrl
|
|
1813
|
+
};
|
|
1814
|
+
const isZai = modelMatchesHost(hostModel, "zai");
|
|
1815
|
+
const isZhipu = modelMatchesHost(hostModel, "zhipu");
|
|
1816
|
+
const isMoonshotNative = modelMatchesHost(hostModel, "moonshotNative");
|
|
1817
|
+
const isXiaomiHost = modelMatchesHost(hostModel, "xiaomi");
|
|
1818
|
+
const isDirectDeepseekApi = modelMatchesHost(hostModel, "deepseekDirect");
|
|
1819
|
+
const isDeepseekReasoning = (modelMatchesHost(hostModel, "deepseekFamily") || facts.is("deepseek")) && reasoningCapable;
|
|
1820
|
+
const isLocalOpenAICompatBackend = PROXY_OPENAI_COMPAT_PROVIDERS[backendProvider] !== true && (LOCAL_OPENAI_COMPAT_PROVIDERS[backendProvider] === true || hasLocalLoopbackBaseUrl(baseUrl));
|
|
1821
|
+
return {
|
|
1822
|
+
facts,
|
|
1823
|
+
isZai,
|
|
1824
|
+
isZhipu,
|
|
1825
|
+
isMoonshotNative,
|
|
1826
|
+
isOpenCodeHost: modelMatchesHost(hostModel, "opencode"),
|
|
1827
|
+
isOpenCodeProvider: provider === "opencode-go" || provider === "opencode-zen",
|
|
1828
|
+
isDirectDeepseekApi,
|
|
1829
|
+
isDeepseekReasoning,
|
|
1830
|
+
isDirectDeepseekReasoning: isDirectDeepseekApi && isDeepseekReasoning,
|
|
1831
|
+
isVenice: modelMatchesHost(hostModel, "venice"),
|
|
1832
|
+
requiresEnabledThinking: isMoonshotNative && facts.is("kimi") && facts.family("k2.7-code"),
|
|
1833
|
+
isXiaomiMimo: isXiaomiHost && facts.is("mimo"),
|
|
1834
|
+
isLocalOpenAICompatBackend,
|
|
1835
|
+
isLocalServingBackend: isLocalOpenAICompatBackend || hasLocalLoopbackBaseUrl(baseUrl),
|
|
1836
|
+
isOpenRouter: modelMatchesHost(hostModel, "openrouter")
|
|
1837
|
+
};
|
|
1838
|
+
}
|
|
1839
|
+
/**
|
|
1840
|
+
* Detected chat-completions baseline. Pure identity policy (Kimi max-token
|
|
1841
|
+
* behavior, DeepSeek reasoning-content contracts, Qwen dialects, sampling
|
|
1842
|
+
* restrictions, effort maps, markup healers, …) is rule-owned and absent
|
|
1843
|
+
* here; compound host×identity branches remain, keyed on identity fields.
|
|
1844
|
+
*/
|
|
1845
|
+
function detectOpenAICompat(spec, d, reasoningCapable) {
|
|
1846
|
+
const provider = spec.provider;
|
|
1847
|
+
const baseUrl = spec.baseUrl;
|
|
1848
|
+
const hostModel = {
|
|
1849
|
+
provider,
|
|
1850
|
+
baseUrl
|
|
1851
|
+
};
|
|
1852
|
+
const facts = d.facts;
|
|
1853
|
+
const isCerebras = modelMatchesHost(hostModel, "cerebras");
|
|
1854
|
+
const isCerebrasHost = hostMatchesUrl(baseUrl, "cerebras");
|
|
1855
|
+
const isKilo = modelMatchesHost(hostModel, "kilo");
|
|
1856
|
+
const isAlibaba = modelMatchesHost(hostModel, "alibabaDashscope");
|
|
1857
|
+
const isXiaomiHost = modelMatchesHost(hostModel, "xiaomi");
|
|
1858
|
+
const isGrok = modelMatchesHost(hostModel, "xai");
|
|
1859
|
+
const isMistral = modelMatchesHost(hostModel, "mistral");
|
|
1860
|
+
const isGoogleAistudioOpenAI = hostMatchesUrl(baseUrl, "googleAistudio");
|
|
1861
|
+
const isOpenAIHost = modelMatchesHost(hostModel, "openai");
|
|
1862
|
+
const isAzureHost = modelMatchesHost(hostModel, "azureOpenAI");
|
|
1863
|
+
const isVercelGateway = modelMatchesHost(hostModel, "vercelAIGateway");
|
|
1864
|
+
const isTogether = modelMatchesHost(hostModel, "together");
|
|
1865
|
+
const isFireworks = hostMatchesUrl(baseUrl, "fireworks");
|
|
1866
|
+
const isGroqHost = modelMatchesHost(hostModel, "groq");
|
|
1867
|
+
const isMiniMaxHost = modelMatchesHost(hostModel, "minimax");
|
|
1868
|
+
const isQwenPortal = modelMatchesHost(hostModel, "qwenPortal");
|
|
1869
|
+
const isMoonshotKimi = facts.is("kimi") && d.isMoonshotNative;
|
|
1870
|
+
const isMoonshotKimiK3 = isMoonshotKimi && facts.family("k3");
|
|
1871
|
+
const usesMoonshotKimiPreservedThinking = isMoonshotKimi && facts.family("k2.6");
|
|
1872
|
+
const isAnthropicModel = modelMatchesHost(hostModel, "anthropic") || facts.is("anthropic");
|
|
1873
|
+
const isQwen = facts.is("qwen");
|
|
1874
|
+
const isDeepseekFamily = modelMatchesHost(hostModel, "deepseekFamily") || facts.is("deepseek");
|
|
1875
|
+
const supportsZaiReasoningEffort = (d.isZai || d.isZhipu) && facts.is("glm") && facts.revGte("5.2");
|
|
1876
|
+
const isNonStandard = isCerebras || isGrok || isMistral || isGoogleAistudioOpenAI || hostMatchesUrl(baseUrl, "chutes") || hostMatchesUrl(baseUrl, "deepseekFamily") || isFireworks || isAlibaba || d.isZai || d.isZhipu || isKilo || isQwen || isXiaomiHost || d.isMoonshotNative || d.isOpenCodeHost;
|
|
1877
|
+
const useMaxTokens = isMistral || d.isMoonshotNative || d.isZai || d.isZhipu || hostMatchesUrl(baseUrl, "chutes") || isFireworks || d.isDirectDeepseekApi;
|
|
1878
|
+
const supportsPromptCacheBreakpoints = isOfficialOpenAIEndpoint(provider, baseUrl) && facts.is("openai") && facts.revGte("5.6");
|
|
1879
|
+
const supportsMultipleSystemMessagesDefault = !isMiniMaxHost && !isAlibaba && !isQwenPortal && !isQwen && (isOpenAIHost || isAzureHost || d.isOpenRouter || isCerebras || isTogether || isFireworks || isGroqHost || isDeepseekFamily || isMistral || isGrok || d.isZai || d.isZhipu || provider === "github-copilot" || provider === "zenmux");
|
|
1880
|
+
const streamIdleTimeoutMs = facts.is("glm") && facts.revGte("5") && !facts.family("vision") && (hostMatchesUrl(baseUrl, "zai") || hostMatchesUrl(baseUrl, "zhipu") || hostMatchesUrl(baseUrl, "opencode")) ? GLM_CODING_PLAN_STREAM_IDLE_TIMEOUT_MS : facts.is("mimo") && hostMatchesUrl(baseUrl, "xiaomi") ? 3e5 : reasoningCapable && facts.is("kimi") && (facts.family("k3") || facts.family("k2.7-code")) && hostMatchesUrl(baseUrl, "moonshotNative") ? 3e5 : reasoningCapable && facts.is("deepseek") && hostMatchesUrl(baseUrl, "deepseekDirect") ? 3e5 : d.isLocalServingBackend ? LOCAL_OPENAI_COMPAT_STREAM_IDLE_TIMEOUT_MS : void 0;
|
|
1881
|
+
const wireModelIdMode = hostMatchesUrl(baseUrl, "openrouter") ? "openrouter" : "raw";
|
|
1882
|
+
const thinkingFormat = facts.is("kimi") && !facts.family("k3") && hostMatchesUrl(baseUrl, "moonshotNative") || hostMatchesUrl(baseUrl, "zai") || hostMatchesUrl(baseUrl, "zhipu") || facts.is("mimo") && hostMatchesUrl(baseUrl, "xiaomi") ? "zai" : hostMatchesUrl(baseUrl, "openrouter") ? "openrouter" : isQwen && hostMatchesUrl(baseUrl, "nvidia") ? "qwen-chat-template" : isQwen && (isFireworks || hostMatchesUrl(baseUrl, "venice")) ? "openai" : hostMatchesUrl(baseUrl, "alibabaDashscope") || isQwen ? "qwen" : "openai";
|
|
1883
|
+
return {
|
|
1884
|
+
supportsStore: !isNonStandard,
|
|
1885
|
+
supportsDeveloperRole: isOpenAIHost || isAzureHost,
|
|
1886
|
+
supportsMultipleSystemMessages: supportsMultipleSystemMessagesDefault,
|
|
1887
|
+
supportsReasoningEffort: !isGrok && !d.isXiaomiMimo && (!(d.isZai || d.isZhipu) || supportsZaiReasoningEffort),
|
|
1888
|
+
trustExplicitThinkingOnly: void 0,
|
|
1889
|
+
supportsReasoningParams: true,
|
|
1890
|
+
supportsSamplingParams: true,
|
|
1891
|
+
supportsPenaltyAndStopParams: !(isGrok && reasoningCapable),
|
|
1892
|
+
reasoningEffortMap: {},
|
|
1893
|
+
supportsUsageInStreaming: !isCerebrasHost,
|
|
1894
|
+
alwaysSendMaxTokens: facts.is("kimi"),
|
|
1895
|
+
disableReasoningOnForcedToolChoice: facts.is("kimi") && !isMoonshotKimiK3 || isAnthropicModel,
|
|
1896
|
+
disableReasoningOnToolChoice: isDeepseekFamily && reasoningCapable && !d.isOpenRouter,
|
|
1897
|
+
disableReasoningWithTools: false,
|
|
1898
|
+
supportsToolChoice: !d.isDirectDeepseekReasoning,
|
|
1899
|
+
supportsForcedToolChoice: !d.requiresEnabledThinking && !(d.isOpenCodeHost && d.isDeepseekReasoning),
|
|
1900
|
+
supportsNamedToolChoice: true,
|
|
1901
|
+
maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
|
|
1902
|
+
requiresToolResultName: isMistral,
|
|
1903
|
+
requiresAssistantAfterToolResult: isMistral,
|
|
1904
|
+
requiresThinkingAsText: isMistral,
|
|
1905
|
+
requiresMistralToolIds: isMistral,
|
|
1906
|
+
thinkingFormat,
|
|
1907
|
+
kimiApiFormat: void 0,
|
|
1908
|
+
reasoningDisableMode: d.isVenice ? "venice-disable-thinking" : resolveReasoningDisableMode(thinkingFormat),
|
|
1909
|
+
omitReasoningEffort: false,
|
|
1910
|
+
includeEncryptedReasoning: true,
|
|
1911
|
+
filterReasoningHistory: d.isOpenRouter && isAnthropicModel,
|
|
1912
|
+
thinkingKeep: usesMoonshotKimiPreservedThinking ? "all" : void 0,
|
|
1913
|
+
reasoningContentField: "reasoning_content",
|
|
1914
|
+
mistralReasoningContentParts: void 0,
|
|
1915
|
+
requiresReasoningContentForToolCalls: facts.is("kimi") && !d.isOpenCodeProvider || isDeepseekFamily && reasoningCapable || d.isXiaomiMimo || d.isOpenRouter && reasoningCapable,
|
|
1916
|
+
requiresReasoningContentForAllAssistantTurns: (isDeepseekFamily && reasoningCapable || d.isXiaomiMimo) && !d.isOpenRouter,
|
|
1917
|
+
allowsSyntheticReasoningContentForToolCalls: (!isDeepseekFamily || !reasoningCapable) && !d.isXiaomiMimo,
|
|
1918
|
+
syntheticReasoningContentFallback: void 0,
|
|
1919
|
+
replayReasoningContent: d.isLocalOpenAICompatBackend,
|
|
1920
|
+
qwenPreserveThinking: (thinkingFormat === "qwen" || thinkingFormat === "qwen-chat-template") && d.isLocalOpenAICompatBackend,
|
|
1921
|
+
qwenTemplateReasoningEffort: false,
|
|
1922
|
+
requiresAssistantContentForToolCalls: facts.is("kimi") || d.isDirectDeepseekReasoning,
|
|
1923
|
+
cacheControlFormat: d.isOpenRouter && isAnthropicModel ? "anthropic" : void 0,
|
|
1924
|
+
supportsPromptCacheBreakpoints,
|
|
1925
|
+
promptCacheBreakpointTtl: supportsPromptCacheBreakpoints ? "30m" : void 0,
|
|
1926
|
+
openRouterRouting: void 0,
|
|
1927
|
+
vercelGatewayRouting: void 0,
|
|
1928
|
+
isOpenRouterHost: d.isOpenRouter,
|
|
1929
|
+
wireModelIdMode,
|
|
1930
|
+
isVercelGatewayHost: isVercelGateway,
|
|
1931
|
+
supportsStrictMode: hostMatchesUrl(baseUrl, "openai") || hostMatchesUrl(baseUrl, "azureOpenAI") || hostMatchesUrl(baseUrl, "cerebras") || hostMatchesUrl(baseUrl, "together") || hostMatchesUrl(baseUrl, "openrouter") || hostMatchesUrl(baseUrl, "deepseekFamily"),
|
|
1932
|
+
extraBody: void 0,
|
|
1933
|
+
toolStrictMode: isCerebrasHost ? "all_strict" : "mixed",
|
|
1934
|
+
toolSchemaFlavor: d.isMoonshotNative || facts.is("kimi") ? "moonshot-mfjs" : d.isLocalOpenAICompatBackend ? "grammar" : void 0,
|
|
1935
|
+
streamFirstEventTimeoutMs: d.isLocalServingBackend ? 0 : void 0,
|
|
1936
|
+
streamIdleTimeoutMs,
|
|
1937
|
+
stripDeepseekSpecialTokens: false,
|
|
1938
|
+
stripThinkingHistory: void 0,
|
|
1939
|
+
officialEndpoint: isOfficialOpenAIEndpoint(spec.provider, baseUrl) || void 0,
|
|
1940
|
+
streamMarkupHealingPattern: detectStreamMarkupHealing(spec.provider, facts, baseUrl),
|
|
1941
|
+
reasoningDeltasMayBeCumulative: false,
|
|
1942
|
+
emptyLengthFinishIsContextError: false,
|
|
1943
|
+
usesOpenAIToolCallIdLimit: false,
|
|
1944
|
+
promptCacheSessionHeader: hostMatchesUrl(baseUrl, "xai") ? "x-grok-conv-id" : void 0,
|
|
1945
|
+
dropThinkingWhenReasoningEffort: false,
|
|
1946
|
+
nativeKimiK3Reasoning: false,
|
|
1947
|
+
zaiReasoningEffortDialect: false,
|
|
1948
|
+
clampOutputToModelMax: d.isLocalOpenAICompatBackend,
|
|
1949
|
+
stripImageInput: false,
|
|
1950
|
+
thinkingLoopGuard: void 0,
|
|
1951
|
+
rejectRootObjectUnion: false,
|
|
1952
|
+
retryWithoutStrictOnGrammarError: false,
|
|
1953
|
+
supportsPromptCacheKey: false
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* Default leaked-markup healer. Kimi/DeepSeek dedicated grammars are keyed on
|
|
1958
|
+
* identity class; official OpenAI heals nothing; everything else defaults to
|
|
1959
|
+
* the generic `thinking` healer. DSML is DeepSeek's own tool-call grammar, so
|
|
1960
|
+
* every host serving a DeepSeek model gets it — gateways, local backends, and
|
|
1961
|
+
* custom providers alike.
|
|
1962
|
+
*/
|
|
1963
|
+
function detectStreamMarkupHealing(provider, facts, baseUrl) {
|
|
1964
|
+
const isKimiK2 = facts.is("kimi") && facts.identity.family?.startsWith("k2") === true;
|
|
1965
|
+
if (provider === "kimi-code" || provider === "moonshot" || isKimiK2) return "kimi";
|
|
1966
|
+
if (facts.is("deepseek")) return "dsml";
|
|
1967
|
+
if (isOfficialOpenAIEndpoint(provider, baseUrl)) return void 0;
|
|
1968
|
+
return "thinking";
|
|
1969
|
+
}
|
|
1970
|
+
function fixupOpenAICompat(spec, compat, d, axes) {
|
|
1971
|
+
const deepseekThinking = compat.extraBody?.thinking;
|
|
1972
|
+
if (d.isDirectDeepseekReasoning && typeof deepseekThinking === "object" && deepseekThinking !== null && "type" in deepseekThinking && deepseekThinking.type === "enabled") {
|
|
1973
|
+
const extraBody = { ...compat.extraBody };
|
|
1974
|
+
delete extraBody.thinking;
|
|
1975
|
+
compat.extraBody = Object.keys(extraBody).length > 0 ? extraBody : void 0;
|
|
1976
|
+
}
|
|
1977
|
+
if (spec.compat?.reasoningDisableMode === void 0 && !("reasoningDisableMode" in axes.wire)) compat.reasoningDisableMode = d.requiresEnabledThinking ? "omit" : d.isDirectDeepseekReasoning ? "zai-thinking-disabled" : d.isVenice ? "venice-disable-thinking" : resolveReasoningDisableMode(compat.thinkingFormat);
|
|
1978
|
+
if (spec.compat?.omitReasoningEffort === void 0 && !("omitReasoningEffort" in axes.wire) && !compat.supportsReasoningEffort) compat.omitReasoningEffort = true;
|
|
1979
|
+
const axisWhenThinking = compatReasoning(spec, axes) ? objectPayload(axes.wire.whenThinking) : void 0;
|
|
1980
|
+
const whenThinkingPolicy = spec.compat?.whenThinking ?? axisWhenThinking ?? (d.isDirectDeepseekReasoning ? { extraBody: {
|
|
1981
|
+
...compat.extraBody,
|
|
1982
|
+
thinking: { type: "enabled" }
|
|
1983
|
+
} } : void 0);
|
|
1984
|
+
if (whenThinkingPolicy) {
|
|
1985
|
+
const variant = {
|
|
1986
|
+
...compat,
|
|
1987
|
+
whenThinking: void 0
|
|
1988
|
+
};
|
|
1989
|
+
applyCompatOverrides(variant, whenThinkingPolicy);
|
|
1990
|
+
if (Reflect.get(whenThinkingPolicy, "reasoningDisableMode") === void 0) variant.reasoningDisableMode = d.isVenice ? "venice-disable-thinking" : resolveReasoningDisableMode(variant.thinkingFormat);
|
|
1991
|
+
if (Reflect.get(whenThinkingPolicy, "omitReasoningEffort") === void 0 && !variant.supportsReasoningEffort) variant.omitReasoningEffort = true;
|
|
1992
|
+
compat.whenThinking = variant;
|
|
1993
|
+
} else compat.whenThinking = void 0;
|
|
1994
|
+
}
|
|
1995
|
+
function resolveOpenAICompletionsPolicy(spec, facts, axes) {
|
|
1996
|
+
const reasoningCapable = compatReasoning(spec, axes);
|
|
1997
|
+
const d = detectOpenAI(spec, facts, reasoningCapable);
|
|
1998
|
+
const compat = detectOpenAICompat(spec, d, reasoningCapable);
|
|
1999
|
+
applyWireAxes(compat, axes.wire, "openai-completions");
|
|
2000
|
+
applyCompatOverrides(compat, spec.compat);
|
|
2001
|
+
overlayEffortMapAxis(compat, axes, spec.compat);
|
|
2002
|
+
fixupOpenAICompat(spec, compat, d, axes);
|
|
2003
|
+
return compat;
|
|
2004
|
+
}
|
|
2005
|
+
/** Detected Responses-API baseline (ported from `buildOpenAIResponsesCompat`). */
|
|
2006
|
+
function resolveOpenAIResponsesPolicy(spec, facts, axes, api) {
|
|
2007
|
+
const baseUrl = spec.baseUrl ?? "";
|
|
2008
|
+
const provider = spec.provider;
|
|
2009
|
+
const backendProvider = spec.providerType ?? provider;
|
|
2010
|
+
const hostModel = {
|
|
2011
|
+
provider,
|
|
2012
|
+
baseUrl
|
|
2013
|
+
};
|
|
2014
|
+
const isAzure = modelMatchesHost(hostModel, "azureOpenAI");
|
|
2015
|
+
const isOpenRouter = modelMatchesHost(hostModel, "openrouter");
|
|
2016
|
+
const isOpenAIUrl = hostMatchesUrl(baseUrl, "openai");
|
|
2017
|
+
const isVercelGateway = modelMatchesHost(hostModel, "vercelAIGateway");
|
|
2018
|
+
const isXaiHost = modelMatchesHost(hostModel, "xai");
|
|
2019
|
+
const supportsPromptCacheBreakpoints = isOfficialOpenAIEndpoint(provider, baseUrl) && facts.is("openai") && facts.revGte("5.6");
|
|
2020
|
+
const thinkingFormat = isOpenRouter ? "openrouter" : "openai";
|
|
2021
|
+
const reasoningCapable = compatReasoning(spec, axes);
|
|
2022
|
+
const isLocalServingBackend = PROXY_OPENAI_COMPAT_PROVIDERS[backendProvider] !== true && LOCAL_OPENAI_COMPAT_PROVIDERS[backendProvider] === true || hasLocalLoopbackBaseUrl(baseUrl);
|
|
2023
|
+
const isAnthropicModel = facts.is("anthropic");
|
|
2024
|
+
const isDeepseekFamily = facts.is("deepseek");
|
|
2025
|
+
const compat = {
|
|
2026
|
+
supportsDeveloperRole: isAzure || isOpenAIUrl || hostMatchesUrl(baseUrl, "githubCopilot"),
|
|
2027
|
+
supportsStrictMode: isAzure || hostMatchesUrl(baseUrl, "openai") || hostMatchesUrl(baseUrl, "azureOpenAI") || hostMatchesUrl(baseUrl, "cerebras") || hostMatchesUrl(baseUrl, "together") || hostMatchesUrl(baseUrl, "openrouter") || hostMatchesUrl(baseUrl, "deepseekFamily"),
|
|
2028
|
+
supportsReasoningEffort: !isXaiHost,
|
|
2029
|
+
trustExplicitThinkingOnly: void 0,
|
|
2030
|
+
supportsLongPromptCacheRetention: isOpenAIUrl,
|
|
2031
|
+
supportsPromptCacheBreakpoints,
|
|
2032
|
+
promptCacheBreakpointTtl: supportsPromptCacheBreakpoints ? "30m" : void 0,
|
|
2033
|
+
strictResponsesPairing: hostMatchesUrl(baseUrl, "azureOpenAI"),
|
|
2034
|
+
supportsImageDetailOriginal: isOpenAIUrl || hostMatchesUrl(baseUrl, "azureOpenAI") || isAzure && !baseUrl || hostMatchesUrl(baseUrl, "openaiCodex"),
|
|
2035
|
+
supportsReasoningSummary: !isXaiHost,
|
|
2036
|
+
statefulResponses: void 0,
|
|
2037
|
+
supportsAllTurnsReasoningContext: false,
|
|
2038
|
+
supportsConfigurationUpdate: false,
|
|
2039
|
+
supportsSteering: false,
|
|
2040
|
+
requiresReasoningOffJuiceInstruction: false,
|
|
2041
|
+
stripImageInput: false,
|
|
2042
|
+
thinkingLoopGuard: void 0,
|
|
2043
|
+
reasoningEffortMap: {},
|
|
2044
|
+
supportsReasoningParams: true,
|
|
2045
|
+
supportsSamplingParams: true,
|
|
2046
|
+
supportsPenaltyAndStopParams: !isXaiHost,
|
|
2047
|
+
thinkingFormat,
|
|
2048
|
+
reasoningDisableMode: resolveReasoningDisableMode(thinkingFormat),
|
|
2049
|
+
omitReasoningEffort: false,
|
|
2050
|
+
includeEncryptedReasoning: true,
|
|
2051
|
+
filterReasoningHistory: isOpenRouter && isAnthropicModel,
|
|
2052
|
+
disableReasoningOnForcedToolChoice: facts.is("kimi"),
|
|
2053
|
+
disableReasoningOnToolChoice: isDeepseekFamily && reasoningCapable && !isOpenRouter,
|
|
2054
|
+
disableReasoningWithTools: false,
|
|
2055
|
+
supportsToolChoice: true,
|
|
2056
|
+
supportsForcedToolChoice: true,
|
|
2057
|
+
supportsNamedToolChoice: true,
|
|
2058
|
+
reasoningContentField: "reasoning_content",
|
|
2059
|
+
requiresReasoningContentForToolCalls: (facts.is("kimi") || isDeepseekFamily && reasoningCapable || isOpenRouter && reasoningCapable) && reasoningCapable,
|
|
2060
|
+
requiresReasoningContentForAllAssistantTurns: isDeepseekFamily && reasoningCapable && !isOpenRouter,
|
|
2061
|
+
allowsSyntheticReasoningContentForToolCalls: !isDeepseekFamily || !reasoningCapable,
|
|
2062
|
+
syntheticReasoningContentFallback: void 0,
|
|
2063
|
+
replayReasoningContent: false,
|
|
2064
|
+
qwenPreserveThinking: false,
|
|
2065
|
+
qwenTemplateReasoningEffort: false,
|
|
2066
|
+
requiresThinkingAsText: false,
|
|
2067
|
+
requiresMistralToolIds: false,
|
|
2068
|
+
requiresToolResultName: false,
|
|
2069
|
+
requiresAssistantAfterToolResult: false,
|
|
2070
|
+
requiresAssistantContentForToolCalls: facts.is("kimi"),
|
|
2071
|
+
openRouterRouting: void 0,
|
|
2072
|
+
vercelGatewayRouting: void 0,
|
|
2073
|
+
isOpenRouterHost: isOpenRouter,
|
|
2074
|
+
isVercelGatewayHost: isVercelGateway,
|
|
2075
|
+
wireModelIdMode: isOpenRouter ? "openrouter" : "raw",
|
|
2076
|
+
toolSchemaFlavor: facts.is("kimi") ? "moonshot-mfjs" : void 0,
|
|
2077
|
+
alwaysSendMaxTokens: facts.is("kimi"),
|
|
2078
|
+
clampOutputToModelMax: PROXY_OPENAI_COMPAT_PROVIDERS[backendProvider] !== true && (LOCAL_OPENAI_COMPAT_PROVIDERS[backendProvider] === true || hasLocalLoopbackBaseUrl(baseUrl)),
|
|
2079
|
+
supportsObfuscationOptOut: isOpenAIUrl || provider === "openai",
|
|
2080
|
+
storeResponses: false,
|
|
2081
|
+
connectionBoundNativeHistory: false,
|
|
2082
|
+
officialEndpoint: isOfficialOpenAIEndpoint(provider, baseUrl) || api === "openai-codex-responses" && isOfficialCodexApiUrl(baseUrl),
|
|
2083
|
+
harmonyLeakMitigation: false,
|
|
2084
|
+
rejectRootObjectUnion: false,
|
|
2085
|
+
retryWithoutStrictOnGrammarError: false,
|
|
2086
|
+
cacheControlFormat: isOpenRouter && isAnthropicModel ? "anthropic" : void 0,
|
|
2087
|
+
stripDeepseekSpecialTokens: false,
|
|
2088
|
+
streamMarkupHealingPattern: detectStreamMarkupHealing(provider, facts, baseUrl),
|
|
2089
|
+
reasoningDeltasMayBeCumulative: false,
|
|
2090
|
+
emptyLengthFinishIsContextError: false,
|
|
2091
|
+
usesOpenAIToolCallIdLimit: false,
|
|
2092
|
+
promptCacheSessionHeader: hostMatchesUrl(baseUrl, "xai") ? "x-grok-conv-id" : void 0,
|
|
2093
|
+
streamFirstEventTimeoutMs: isLocalServingBackend ? 0 : spec.compat?.streamFirstEventTimeoutMs,
|
|
2094
|
+
streamIdleTimeoutMs: isLocalServingBackend ? LOCAL_OPENAI_COMPAT_STREAM_IDLE_TIMEOUT_MS : spec.compat?.streamIdleTimeoutMs
|
|
2095
|
+
};
|
|
2096
|
+
applyWireAxes(compat, axes.wire, api);
|
|
2097
|
+
applyCompatOverrides(compat, spec.compat);
|
|
2098
|
+
overlayEffortMapAxis(compat, axes, spec.compat);
|
|
2099
|
+
if (isXaiHost && "reasoningEffortMap" in axes.wire) {
|
|
2100
|
+
const canonical = effortRecord(axes.wire.reasoningEffortMap) ?? {};
|
|
2101
|
+
compat.reasoningEffortMap = {
|
|
2102
|
+
...compat.reasoningEffortMap,
|
|
2103
|
+
...canonical
|
|
2104
|
+
};
|
|
2105
|
+
for (const key of ["xhigh", "max"]) if (!(key in canonical)) delete compat.reasoningEffortMap[key];
|
|
2106
|
+
}
|
|
2107
|
+
if (spec.compat?.reasoningDisableMode === void 0 && !("reasoningDisableMode" in axes.wire)) compat.reasoningDisableMode = resolveReasoningDisableMode(compat.thinkingFormat);
|
|
2108
|
+
if (spec.compat?.omitReasoningEffort === void 0 && !("omitReasoningEffort" in axes.wire) && !compat.supportsReasoningEffort) compat.omitReasoningEffort = true;
|
|
2109
|
+
if (provider === "xai-oauth" && axes.wire.supportsReasoningEffort === true && spec.compat?.supportsReasoningEffort !== false) {
|
|
2110
|
+
compat.supportsReasoningEffort = true;
|
|
2111
|
+
compat.omitReasoningEffort = false;
|
|
2112
|
+
}
|
|
2113
|
+
return compat;
|
|
2114
|
+
}
|
|
2115
|
+
function pickResponsesOnly(compat) {
|
|
2116
|
+
return {
|
|
2117
|
+
supportsLongPromptCacheRetention: compat.supportsLongPromptCacheRetention,
|
|
2118
|
+
strictResponsesPairing: compat.strictResponsesPairing,
|
|
2119
|
+
supportsImageDetailOriginal: compat.supportsImageDetailOriginal,
|
|
2120
|
+
supportsObfuscationOptOut: compat.supportsObfuscationOptOut,
|
|
2121
|
+
storeResponses: compat.storeResponses,
|
|
2122
|
+
connectionBoundNativeHistory: compat.connectionBoundNativeHistory,
|
|
2123
|
+
supportsAllTurnsReasoningContext: compat.supportsAllTurnsReasoningContext,
|
|
2124
|
+
supportsConfigurationUpdate: compat.supportsConfigurationUpdate,
|
|
2125
|
+
supportsSteering: compat.supportsSteering,
|
|
2126
|
+
officialEndpoint: compat.officialEndpoint,
|
|
2127
|
+
harmonyLeakMitigation: compat.harmonyLeakMitigation,
|
|
2128
|
+
cacheControlFormat: compat.cacheControlFormat,
|
|
2129
|
+
requiresReasoningOffJuiceInstruction: compat.requiresReasoningOffJuiceInstruction,
|
|
2130
|
+
supportsReasoningSummary: compat.supportsReasoningSummary,
|
|
2131
|
+
statefulResponses: compat.statefulResponses,
|
|
2132
|
+
isVercelGatewayHost: compat.isVercelGatewayHost
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
function resolveAnthropicPolicy(spec, facts, axes) {
|
|
2136
|
+
const baseUrl = spec.baseUrl;
|
|
2137
|
+
const official = isOfficialAnthropicApiUrl(baseUrl);
|
|
2138
|
+
const isCopilot = modelMatchesHost(spec, "githubCopilot");
|
|
2139
|
+
const isZenmux = modelMatchesHost(spec, "zenmux");
|
|
2140
|
+
const requiresThinkingEnabled = modelMatchesHost(spec, "moonshotNative") && facts.kimiMandatoryThinking;
|
|
2141
|
+
const bedrockMessagesApi = isBedrockAnthropicRoute(baseUrl);
|
|
2142
|
+
const rejectsStrictTools = isAzureAnthropicRoute(baseUrl) || bedrockMessagesApi;
|
|
2143
|
+
const signingEndpoint = official || isCopilot || isZenmux || isAnthropicSigningProxyUrl(baseUrl);
|
|
2144
|
+
const compat = {
|
|
2145
|
+
officialEndpoint: official,
|
|
2146
|
+
signingEndpoint,
|
|
2147
|
+
supportsContextManagement: true,
|
|
2148
|
+
supportsServerCompaction: false,
|
|
2149
|
+
firstPartyProvider: false,
|
|
2150
|
+
honorsClaudeCodeBetaKillSwitch: false,
|
|
2151
|
+
supportsOutputEffort: true,
|
|
2152
|
+
disableStrictTools: rejectsStrictTools,
|
|
2153
|
+
bedrockMessagesApi: bedrockMessagesApi || void 0,
|
|
2154
|
+
disableAdaptiveThinking: false,
|
|
2155
|
+
allowAnthropicHeaderOverrides: false,
|
|
2156
|
+
supportsEagerToolInputStreaming: official,
|
|
2157
|
+
supportsLongCacheRetention: official,
|
|
2158
|
+
supportsMidConversationSystem: official && !facts.family("sonnet") && facts.anthropicAdaptiveGenAtLeast("4.8"),
|
|
2159
|
+
supportsTurnScopedSystem: false,
|
|
2160
|
+
supportsMidConversationToolChanges: false,
|
|
2161
|
+
supportsPerMessageEffort: false,
|
|
2162
|
+
supportsThinkingBindingControls: false,
|
|
2163
|
+
supportsBetweenToolsThinking: false,
|
|
2164
|
+
supportsForcedToolChoice: !requiresThinkingEnabled && !facts.family("fable", "mythos"),
|
|
2165
|
+
supportsSamplingParams: true,
|
|
2166
|
+
requiresToolResultId: false,
|
|
2167
|
+
requiresThinkingEnabled,
|
|
2168
|
+
replayUnsignedThinking: !signingEndpoint && (compatReasoning(spec, axes) || modelMatchesHost(spec, "deepseekFamily")),
|
|
2169
|
+
escapeBuiltinToolNames: false,
|
|
2170
|
+
injectClaudeCodeInstruction: true,
|
|
2171
|
+
stripImageInput: false,
|
|
2172
|
+
maxImageDimension: void 0,
|
|
2173
|
+
maxImagePayloadBytes: void 0,
|
|
2174
|
+
thinkingLoopGuard: void 0,
|
|
2175
|
+
stripThinkingHistory: void 0,
|
|
2176
|
+
fastMode: void 0,
|
|
2177
|
+
streamIdleTimeoutMs: spec.compat?.streamIdleTimeoutMs
|
|
2178
|
+
};
|
|
2179
|
+
applyWireAxes(compat, axes.wire, "anthropic-messages");
|
|
2180
|
+
applyCompatOverrides(compat, spec.compat);
|
|
2181
|
+
return compat;
|
|
2182
|
+
}
|
|
2183
|
+
const BEDROCK_REASONING_STREAM_IDLE_TIMEOUT_MS = 6e5;
|
|
2184
|
+
function resolveBedrockPolicy(spec, axes) {
|
|
2185
|
+
const compat = {
|
|
2186
|
+
promptCacheMode: "none",
|
|
2187
|
+
supportsServerCompaction: false,
|
|
2188
|
+
supportsLongPromptCacheRetention: false,
|
|
2189
|
+
promptCacheMinimumTokens: 0,
|
|
2190
|
+
promptCacheMaximumCheckpoints: 0,
|
|
2191
|
+
supportsForcedToolChoice: true,
|
|
2192
|
+
supportsThinkingBindingControls: true
|
|
2193
|
+
};
|
|
2194
|
+
compat.streamIdleTimeoutMs = compatReasoning(spec, axes) ? BEDROCK_REASONING_STREAM_IDLE_TIMEOUT_MS : void 0;
|
|
2195
|
+
applyWireAxes(compat, axes.wire, "bedrock-converse-stream");
|
|
2196
|
+
applyCompatOverrides(compat, spec.compat);
|
|
2197
|
+
return compat;
|
|
2198
|
+
}
|
|
2199
|
+
function resolveOllamaPolicy(spec, axes) {
|
|
2200
|
+
const compat = { stripThinkingHistory: false };
|
|
2201
|
+
applyWireAxes(compat, axes.wire, "ollama-chat");
|
|
2202
|
+
applyCompatOverrides(compat, spec.compat);
|
|
2203
|
+
return compat;
|
|
2204
|
+
}
|
|
2205
|
+
function resolveDevinPolicy(spec, axes) {
|
|
2206
|
+
const compat = {
|
|
2207
|
+
trustExplicitThinkingOnly: false,
|
|
2208
|
+
modelRouter: false,
|
|
2209
|
+
supportsParallelToolCalls: false
|
|
2210
|
+
};
|
|
2211
|
+
applyWireAxes(compat, axes.wire, "devin-agent");
|
|
2212
|
+
applyCompatOverrides(compat, spec.compat);
|
|
2213
|
+
return compat;
|
|
2214
|
+
}
|
|
2215
|
+
function resolveGooglePolicy(spec, axes) {
|
|
2216
|
+
const compat = {
|
|
2217
|
+
supportsFunctionPartId: false,
|
|
2218
|
+
requiresSkipThoughtSignature: false,
|
|
2219
|
+
requiresSkipThoughtSignatureOnFirstFunctionCall: false,
|
|
2220
|
+
dropUnsignedThinking: false,
|
|
2221
|
+
ccaLegacyParametersSchema: false,
|
|
2222
|
+
multimodalFunctionResponse: false,
|
|
2223
|
+
flashStreamLeakWorkaround: false,
|
|
2224
|
+
claudeThinkingBetaHeader: false,
|
|
2225
|
+
antigravityClaudeToolMode: false,
|
|
2226
|
+
stripImageInput: false,
|
|
2227
|
+
maxImageDimension: void 0,
|
|
2228
|
+
maxImagePayloadBytes: void 0
|
|
2229
|
+
};
|
|
2230
|
+
applyWireAxes(compat, axes.wire, spec.api);
|
|
2231
|
+
applyCompatOverrides(compat, spec.compat);
|
|
2232
|
+
return compat;
|
|
2233
|
+
}
|
|
2234
|
+
const DEFAULT_REASONING_EFFORTS = [
|
|
2235
|
+
"minimal",
|
|
2236
|
+
"low",
|
|
2237
|
+
"medium",
|
|
2238
|
+
"high"
|
|
2239
|
+
];
|
|
2240
|
+
const DEFAULT_REASONING_EFFORTS_WITH_XHIGH = [...DEFAULT_REASONING_EFFORTS, "xhigh"];
|
|
2241
|
+
function omitsWireReasoningEffort(api, compat) {
|
|
2242
|
+
if (api !== "openai-responses" && api !== "openai-codex-responses" && api !== "azure-openai-responses") return false;
|
|
2243
|
+
return compat !== void 0 && "supportsReasoningEffort" in compat && compat.supportsReasoningEffort === false;
|
|
2244
|
+
}
|
|
2245
|
+
function readCompatEffortMap(compat) {
|
|
2246
|
+
if (compat === void 0 || !("reasoningEffortMap" in compat)) return void 0;
|
|
2247
|
+
const map = compat.reasoningEffortMap;
|
|
2248
|
+
return map && Object.keys(map).length > 0 ? map : void 0;
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* URL-keyed Fireworks effort remap: covers custom provider ids pointed at the
|
|
2252
|
+
* Fireworks host, which the `fireworks`/`firepass` `thinking-effort-map`
|
|
2253
|
+
* rules cannot match. Rule and configured maps override it.
|
|
2254
|
+
*/
|
|
2255
|
+
const FIREWORKS_THINKING_EFFORT_MAP = { ["minimal"]: "none" };
|
|
2256
|
+
/** Identity/API-derived default control mode; `thinking-mode` rules override. */
|
|
2257
|
+
function defaultThinkingMode(spec, facts) {
|
|
2258
|
+
switch (spec.api) {
|
|
2259
|
+
case "google-generative-ai":
|
|
2260
|
+
case "google-gemini-cli":
|
|
2261
|
+
case "google-vertex": return "budget";
|
|
2262
|
+
case "anthropic-messages":
|
|
2263
|
+
if (facts.is("anthropic")) {
|
|
2264
|
+
if (facts.revGte(facts.family("haiku") ? "5.5" : "4.6")) return "anthropic-adaptive";
|
|
2265
|
+
if (facts.family("opus") && facts.revGte("4.5")) return "anthropic-budget-effort";
|
|
2266
|
+
}
|
|
2267
|
+
return "budget";
|
|
2268
|
+
case "bedrock-converse-stream":
|
|
2269
|
+
if (facts.is("anthropic")) {
|
|
2270
|
+
if (facts.anthropicAdaptiveGenAtLeast("4.6")) return "anthropic-adaptive";
|
|
2271
|
+
if (facts.family("opus") && facts.revGte("4.5")) return "anthropic-budget-effort";
|
|
2272
|
+
}
|
|
2273
|
+
if (facts.is("openai")) return "effort";
|
|
2274
|
+
return "budget";
|
|
2275
|
+
default: return "effort";
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
/** API/compat-generic fallback ladder for targets no rule covered. */
|
|
2279
|
+
function fallbackEfforts(spec, compat) {
|
|
2280
|
+
if (spec.api === "anthropic-messages") return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
2281
|
+
if (spec.api === "bedrock-converse-stream") return DEFAULT_REASONING_EFFORTS;
|
|
2282
|
+
if ((spec.api === "openai-completions" || spec.api === "openrouter") && compat !== void 0 && "thinkingFormat" in compat) {
|
|
2283
|
+
if (compat.thinkingFormat === "openai" && compat.supportsReasoningEffort) return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
2284
|
+
return DEFAULT_REASONING_EFFORTS;
|
|
2285
|
+
}
|
|
2286
|
+
if (spec.api === "openai-responses" || spec.api === "openai-codex-responses" || spec.api === "azure-openai-responses") return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
|
|
2287
|
+
return DEFAULT_REASONING_EFFORTS;
|
|
2288
|
+
}
|
|
2289
|
+
function filterEffortMap(map, efforts) {
|
|
2290
|
+
let filtered;
|
|
2291
|
+
for (const effort of efforts) {
|
|
2292
|
+
const mapped = map[effort];
|
|
2293
|
+
if (mapped === void 0) continue;
|
|
2294
|
+
filtered ??= {};
|
|
2295
|
+
filtered[effort] = mapped;
|
|
2296
|
+
}
|
|
2297
|
+
return filtered;
|
|
2298
|
+
}
|
|
2299
|
+
function readRuleThinking(axes) {
|
|
2300
|
+
const raw = axes.thinking;
|
|
2301
|
+
const out = {};
|
|
2302
|
+
const mode = thinkingMode(raw.mode);
|
|
2303
|
+
if (mode !== void 0) out.mode = mode;
|
|
2304
|
+
const efforts = effortList(raw.efforts);
|
|
2305
|
+
if (efforts !== void 0) out.efforts = efforts;
|
|
2306
|
+
const defaultLevel = effortValue(raw.defaultLevel);
|
|
2307
|
+
if (defaultLevel !== void 0) out.defaultLevel = defaultLevel;
|
|
2308
|
+
const effortMap = effortRecord(raw.effortMap);
|
|
2309
|
+
if (effortMap !== void 0) out.effortMap = effortMap;
|
|
2310
|
+
const effortBudgets = effortNumberRecord(raw.effortBudgets);
|
|
2311
|
+
if (effortBudgets !== void 0) out.effortBudgets = effortBudgets;
|
|
2312
|
+
if (typeof raw.requiresEffort === "boolean") out.requiresEffort = raw.requiresEffort;
|
|
2313
|
+
if (typeof raw.suppressWhenOff === "boolean") out.suppressWhenOff = raw.suppressWhenOff;
|
|
2314
|
+
if (typeof raw.supportsDisplay === "boolean") out.supportsDisplay = raw.supportsDisplay;
|
|
2315
|
+
if (typeof raw.prefixBinding === "boolean") out.prefixBinding = raw.prefixBinding;
|
|
2316
|
+
if (typeof raw.upgradeNeutral === "boolean") out.upgradeNeutral = raw.upgradeNeutral;
|
|
2317
|
+
if (typeof raw.unverified === "boolean") out.unverified = raw.unverified;
|
|
2318
|
+
return out;
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2321
|
+
* Compat-time reasoning capability. `axes.reasoning` also promotes targets on
|
|
2322
|
+
* reviewed effort corrections (the cascade's thinking-axis gate), but compat
|
|
2323
|
+
* may only be repaired where the matching contract opted in with
|
|
2324
|
+
* `thinking-upgrade-neutral`; everywhere else a spec that reports no reasoning
|
|
2325
|
+
* stays the authoritative capability surface.
|
|
2326
|
+
*/
|
|
2327
|
+
function compatReasoning(spec, axes) {
|
|
2328
|
+
if (spec.reasoning) return true;
|
|
2329
|
+
return axes.reasoning && readRuleThinking(axes).upgradeNeutral === true;
|
|
2330
|
+
}
|
|
2331
|
+
/** Identity-derived `requiresEffort` default (mandatory-reasoning lineages). */
|
|
2332
|
+
function impliesMandatoryReasoning(facts, modelId) {
|
|
2333
|
+
if (facts.identity.thinkingVariant) return true;
|
|
2334
|
+
if (stripThinkingVariantSuffix(modelId) !== void 0) return true;
|
|
2335
|
+
return false;
|
|
2336
|
+
}
|
|
2337
|
+
function isQwenTemplateReasoningEffortCompat(compat) {
|
|
2338
|
+
return compat !== void 0 && "qwenTemplateReasoningEffort" in compat && compat.qwenTemplateReasoningEffort === true;
|
|
2339
|
+
}
|
|
2340
|
+
function resolveThinkingPolicy(spec, facts, axes, compat) {
|
|
2341
|
+
const rule = readRuleThinking(axes);
|
|
2342
|
+
const explicitThinking = spec.thinking !== void 0 && Array.isArray(spec.thinking.efforts) && spec.thinking.efforts.length > 0 ? spec.thinking : void 0;
|
|
2343
|
+
const unverified = !compatReasoning(spec, axes);
|
|
2344
|
+
if (unverified && (explicitThinking !== void 0 || facts.identity.class !== "unknown" || rule.unverified !== true)) return;
|
|
2345
|
+
if (omitsWireReasoningEffort(spec.api, compat)) return void 0;
|
|
2346
|
+
if (explicitThinking !== void 0) return fillExplicitThinking(spec, facts, compat, explicitThinking, rule);
|
|
2347
|
+
if (compat !== void 0 && "trustExplicitThinkingOnly" in compat && compat.trustExplicitThinkingOnly === true) return;
|
|
2348
|
+
if (rule.efforts?.length === 0) return void 0;
|
|
2349
|
+
const config = {
|
|
2350
|
+
mode: rule.mode ?? defaultThinkingMode(spec, facts),
|
|
2351
|
+
efforts: rule.efforts ?? fallbackEfforts(spec, compat)
|
|
2352
|
+
};
|
|
2353
|
+
if (config.efforts.length === 0) throw new Error(`Model ${spec.provider}/${spec.id} resolved to an empty thinking range`);
|
|
2354
|
+
const effortMap = mergeEffortMap(spec, rule.effortMap, compat, config.efforts);
|
|
2355
|
+
if (effortMap !== void 0) config.effortMap = effortMap;
|
|
2356
|
+
if (unverified) {
|
|
2357
|
+
config.unverified = true;
|
|
2358
|
+
return config;
|
|
2359
|
+
}
|
|
2360
|
+
if (rule.defaultLevel !== void 0) config.defaultLevel = rule.defaultLevel;
|
|
2361
|
+
if (rule.effortBudgets !== void 0) config.effortBudgets = rule.effortBudgets;
|
|
2362
|
+
if (rule.supportsDisplay ?? defaultSupportsDisplay(spec, facts)) config.supportsDisplay = true;
|
|
2363
|
+
if (rule.prefixBinding) config.prefixBinding = true;
|
|
2364
|
+
if (rule.requiresEffort ?? (impliesMandatoryReasoning(facts, spec.id) || isQwenTemplateReasoningEffortCompat(compat))) config.requiresEffort = true;
|
|
2365
|
+
if (rule.suppressWhenOff) config.suppressWhenOff = true;
|
|
2366
|
+
return config;
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Displayable-thinking default: an api-conditioned lineage fact (Anthropic
|
|
2370
|
+
* wire dialects streaming adaptive Claude 4.7+ thinking) that KDL cannot
|
|
2371
|
+
* express — cascade selectors have no api dimension. `thinking-supports-display`
|
|
2372
|
+
* rules override per deployment.
|
|
2373
|
+
*/
|
|
2374
|
+
function defaultSupportsDisplay(spec, facts) {
|
|
2375
|
+
return (spec.api === "anthropic-messages" || spec.api === "bedrock-converse-stream") && facts.anthropicAdaptiveGenAtLeast("4.7");
|
|
2376
|
+
}
|
|
2377
|
+
function mergeEffortMap(spec, ruleMap, compat, efforts) {
|
|
2378
|
+
const detected = (spec.api === "openai-completions" || spec.api === "openrouter") && modelMatchesHost({
|
|
2379
|
+
provider: spec.provider,
|
|
2380
|
+
baseUrl: spec.baseUrl ?? ""
|
|
2381
|
+
}, "fireworks") ? FIREWORKS_THINKING_EFFORT_MAP : void 0;
|
|
2382
|
+
const configured = readCompatEffortMap(compat);
|
|
2383
|
+
if (detected === void 0 && ruleMap === void 0 && configured === void 0) return void 0;
|
|
2384
|
+
return filterEffortMap({
|
|
2385
|
+
...detected,
|
|
2386
|
+
...ruleMap,
|
|
2387
|
+
...configured
|
|
2388
|
+
}, efforts);
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* Backfill missing wire/default fields onto explicit thinking metadata.
|
|
2392
|
+
* Explicit spec thinking is layer 4 of the resolution order: its ladder and
|
|
2393
|
+
* every explicitly-set field always win; only absent fields are filled from
|
|
2394
|
+
* the rules and identity-derived defaults.
|
|
2395
|
+
*/
|
|
2396
|
+
function fillExplicitThinking(spec, facts, compat, thinking, rule) {
|
|
2397
|
+
const effortMap = thinking.effortMap === void 0 ? mergeEffortMap(spec, rule.effortMap, compat, thinking.efforts) : void 0;
|
|
2398
|
+
const needsDisplay = thinking.supportsDisplay === void 0 && (rule.supportsDisplay ?? defaultSupportsDisplay(spec, facts));
|
|
2399
|
+
const needsRequiresEffort = thinking.requiresEffort === void 0 && (rule.requiresEffort ?? (impliesMandatoryReasoning(facts, spec.id) || isQwenTemplateReasoningEffortCompat(compat)));
|
|
2400
|
+
const needsDefaultLevel = thinking.defaultLevel === void 0 && rule.defaultLevel !== void 0;
|
|
2401
|
+
const needsPrefixBinding = thinking.prefixBinding === void 0 && rule.prefixBinding === true;
|
|
2402
|
+
const needsEffortBudgets = thinking.effortBudgets === void 0 && rule.effortBudgets !== void 0;
|
|
2403
|
+
if (effortMap === void 0 && !needsDisplay && !needsRequiresEffort && !needsDefaultLevel && !needsPrefixBinding && !needsEffortBudgets) return thinking;
|
|
2404
|
+
const filled = { ...thinking };
|
|
2405
|
+
if (effortMap !== void 0) filled.effortMap = effortMap;
|
|
2406
|
+
if (needsDisplay) filled.supportsDisplay = true;
|
|
2407
|
+
if (needsDefaultLevel && rule.defaultLevel !== void 0) filled.defaultLevel = rule.defaultLevel;
|
|
2408
|
+
if (needsRequiresEffort) filled.requiresEffort = true;
|
|
2409
|
+
if (needsPrefixBinding) filled.prefixBinding = true;
|
|
2410
|
+
if (needsEffortBudgets) filled.effortBudgets = rule.effortBudgets;
|
|
2411
|
+
return filled;
|
|
2412
|
+
}
|
|
2413
|
+
function buildResolveTarget(spec, identity, providerType = spec.providerType ?? spec.provider) {
|
|
2414
|
+
const target = {
|
|
2415
|
+
provider: providerType,
|
|
2416
|
+
api: spec.api,
|
|
2417
|
+
class: identity.class,
|
|
2418
|
+
model: policyModelId(spec),
|
|
2419
|
+
reasoning: Boolean(spec.reasoning)
|
|
2420
|
+
};
|
|
2421
|
+
if (identity.family !== void 0) target.family = identity.family;
|
|
2422
|
+
if (identity.revision !== void 0) target.revision = identity.revision;
|
|
2423
|
+
return target;
|
|
2424
|
+
}
|
|
2425
|
+
function specUsesApi(spec, api) {
|
|
2426
|
+
return spec.api === api;
|
|
2427
|
+
}
|
|
2428
|
+
/**
|
|
2429
|
+
* Catalog-data axis assignments for one model spec — the `catalog` field of
|
|
2430
|
+
* {@link resolveModelPolicy} without resolving the compat and thinking
|
|
2431
|
+
* policies, which dominate its cost on catalog-wide scans.
|
|
2432
|
+
*/
|
|
2433
|
+
function resolveCatalogAxes(spec) {
|
|
2434
|
+
return resolveCascade(buildResolveTarget(spec, resolveIdentity(spec))).catalog;
|
|
2435
|
+
}
|
|
2436
|
+
function resolveModelPolicy(input, route) {
|
|
2437
|
+
const spec = withoutUnverifiedThinking(input);
|
|
2438
|
+
const identity = resolveIdentity(spec);
|
|
2439
|
+
const facts = new IdentityFacts(identity);
|
|
2440
|
+
const target = buildResolveTarget(spec, identity);
|
|
2441
|
+
target.upstream = route?.upstream;
|
|
2442
|
+
const axes = resolveCascade(target);
|
|
2443
|
+
let compat;
|
|
2444
|
+
if (specUsesApi(spec, "openrouter")) {
|
|
2445
|
+
const chat = resolveOpenAICompletionsPolicy(spec, facts, axes);
|
|
2446
|
+
const responses = resolveOpenAIResponsesPolicy(spec, facts, axes, "openrouter");
|
|
2447
|
+
compat = {
|
|
2448
|
+
...chat,
|
|
2449
|
+
...pickResponsesOnly(responses)
|
|
2450
|
+
};
|
|
2451
|
+
} else if (specUsesApi(spec, "openai-completions")) compat = resolveOpenAICompletionsPolicy(spec, facts, axes);
|
|
2452
|
+
else if (specUsesApi(spec, "openai-responses") || specUsesApi(spec, "azure-openai-responses") || specUsesApi(spec, "openai-codex-responses")) compat = resolveOpenAIResponsesPolicy(spec, facts, axes, spec.api);
|
|
2453
|
+
else if (specUsesApi(spec, "anthropic-messages")) compat = resolveAnthropicPolicy(spec, facts, axes);
|
|
2454
|
+
else if (specUsesApi(spec, "bedrock-converse-stream")) compat = resolveBedrockPolicy(spec, axes);
|
|
2455
|
+
else if (specUsesApi(spec, "ollama-chat")) compat = resolveOllamaPolicy(spec, axes);
|
|
2456
|
+
else if (specUsesApi(spec, "devin-agent")) compat = resolveDevinPolicy(spec, axes);
|
|
2457
|
+
else if (specUsesApi(spec, "google-generative-ai") || specUsesApi(spec, "google-vertex") || specUsesApi(spec, "google-gemini-cli")) compat = resolveGooglePolicy(spec, axes);
|
|
2458
|
+
else compat = void 0;
|
|
2459
|
+
return {
|
|
2460
|
+
identity,
|
|
2461
|
+
compat,
|
|
2462
|
+
thinking: resolveThinkingPolicy(spec, facts, axes, compat),
|
|
2463
|
+
catalog: axes.catalog,
|
|
2464
|
+
request: resolveRequestPolicy(axes)
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
/**
|
|
2468
|
+
* Whether reviewed rules know THIS model's effort ladder, as opposed to it
|
|
2469
|
+
* inheriting a provider-wide default or {@link resolveThinkingPolicy} falling
|
|
2470
|
+
* through to the neutral wire ladder.
|
|
2471
|
+
*
|
|
2472
|
+
* Discovery uses this to tell "omp knows this model's tiers" apart from "omp
|
|
2473
|
+
* is guessing them", so catalog-published tiers can correct the guess without
|
|
2474
|
+
* ever overriding reviewed knowledge.
|
|
2475
|
+
*/
|
|
2476
|
+
function hasModelScopedEffortLadder(spec) {
|
|
2477
|
+
return hasModelScopedEffortsRule(buildResolveTarget(spec, resolveIdentity(spec)));
|
|
2478
|
+
}
|
|
2479
|
+
//#endregion
|
|
2480
|
+
//#region node_modules/.pnpm/@oh-my-pi+pi-catalog@18.12.6/node_modules/@oh-my-pi/pi-catalog/src/identity/id.ts
|
|
2481
|
+
const LEADING_BRACKETED_AFFIX_PATTERN = /^(?:\s*(?:\[|【)[^\]】]+(?:\]|】)\s*)+/u;
|
|
2482
|
+
const TRAILING_BRACKETED_AFFIX_PATTERN = /(?:\s*(?:\[|【)[^\]】]+(?:\]|】)\s*)+$/u;
|
|
2483
|
+
/** Strip a provider namespace prefix (`openai/gpt-5.4` → `gpt-5.4`). */
|
|
2484
|
+
const bareModelIdCache = /* @__PURE__ */ new Map();
|
|
2485
|
+
function bareModelId(modelId) {
|
|
2486
|
+
const cached = bareModelIdCache.get(modelId);
|
|
2487
|
+
if (cached !== void 0) return cached;
|
|
2488
|
+
const separator = modelId.lastIndexOf("/");
|
|
2489
|
+
const result = separator === -1 ? modelId : modelId.slice(separator + 1);
|
|
2490
|
+
bareModelIdCache.set(modelId, result);
|
|
2491
|
+
return result;
|
|
2492
|
+
}
|
|
2493
|
+
const MODEL_ID_SEGMENT_PATTERN = /[a-z0-9.:-]+/g;
|
|
2494
|
+
const MODEL_FAMILY_PREFIX_PATTERN = /^(claude|gemini|gpt|grok|glm|qwen|deepseek|kimi|mimo|doubao|ernie|gpt-oss|gemma|minimax|step|command|jamba|llama|o[1345])/i;
|
|
2495
|
+
function normalizeModelIdWhitespace(value) {
|
|
2496
|
+
return value.trim().replace(/\s+/g, " ");
|
|
2497
|
+
}
|
|
2498
|
+
/** Ordering for model-like segments: longest first, ties broken lexicographically. */
|
|
2499
|
+
function compareSegmentPreference(left, right) {
|
|
2500
|
+
return left.length !== right.length ? right.length - left.length : left.localeCompare(right);
|
|
2501
|
+
}
|
|
2502
|
+
function getModelLikeIdSegments(modelId) {
|
|
2503
|
+
const matches = normalizeModelIdWhitespace(modelId).toLowerCase().match(MODEL_ID_SEGMENT_PATTERN);
|
|
2504
|
+
if (!matches) return [];
|
|
2505
|
+
const segments = /* @__PURE__ */ new Set();
|
|
2506
|
+
for (const segment of matches) if (MODEL_FAMILY_PREFIX_PATTERN.test(segment) && /\d/.test(segment)) segments.add(segment);
|
|
2507
|
+
return [...segments].sort(compareSegmentPreference);
|
|
2508
|
+
}
|
|
2509
|
+
function getLongestModelLikeIdSegment(modelId) {
|
|
2510
|
+
const matches = normalizeModelIdWhitespace(modelId).toLowerCase().match(MODEL_ID_SEGMENT_PATTERN);
|
|
2511
|
+
if (!matches) return void 0;
|
|
2512
|
+
let best;
|
|
2513
|
+
for (const segment of matches) if (MODEL_FAMILY_PREFIX_PATTERN.test(segment) && /\d/.test(segment) && (best === void 0 || compareSegmentPreference(segment, best) < 0)) best = segment;
|
|
2514
|
+
return best;
|
|
2515
|
+
}
|
|
2516
|
+
function hasBracketAffixMarker(value) {
|
|
2517
|
+
for (let index = 0; index < value.length; index++) {
|
|
2518
|
+
const code = value.charCodeAt(index);
|
|
2519
|
+
if (code === 91 || code === 93 || code === 12304 || code === 12305) return true;
|
|
2520
|
+
}
|
|
2521
|
+
return false;
|
|
2522
|
+
}
|
|
2523
|
+
/**
|
|
2524
|
+
* Strip reseller / wrapper tags that are injected as bracketed affixes around an
|
|
2525
|
+
* upstream model id, e.g.
|
|
2526
|
+
* "[Kiro] claude-opus-4-8" -> "claude-opus-4-8"
|
|
2527
|
+
* "[gcli转] gemini-3.1-pro-preview [假流]" -> "gemini-3.1-pro-preview"
|
|
2528
|
+
*
|
|
2529
|
+
* Candidates are returned most-stripped first: both ends, then leading-only, then trailing-only.
|
|
2530
|
+
*/
|
|
2531
|
+
function getBracketStrippedModelIdCandidates(modelId) {
|
|
2532
|
+
if (!hasBracketAffixMarker(modelId)) return [];
|
|
2533
|
+
const normalized = normalizeModelIdWhitespace(modelId);
|
|
2534
|
+
if (!normalized) return [];
|
|
2535
|
+
const strippedLeading = normalized.replace(LEADING_BRACKETED_AFFIX_PATTERN, "");
|
|
2536
|
+
const withoutLeading = normalizeModelIdWhitespace(strippedLeading);
|
|
2537
|
+
const withoutTrailing = normalizeModelIdWhitespace(normalized.replace(TRAILING_BRACKETED_AFFIX_PATTERN, ""));
|
|
2538
|
+
const withoutBoth = normalizeModelIdWhitespace(strippedLeading.replace(TRAILING_BRACKETED_AFFIX_PATTERN, ""));
|
|
2539
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
2540
|
+
for (const candidate of [
|
|
2541
|
+
withoutBoth,
|
|
2542
|
+
withoutLeading,
|
|
2543
|
+
withoutTrailing
|
|
2544
|
+
]) if (candidate && candidate !== normalized) candidates.add(candidate);
|
|
2545
|
+
return [...candidates];
|
|
2546
|
+
}
|
|
2547
|
+
//#endregion
|
|
2548
|
+
export { withoutUnverifiedThinking as A, applyCodexResidencyHeader as C, mapEffortToAnthropicAdaptiveEffort as D, getSupportedEfforts as E, resolveVertexEndpointHost as F, Effort as I, THINKING_EFFORTS as L, isBedrockRouteUrl as M, isVertexRawPredictUrl as N, requireSupportedEffort as O, modelMatchesHost as P, OPENAI_HEADER_VALUES as S, getCodexAccountId as T, isBedrockAnthropicRoute as _, hasModelScopedEffortLadder as a, CODEX_CLIENT_VERSION as b, FACTORY_DROID_WIRES as c, factoryDroidModelCacheProviderId as d, factoryDroidWireBaseUrl as f, isAnthropicSigningProxyUrl as g, MODEL_KINDS as h, getModelLikeIdSegments as i, hostMatchesUrl as j, resolveWireModelId as k, factoryDroidApiBaseUrl as l, KIND_API_KINDS as m, getBracketStrippedModelIdCandidates as n, resolveCatalogAxes as o, resolveFactoryDroidInferenceRegion as p, getLongestModelLikeIdSegment as r, resolveModelPolicy as s, bareModelId as t, factoryDroidClientHeaders as u, isOfficialAnthropicApiUrl as v, codexRoutingHint as w, OPENAI_HEADERS as x, CODEX_BASE_URL as y };
|