@linxiraos/pi-catalog 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -0
- package/README.md +4 -2
- package/THIRD-PARTY-NOTICES.txt +55 -5
- package/dist/types/build.d.ts +3 -2
- package/dist/types/compat/auth-ids.d.ts +2 -2
- package/dist/types/compat/axes.d.ts +1 -1
- package/dist/types/compat/behavior.d.ts +5 -0
- package/dist/types/compat/catalog-endpoint.d.ts +8 -0
- package/dist/types/compat/image-tokenization.d.ts +9 -1
- package/dist/types/compat/judgment-images.d.ts +25 -0
- package/dist/types/compat/provider-ids.d.ts +1 -1
- package/dist/types/compat/request-compression.d.ts +23 -0
- package/dist/types/compat/request-size.d.ts +12 -3
- package/dist/types/compat/thinking-replay.d.ts +9 -0
- package/dist/types/compat/types.d.ts +4 -0
- package/dist/types/discovery/antigravity.d.ts +7 -0
- package/dist/types/model-cache.d.ts +7 -0
- package/dist/types/model-manager.d.ts +6 -0
- package/dist/types/model-thinking.d.ts +11 -1
- package/dist/types/models.d.ts +16 -1
- package/dist/types/pricing.d.ts +37 -1
- package/dist/types/provider-models/openai-compat.d.ts +18 -46
- package/dist/types/provider-models/special.d.ts +6 -1
- package/dist/types/types.d.ts +63 -12
- package/dist/types/wire/codex.d.ts +2 -0
- package/package.json +5 -5
- package/src/build.d.ts +3 -2
- package/src/build.ts +159 -80
- package/src/compat/auth-ids.ts +2 -0
- package/src/compat/axes.d.ts +1 -1
- package/src/compat/axes.ts +129 -4
- package/src/compat/behavior.d.ts +5 -0
- package/src/compat/behavior.ts +9 -1
- package/src/compat/cascade.ts +4 -1
- package/src/compat/catalog-endpoint.ts +43 -0
- package/src/compat/image-tokenization.ts +28 -10
- package/src/compat/judgment-images.ts +83 -0
- package/src/compat/provider-ids.d.ts +1 -1
- package/src/compat/provider-ids.ts +1 -0
- package/src/compat/request-compression.ts +43 -0
- package/src/compat/request-size.ts +20 -5
- package/src/compat/resolve.ts +90 -53
- package/src/compat/rules/README.md +32 -3
- package/src/compat/rules/auth/_order.kdl +1 -1
- package/src/compat/rules/auth/google-antigravity.kdl +1 -1
- package/src/compat/rules/auth/sambanova.kdl +9 -0
- package/src/compat/rules/auth/stencil.kdl +1 -1
- package/src/compat/rules/classes/anthropic.kdl +42 -2
- package/src/compat/rules/classes/deepseek.kdl +4 -0
- package/src/compat/rules/classes/gemini.kdl +46 -2
- package/src/compat/rules/classes/meta.kdl +7 -0
- package/src/compat/rules/classes/minimax.kdl +11 -1
- package/src/compat/rules/classes/openai.kdl +34 -3
- package/src/compat/rules/classes/xai.kdl +26 -0
- package/src/compat/rules/providers/amazon-bedrock.kdl +1 -0
- package/src/compat/rules/providers/anthropic.kdl +23 -0
- package/src/compat/rules/providers/azure.kdl +2 -0
- package/src/compat/rules/providers/cerebras.kdl +3 -0
- package/src/compat/rules/providers/cline-pass.kdl +16 -0
- package/src/compat/rules/providers/commandcode.kdl +10 -0
- package/src/compat/rules/providers/cursor.kdl +50 -1
- package/src/compat/rules/providers/deepinfra.kdl +3 -1
- package/src/compat/rules/providers/devin.kdl +117 -6
- package/src/compat/rules/providers/factory-droid.kdl +6 -0
- package/src/compat/rules/providers/firepass.kdl +15 -0
- package/src/compat/rules/providers/fireworks.kdl +26 -0
- package/src/compat/rules/providers/github-copilot.kdl +24 -0
- package/src/compat/rules/providers/google-antigravity.kdl +37 -0
- package/src/compat/rules/providers/google-gemini-cli.kdl +1 -0
- package/src/compat/rules/providers/google-vertex.kdl +5 -0
- package/src/compat/rules/providers/google.kdl +1 -0
- package/src/compat/rules/providers/image-tokenization.kdl +2 -1
- package/src/compat/rules/providers/local.kdl +8 -1
- package/src/compat/rules/providers/meta.kdl +16 -0
- package/src/compat/rules/providers/moonshot.kdl +2 -0
- package/src/compat/rules/providers/muse-code.kdl +49 -0
- package/src/compat/rules/providers/native-tools.kdl +3 -0
- package/src/compat/rules/providers/ollama-cloud.kdl +10 -0
- package/src/compat/rules/providers/openai-codex.kdl +29 -5
- package/src/compat/rules/providers/openai.kdl +1 -0
- package/src/compat/rules/providers/opencode-go.kdl +35 -0
- package/src/compat/rules/providers/opencode-zen.kdl +4 -0
- package/src/compat/rules/providers/openrouter.kdl +27 -1
- package/src/compat/rules/providers/sambanova.kdl +60 -0
- package/src/compat/rules/providers/tool-call-names.kdl +37 -0
- package/src/compat/rules/providers/typesafe.kdl +22 -1
- package/src/compat/rules/providers/umans.kdl +7 -0
- package/src/compat/rules/providers/venice.kdl +7 -0
- package/src/compat/rules/providers/web.kdl +7 -0
- package/src/compat/rules/providers/xai-oauth.kdl +16 -6
- package/src/compat/rules/providers/xai.kdl +3 -0
- package/src/compat/rules/providers/zai.kdl +5 -0
- package/src/compat/rules/runtime/behavior.kdl +129 -8
- package/src/compat/rules/taxonomy/_collapse.kdl +11 -0
- package/src/compat/rules/taxonomy/gemma.kdl +1 -0
- package/src/compat/rules/taxonomy/meta.kdl +7 -0
- package/src/compat/rules/taxonomy/openai.kdl +2 -0
- package/src/compat/rules.json +1 -1
- package/src/compat/thinking-replay.ts +33 -0
- package/src/compat/types.d.ts +4 -0
- package/src/compat/types.ts +4 -0
- package/src/discovery/antigravity.d.ts +7 -0
- package/src/discovery/antigravity.ts +13 -0
- package/src/discovery/codex.ts +0 -4
- package/src/discovery/cursor.ts +28 -20
- package/src/discovery/devin.ts +7 -8
- package/src/model-cache.d.ts +7 -0
- package/src/model-cache.ts +8 -0
- package/src/model-manager.d.ts +6 -0
- package/src/model-manager.ts +41 -21
- package/src/model-thinking.d.ts +80 -0
- package/src/model-thinking.ts +13 -1
- package/src/model-tokenizer.ts +7 -1
- package/src/models.d.ts +16 -1
- package/src/models.json +1 -1
- package/src/models.ts +26 -12
- package/src/pricing.d.ts +37 -1
- package/src/pricing.ts +123 -12
- package/src/provider-models/bundled-references.ts +5 -2
- package/src/provider-models/cache-provider-id.ts +1 -1
- package/src/provider-models/descriptors.ts +2 -0
- package/src/provider-models/google.ts +7 -1
- package/src/provider-models/openai-compat.d.ts +18 -46
- package/src/provider-models/openai-compat.ts +188 -104
- package/src/provider-models/special.d.ts +6 -1
- package/src/provider-models/special.ts +15 -9
- package/src/types.d.ts +63 -12
- package/src/types.ts +71 -9
- package/src/wire/codex.d.ts +2 -0
- package/src/wire/codex.ts +17 -0
|
@@ -39,8 +39,17 @@ import {
|
|
|
39
39
|
type OpenAICompat,
|
|
40
40
|
type Provider,
|
|
41
41
|
type ThinkingConfig,
|
|
42
|
+
type TokenCost,
|
|
42
43
|
} from "../types";
|
|
43
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
discoveryFetch,
|
|
46
|
+
isAnthropicOAuthToken,
|
|
47
|
+
isRecord,
|
|
48
|
+
toBoolean,
|
|
49
|
+
toNumber,
|
|
50
|
+
toPositiveNumber,
|
|
51
|
+
toPositiveNumberOrNull,
|
|
52
|
+
} from "../utils";
|
|
44
53
|
import { ALIBABA_TOKEN_PLAN_BASE_URL, parseAlibabaTokenPlanCredential } from "../wire/alibaba-token-plan";
|
|
45
54
|
import { normalizeCharmHyperBaseUrl } from "../wire/charm-hyper";
|
|
46
55
|
import { CLINEPASS_API_BASE_URL, clinePassClientHeaders } from "../wire/cline-pass";
|
|
@@ -550,6 +559,40 @@ function mapWithBundledReference<TApi extends Api>(
|
|
|
550
559
|
};
|
|
551
560
|
}
|
|
552
561
|
|
|
562
|
+
/**
|
|
563
|
+
* Rates from an OpenRouter-shaped `pricing` object (USD per token, as decimal
|
|
564
|
+
* strings). Missing, zero, or negative legs (`-1` marks router-priced rows)
|
|
565
|
+
* map to 0, which the model manager fills from its reference sources.
|
|
566
|
+
*/
|
|
567
|
+
function perTokenPricingCost(pricing: unknown): TokenCost {
|
|
568
|
+
const rates = isRecord(pricing) ? pricing : {};
|
|
569
|
+
return {
|
|
570
|
+
input: toPositiveNumber(rates.prompt, 0) * 1_000_000,
|
|
571
|
+
output: toPositiveNumber(rates.completion, 0) * 1_000_000,
|
|
572
|
+
cacheRead: toPositiveNumber(rates.input_cache_read, 0) * 1_000_000,
|
|
573
|
+
cacheWrite: toPositiveNumber(rates.input_cache_write, 0) * 1_000_000,
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Rates from NanoGPT's detailed roster: prompt and completion are USD per
|
|
579
|
+
* million tokens, the cache legs per thousand. Legs the row does not price
|
|
580
|
+
* keep `fallback`.
|
|
581
|
+
*/
|
|
582
|
+
function nanoGptPricingCost(pricing: unknown, fallback: TokenCost): TokenCost {
|
|
583
|
+
if (!isRecord(pricing) || pricing.unit !== "per_million_tokens") return fallback;
|
|
584
|
+
const perThousand = (value: unknown, fallbackRate: number): number => {
|
|
585
|
+
const rate = toPositiveNumber(value, 0);
|
|
586
|
+
return rate > 0 ? rate * 1_000 : fallbackRate;
|
|
587
|
+
};
|
|
588
|
+
return {
|
|
589
|
+
input: toPositiveNumber(pricing.prompt, fallback.input),
|
|
590
|
+
output: toPositiveNumber(pricing.completion, fallback.output),
|
|
591
|
+
cacheRead: perThousand(pricing.cacheReadInputPer1kTokens, fallback.cacheRead),
|
|
592
|
+
cacheWrite: perThousand(pricing.cacheWriteInputPer1kTokens, fallback.cacheWrite),
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
553
596
|
function normalizeAnthropicBaseUrl(baseUrl: string | undefined, fallback: string): string {
|
|
554
597
|
const value = baseUrl?.trim();
|
|
555
598
|
if (!value) {
|
|
@@ -1265,6 +1308,87 @@ export function cerebrasModelManagerOptions(
|
|
|
1265
1308
|
});
|
|
1266
1309
|
}
|
|
1267
1310
|
|
|
1311
|
+
// ---------------------------------------------------------------------------
|
|
1312
|
+
// 3.5 SambaNova
|
|
1313
|
+
// ---------------------------------------------------------------------------
|
|
1314
|
+
|
|
1315
|
+
/** SambaNova discovery inputs: `apiKey` enables the live roster; `baseUrl` defaults to SambaNova Cloud. */
|
|
1316
|
+
export interface SambaNovaModelManagerConfig {
|
|
1317
|
+
apiKey?: string;
|
|
1318
|
+
baseUrl?: string;
|
|
1319
|
+
fetch?: FetchImpl;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* One SambaNova per-token USD rate (a decimal string) as a per-million rate.
|
|
1324
|
+
* `absent` is the value for a missing field; a present field that is not a
|
|
1325
|
+
* nonnegative number yields `undefined`.
|
|
1326
|
+
*/
|
|
1327
|
+
function sambanovaRate(value: unknown, absent: number | undefined): number | undefined {
|
|
1328
|
+
if (value === undefined || value === null) return absent;
|
|
1329
|
+
const parsed = toNumber(value);
|
|
1330
|
+
return parsed !== undefined && parsed >= 0 ? Math.round(parsed * 1e12) / 1e6 : undefined;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* Map SambaNova's `pricing` block. Prompt and completion rates are required;
|
|
1335
|
+
* cache rates default to 0 when absent. An unreadable card stays unpriced
|
|
1336
|
+
* (`unknown`) instead of reading as free, and an all-zero card is a declared
|
|
1337
|
+
* free tier.
|
|
1338
|
+
*/
|
|
1339
|
+
function sambanovaPricing(
|
|
1340
|
+
pricing: unknown,
|
|
1341
|
+
fallback: TokenCost,
|
|
1342
|
+
): Pick<ModelSpec<"openai-completions">, "cost" | "pricingStatus"> {
|
|
1343
|
+
const rates = isRecord(pricing) ? pricing : {};
|
|
1344
|
+
const input = sambanovaRate(rates.prompt, undefined);
|
|
1345
|
+
const output = sambanovaRate(rates.completion, undefined);
|
|
1346
|
+
const cacheRead = sambanovaRate(rates.input_cache_read, 0);
|
|
1347
|
+
const cacheWrite = sambanovaRate(rates.input_cache_write, 0);
|
|
1348
|
+
if (input === undefined || output === undefined || cacheRead === undefined || cacheWrite === undefined) {
|
|
1349
|
+
return { cost: fallback, pricingStatus: "unknown" };
|
|
1350
|
+
}
|
|
1351
|
+
const cost = { input, output, cacheRead, cacheWrite };
|
|
1352
|
+
return input + output + cacheRead + cacheWrite === 0 ? { cost, pricingStatus: "free" } : { cost };
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
/**
|
|
1356
|
+
* SambaNova Cloud's model manager. `/v1/models` requires a key, so a keyless
|
|
1357
|
+
* manager serves only the bundled seed; with a key the live roster replaces
|
|
1358
|
+
* it. Rows carry pricing and limits but no capability metadata: reasoning,
|
|
1359
|
+
* thinking wire format, and image input come from `providers/sambanova.kdl`.
|
|
1360
|
+
*/
|
|
1361
|
+
export function sambanovaModelManagerOptions(
|
|
1362
|
+
config?: SambaNovaModelManagerConfig,
|
|
1363
|
+
): ModelManagerOptions<"openai-completions"> {
|
|
1364
|
+
const apiKey = config?.apiKey;
|
|
1365
|
+
const baseUrl = config?.baseUrl ?? "https://api.sambanova.ai/v1";
|
|
1366
|
+
return {
|
|
1367
|
+
providerId: "sambanova",
|
|
1368
|
+
dynamicModelsAuthoritative: true,
|
|
1369
|
+
// The live `pricing` block is the tariff: an explicit `0` must not revert
|
|
1370
|
+
// to the bundled seed's rate.
|
|
1371
|
+
dynamicCostAuthoritative: true,
|
|
1372
|
+
...(apiKey && {
|
|
1373
|
+
fetchDynamicModels: () =>
|
|
1374
|
+
fetchOpenAICompatibleModels({
|
|
1375
|
+
api: "openai-completions",
|
|
1376
|
+
provider: "sambanova",
|
|
1377
|
+
baseUrl,
|
|
1378
|
+
apiKey,
|
|
1379
|
+
filterModel: (_entry, model) => !isExcludedModel("sambanova", model.id),
|
|
1380
|
+
mapModel: (entry, defaults) => ({
|
|
1381
|
+
...defaults,
|
|
1382
|
+
...sambanovaPricing(entry.pricing, defaults.cost),
|
|
1383
|
+
contextWindow: toPositiveNumber(entry.context_length, defaults.contextWindow),
|
|
1384
|
+
maxTokens: toPositiveNumber(entry.max_completion_tokens, defaults.maxTokens),
|
|
1385
|
+
}),
|
|
1386
|
+
fetch: config?.fetch,
|
|
1387
|
+
}),
|
|
1388
|
+
}),
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1268
1392
|
// ---------------------------------------------------------------------------
|
|
1269
1393
|
// 4. Hugging Face
|
|
1270
1394
|
// ---------------------------------------------------------------------------
|
|
@@ -2296,74 +2420,6 @@ export function zhipuCodingPlanModelManagerOptions(
|
|
|
2296
2420
|
// 7.5 Fireworks
|
|
2297
2421
|
// ---------------------------------------------------------------------------
|
|
2298
2422
|
|
|
2299
|
-
/**
|
|
2300
|
-
* Fireworks-published cap for the Kimi K2 family. Fireworks' `/v1/models`
|
|
2301
|
-
* envelope generically reports `max_completion_tokens: 65536` for every Kimi
|
|
2302
|
-
* deployment, but Kimi K2 (instruct / thinking / turbo) on Fireworks is
|
|
2303
|
-
* documented to ship long reasoning traces that should be bounded — capping
|
|
2304
|
-
* at 32,768 prevents handing callers a budget the router cannot honor.
|
|
2305
|
-
* See https://github.com/can1357/oh-my-pi/issues/1849.
|
|
2306
|
-
*/
|
|
2307
|
-
export const FIREWORKS_KIMI_MAX_TOKENS = 32_768;
|
|
2308
|
-
|
|
2309
|
-
/**
|
|
2310
|
-
* Fireworks' output ceiling for Kimi K2.7-Code specifically. Its `/v1/models`
|
|
2311
|
-
* generic `max_completion_tokens` is 65,536 and Fireworks serves it in full —
|
|
2312
|
-
* verified with a single completion emitting 58,971 output tokens and
|
|
2313
|
-
* `max_tokens: 200000` accepted without error. Unlike the older K2.5/K2.6
|
|
2314
|
-
* family (see {@link FIREWORKS_KIMI_MAX_TOKENS}), K2.7-Code is not clamped to
|
|
2315
|
-
* 32,768; that ceiling only truncated it.
|
|
2316
|
-
*/
|
|
2317
|
-
export const FIREWORKS_KIMI_K27_CODE_MAX_TOKENS = 65_536;
|
|
2318
|
-
|
|
2319
|
-
/**
|
|
2320
|
-
* Returns true for the Kimi K2.5 / K2.6 family served by Fireworks-backed
|
|
2321
|
-
* providers (`fireworks` direct, `firepass` router) that share the 32,768
|
|
2322
|
-
* `maxTokens` ceiling. Matches both the public catalog id (`kimi-k2.5`,
|
|
2323
|
-
* `kimi-k2.6`, `kimi-k2.6-turbo`) and the canonical Fireworks wire id
|
|
2324
|
-
* (`accounts/fireworks/{models,routers}/kimi-k2…`).
|
|
2325
|
-
*
|
|
2326
|
-
* K2.7-Code (incl. `-fast` / `-highspeed`) is deliberately excluded: unlike the
|
|
2327
|
-
* earlier K2 family it serves its full context on Fireworks — verified with a
|
|
2328
|
-
* single completion emitting 58,971 output tokens and `max_tokens: 200000`
|
|
2329
|
-
* accepted without error — so the 32,768 cap would only truncate it. It inherits
|
|
2330
|
-
* Fireworks' reported `max_completion_tokens` (65,536) instead.
|
|
2331
|
-
*/
|
|
2332
|
-
export function isFireworksKimiK2ModelId(modelId: string): boolean {
|
|
2333
|
-
const identity = classifyModel("fireworks", modelId, { lenient: true });
|
|
2334
|
-
if (identity.class !== "kimi" || identity.family === "k2.7-code") return false;
|
|
2335
|
-
return identity.family?.startsWith("k2") === true;
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
/**
|
|
2339
|
-
* Clamp the Kimi K2 family's `maxTokens` to {@link FIREWORKS_KIMI_MAX_TOKENS}
|
|
2340
|
-
* on Fireworks-backed providers, leaving every other model untouched.
|
|
2341
|
-
*/
|
|
2342
|
-
export function clampFireworksKimiMaxTokens(modelId: string, candidate: number): number;
|
|
2343
|
-
export function clampFireworksKimiMaxTokens(modelId: string, candidate: number | null): number | null;
|
|
2344
|
-
export function clampFireworksKimiMaxTokens(modelId: string, candidate: number | null): number | null {
|
|
2345
|
-
if (candidate === null) return null;
|
|
2346
|
-
return isFireworksKimiK2ModelId(modelId) ? Math.min(candidate, FIREWORKS_KIMI_MAX_TOKENS) : candidate;
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
|
-
/**
|
|
2350
|
-
* Kimi K2.7 Code's documented recommended output budget. Some provider
|
|
2351
|
-
* discovery rows report the context-sized `max_completion_tokens` instead.
|
|
2352
|
-
*/
|
|
2353
|
-
export const KIMI_K27_CODE_RECOMMENDED_MAX_TOKENS = 32_768;
|
|
2354
|
-
|
|
2355
|
-
export function isKimiK27CodeModelId(modelId: string): boolean {
|
|
2356
|
-
const identity = classifyModel("fireworks", modelId, { lenient: true });
|
|
2357
|
-
return identity.class === "kimi" && identity.family === "k2.7-code";
|
|
2358
|
-
}
|
|
2359
|
-
|
|
2360
|
-
export function clampKimiK27CodeMaxTokens(modelId: string, candidate: number): number;
|
|
2361
|
-
export function clampKimiK27CodeMaxTokens(modelId: string, candidate: number | null): number | null;
|
|
2362
|
-
export function clampKimiK27CodeMaxTokens(modelId: string, candidate: number | null): number | null {
|
|
2363
|
-
if (candidate === null) return null;
|
|
2364
|
-
return isKimiK27CodeModelId(modelId) ? Math.min(candidate, KIMI_K27_CODE_RECOMMENDED_MAX_TOKENS) : candidate;
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
2423
|
/**
|
|
2368
2424
|
* Fireworks Fast variants we surface: the Fast table on
|
|
2369
2425
|
* https://docs.fireworks.ai/serverless/serverless-modes, minus the US-only
|
|
@@ -2493,15 +2549,9 @@ function mapFireworksControlPlaneModel(
|
|
|
2493
2549
|
const supportsImage = toBoolean(record.supportsImageInput) === true;
|
|
2494
2550
|
const supportsTools = toBoolean(record.supportsTools);
|
|
2495
2551
|
const contextWindow = toPositiveNumber(record.contextLength, reference?.contextWindow ?? null);
|
|
2496
|
-
// The control plane reports no max-output budget
|
|
2497
|
-
//
|
|
2498
|
-
|
|
2499
|
-
const fallbackMaxTokens = isKimiK27CodeModelId(publicModelId)
|
|
2500
|
-
? FIREWORKS_KIMI_K27_CODE_MAX_TOKENS
|
|
2501
|
-
: isFireworksKimiK2ModelId(publicModelId)
|
|
2502
|
-
? FIREWORKS_KIMI_MAX_TOKENS
|
|
2503
|
-
: null;
|
|
2504
|
-
const maxTokens = clampFireworksKimiMaxTokens(publicModelId, reference?.maxTokens ?? fallbackMaxTokens);
|
|
2552
|
+
// The control plane reports no max-output budget; Fireworks' per-model
|
|
2553
|
+
// output caps and fallbacks are rule-owned (providers/fireworks.kdl).
|
|
2554
|
+
const maxTokens = reference?.maxTokens ?? null;
|
|
2505
2555
|
const base: ModelSpec<"openai-completions"> = reference ?? {
|
|
2506
2556
|
id: publicModelId,
|
|
2507
2557
|
name,
|
|
@@ -2941,8 +2991,9 @@ function buildClinePassFreeModel(id: string, liveCatalog?: ClinePassLiveCatalog)
|
|
|
2941
2991
|
input: upstream?.input ?? live?.input ?? fallback.input,
|
|
2942
2992
|
contextWindow: upstream?.contextWindow ?? live?.contextWindow ?? fallback.contextWindow,
|
|
2943
2993
|
maxTokens: upstream?.maxTokens ?? live?.maxTokens ?? fallback.maxTokens,
|
|
2944
|
-
//
|
|
2945
|
-
|
|
2994
|
+
// No reasoning-effort control means no effort ladder: the cline-pass
|
|
2995
|
+
// rules trust only explicit thinking, so a copied ladder would surface.
|
|
2996
|
+
thinking: undefined,
|
|
2946
2997
|
compat: { supportsReasoningEffort: false, wireModelIdMode: "raw" },
|
|
2947
2998
|
};
|
|
2948
2999
|
}
|
|
@@ -3215,12 +3266,18 @@ export interface OpenCodeModelManagerConfig {
|
|
|
3215
3266
|
fetch?: FetchImpl;
|
|
3216
3267
|
}
|
|
3217
3268
|
|
|
3269
|
+
const OPENCODE_ZEN_BASE_PATH = "https://opencode.ai/zen";
|
|
3270
|
+
const OPENCODE_ZEN_GOOGLE_BASE_URL = "https://opencode.ai/inference/google/v1beta";
|
|
3271
|
+
|
|
3218
3272
|
function normalizeOpenCodeBasePath(baseUrl: string | undefined, fallbackBasePath: string): string {
|
|
3219
3273
|
const value = normalizeAnthropicBaseUrl(baseUrl, fallbackBasePath);
|
|
3220
3274
|
return value.endsWith("/v1") ? value.slice(0, -3) : value;
|
|
3221
3275
|
}
|
|
3222
3276
|
|
|
3223
3277
|
function openCodeBaseUrlForApi(api: Api, basePath: string): string {
|
|
3278
|
+
if (api === "google-generative-ai" && basePath === OPENCODE_ZEN_BASE_PATH) {
|
|
3279
|
+
return OPENCODE_ZEN_GOOGLE_BASE_URL;
|
|
3280
|
+
}
|
|
3224
3281
|
return api === "anthropic-messages" ? basePath : `${basePath}/v1`;
|
|
3225
3282
|
}
|
|
3226
3283
|
|
|
@@ -3234,7 +3291,12 @@ function openCodeBaseUrlForApi(api: Api, basePath: string): string {
|
|
|
3234
3291
|
// rules (`runtime/behavior.kdl`; #887, #1617, #8957).
|
|
3235
3292
|
// Runtime-discovered rows cached before model-identity corrections retain
|
|
3236
3293
|
// stale capability metadata until the authoritative catalog TTL expires.
|
|
3237
|
-
const OPENCODE_CACHE_MIGRATION_MODEL_IDS = [
|
|
3294
|
+
const OPENCODE_CACHE_MIGRATION_MODEL_IDS = [
|
|
3295
|
+
"deepseek-flash",
|
|
3296
|
+
"glm-5.3-flash",
|
|
3297
|
+
"longcat-2.5-preview-free",
|
|
3298
|
+
"space-bunny-free",
|
|
3299
|
+
] as const;
|
|
3238
3300
|
const OPENCODE_ZEN_CACHE_MIGRATION_MODEL_IDS = ["gemini-3.7-flash", "gemini-3.8-flash"] as const;
|
|
3239
3301
|
|
|
3240
3302
|
// Billing-variant suffixes the OpenCode gateways append to a base model id
|
|
@@ -3269,7 +3331,10 @@ function openCodeModelManagerOptions(
|
|
|
3269
3331
|
const apiOverrides = (id: string): Api | undefined => {
|
|
3270
3332
|
// Route pins live in the `api-routes` behavior rules.
|
|
3271
3333
|
const route = apiRouteFor(providerId, id)?.api;
|
|
3272
|
-
return route === "openai-completions" ||
|
|
3334
|
+
return route === "openai-completions" ||
|
|
3335
|
+
route === "openai-responses" ||
|
|
3336
|
+
route === "anthropic-messages" ||
|
|
3337
|
+
route === "google-generative-ai"
|
|
3273
3338
|
? route
|
|
3274
3339
|
: undefined;
|
|
3275
3340
|
};
|
|
@@ -3893,6 +3958,7 @@ export function kiloModelManagerOptions(config?: KiloModelManagerConfig): ModelM
|
|
|
3893
3958
|
provider: "kilo",
|
|
3894
3959
|
baseUrl,
|
|
3895
3960
|
apiKey,
|
|
3961
|
+
mapModel: (entry, defaults) => ({ ...defaults, cost: perTokenPricingCost(entry.pricing) }),
|
|
3896
3962
|
fetch: config?.fetch,
|
|
3897
3963
|
}),
|
|
3898
3964
|
};
|
|
@@ -4570,11 +4636,7 @@ export function veniceModelManagerOptions(
|
|
|
4570
4636
|
config,
|
|
4571
4637
|
mapModel: (entry, defaults, reference) => {
|
|
4572
4638
|
const model = mapWithBundledReference(entry, defaults, reference);
|
|
4573
|
-
return {
|
|
4574
|
-
...model,
|
|
4575
|
-
maxTokens: clampKimiK27CodeMaxTokens(defaults.id, model.maxTokens),
|
|
4576
|
-
compat: { ...model.compat, supportsUsageInStreaming: false },
|
|
4577
|
-
};
|
|
4639
|
+
return { ...model, compat: { ...model.compat, supportsUsageInStreaming: false } };
|
|
4578
4640
|
},
|
|
4579
4641
|
});
|
|
4580
4642
|
}
|
|
@@ -4727,7 +4789,9 @@ function museSparkLineageSpec(id: string): ModelSpec<"openai-responses"> | undef
|
|
|
4727
4789
|
let templateRevision: readonly [number, number, number] | undefined;
|
|
4728
4790
|
for (const model of seedModels<"openai-responses">("meta")) {
|
|
4729
4791
|
if ((billingVariantPlain(model.id) !== undefined) !== contributor) continue;
|
|
4730
|
-
const
|
|
4792
|
+
const seedIdentity = classifyModel("meta", model.id, { lenient: true });
|
|
4793
|
+
if (seedIdentity.family !== "muse-spark") continue;
|
|
4794
|
+
const candidate = parseRevision(seedIdentity.revision ?? "");
|
|
4731
4795
|
if (candidate === undefined) continue;
|
|
4732
4796
|
if (templateRevision === undefined || compareRevision(candidate, templateRevision) > 0) {
|
|
4733
4797
|
template = model;
|
|
@@ -6282,10 +6346,29 @@ export function nanoGptModelManagerOptions(
|
|
|
6282
6346
|
provider: "nanogpt",
|
|
6283
6347
|
baseUrl,
|
|
6284
6348
|
apiKey,
|
|
6349
|
+
// Only the detailed roster carries per-model pricing, names, and limits.
|
|
6350
|
+
query: { detailed: "true" },
|
|
6285
6351
|
mapModel: (entry, defaults) => {
|
|
6286
6352
|
const reference = resolveReference(defaults.id);
|
|
6287
|
-
|
|
6288
|
-
|
|
6353
|
+
// The detailed roster names its output cap `max_output_tokens`.
|
|
6354
|
+
const maxOutputTokens = entry.max_output_tokens ?? entry.max_completion_tokens;
|
|
6355
|
+
const mapped = mapWithBundledReference(
|
|
6356
|
+
{ ...entry, max_completion_tokens: maxOutputTokens },
|
|
6357
|
+
reference
|
|
6358
|
+
? defaults
|
|
6359
|
+
: {
|
|
6360
|
+
...defaults,
|
|
6361
|
+
contextWindow: toPositiveNumberOrNull(entry.context_length),
|
|
6362
|
+
maxTokens: toPositiveNumberOrNull(maxOutputTokens),
|
|
6363
|
+
},
|
|
6364
|
+
reference,
|
|
6365
|
+
);
|
|
6366
|
+
return {
|
|
6367
|
+
...mapped,
|
|
6368
|
+
api: "openai-completions",
|
|
6369
|
+
provider: "nanogpt",
|
|
6370
|
+
cost: { ...mapped.cost, ...nanoGptPricingCost(entry.pricing, mapped.cost) },
|
|
6371
|
+
};
|
|
6289
6372
|
},
|
|
6290
6373
|
filterModel: (_entry, model) => {
|
|
6291
6374
|
const match = NANO_GPT_THINKING_SUFFIX_RE.exec(model.id);
|
|
@@ -6969,13 +7052,10 @@ function createOpenCodeApiResolution(
|
|
|
6969
7052
|
rules: ApiResolutionRule[];
|
|
6970
7053
|
} {
|
|
6971
7054
|
const completionsBaseUrl = `${basePath}/v1`;
|
|
6972
|
-
|
|
6973
|
-
// - openai-completions / openai-responses / google-generative-ai → /v1
|
|
6974
|
-
// - anthropic-messages → bare basePath (the Anthropic client appends /v1/messages)
|
|
6975
|
-
const baseUrlForApi = (api: Api): string => (api === "anthropic-messages" ? basePath : completionsBaseUrl);
|
|
7055
|
+
const baseUrlForApi = (api: Api): string => openCodeBaseUrlForApi(api, basePath);
|
|
6976
7056
|
// Per-id route pins live in the `api-routes` behavior rules.
|
|
6977
7057
|
const overrideRules: ApiResolutionRule[] = (
|
|
6978
|
-
["openai-completions", "openai-responses", "anthropic-messages"] as const
|
|
7058
|
+
["openai-completions", "openai-responses", "anthropic-messages", "google-generative-ai"] as const
|
|
6979
7059
|
).map(api => ({
|
|
6980
7060
|
matches: modelId => apiRouteFor(providerId, modelId)?.api === api,
|
|
6981
7061
|
resolved: { api, baseUrl: baseUrlForApi(api) },
|
|
@@ -7004,8 +7084,17 @@ function createOpenCodeApiResolution(
|
|
|
7004
7084
|
|
|
7005
7085
|
// Resolver rules for the models.dev descriptor path; the per-id pins live in
|
|
7006
7086
|
// the `api-routes` behavior rules, which also drive the live-discovery mapper.
|
|
7007
|
-
const OPENCODE_ZEN_API_RESOLUTION = createOpenCodeApiResolution(
|
|
7008
|
-
const
|
|
7087
|
+
const OPENCODE_ZEN_API_RESOLUTION = createOpenCodeApiResolution(OPENCODE_ZEN_BASE_PATH, "opencode-zen");
|
|
7088
|
+
const OPENCODE_GO_BASE_PATH = "https://opencode.ai/inference/go";
|
|
7089
|
+
const OPENCODE_GO_API_RESOLUTION = createOpenCodeApiResolution(OPENCODE_GO_BASE_PATH, "opencode-go");
|
|
7090
|
+
|
|
7091
|
+
/**
|
|
7092
|
+
* OpenCode Go endpoint for one request API. The model generator re-bases
|
|
7093
|
+
* previous-snapshot rows with it so retained rows follow endpoint moves.
|
|
7094
|
+
*/
|
|
7095
|
+
export function openCodeGoBaseUrlForApi(api: Api): string {
|
|
7096
|
+
return openCodeBaseUrlForApi(api, OPENCODE_GO_BASE_PATH);
|
|
7097
|
+
}
|
|
7009
7098
|
|
|
7010
7099
|
const COPILOT_BASE_URL = "https://api.githubcopilot.com";
|
|
7011
7100
|
|
|
@@ -7383,7 +7472,7 @@ const OPENCODE_MODELS_DEV_DESCRIPTORS: readonly ModelsDevProviderDescriptor[] =
|
|
|
7383
7472
|
OPENCODE_ZEN_API_RESOLUTION.defaultResolution,
|
|
7384
7473
|
),
|
|
7385
7474
|
}),
|
|
7386
|
-
openAiCompletionsDescriptor("opencode-go", "opencode-go",
|
|
7475
|
+
openAiCompletionsDescriptor("opencode-go", "opencode-go", `${OPENCODE_GO_BASE_PATH}/v1`, {
|
|
7387
7476
|
filterModel: filterActiveToolCallModels,
|
|
7388
7477
|
resolveApi: (modelId, raw) =>
|
|
7389
7478
|
resolveApiByRules(
|
|
@@ -7464,12 +7553,7 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_SPECIALIZED: readonly ModelsDevProviderDes
|
|
|
7464
7553
|
// --- Synthetic ---
|
|
7465
7554
|
openAiCompletionsDescriptor("synthetic", "synthetic", "https://api.synthetic.new/openai/v1"),
|
|
7466
7555
|
// --- Venice AI ---
|
|
7467
|
-
openAiCompletionsDescriptor("venice", "venice", "https://api.venice.ai/api/v1",
|
|
7468
|
-
transformModel: model => {
|
|
7469
|
-
const maxTokens = clampKimiK27CodeMaxTokens(model.id, model.maxTokens);
|
|
7470
|
-
return maxTokens === model.maxTokens ? model : { ...model, maxTokens };
|
|
7471
|
-
},
|
|
7472
|
-
}),
|
|
7556
|
+
openAiCompletionsDescriptor("venice", "venice", "https://api.venice.ai/api/v1"),
|
|
7473
7557
|
// --- Ollama Cloud ---
|
|
7474
7558
|
simpleModelsDevDescriptor("ollama-cloud", "ollama-cloud", "ollama-chat", "https://ollama.com"),
|
|
7475
7559
|
// --- Xiaomi Token Plan ---
|
|
@@ -73,7 +73,12 @@ export interface TypeSafeModelManagerConfig {
|
|
|
73
73
|
baseUrl?: string;
|
|
74
74
|
fetch?: FetchImpl;
|
|
75
75
|
}
|
|
76
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Discover account-visible judge models while keeping the bundled offline seed. `GET /v1/models` lists
|
|
78
|
+
* only aliases, yet TypeSafe accepts versioned ids whether or not they are listed, so a successful
|
|
79
|
+
* discovery is extended with the seed rows it omits (the pinned default judge, `jev-1.14.0`) instead of
|
|
80
|
+
* pruning them as `dynamicModelsAuthoritative` otherwise would.
|
|
81
|
+
*/
|
|
77
82
|
export declare function typesafeModelManagerOptions(config?: TypeSafeModelManagerConfig): ModelManagerOptions<"typesafe">;
|
|
78
83
|
export interface ZaiModelManagerConfig {
|
|
79
84
|
}
|
|
@@ -64,6 +64,7 @@ export function openaiCodexModelManagerOptions(
|
|
|
64
64
|
providerId: "openai-codex",
|
|
65
65
|
cacheProviderId: resolveModelCacheProviderId("openai-codex", { baseUrl }),
|
|
66
66
|
dynamicModelsAuthoritative: providerEntry("openai-codex")?.dynamicModelsAuthoritative === true,
|
|
67
|
+
keepStaticNames: providerEntry("openai-codex")?.keepBundledNames === true,
|
|
67
68
|
...(resolveAccounts
|
|
68
69
|
? {
|
|
69
70
|
fetchDynamicModels: async () => {
|
|
@@ -421,25 +422,30 @@ export interface TypeSafeModelManagerConfig {
|
|
|
421
422
|
fetch?: FetchImpl;
|
|
422
423
|
}
|
|
423
424
|
|
|
424
|
-
/**
|
|
425
|
+
/**
|
|
426
|
+
* Discover account-visible judge models while keeping the bundled offline seed. `GET /v1/models` lists
|
|
427
|
+
* only aliases, yet TypeSafe accepts versioned ids whether or not they are listed, so a successful
|
|
428
|
+
* discovery is extended with the seed rows it omits (the pinned default judge, `jev-1.14.0`) instead of
|
|
429
|
+
* pruning them as `dynamicModelsAuthoritative` otherwise would.
|
|
430
|
+
*/
|
|
425
431
|
export function typesafeModelManagerOptions(config: TypeSafeModelManagerConfig = {}): ModelManagerOptions<"typesafe"> {
|
|
426
432
|
const { apiKey } = config;
|
|
427
433
|
const envBaseUrl = Bun.env.TYPESAFE_BASE_URL?.trim();
|
|
428
434
|
const baseUrl = (config.baseUrl ?? (envBaseUrl || TYPESAFE_DEFAULT_BASE_URL)).replace(/\/+$/, "");
|
|
429
|
-
const staticModels = seedModels<"typesafe">("typesafe");
|
|
435
|
+
const staticModels = seedModels<"typesafe">("typesafe").map(model => ({ ...model, baseUrl }));
|
|
430
436
|
return {
|
|
431
437
|
providerId: "typesafe",
|
|
432
438
|
cacheProviderId: resolveModelCacheProviderId("typesafe"),
|
|
433
|
-
staticModels
|
|
439
|
+
staticModels,
|
|
434
440
|
...(apiKey ? { dynamicModelsAuthoritative: true } : undefined),
|
|
435
441
|
...(apiKey
|
|
436
442
|
? {
|
|
437
|
-
fetchDynamicModels: () =>
|
|
438
|
-
fetchTypeSafeModels({
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
+
fetchDynamicModels: async () => {
|
|
444
|
+
const discovered = await fetchTypeSafeModels({ apiKey, baseUrl, fetch: config.fetch });
|
|
445
|
+
if (!discovered) return discovered;
|
|
446
|
+
const listed = new Set(discovered.map(model => model.id));
|
|
447
|
+
return [...discovered, ...staticModels.filter(model => !listed.has(model.id))];
|
|
448
|
+
},
|
|
443
449
|
}
|
|
444
450
|
: undefined),
|
|
445
451
|
};
|