@rigelbuild/omp-catalog 18.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1204 -0
- package/LICENSE +22 -0
- package/README.md +43 -0
- package/THIRD-PARTY-NOTICES.txt +22909 -0
- package/dist/types/build.d.ts +19 -0
- package/dist/types/compat/anthropic.d.ts +18 -0
- package/dist/types/compat/apply.d.ts +7 -0
- package/dist/types/compat/bedrock.d.ts +3 -0
- package/dist/types/compat/devin.d.ts +9 -0
- package/dist/types/compat/openai.d.ts +28 -0
- package/dist/types/discovery/antigravity.d.ts +70 -0
- package/dist/types/discovery/codex.d.ts +36 -0
- package/dist/types/discovery/cursor-proto.d.ts +5368 -0
- package/dist/types/discovery/cursor.d.ts +23 -0
- package/dist/types/discovery/devin-proto.d.ts +1549 -0
- package/dist/types/discovery/devin.d.ts +24 -0
- package/dist/types/discovery/gemini-cli.d.ts +33 -0
- package/dist/types/discovery/gemini.d.ts +25 -0
- package/dist/types/discovery/gitlab-duo-workflow.d.ts +30 -0
- package/dist/types/discovery/index.d.ts +7 -0
- package/dist/types/discovery/openai-compatible.d.ts +89 -0
- package/dist/types/discovery/protobuf.d.ts +102 -0
- package/dist/types/effort.d.ts +10 -0
- package/dist/types/fireworks-model-id.d.ts +23 -0
- package/dist/types/hosts.d.ts +169 -0
- package/dist/types/identity/bundled.d.ts +3 -0
- package/dist/types/identity/classify.d.ts +69 -0
- package/dist/types/identity/dialect.d.ts +3 -0
- package/dist/types/identity/family.d.ts +239 -0
- package/dist/types/identity/id.d.ts +12 -0
- package/dist/types/identity/index.d.ts +8 -0
- package/dist/types/identity/markers.d.ts +4 -0
- package/dist/types/identity/priority.d.ts +1 -0
- package/dist/types/identity/reference.d.ts +29 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/model-cache.d.ts +23 -0
- package/dist/types/model-manager.d.ts +77 -0
- package/dist/types/model-thinking.d.ts +70 -0
- package/dist/types/model-tokenizer.d.ts +8 -0
- package/dist/types/models.d.ts +14 -0
- package/dist/types/openai-pricing.d.ts +17 -0
- package/dist/types/provider-models/bundled-references.d.ts +13 -0
- package/dist/types/provider-models/cache-provider-id.d.ts +11 -0
- package/dist/types/provider-models/descriptor-types.d.ts +76 -0
- package/dist/types/provider-models/descriptors.d.ts +505 -0
- package/dist/types/provider-models/google.d.ts +29 -0
- package/dist/types/provider-models/index.d.ts +7 -0
- package/dist/types/provider-models/ollama.d.ts +22 -0
- package/dist/types/provider-models/openai-compat.d.ts +646 -0
- package/dist/types/provider-models/special.d.ts +53 -0
- package/dist/types/types.d.ts +876 -0
- package/dist/types/utils.d.ts +20 -0
- package/dist/types/variant-collapse.d.ts +137 -0
- package/dist/types/wire/alibaba-token-plan.d.ts +22 -0
- package/dist/types/wire/codex.d.ts +66 -0
- package/dist/types/wire/coreweave.d.ts +12 -0
- package/dist/types/wire/gemini-headers.d.ts +62 -0
- package/dist/types/wire/github-copilot.d.ts +47 -0
- package/dist/types/wire/image-fetchers.d.ts +76 -0
- package/package.json +102 -0
- package/src/build.ts +103 -0
- package/src/compat/anthropic.ts +179 -0
- package/src/compat/apply.ts +15 -0
- package/src/compat/bedrock.ts +153 -0
- package/src/compat/devin.ts +12 -0
- package/src/compat/openai.ts +864 -0
- package/src/discovery/antigravity.ts +257 -0
- package/src/discovery/codex.ts +456 -0
- package/src/discovery/cursor-proto.ts +8329 -0
- package/src/discovery/cursor.ts +409 -0
- package/src/discovery/devin-proto.ts +2037 -0
- package/src/discovery/devin.ts +152 -0
- package/src/discovery/gemini-cli.ts +198 -0
- package/src/discovery/gemini.ts +260 -0
- package/src/discovery/gitlab-duo-workflow.ts +859 -0
- package/src/discovery/index.ts +7 -0
- package/src/discovery/openai-compatible.ts +266 -0
- package/src/discovery/protobuf.ts +1073 -0
- package/src/effort.ts +18 -0
- package/src/fireworks-model-id.ts +50 -0
- package/src/hosts.ts +178 -0
- package/src/identity/bundled.ts +28 -0
- package/src/identity/classify.ts +242 -0
- package/src/identity/dialect.ts +42 -0
- package/src/identity/family.ts +477 -0
- package/src/identity/id.ts +81 -0
- package/src/identity/index.ts +8 -0
- package/src/identity/markers.ts +49 -0
- package/src/identity/priority.ts +59 -0
- package/src/identity/reference.ts +167 -0
- package/src/index.ts +19 -0
- package/src/model-cache.ts +346 -0
- package/src/model-manager.ts +683 -0
- package/src/model-thinking.ts +925 -0
- package/src/model-tokenizer.ts +75 -0
- package/src/models.json +304657 -0
- package/src/models.json.d.ts +9 -0
- package/src/models.ts +108 -0
- package/src/openai-pricing.ts +29 -0
- package/src/provider-models/bundled-references.ts +87 -0
- package/src/provider-models/cache-provider-id.ts +97 -0
- package/src/provider-models/descriptor-types.ts +85 -0
- package/src/provider-models/descriptors.ts +585 -0
- package/src/provider-models/google.ts +117 -0
- package/src/provider-models/index.ts +7 -0
- package/src/provider-models/ollama.ts +204 -0
- package/src/provider-models/openai-compat.ts +6405 -0
- package/src/provider-models/special.ts +213 -0
- package/src/types.ts +1032 -0
- package/src/utils.ts +69 -0
- package/src/variant-collapse.ts +1555 -0
- package/src/wire/alibaba-token-plan.ts +54 -0
- package/src/wire/codex.ts +117 -0
- package/src/wire/coreweave.ts +51 -0
- package/src/wire/gemini-headers.ts +135 -0
- package/src/wire/github-copilot.ts +147 -0
- package/src/wire/image-fetchers.ts +159 -0
package/src/effort.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** User-facing thinking levels, ordered least to most intensive. */
|
|
2
|
+
export const enum Effort {
|
|
3
|
+
Minimal = "minimal",
|
|
4
|
+
Low = "low",
|
|
5
|
+
Medium = "medium",
|
|
6
|
+
High = "high",
|
|
7
|
+
XHigh = "xhigh",
|
|
8
|
+
Max = "max",
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const THINKING_EFFORTS: readonly Effort[] = [
|
|
12
|
+
Effort.Minimal,
|
|
13
|
+
Effort.Low,
|
|
14
|
+
Effort.Medium,
|
|
15
|
+
Effort.High,
|
|
16
|
+
Effort.XHigh,
|
|
17
|
+
Effort.Max,
|
|
18
|
+
];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const FIREWORKS_WIRE_PREFIX = "accounts/fireworks/models/";
|
|
2
|
+
const FIREPASS_WIRE_PREFIX = "accounts/fireworks/routers/";
|
|
3
|
+
const VERSION_SEPARATOR_PATTERN = /(?<=\d)p(?=\d)/g;
|
|
4
|
+
const VERSION_DOT_PATTERN = /(?<=\d)\.(?=\d)/g;
|
|
5
|
+
|
|
6
|
+
export function toFireworksPublicModelId(modelId: string): string {
|
|
7
|
+
const stripped = modelId.startsWith(FIREWORKS_WIRE_PREFIX) ? modelId.slice(FIREWORKS_WIRE_PREFIX.length) : modelId;
|
|
8
|
+
return stripped.replace(VERSION_SEPARATOR_PATTERN, ".");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function toFireworksWireModelId(modelId: string): string {
|
|
12
|
+
const stripped = modelId.startsWith(FIREWORKS_WIRE_PREFIX) ? modelId.slice(FIREWORKS_WIRE_PREFIX.length) : modelId;
|
|
13
|
+
return `${FIREWORKS_WIRE_PREFIX}${stripped.replace(VERSION_DOT_PATTERN, "p")}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fire Pass exposes its Kimi K2.6 Turbo subscription through a dedicated router
|
|
18
|
+
* endpoint at `accounts/fireworks/routers/<id>` rather than the `models/` namespace.
|
|
19
|
+
* We keep a friendly public id (e.g. `kimi-k2.6-turbo`) in the catalog and translate
|
|
20
|
+
* to the wire form (`accounts/fireworks/routers/kimi-k2p6-turbo`) at request time.
|
|
21
|
+
*/
|
|
22
|
+
export function toFirepassPublicModelId(modelId: string): string {
|
|
23
|
+
const stripped = modelId.startsWith(FIREPASS_WIRE_PREFIX) ? modelId.slice(FIREPASS_WIRE_PREFIX.length) : modelId;
|
|
24
|
+
return stripped.replace(VERSION_SEPARATOR_PATTERN, ".");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function toFirepassWireModelId(modelId: string): string {
|
|
28
|
+
const stripped = modelId.startsWith(FIREPASS_WIRE_PREFIX) ? modelId.slice(FIREPASS_WIRE_PREFIX.length) : modelId;
|
|
29
|
+
return `${FIREPASS_WIRE_PREFIX}${stripped.replace(VERSION_DOT_PATTERN, "p")}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Public-id suffix marking a Fireworks "Fast" serving-path variant. Fast is a
|
|
34
|
+
* higher-throughput route (100+ tok/s) exposed under a dedicated router id
|
|
35
|
+
* (`accounts/fireworks/routers/<id>-fast`), not a separate model — same weights,
|
|
36
|
+
* higher price, no Priority tier. We keep a friendly `<id>-fast` public id and
|
|
37
|
+
* translate it to the router wire form at request time (compat
|
|
38
|
+
* `wireModelIdMode: "firepass"`). See https://docs.fireworks.ai/serverless/serving-paths.
|
|
39
|
+
*/
|
|
40
|
+
export const FIREWORKS_FAST_SUFFIX = "-fast";
|
|
41
|
+
|
|
42
|
+
/** True for a Fireworks public model id that selects the Fast serving path. */
|
|
43
|
+
export function isFireworksFastModelId(modelId: string): boolean {
|
|
44
|
+
return modelId.endsWith(FIREWORKS_FAST_SUFFIX);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Strip the Fast suffix to recover the base (Standard-tier) model id. */
|
|
48
|
+
export function toFireworksBaseModelId(modelId: string): string {
|
|
49
|
+
return modelId.endsWith(FIREWORKS_FAST_SUFFIX) ? modelId.slice(0, -FIREWORKS_FAST_SUFFIX.length) : modelId;
|
|
50
|
+
}
|
package/src/hosts.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known model-endpoint host classification — the single vocabulary for the
|
|
3
|
+
* `provider === id || baseUrl.includes(marker)` idiom that gates wire-level
|
|
4
|
+
* behavior (compat detection, routing, header shaping, watchdog floors).
|
|
5
|
+
*
|
|
6
|
+
* Markers are case-insensitive substrings matched against the base URL, NOT
|
|
7
|
+
* parsed hostnames: proxies regularly embed the upstream host in a path
|
|
8
|
+
* segment, and the historical call sites all used substring semantics.
|
|
9
|
+
* Callers that need strict hostname matching — where a substring false
|
|
10
|
+
* positive is dangerous, e.g. the Anthropic official-endpoint OAuth gate —
|
|
11
|
+
* parse the URL and compare the hostname themselves.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface HostClassSpec {
|
|
15
|
+
/** Provider ids that imply this host class regardless of baseUrl. */
|
|
16
|
+
readonly providers?: readonly string[];
|
|
17
|
+
/** Provider-id prefixes that imply this host class (e.g. `xiaomi-token-plan-`). */
|
|
18
|
+
readonly providerPrefixes?: readonly string[];
|
|
19
|
+
/** Lowercase ASCII substrings matched case-insensitively against the base URL. */
|
|
20
|
+
readonly urlMarkers: readonly string[];
|
|
21
|
+
// Strict hostname matching is intentionally not modeled here: the one
|
|
22
|
+
// auth-sensitive consumer (Anthropic official-endpoint) parses the URL
|
|
23
|
+
// itself; every other call site is benign and uses substring matching.
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const KNOWN_HOSTS = {
|
|
27
|
+
openai: { providers: ["openai"], urlMarkers: ["api.openai.com"] },
|
|
28
|
+
azureOpenAI: {
|
|
29
|
+
providers: ["azure"],
|
|
30
|
+
urlMarkers: [".openai.azure.com", "azure.com/openai", "models.inference.ai.azure.com"],
|
|
31
|
+
},
|
|
32
|
+
openrouter: { providers: ["openrouter"], urlMarkers: ["openrouter.ai"] },
|
|
33
|
+
vercelAIGateway: { providers: ["vercel-ai-gateway"], urlMarkers: ["ai-gateway.vercel.sh"] },
|
|
34
|
+
githubCopilot: { providers: ["github-copilot"], urlMarkers: ["githubcopilot.com", "copilot-api."] },
|
|
35
|
+
anthropic: { providers: ["anthropic"], urlMarkers: ["api.anthropic.com"] },
|
|
36
|
+
/** DeepSeek's first-party API only — gates direct-API quirks (max_tokens field, thinking extraBody). */
|
|
37
|
+
deepseekDirect: { providers: ["deepseek"], urlMarkers: ["api.deepseek.com"] },
|
|
38
|
+
/** Any DeepSeek-operated host (first-party API, web-chat fronts). Wider than `deepseekDirect` on purpose. */
|
|
39
|
+
deepseekFamily: { providers: ["deepseek"], urlMarkers: ["deepseek.com"] },
|
|
40
|
+
cerebras: { providers: ["cerebras"], urlMarkers: ["cerebras.ai"] },
|
|
41
|
+
zai: { providers: ["zai"], urlMarkers: ["api.z.ai"] },
|
|
42
|
+
zhipu: { providers: ["zhipu-coding-plan"], urlMarkers: ["open.bigmodel.cn"] },
|
|
43
|
+
kilo: { providers: ["kilo"], urlMarkers: ["api.kilo.ai"] },
|
|
44
|
+
alibabaDashscope: {
|
|
45
|
+
providers: ["alibaba-coding-plan", "alibaba-token-plan"],
|
|
46
|
+
urlMarkers: ["dashscope", "token-plan."],
|
|
47
|
+
},
|
|
48
|
+
umans: { providers: ["umans"], urlMarkers: ["api.code.umans.ai"] },
|
|
49
|
+
xiaomi: { providers: ["xiaomi"], providerPrefixes: ["xiaomi-token-plan-"], urlMarkers: ["xiaomimimo.com"] },
|
|
50
|
+
xai: { providers: ["xai", "xai-oauth"], urlMarkers: ["api.x.ai"] },
|
|
51
|
+
mistral: { providers: ["mistral"], urlMarkers: ["mistral.ai"] },
|
|
52
|
+
together: { providers: ["together"], urlMarkers: ["api.together.xyz"] },
|
|
53
|
+
baseten: { providers: ["baseten"], urlMarkers: ["baseten.co"] },
|
|
54
|
+
/** URL-only on purpose: the `fireworks`/`firepass` providers route per-model and not every model is Fireworks-shaped. */
|
|
55
|
+
fireworks: { urlMarkers: ["fireworks.ai"] },
|
|
56
|
+
groq: { providers: ["groq"], urlMarkers: ["api.groq.com"] },
|
|
57
|
+
minimax: {
|
|
58
|
+
providers: ["minimax", "minimax-code", "minimax-code-cn"],
|
|
59
|
+
urlMarkers: ["api.minimax.io", "api.minimaxi.com"],
|
|
60
|
+
},
|
|
61
|
+
qwenPortal: { providers: ["qwen-portal"], urlMarkers: ["portal.qwen.ai"] },
|
|
62
|
+
/** NVIDIA NIM (`integrate.api.nvidia.com`). Qwen NIM endpoints take `chat_template_kwargs.enable_thinking`, never top-level `enable_thinking`. */
|
|
63
|
+
nvidia: { providers: ["nvidia"], urlMarkers: ["integrate.api.nvidia.com"] },
|
|
64
|
+
/** 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). */
|
|
65
|
+
venice: { providers: ["venice"], urlMarkers: ["api.venice.ai"] },
|
|
66
|
+
moonshotNative: { providers: ["moonshot", "kimi-code"], urlMarkers: ["api.moonshot.ai", "api.kimi.com"] },
|
|
67
|
+
/** 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. */
|
|
68
|
+
googleAistudio: { providers: [], urlMarkers: ["generativelanguage.googleapis.com"] },
|
|
69
|
+
opencode: { providers: ["opencode-go", "opencode-zen"], urlMarkers: ["opencode.ai"] },
|
|
70
|
+
/** ZenMux's Anthropic-compatible proxy (`zenmux.ai/api/anthropic`) forwards to signature-enforcing Anthropic. */
|
|
71
|
+
zenmux: { providers: ["zenmux"], urlMarkers: ["zenmux.ai"] },
|
|
72
|
+
chutes: { urlMarkers: ["chutes.ai"] },
|
|
73
|
+
} as const satisfies Record<string, HostClassSpec>;
|
|
74
|
+
|
|
75
|
+
export type KnownHost = keyof typeof KNOWN_HOSTS;
|
|
76
|
+
|
|
77
|
+
// Host checks fan out across every compatibility field for a model. Bound the
|
|
78
|
+
// cache because custom providers may contribute arbitrary endpoints at runtime.
|
|
79
|
+
const MAX_URL_HOST_MATCHES = 512;
|
|
80
|
+
const urlHostMatches = new Map<string, Map<KnownHost, boolean>>();
|
|
81
|
+
|
|
82
|
+
function getUrlHostMatches(baseUrl: string): Map<KnownHost, boolean> {
|
|
83
|
+
let matches = urlHostMatches.get(baseUrl);
|
|
84
|
+
if (matches !== undefined) return matches;
|
|
85
|
+
if (urlHostMatches.size === MAX_URL_HOST_MATCHES) urlHostMatches.clear();
|
|
86
|
+
matches = new Map<KnownHost, boolean>();
|
|
87
|
+
urlHostMatches.set(baseUrl, matches);
|
|
88
|
+
return matches;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** URL-only host check (for call sites that have no provider id, e.g. raw env config). */
|
|
92
|
+
export function hostMatchesUrl(baseUrl: string | undefined, host: KnownHost): boolean {
|
|
93
|
+
if (!baseUrl) return false;
|
|
94
|
+
const matches = getUrlHostMatches(baseUrl);
|
|
95
|
+
const cached = matches.get(host);
|
|
96
|
+
if (cached !== undefined) return cached;
|
|
97
|
+
const spec: HostClassSpec = KNOWN_HOSTS[host];
|
|
98
|
+
for (const marker of spec.urlMarkers) {
|
|
99
|
+
if (includesAsciiCaseInsensitive(baseUrl, marker)) {
|
|
100
|
+
matches.set(host, true);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
matches.set(host, false);
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Provider-or-URL host check — the canonical `provider === id || baseUrl.includes(marker)` idiom. */
|
|
109
|
+
export function modelMatchesHost(model: { provider: string; baseUrl: string }, host: KnownHost): boolean {
|
|
110
|
+
const spec: HostClassSpec = KNOWN_HOSTS[host];
|
|
111
|
+
if (spec.providers) {
|
|
112
|
+
for (const provider of spec.providers) {
|
|
113
|
+
if (model.provider === provider) return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (spec.providerPrefixes) {
|
|
117
|
+
for (const prefix of spec.providerPrefixes) {
|
|
118
|
+
if (model.provider.startsWith(prefix)) return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return hostMatchesUrl(model.baseUrl, host);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function includesAsciiCaseInsensitive(value: string, lowerNeedle: string): boolean {
|
|
125
|
+
const needleLength = lowerNeedle.length;
|
|
126
|
+
const end = value.length - needleLength;
|
|
127
|
+
for (let start = 0; start <= end; start++) {
|
|
128
|
+
let offset = 0;
|
|
129
|
+
for (; offset < needleLength; offset++) {
|
|
130
|
+
if ((value.charCodeAt(start + offset) | 0x20) !== lowerNeedle.charCodeAt(offset)) break;
|
|
131
|
+
}
|
|
132
|
+
if (offset === needleLength) return true;
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// --- Endpoint-shape predicates (URL path/verb shapes, not vendor hosts) ---
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Hostname for a Vertex AI GenerateContent / rawPredict / OpenAI-compat
|
|
141
|
+
* request for the given location.
|
|
142
|
+
*
|
|
143
|
+
* - `global` → global endpoint (`aiplatform.googleapis.com`)
|
|
144
|
+
* - `eu` / `us` multi-regions → REP endpoints (`aiplatform.{eu|us}.rep.googleapis.com`)
|
|
145
|
+
* - every other location → regional (`{location}-aiplatform.googleapis.com`)
|
|
146
|
+
*
|
|
147
|
+
* Multi-region codes do NOT follow the regional `{location}-aiplatform` pattern;
|
|
148
|
+
* interpolating them that way yields hosts like `eu-aiplatform.googleapis.com`
|
|
149
|
+
* that 404.
|
|
150
|
+
*/
|
|
151
|
+
export function resolveVertexEndpointHost(location: string): string {
|
|
152
|
+
if (location === "global") return "aiplatform.googleapis.com";
|
|
153
|
+
if (location === "eu" || location === "us") return `aiplatform.${location}.rep.googleapis.com`;
|
|
154
|
+
return `${location}-aiplatform.googleapis.com`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Vertex AI express-mode OpenAI-compatible endpoint (`…/endpoints/openapi`). */
|
|
158
|
+
export function isVertexExpressOpenAIUrl(baseUrl: string): boolean {
|
|
159
|
+
return baseUrl.includes("/endpoints/openapi");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Vertex AI Anthropic raw-predict endpoints (`:streamRawPredict` / `:rawPredict`). */
|
|
163
|
+
export function isVertexRawPredictUrl(baseUrl: string): boolean {
|
|
164
|
+
return baseUrl.includes(":streamRawPredict") || baseUrl.includes(":rawPredict");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Azure OpenAI deployment-scoped path (`…/deployments/<name>/…`). */
|
|
168
|
+
export function isAzureDeploymentsUrl(baseUrl: string): boolean {
|
|
169
|
+
return baseUrl.includes("/deployments/");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Alibaba DashScope consumer `compatible-mode` endpoint (rejects multimodal arrays for some text-only SKUs). */
|
|
173
|
+
export function isDashscopeCompatibleModeUrl(baseUrl: string): boolean {
|
|
174
|
+
const normalized = baseUrl.toLowerCase();
|
|
175
|
+
return (
|
|
176
|
+
normalized.includes("dashscope") && normalized.includes("aliyuncs.com") && normalized.includes("/compatible-mode")
|
|
177
|
+
);
|
|
178
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memoized proxy-reference index over the bundled model catalog.
|
|
3
|
+
*
|
|
4
|
+
* Lazy: walking every bundled model (~12K) triggers thinking enrichment, so the
|
|
5
|
+
* walk is deferred off module load and performed once. Consumers that need
|
|
6
|
+
* non-bundled reference data use the pure builder directly
|
|
7
|
+
* ({@link buildModelReferenceIndex}).
|
|
8
|
+
*/
|
|
9
|
+
import { getBundledModels, getBundledProviders } from "../models";
|
|
10
|
+
import type { Api, Model } from "../types";
|
|
11
|
+
import { buildModelReferenceIndex, type ModelReferenceIndex } from "./reference";
|
|
12
|
+
|
|
13
|
+
let bundledModels: readonly Model<Api>[] | undefined;
|
|
14
|
+
|
|
15
|
+
function getBundledModelList(): readonly Model<Api>[] {
|
|
16
|
+
bundledModels ??= getBundledProviders().flatMap(
|
|
17
|
+
provider => getBundledModels(provider as Parameters<typeof getBundledModels>[0]) as Model<Api>[],
|
|
18
|
+
);
|
|
19
|
+
return bundledModels;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let referenceIndex: ModelReferenceIndex | undefined;
|
|
23
|
+
|
|
24
|
+
/** Proxy-reference index over the bundled catalog. */
|
|
25
|
+
export function getBundledModelReferenceIndex(): ModelReferenceIndex {
|
|
26
|
+
referenceIndex ??= buildModelReferenceIndex(getBundledModelList());
|
|
27
|
+
return referenceIndex;
|
|
28
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-id classification: parse a model id into its family (gemini / anthropic /
|
|
3
|
+
* openai), kind/variant, and version. This is the shared layer both catalog
|
|
4
|
+
* policy rules (`model-thinking.ts`) and downstream consumers build on —
|
|
5
|
+
* classification lives here, the rules that consume it stay with their domain.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type SemVer = {
|
|
9
|
+
major: number;
|
|
10
|
+
minor: number;
|
|
11
|
+
patch: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type GeminiKind = "pro" | "flash";
|
|
15
|
+
export type AnthropicKind = "opus" | "sonnet" | "fable" | "mythos";
|
|
16
|
+
export type OpenAIVariant = "base" | "codex" | "codex-max" | "codex-mini" | "codex-spark" | "mini" | "max" | "nano";
|
|
17
|
+
export type GlmVariant = "base" | "air" | "turbo" | "flash" | "flashx" | "preview";
|
|
18
|
+
|
|
19
|
+
export interface GeminiModel {
|
|
20
|
+
family: "gemini";
|
|
21
|
+
kind: GeminiKind;
|
|
22
|
+
version: SemVer;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface AnthropicModel {
|
|
26
|
+
family: "anthropic";
|
|
27
|
+
kind: AnthropicKind;
|
|
28
|
+
version: SemVer;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface OpenAIModel {
|
|
32
|
+
family: "openai";
|
|
33
|
+
variant: OpenAIVariant;
|
|
34
|
+
version: SemVer;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface GlmModel {
|
|
38
|
+
family: "glm";
|
|
39
|
+
/** Suffix variant (`-air`, `-turbo`, `-flash`, `-flashx`, `-preview`); `base` when none. */
|
|
40
|
+
variant: GlmVariant;
|
|
41
|
+
/** Vision SKU — the `v` that attaches directly to the version (`glm-4v`, `glm-4.5v`). */
|
|
42
|
+
vision: boolean;
|
|
43
|
+
version: SemVer;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface UnknownModel {
|
|
47
|
+
family: "unknown";
|
|
48
|
+
id: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type ParsedModel = GeminiModel | AnthropicModel | OpenAIModel | UnknownModel;
|
|
52
|
+
|
|
53
|
+
/** Strip a provider namespace prefix (`openai/gpt-5.4` → `gpt-5.4`). */
|
|
54
|
+
// Cache keyed by model id (a bounded set of bundled/aggregator ids), so no eviction is needed.
|
|
55
|
+
const bareModelIdCache = new Map<string, string>();
|
|
56
|
+
export function bareModelId(modelId: string): string {
|
|
57
|
+
const cached = bareModelIdCache.get(modelId);
|
|
58
|
+
if (cached !== undefined) return cached;
|
|
59
|
+
const p = modelId.lastIndexOf("/");
|
|
60
|
+
const result = p !== -1 ? modelId.slice(p + 1) : modelId;
|
|
61
|
+
bareModelIdCache.set(modelId, result);
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function parseKnownModel(modelId: string): ParsedModel {
|
|
66
|
+
const canonicalId = bareModelId(modelId);
|
|
67
|
+
return (
|
|
68
|
+
parseGeminiModel(canonicalId) ??
|
|
69
|
+
parseAnthropicModel(canonicalId) ??
|
|
70
|
+
parseOpenAIModel(canonicalId) ?? { family: "unknown", id: canonicalId }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Wrap a parse function in a per-id memo cache. Caches the `null` result too, so
|
|
76
|
+
* repeated misses (the common case — ids of other families) stay O(1) and never
|
|
77
|
+
* re-run the regex/semver work.
|
|
78
|
+
*/
|
|
79
|
+
function parser<T>(parse: (modelId: string) => T | null): (modelId: string) => T | null {
|
|
80
|
+
const cache = new Map<string, T | null>();
|
|
81
|
+
return modelId => {
|
|
82
|
+
const hit = cache.get(modelId);
|
|
83
|
+
if (hit !== undefined || cache.has(modelId)) {
|
|
84
|
+
return hit ?? null;
|
|
85
|
+
}
|
|
86
|
+
const result = parse(modelId);
|
|
87
|
+
cache.set(modelId, result);
|
|
88
|
+
return result;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const GEMINI_SUFFIX = "-preview";
|
|
93
|
+
export const parseGeminiModel = parser((modelId): GeminiModel | null => {
|
|
94
|
+
if (modelId.endsWith(GEMINI_SUFFIX)) {
|
|
95
|
+
modelId = modelId.slice(0, -GEMINI_SUFFIX.length);
|
|
96
|
+
}
|
|
97
|
+
const match = /gemini-(\d+(?:\.\d+){0,2})-(pro|flash)\b/.exec(modelId);
|
|
98
|
+
if (!match) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
const version = parseSemVer(match[1]);
|
|
102
|
+
if (!version) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
return { family: "gemini", kind: match[2] as GeminiKind, version };
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export const parseAnthropicModel = parser((modelId): AnthropicModel | null => {
|
|
109
|
+
const kindFirst = /claude-(opus|sonnet|fable|mythos)-(\d{1,2}(?:[.-]\d{1,2}){0,2})\b/.exec(modelId);
|
|
110
|
+
const versionFirst = kindFirst
|
|
111
|
+
? null
|
|
112
|
+
: /claude-(\d{1,2}(?:[.-]\d{1,2}){0,2})-(opus|sonnet|fable|mythos)\b/.exec(modelId);
|
|
113
|
+
const kind = kindFirst?.[1] ?? versionFirst?.[2];
|
|
114
|
+
const versionInput = kindFirst?.[2] ?? versionFirst?.[1];
|
|
115
|
+
if (!kind || !versionInput) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const version = parseSemVer(versionInput);
|
|
119
|
+
if (!version) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return { family: "anthropic", kind: kind as AnthropicKind, version };
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Rolling OpenAI aliases inherit wire capabilities from their current default
|
|
127
|
+
* snapshots. Keep this map aligned with the model docs when an alias advances.
|
|
128
|
+
*/
|
|
129
|
+
const OPENAI_ALIAS_VERSIONS: Readonly<Record<string, string>> = {
|
|
130
|
+
"daybreak-blue-latest": "5.6",
|
|
131
|
+
"gpt-daybreak-blue-latest": "5.6",
|
|
132
|
+
"daybreak-red-latest": "5.6",
|
|
133
|
+
"gpt-daybreak-red-latest": "5.6",
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const parseOpenAIModel = parser((modelId): OpenAIModel | null => {
|
|
137
|
+
const aliasVersion = OPENAI_ALIAS_VERSIONS[modelId];
|
|
138
|
+
const match = aliasVersion
|
|
139
|
+
? null
|
|
140
|
+
: /gpt-(\d+(?:\.\d+){0,2})(?:-(codex-spark|codex-mini|codex-max|codex|mini|max|nano))?\b/.exec(modelId);
|
|
141
|
+
const versionInput = aliasVersion ?? match?.[1];
|
|
142
|
+
if (!versionInput) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const version = parseSemVer(versionInput);
|
|
146
|
+
if (!version) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return { family: "openai", variant: (match?.[2] as OpenAIVariant | undefined) ?? "base", version };
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Parse a GLM (Zhipu / Z.AI) model id into family + variant + vision + version.
|
|
154
|
+
* Shape: `glm-<version>[v][-<variant>]` — e.g. `glm-4.5`, `glm-4.5-air`,
|
|
155
|
+
* `glm-5-turbo`, `glm-4.5v`, `glm-5-preview`. The `v` (vision) attaches to the
|
|
156
|
+
* version; other variants are `-` suffixes. Standalone like `parseAnthropicModel`
|
|
157
|
+
* is used in family.ts — GLM needs no global thinking policy, so it stays out of
|
|
158
|
+
* `parseKnownModel`.
|
|
159
|
+
*/
|
|
160
|
+
export const parseGlmModel = parser((modelId): GlmModel | null => {
|
|
161
|
+
const match = /glm-(\d{1,2}(?:\.\d+)?)(v)?(?:-(air|turbo|flashx|flash|preview))?\b/i.exec(modelId);
|
|
162
|
+
if (!match) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
const version = parseSemVer(match[1]);
|
|
166
|
+
if (!version) {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
family: "glm",
|
|
171
|
+
variant: (match[3]?.toLowerCase() as GlmVariant | undefined) ?? "base",
|
|
172
|
+
vision: match[2]?.toLowerCase() === "v",
|
|
173
|
+
version,
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
export function isFableOrMythos(kind: AnthropicKind): boolean {
|
|
178
|
+
return kind === "fable" || kind === "mythos";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Returns true if the parsed Anthropic model is part of the adaptive-thinking
|
|
183
|
+
* Claude generation at or above a specific capability threshold.
|
|
184
|
+
* - Opus has a configurable minimum version floor (e.g. "4.6", "4.7", "4.8").
|
|
185
|
+
* - Sonnet, Fable, and Mythos all require version 5 or higher.
|
|
186
|
+
*/
|
|
187
|
+
export function isAnthropicAdaptiveGenAtLeast(parsed: AnthropicModel, opusMin: "4.6" | "4.7" | "4.8"): boolean {
|
|
188
|
+
if (parsed.kind === "opus") {
|
|
189
|
+
return semverGte(parsed.version, opusMin);
|
|
190
|
+
}
|
|
191
|
+
// Sonnet 5+, Fable 5+, Mythos 5+, and any future gen-5+ models
|
|
192
|
+
return semverGte(parsed.version, "5");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function createSemVer(major: number, minor: number, patch = 0): SemVer {
|
|
196
|
+
return { major, minor, patch };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Fast path for the common 1–2 component versions; anything the table misses
|
|
200
|
+
// (large minors, 3-part versions) parses dynamically below so no future
|
|
201
|
+
// version ever classifies as unknown (the failure class #8256 fixed).
|
|
202
|
+
const precomputeTable: Record<string, SemVer> = {};
|
|
203
|
+
for (let major = 0; major <= 9; major++) {
|
|
204
|
+
for (let minor = 0; minor <= 10; minor++) {
|
|
205
|
+
const version = createSemVer(major, minor, 0);
|
|
206
|
+
precomputeTable[`${major}.${minor}`] = version;
|
|
207
|
+
precomputeTable[`${major}-${minor}`] = version;
|
|
208
|
+
}
|
|
209
|
+
precomputeTable[`${major}`] = createSemVer(major, 0, 0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const SEMVER_PATTERN = /^(\d{1,2})(?:[.-](\d{1,2}))?(?:[.-](\d{1,2}))?$/;
|
|
213
|
+
|
|
214
|
+
export function parseSemVer(version: string): SemVer | null {
|
|
215
|
+
const hit = precomputeTable[version];
|
|
216
|
+
if (hit) return hit;
|
|
217
|
+
const match = SEMVER_PATTERN.exec(version);
|
|
218
|
+
if (!match) return null;
|
|
219
|
+
return createSemVer(Number(match[1]), Number(match[2] ?? 0), Number(match[3] ?? 0));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function semverGte(left: SemVer | string, right: SemVer | string): boolean {
|
|
223
|
+
return compareSemVer(left, right) >= 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function semverEqual(left: SemVer | string, right: SemVer | string): boolean {
|
|
227
|
+
return compareSemVer(left, right) === 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function compareSemVer(left: SemVer | string | null, right: SemVer | string | null): number {
|
|
231
|
+
left = typeof left === "string" ? parseSemVer(left) : left;
|
|
232
|
+
right = typeof right === "string" ? parseSemVer(right) : right;
|
|
233
|
+
if (!left || !right) return (left ? 1 : 0) - (right ? 1 : 0);
|
|
234
|
+
|
|
235
|
+
if (left.major !== right.major) {
|
|
236
|
+
return left.major - right.major;
|
|
237
|
+
}
|
|
238
|
+
if (left.minor !== right.minor) {
|
|
239
|
+
return left.minor - right.minor;
|
|
240
|
+
}
|
|
241
|
+
return left.patch - right.patch;
|
|
242
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { modelFamilyToken } from "./family";
|
|
2
|
+
|
|
3
|
+
export type Dialect =
|
|
4
|
+
| "glm"
|
|
5
|
+
| "hermes"
|
|
6
|
+
| "kimi"
|
|
7
|
+
| "xml"
|
|
8
|
+
| "anthropic"
|
|
9
|
+
| "deepseek"
|
|
10
|
+
| "harmony"
|
|
11
|
+
| "qwen3"
|
|
12
|
+
| "gemini"
|
|
13
|
+
| "gemma"
|
|
14
|
+
| "minimax";
|
|
15
|
+
|
|
16
|
+
export const FALLBACK_DIALECT: Dialect = "xml";
|
|
17
|
+
|
|
18
|
+
export function preferredDialect(modelId: string): Dialect {
|
|
19
|
+
switch (modelFamilyToken(modelId)) {
|
|
20
|
+
case "anthropic":
|
|
21
|
+
return "anthropic";
|
|
22
|
+
case "glm":
|
|
23
|
+
return "glm";
|
|
24
|
+
case "gemini":
|
|
25
|
+
return "gemini";
|
|
26
|
+
case "gemma":
|
|
27
|
+
return "gemma";
|
|
28
|
+
case "kimi":
|
|
29
|
+
return "kimi";
|
|
30
|
+
case "qwen":
|
|
31
|
+
return "qwen3";
|
|
32
|
+
case "deepseek":
|
|
33
|
+
return "deepseek";
|
|
34
|
+
case "minimax":
|
|
35
|
+
return "minimax";
|
|
36
|
+
case "openai":
|
|
37
|
+
case "gpt-oss":
|
|
38
|
+
return "harmony";
|
|
39
|
+
default:
|
|
40
|
+
return FALLBACK_DIALECT;
|
|
41
|
+
}
|
|
42
|
+
}
|