@linxiraos/pi-catalog 1.1.7 → 1.1.9
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 +7 -8
- package/README.md +9 -8
- package/package.json +96 -100
- package/src/build.ts +165 -48
- package/src/cline-pass-model-id.ts +11 -0
- package/src/compat/anthropic.ts +15 -136
- package/src/compat/auth-ids.ts +157 -0
- package/src/compat/auth.ts +60 -0
- package/src/compat/axes.ts +301 -0
- package/src/compat/behavior.ts +207 -0
- package/src/compat/cascade.ts +230 -0
- package/src/{variant-collapse.ts → compat/collapse.ts} +381 -773
- package/src/compat/delegation.ts +37 -0
- package/src/compat/openai.ts +17 -848
- package/src/compat/resolve.ts +1231 -0
- package/src/compat/revision.ts +154 -0
- package/src/compat/rules/README.md +315 -0
- package/src/compat/rules/auth/_order.kdl +13 -0
- package/src/compat/rules/auth/abliteration.kdl +9 -0
- package/src/compat/rules/auth/aiand.kdl +9 -0
- package/src/compat/rules/auth/aimlapi.kdl +3 -0
- package/src/compat/rules/auth/alibaba-coding-plan.kdl +5 -0
- package/src/compat/rules/auth/alibaba-token-plan.kdl +11 -0
- package/src/compat/rules/auth/amazon-bedrock.kdl +5 -0
- package/src/compat/rules/auth/anthropic.kdl +53 -0
- package/src/compat/rules/auth/azure.kdl +3 -0
- package/src/compat/rules/auth/baseten.kdl +9 -0
- package/src/compat/rules/auth/bedrock-mantle.kdl +5 -0
- package/src/compat/rules/auth/cerebras.kdl +9 -0
- package/src/compat/rules/auth/cline-pass.kdl +12 -0
- package/src/compat/rules/auth/cloudflare-ai-gateway.kdl +4 -0
- package/src/compat/rules/auth/coreweave.kdl +9 -0
- package/src/compat/rules/auth/cursor.kdl +5 -0
- package/src/compat/rules/auth/deepinfra.kdl +11 -0
- package/src/compat/rules/auth/deepseek.kdl +10 -0
- package/src/compat/rules/auth/devin.kdl +28 -0
- package/src/compat/rules/auth/exa.kdl +9 -0
- package/src/compat/rules/auth/firepass.kdl +14 -0
- package/src/compat/rules/auth/fireworks.kdl +16 -0
- package/src/compat/rules/auth/github-copilot.kdl +7 -0
- package/src/compat/rules/auth/gitlab-duo-agent.kdl +23 -0
- package/src/compat/rules/auth/gitlab-duo.kdl +24 -0
- package/src/compat/rules/auth/gmi-cloud.kdl +9 -0
- package/src/compat/rules/auth/google-antigravity.kdl +28 -0
- package/src/compat/rules/auth/google-gemini-cli.kdl +26 -0
- package/src/compat/rules/auth/google-vertex.kdl +5 -0
- package/src/compat/rules/auth/google.kdl +3 -0
- package/src/compat/rules/auth/groq.kdl +3 -0
- package/src/compat/rules/auth/huggingface.kdl +9 -0
- package/src/compat/rules/auth/kagi.kdl +9 -0
- package/src/compat/rules/auth/kilo.kdl +4 -0
- package/src/compat/rules/auth/kimi-code.kdl +18 -0
- package/src/compat/rules/auth/litellm.kdl +8 -0
- package/src/compat/rules/auth/llama.cpp.kdl +10 -0
- package/src/compat/rules/auth/lm-studio.kdl +7 -0
- package/src/compat/rules/auth/meta.kdl +9 -0
- package/src/compat/rules/auth/minimax-code-cn.kdl +9 -0
- package/src/compat/rules/auth/minimax-code.kdl +9 -0
- package/src/compat/rules/auth/minimax.kdl +3 -0
- package/src/compat/rules/auth/mistral.kdl +3 -0
- package/src/compat/rules/auth/moonshot.kdl +9 -0
- package/src/compat/rules/auth/nanogpt.kdl +9 -0
- package/src/compat/rules/auth/novita.kdl +12 -0
- package/src/compat/rules/auth/nvidia.kdl +11 -0
- package/src/compat/rules/auth/ollama-cloud.kdl +8 -0
- package/src/compat/rules/auth/ollama.kdl +9 -0
- package/src/compat/rules/auth/openai-codex-device.kdl +20 -0
- package/src/compat/rules/auth/openai-codex.kdl +33 -0
- package/src/compat/rules/auth/openai.kdl +3 -0
- package/src/compat/rules/auth/opencode-go.kdl +10 -0
- package/src/compat/rules/auth/opencode-zen.kdl +9 -0
- package/src/compat/rules/auth/openrouter.kdl +32 -0
- package/src/compat/rules/auth/parallel.kdl +9 -0
- package/src/compat/rules/auth/perplexity.kdl +7 -0
- package/src/compat/rules/auth/qianfan.kdl +9 -0
- package/src/compat/rules/auth/qwen-portal.kdl +9 -0
- package/src/compat/rules/auth/sakana.kdl +9 -0
- package/src/compat/rules/auth/siliconflow-cn.kdl +9 -0
- package/src/compat/rules/auth/siliconflow.kdl +9 -0
- package/src/compat/rules/auth/synthetic.kdl +9 -0
- package/src/compat/rules/auth/tavily.kdl +9 -0
- package/src/compat/rules/auth/together.kdl +14 -0
- package/src/compat/rules/auth/umans.kdl +9 -0
- package/src/compat/rules/auth/venice.kdl +9 -0
- package/src/compat/rules/auth/vercel-ai-gateway.kdl +8 -0
- package/src/compat/rules/auth/vllm.kdl +9 -0
- package/src/compat/rules/auth/wafer-serverless.kdl +11 -0
- package/src/compat/rules/auth/xai-oauth.kdl +27 -0
- package/src/compat/rules/auth/xai.kdl +9 -0
- package/src/compat/rules/auth/xiaomi-token-plan-ams.kdl +9 -0
- package/src/compat/rules/auth/xiaomi-token-plan-cn.kdl +9 -0
- package/src/compat/rules/auth/xiaomi-token-plan-sgp.kdl +9 -0
- package/src/compat/rules/auth/xiaomi.kdl +6 -0
- package/src/compat/rules/auth/yolo-auto.kdl +11 -0
- package/src/compat/rules/auth/zai-coding-plan.kdl +33 -0
- package/src/compat/rules/auth/zai.kdl +9 -0
- package/src/compat/rules/auth/zenmux.kdl +9 -0
- package/src/compat/rules/auth/zhipu-coding-plan.kdl +9 -0
- package/src/compat/rules/classes/amazon.kdl +21 -0
- package/src/compat/rules/classes/anthropic.kdl +285 -0
- package/src/compat/rules/classes/baidu.kdl +9 -0
- package/src/compat/rules/classes/bytedance.kdl +9 -0
- package/src/compat/rules/classes/cohere.kdl +7 -0
- package/src/compat/rules/classes/deepseek.kdl +80 -0
- package/src/compat/rules/classes/gemini.kdl +107 -0
- package/src/compat/rules/classes/gemma.kdl +14 -0
- package/src/compat/rules/classes/glm.kdl +115 -0
- package/src/compat/rules/classes/gpt-oss.kdl +16 -0
- package/src/compat/rules/classes/kimi.kdl +113 -0
- package/src/compat/rules/classes/meta.kdl +15 -0
- package/src/compat/rules/classes/mimo.kdl +32 -0
- package/src/compat/rules/classes/minimax.kdl +79 -0
- package/src/compat/rules/classes/mistral.kdl +19 -0
- package/src/compat/rules/classes/openai.kdl +58 -0
- package/src/compat/rules/classes/qwen.kdl +72 -0
- package/src/compat/rules/classes/stepfun.kdl +10 -0
- package/src/compat/rules/classes/xai.kdl +45 -0
- package/src/compat/rules/providers/abliteration.kdl +8 -0
- package/src/compat/rules/providers/aiand.kdl +19 -0
- package/src/compat/rules/providers/aimlapi.kdl +24 -0
- package/src/compat/rules/providers/alibaba-coding-plan.kdl +20 -0
- package/src/compat/rules/providers/alibaba-token-plan.kdl +35 -0
- package/src/compat/rules/providers/amazon-bedrock.kdl +36 -0
- package/src/compat/rules/providers/anthropic.kdl +24 -0
- package/src/compat/rules/providers/azure.kdl +19 -0
- package/src/compat/rules/providers/baseten.kdl +19 -0
- package/src/compat/rules/providers/bedrock-mantle.kdl +15 -0
- package/src/compat/rules/providers/cerebras.kdl +19 -0
- package/src/compat/rules/providers/cline-pass.kdl +8 -0
- package/src/compat/rules/providers/cloudflare-ai-gateway.kdl +26 -0
- package/src/compat/rules/providers/coreweave.kdl +16 -0
- package/src/compat/rules/providers/cursor.kdl +52 -0
- package/src/compat/rules/providers/deepseek.kdl +21 -0
- package/src/compat/rules/providers/firepass.kdl +15 -0
- package/src/compat/rules/providers/fireworks.kdl +43 -0
- package/src/compat/rules/providers/github-copilot.kdl +62 -0
- package/src/compat/rules/providers/gitlab-duo.kdl +24 -0
- package/src/compat/rules/providers/gmi-cloud.kdl +8 -0
- package/src/compat/rules/providers/google-antigravity.kdl +59 -0
- package/src/compat/rules/providers/google-gemini-cli.kdl +12 -0
- package/src/compat/rules/providers/google-vertex.kdl +25 -0
- package/src/compat/rules/providers/google.kdl +17 -0
- package/src/compat/rules/providers/groq.kdl +8 -0
- package/src/compat/rules/providers/huggingface.kdl +24 -0
- package/src/compat/rules/providers/kilo.kdl +51 -0
- package/src/compat/rules/providers/kimi-code.kdl +37 -0
- package/src/compat/rules/providers/llama.cpp.kdl +6 -0
- package/src/compat/rules/providers/lm-studio.kdl +6 -0
- package/src/compat/rules/providers/meta.kdl +10 -0
- package/src/compat/rules/providers/minimax-code-cn.kdl +13 -0
- package/src/compat/rules/providers/minimax-code.kdl +13 -0
- package/src/compat/rules/providers/minimax.kdl +6 -0
- package/src/compat/rules/providers/mistral.kdl +8 -0
- package/src/compat/rules/providers/moonshot.kdl +36 -0
- package/src/compat/rules/providers/nanogpt.kdl +100 -0
- package/src/compat/rules/providers/novita.kdl +34 -0
- package/src/compat/rules/providers/nvidia.kdl +27 -0
- package/src/compat/rules/providers/ollama-cloud.kdl +20 -0
- package/src/compat/rules/providers/ollama.kdl +12 -0
- package/src/compat/rules/providers/openai-codex.kdl +121 -0
- package/src/compat/rules/providers/openai.kdl +82 -0
- package/src/compat/rules/providers/opencode-go.kdl +97 -0
- package/src/compat/rules/providers/opencode-zen.kdl +103 -0
- package/src/compat/rules/providers/openrouter.kdl +66 -0
- package/src/compat/rules/providers/qianfan.kdl +8 -0
- package/src/compat/rules/providers/sakana.kdl +10 -0
- package/src/compat/rules/providers/synthetic.kdl +16 -0
- package/src/compat/rules/providers/together.kdl +22 -0
- package/src/compat/rules/providers/umans.kdl +28 -0
- package/src/compat/rules/providers/venice.kdl +46 -0
- package/src/compat/rules/providers/vercel-ai-gateway.kdl +73 -0
- package/src/compat/rules/providers/vllm.kdl +8 -0
- package/src/compat/rules/providers/wafer-serverless.kdl +35 -0
- package/src/compat/rules/providers/xai-oauth.kdl +48 -0
- package/src/compat/rules/providers/xai.kdl +50 -0
- package/src/compat/rules/providers/xiaomi-token-plan-ams.kdl +9 -0
- package/src/compat/rules/providers/xiaomi-token-plan-cn.kdl +9 -0
- package/src/compat/rules/providers/xiaomi-token-plan-sgp.kdl +9 -0
- package/src/compat/rules/providers/xiaomi.kdl +22 -0
- package/src/compat/rules/providers/yolo-auto.kdl +16 -0
- package/src/compat/rules/providers/zai.kdl +36 -0
- package/src/compat/rules/providers/zenmux.kdl +108 -0
- package/src/compat/rules/providers/zhipu-coding-plan.kdl +49 -0
- package/src/compat/rules/runtime/behavior.kdl +254 -0
- package/src/compat/rules/taxonomy/_collapse.kdl +747 -0
- package/src/compat/rules/taxonomy/_discovery.kdl +60 -0
- package/src/compat/rules/taxonomy/ai21.kdl +6 -0
- package/src/compat/rules/taxonomy/amazon.kdl +8 -0
- package/src/compat/rules/taxonomy/anthropic.kdl +17 -0
- package/src/compat/rules/taxonomy/baidu.kdl +5 -0
- package/src/compat/rules/taxonomy/bytedance.kdl +5 -0
- package/src/compat/rules/taxonomy/cohere.kdl +6 -0
- package/src/compat/rules/taxonomy/deepseek.kdl +18 -0
- package/src/compat/rules/taxonomy/gemini.kdl +9 -0
- package/src/compat/rules/taxonomy/gemma.kdl +3 -0
- package/src/compat/rules/taxonomy/glm.kdl +17 -0
- package/src/compat/rules/taxonomy/gpt-oss.kdl +11 -0
- package/src/compat/rules/taxonomy/kimi.kdl +20 -0
- package/src/compat/rules/taxonomy/meta.kdl +10 -0
- package/src/compat/rules/taxonomy/mimo.kdl +7 -0
- package/src/compat/rules/taxonomy/minimax.kdl +9 -0
- package/src/compat/rules/taxonomy/mistral.kdl +7 -0
- package/src/compat/rules/taxonomy/openai.kdl +43 -0
- package/src/compat/rules/taxonomy/qwen.kdl +33 -0
- package/src/compat/rules/taxonomy/stepfun.kdl +5 -0
- package/src/compat/rules/taxonomy/unknown.kdl +3 -0
- package/src/compat/rules/taxonomy/xai.kdl +12 -0
- package/src/compat/rules.d.json.ts +5 -0
- package/src/compat/rules.json +16984 -0
- package/src/compat/taxonomy.ts +452 -0
- package/src/compat/types.ts +588 -0
- package/src/discovery/antigravity.ts +5 -6
- package/src/discovery/codex.ts +22 -7
- package/src/discovery/cursor-proto.ts +7 -7
- package/src/discovery/cursor.ts +47 -58
- package/src/discovery/devin-proto.ts +613 -0
- package/src/discovery/devin.ts +358 -52
- package/src/discovery/gemini-cli.ts +14 -11
- package/src/discovery/gemini.ts +2 -0
- package/src/identity/dialect.ts +2 -2
- package/src/identity/id.ts +12 -0
- package/src/identity/index.ts +3 -3
- package/src/identity/metrics.ts +165 -0
- package/src/identity/reference.ts +37 -5
- package/src/index.ts +1 -1
- package/src/model-manager.ts +19 -6
- package/src/model-thinking.ts +6 -810
- package/src/model-tokenizer.ts +37 -44
- package/src/models.json +89584 -22358
- package/src/models.ts +6 -1
- package/src/provider-models/bundled-references.ts +3 -1
- package/src/provider-models/cline-pass.ts +226 -0
- package/src/provider-models/descriptors.ts +18 -0
- package/src/provider-models/google.ts +14 -5
- package/src/provider-models/models-dev-policies.ts +4 -29
- package/src/provider-models/ollama.ts +15 -2
- package/src/provider-models/openai-compat.ts +798 -520
- package/src/provider-models/special.ts +211 -12
- package/src/types.ts +214 -11
- package/src/wire/cline-pass.ts +35 -0
- package/src/wire/codex.ts +23 -2
- package/src/wire/devin-proto.ts +30 -0
- package/src/wire/devin.ts +72 -0
- package/src/wire/github-copilot.ts +49 -7
- package/THIRD-PARTY-NOTICES.txt +0 -22909
- package/dist/types/build.d.ts +0 -19
- package/dist/types/compat/anthropic.d.ts +0 -18
- package/dist/types/compat/apply.d.ts +0 -7
- package/dist/types/compat/bedrock.d.ts +0 -3
- package/dist/types/compat/devin.d.ts +0 -9
- package/dist/types/compat/openai.d.ts +0 -28
- package/dist/types/discovery/antigravity.d.ts +0 -70
- package/dist/types/discovery/codex.d.ts +0 -36
- package/dist/types/discovery/cursor-proto.d.ts +0 -5368
- package/dist/types/discovery/cursor.d.ts +0 -29
- package/dist/types/discovery/devin-proto.d.ts +0 -1549
- package/dist/types/discovery/devin.d.ts +0 -24
- package/dist/types/discovery/gemini-cli.d.ts +0 -33
- package/dist/types/discovery/gemini.d.ts +0 -25
- package/dist/types/discovery/gitlab-duo-workflow.d.ts +0 -30
- package/dist/types/discovery/index.d.ts +0 -7
- package/dist/types/discovery/openai-compatible.d.ts +0 -89
- package/dist/types/discovery/protobuf.d.ts +0 -102
- package/dist/types/effort.d.ts +0 -10
- package/dist/types/fireworks-model-id.d.ts +0 -23
- package/dist/types/hosts.d.ts +0 -169
- package/dist/types/identity/bundled.d.ts +0 -3
- package/dist/types/identity/classify.d.ts +0 -69
- package/dist/types/identity/dialect.d.ts +0 -3
- package/dist/types/identity/family.d.ts +0 -243
- package/dist/types/identity/id.d.ts +0 -12
- package/dist/types/identity/index.d.ts +0 -8
- package/dist/types/identity/markers.d.ts +0 -4
- package/dist/types/identity/priority.d.ts +0 -1
- package/dist/types/identity/reference.d.ts +0 -29
- package/dist/types/index.d.ts +0 -19
- package/dist/types/model-cache.d.ts +0 -23
- package/dist/types/model-manager.d.ts +0 -93
- package/dist/types/model-thinking.d.ts +0 -81
- package/dist/types/model-tokenizer.d.ts +0 -8
- package/dist/types/models.d.ts +0 -17
- package/dist/types/openai-pricing.d.ts +0 -17
- package/dist/types/provider-models/bundled-references.d.ts +0 -13
- package/dist/types/provider-models/cache-provider-id.d.ts +0 -11
- package/dist/types/provider-models/descriptor-types.d.ts +0 -76
- package/dist/types/provider-models/descriptors.d.ts +0 -524
- package/dist/types/provider-models/google.d.ts +0 -29
- package/dist/types/provider-models/index.d.ts +0 -7
- package/dist/types/provider-models/models-dev-policies.d.ts +0 -8
- package/dist/types/provider-models/ollama.d.ts +0 -22
- package/dist/types/provider-models/openai-compat.d.ts +0 -703
- package/dist/types/provider-models/special.d.ts +0 -53
- package/dist/types/types.d.ts +0 -879
- package/dist/types/utils.d.ts +0 -20
- package/dist/types/variant-collapse.d.ts +0 -146
- package/dist/types/wire/alibaba-token-plan.d.ts +0 -22
- package/dist/types/wire/cloudflare-ai-gateway.d.ts +0 -18
- package/dist/types/wire/codex.d.ts +0 -66
- package/dist/types/wire/coreweave.d.ts +0 -12
- package/dist/types/wire/gemini-headers.d.ts +0 -62
- package/dist/types/wire/github-copilot.d.ts +0 -47
- package/dist/types/wire/image-fetchers.d.ts +0 -76
- package/src/compat/bedrock.ts +0 -153
- package/src/compat/devin.ts +0 -12
- package/src/identity/classify.ts +0 -242
- package/src/identity/family.ts +0 -496
- package/src/identity/markers.ts +0 -49
- package/src/openai-pricing.ts +0 -29
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The closed compat-axis vocabulary: every KDL cascade directive, the resolved
|
|
3
|
+
* camelCase field it assigns, its value shape, and — for wire axes — the
|
|
4
|
+
* compat records it applies to.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth shared by the compile-time rule compiler
|
|
7
|
+
* (`scripts/compat-compiler`) and the runtime engine (`./resolve`). The
|
|
8
|
+
* compiler rejects any directive absent from this table; the runtime assigns
|
|
9
|
+
* a wire axis onto a model's compat record only when the model's API maps to
|
|
10
|
+
* one of the axis's declared records.
|
|
11
|
+
*/
|
|
12
|
+
import type { Effort } from "../effort";
|
|
13
|
+
import type { ThinkingControlMode } from "../types";
|
|
14
|
+
|
|
15
|
+
/** Value shape a directive accepts (see `rules/README.md`). */
|
|
16
|
+
export type AxisShape = "scalar" | "array" | "object";
|
|
17
|
+
|
|
18
|
+
/** Axis namespace: request-wire compat, thinking control surface, or catalog metadata. */
|
|
19
|
+
export type AxisSet = "wire" | "thinking" | "catalog";
|
|
20
|
+
|
|
21
|
+
/** Resolved compat record families a wire axis may be assigned onto. */
|
|
22
|
+
export type CompatRecordName = "openai" | "openai-responses" | "anthropic" | "bedrock" | "devin" | "google";
|
|
23
|
+
|
|
24
|
+
/** One axis definition: resolved key, namespace, shape, and applicability. */
|
|
25
|
+
export interface AxisDef {
|
|
26
|
+
/** Resolved camelCase field the directive assigns. */
|
|
27
|
+
key: string;
|
|
28
|
+
set: AxisSet;
|
|
29
|
+
shape: AxisShape;
|
|
30
|
+
/** Wire axes only: records this key exists on. */
|
|
31
|
+
records?: readonly CompatRecordName[];
|
|
32
|
+
/** Closed value vocabulary for string scalars / string arrays. */
|
|
33
|
+
values?: readonly string[];
|
|
34
|
+
/**
|
|
35
|
+
* Object axes only: payload child names are literal wire JSON keys copied
|
|
36
|
+
* verbatim (`extra-body`). Default object payloads author kebab-case names
|
|
37
|
+
* that compile to camelCase resolved keys.
|
|
38
|
+
*/
|
|
39
|
+
verbatimKeys?: true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const OAI = ["openai", "openai-responses"] as const;
|
|
43
|
+
const EFFORTS = ["minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
44
|
+
|
|
45
|
+
/** Effort tiers accepted by taxonomy collapse/override vocabulary (`Effort` ∪ `"off"`). */
|
|
46
|
+
export const EFFORT_TIERS: readonly string[] = [...EFFORTS, "off"];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* How hard agent prompts push subagent delegation for a model lineage
|
|
50
|
+
* (`delegation-bias` axis). `eager` is the unassigned default: the prompt
|
|
51
|
+
* actively pushes fan-out. `restrained` keeps fan-out for genuinely parallel
|
|
52
|
+
* slices but forbids reflexive scouts and single-agent babysitting. `gated`
|
|
53
|
+
* forbids subagents unless the user or a skill asks.
|
|
54
|
+
*/
|
|
55
|
+
export const DELEGATION_BIASES = ["eager", "restrained", "gated"] as const;
|
|
56
|
+
const THINKING_MODES = [
|
|
57
|
+
"effort",
|
|
58
|
+
"budget",
|
|
59
|
+
"google-level",
|
|
60
|
+
"anthropic-adaptive",
|
|
61
|
+
"anthropic-budget-effort",
|
|
62
|
+
] as const satisfies readonly ThinkingControlMode[];
|
|
63
|
+
|
|
64
|
+
/** Narrow a KDL string to an effort tier (`Effort` ∪ `"off"`). */
|
|
65
|
+
export function isEffortTier(value: string): value is Effort | "off" {
|
|
66
|
+
return EFFORT_TIERS.includes(value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Narrow a KDL string to a thinking control mode. */
|
|
70
|
+
export function isThinkingMode(value: string): value is ThinkingControlMode {
|
|
71
|
+
return (THINKING_MODES as readonly string[]).includes(value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function wire(
|
|
75
|
+
key: string,
|
|
76
|
+
records: readonly CompatRecordName[],
|
|
77
|
+
shape: AxisShape = "scalar",
|
|
78
|
+
values?: readonly string[],
|
|
79
|
+
): AxisDef {
|
|
80
|
+
return { key, set: "wire", shape, records, values };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* KDL directive → axis definition. Keys are the kebab-case directive
|
|
85
|
+
* spellings accepted inside `classes/*.kdl` and `providers/*.kdl` rule
|
|
86
|
+
* blocks.
|
|
87
|
+
*/
|
|
88
|
+
export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
89
|
+
// ── wire: OpenAI-compatible surfaces (chat completions + Responses) ──
|
|
90
|
+
"allows-synthetic-reasoning-content-for-tool-calls": wire("allowsSyntheticReasoningContentForToolCalls", OAI),
|
|
91
|
+
"always-send-max-tokens": wire("alwaysSendMaxTokens", OAI),
|
|
92
|
+
"cache-control-format": wire("cacheControlFormat", OAI, "scalar", ["anthropic"]),
|
|
93
|
+
"clamp-output-to-model-max": wire("clampOutputToModelMax", ["openai"]),
|
|
94
|
+
"disable-reasoning-on-forced-tool-choice": wire("disableReasoningOnForcedToolChoice", OAI),
|
|
95
|
+
"disable-reasoning-on-tool-choice": wire("disableReasoningOnToolChoice", OAI),
|
|
96
|
+
"drop-thinking-when-reasoning-effort": wire("dropThinkingWhenReasoningEffort", ["openai"]),
|
|
97
|
+
"empty-length-finish-is-context-error": wire("emptyLengthFinishIsContextError", OAI),
|
|
98
|
+
"extra-body": { ...wire("extraBody", ["openai"], "object"), verbatimKeys: true },
|
|
99
|
+
"filter-reasoning-history": wire("filterReasoningHistory", OAI),
|
|
100
|
+
"include-encrypted-reasoning": wire("includeEncryptedReasoning", OAI),
|
|
101
|
+
"kimi-api-format": wire("kimiApiFormat", ["openai"], "scalar", ["openai", "anthropic"]),
|
|
102
|
+
"max-tokens-field": wire("maxTokensField", ["openai"], "scalar", ["max_completion_tokens", "max_tokens"]),
|
|
103
|
+
"native-kimi-k3-reasoning": wire("nativeKimiK3Reasoning", ["openai"]),
|
|
104
|
+
"omit-reasoning-effort": wire("omitReasoningEffort", OAI),
|
|
105
|
+
"prompt-cache-breakpoint-ttl": wire("promptCacheBreakpointTtl", OAI, "scalar", ["30m"]),
|
|
106
|
+
"prompt-cache-session-header": wire("promptCacheSessionHeader", OAI, "scalar", ["x-grok-conv-id"]),
|
|
107
|
+
"qwen-preserve-thinking": wire("qwenPreserveThinking", ["openai"]),
|
|
108
|
+
"reject-root-object-union": wire("rejectRootObjectUnion", OAI),
|
|
109
|
+
"retry-without-strict-on-grammar-error": wire("retryWithoutStrictOnGrammarError", OAI),
|
|
110
|
+
"reasoning-content-field": wire("reasoningContentField", OAI, "scalar", [
|
|
111
|
+
"reasoning_content",
|
|
112
|
+
"reasoning",
|
|
113
|
+
"reasoning_text",
|
|
114
|
+
]),
|
|
115
|
+
"reasoning-deltas-may-be-cumulative": wire("reasoningDeltasMayBeCumulative", OAI),
|
|
116
|
+
"reasoning-disable-mode": wire("reasoningDisableMode", OAI, "scalar", [
|
|
117
|
+
"omit",
|
|
118
|
+
"lowest-effort",
|
|
119
|
+
"none-effort",
|
|
120
|
+
"openrouter-enabled-false",
|
|
121
|
+
"cline-enabled-false",
|
|
122
|
+
"venice-disable-thinking",
|
|
123
|
+
"zai-thinking-disabled",
|
|
124
|
+
"qwen-enable-thinking-false",
|
|
125
|
+
"qwen-template-false",
|
|
126
|
+
"chat-template-thinking-false",
|
|
127
|
+
]),
|
|
128
|
+
"reasoning-effort-map": wire("reasoningEffortMap", OAI, "object"),
|
|
129
|
+
"replay-reasoning-content": wire("replayReasoningContent", ["openai"]),
|
|
130
|
+
"requires-assistant-after-tool-result": wire("requiresAssistantAfterToolResult", ["openai"]),
|
|
131
|
+
"requires-assistant-content-for-tool-calls": wire("requiresAssistantContentForToolCalls", OAI),
|
|
132
|
+
"requires-mistral-tool-ids": wire("requiresMistralToolIds", ["openai"]),
|
|
133
|
+
"requires-reasoning-content-for-all-assistant-turns": wire("requiresReasoningContentForAllAssistantTurns", OAI),
|
|
134
|
+
"requires-reasoning-content-for-tool-calls": wire("requiresReasoningContentForToolCalls", OAI),
|
|
135
|
+
"requires-thinking-as-text": wire("requiresThinkingAsText", ["openai"]),
|
|
136
|
+
"requires-tool-result-name": wire("requiresToolResultName", ["openai"]),
|
|
137
|
+
"strict-responses-pairing": wire("strictResponsesPairing", ["openai-responses"]),
|
|
138
|
+
"requires-reasoning-off-juice-instruction": wire("requiresReasoningOffJuiceInstruction", ["openai-responses"]),
|
|
139
|
+
"supports-all-turns-reasoning-context": wire("supportsAllTurnsReasoningContext", ["openai-responses"]),
|
|
140
|
+
"supports-configuration-update": wire("supportsConfigurationUpdate", ["openai-responses"]),
|
|
141
|
+
"strip-deepseek-special-tokens": wire("stripDeepseekSpecialTokens", OAI),
|
|
142
|
+
"stream-markup-healing-pattern": wire("streamMarkupHealingPattern", OAI, "scalar", [
|
|
143
|
+
"kimi",
|
|
144
|
+
"dsml",
|
|
145
|
+
"qwen",
|
|
146
|
+
"thinking",
|
|
147
|
+
"harmony",
|
|
148
|
+
]),
|
|
149
|
+
"supports-developer-role": wire("supportsDeveloperRole", OAI),
|
|
150
|
+
"supports-image-detail-original": wire("supportsImageDetailOriginal", ["openai-responses"]),
|
|
151
|
+
"supports-long-prompt-cache-retention": wire("supportsLongPromptCacheRetention", [...OAI, "bedrock"]),
|
|
152
|
+
"supports-multiple-system-messages": wire("supportsMultipleSystemMessages", ["openai"]),
|
|
153
|
+
"supports-named-tool-choice": wire("supportsNamedToolChoice", OAI),
|
|
154
|
+
"supports-obfuscation-opt-out": wire("supportsObfuscationOptOut", ["openai-responses"]),
|
|
155
|
+
"harmony-leak-mitigation": wire("harmonyLeakMitigation", ["openai-responses"]),
|
|
156
|
+
"supports-penalty-and-stop-params": wire("supportsPenaltyAndStopParams", OAI),
|
|
157
|
+
"supports-prompt-cache-breakpoints": wire("supportsPromptCacheBreakpoints", OAI),
|
|
158
|
+
"supports-prompt-cache-key": wire("supportsPromptCacheKey", ["openai"]),
|
|
159
|
+
"supports-reasoning-effort": wire("supportsReasoningEffort", OAI),
|
|
160
|
+
"supports-reasoning-params": wire("supportsReasoningParams", OAI),
|
|
161
|
+
"supports-reasoning-summary": wire("supportsReasoningSummary", ["openai-responses"]),
|
|
162
|
+
"supports-store": wire("supportsStore", ["openai"]),
|
|
163
|
+
"supports-strict-mode": wire("supportsStrictMode", OAI),
|
|
164
|
+
"supports-tool-choice": wire("supportsToolChoice", OAI),
|
|
165
|
+
"supports-usage-in-streaming": wire("supportsUsageInStreaming", ["openai"]),
|
|
166
|
+
"template-reasoning-effort": wire("qwenTemplateReasoningEffort", ["openai"]),
|
|
167
|
+
"thinking-format": wire("thinkingFormat", OAI, "scalar", [
|
|
168
|
+
"openai",
|
|
169
|
+
"openrouter",
|
|
170
|
+
"zai",
|
|
171
|
+
"kimi",
|
|
172
|
+
"qwen",
|
|
173
|
+
"qwen-chat-template",
|
|
174
|
+
"chat-template",
|
|
175
|
+
]),
|
|
176
|
+
"thinking-keep": wire("thinkingKeep", ["openai"]),
|
|
177
|
+
"tool-schema-flavor": wire("toolSchemaFlavor", OAI, "scalar", ["moonshot-mfjs", "grammar", "none"]),
|
|
178
|
+
"tool-strict-mode": wire("toolStrictMode", ["openai"], "scalar", ["all_strict", "none", "mixed"]),
|
|
179
|
+
"uses-openai-tool-call-id-limit": wire("usesOpenAIToolCallIdLimit", OAI),
|
|
180
|
+
"when-thinking": wire("whenThinking", ["openai"], "object"),
|
|
181
|
+
"wire-model-id-mode": wire("wireModelIdMode", OAI, "scalar", [
|
|
182
|
+
"raw",
|
|
183
|
+
"cline-pass",
|
|
184
|
+
"firepass",
|
|
185
|
+
"fireworks",
|
|
186
|
+
"openrouter",
|
|
187
|
+
]),
|
|
188
|
+
"zai-reasoning-effort-dialect": wire("zaiReasoningEffortDialect", ["openai"]),
|
|
189
|
+
|
|
190
|
+
// ── wire: anthropic-messages ──
|
|
191
|
+
"allow-anthropic-header-overrides": wire("allowAnthropicHeaderOverrides", ["anthropic"]),
|
|
192
|
+
"disable-adaptive-thinking": wire("disableAdaptiveThinking", ["anthropic"]),
|
|
193
|
+
"disable-strict-tools": wire("disableStrictTools", ["anthropic"]),
|
|
194
|
+
"escape-builtin-tool-names": wire("escapeBuiltinToolNames", ["anthropic"]),
|
|
195
|
+
"inject-claude-code-instruction": wire("injectClaudeCodeInstruction", ["anthropic"]),
|
|
196
|
+
"official-endpoint": wire("officialEndpoint", ["anthropic", "openai-responses"]),
|
|
197
|
+
"replay-unsigned-thinking": wire("replayUnsignedThinking", ["anthropic"]),
|
|
198
|
+
"requires-thinking-enabled": wire("requiresThinkingEnabled", ["anthropic"]),
|
|
199
|
+
"requires-tool-result-id": wire("requiresToolResultId", ["anthropic"]),
|
|
200
|
+
"signing-endpoint": wire("signingEndpoint", ["anthropic"]),
|
|
201
|
+
"supports-context-management": wire("supportsContextManagement", ["anthropic"]),
|
|
202
|
+
"supports-output-effort": wire("supportsOutputEffort", ["anthropic"]),
|
|
203
|
+
"supports-eager-tool-input-streaming": wire("supportsEagerToolInputStreaming", ["anthropic"]),
|
|
204
|
+
"supports-long-cache-retention": wire("supportsLongCacheRetention", ["anthropic"]),
|
|
205
|
+
"supports-mid-conversation-system": wire("supportsMidConversationSystem", ["anthropic"]),
|
|
206
|
+
"supports-mid-conversation-tool-changes": wire("supportsMidConversationToolChanges", ["anthropic"]),
|
|
207
|
+
"supports-per-message-effort": wire("supportsPerMessageEffort", ["anthropic"]),
|
|
208
|
+
"supports-thinking-binding-controls": wire("supportsThinkingBindingControls", ["anthropic"]),
|
|
209
|
+
"supports-turn-scoped-system": wire("supportsTurnScopedSystem", ["anthropic"]),
|
|
210
|
+
|
|
211
|
+
// ── wire: bedrock-converse-stream ──
|
|
212
|
+
"prompt-cache-maximum-checkpoints": wire("promptCacheMaximumCheckpoints", ["bedrock"]),
|
|
213
|
+
"prompt-cache-minimum-tokens": wire("promptCacheMinimumTokens", ["bedrock"]),
|
|
214
|
+
"prompt-cache-mode": wire("promptCacheMode", ["bedrock"], "scalar", ["none", "automatic", "explicit"]),
|
|
215
|
+
|
|
216
|
+
// ── wire: devin-agent ──
|
|
217
|
+
"model-router": wire("modelRouter", ["devin"]),
|
|
218
|
+
"supports-parallel-tool-calls": wire("supportsParallelToolCalls", ["devin"]),
|
|
219
|
+
"trust-explicit-thinking-only": wire("trustExplicitThinkingOnly", ["devin"]),
|
|
220
|
+
|
|
221
|
+
// ── wire: google APIs ──
|
|
222
|
+
"antigravity-claude-tool-mode": wire("antigravityClaudeToolMode", ["google"]),
|
|
223
|
+
"antigravity-usage-label": wire("antigravityUsageLabel", ["google"]),
|
|
224
|
+
"cca-legacy-parameters-schema": wire("ccaLegacyParametersSchema", ["google"]),
|
|
225
|
+
"claude-thinking-beta-header": wire("claudeThinkingBetaHeader", ["google"]),
|
|
226
|
+
"drop-unsigned-thinking": wire("dropUnsignedThinking", ["google"]),
|
|
227
|
+
"flash-stream-leak-workaround": wire("flashStreamLeakWorkaround", ["google"]),
|
|
228
|
+
"multimodal-function-response": wire("multimodalFunctionResponse", ["google"]),
|
|
229
|
+
"requires-skip-thought-signature": wire("requiresSkipThoughtSignature", ["google"]),
|
|
230
|
+
"requires-skip-thought-signature-on-first-function-call": wire("requiresSkipThoughtSignatureOnFirstFunctionCall", [
|
|
231
|
+
"google",
|
|
232
|
+
]),
|
|
233
|
+
"supports-function-part-id": wire("supportsFunctionPartId", ["google"]),
|
|
234
|
+
|
|
235
|
+
// ── wire: shared across surfaces ──
|
|
236
|
+
"stream-first-event-timeout-ms": wire("streamFirstEventTimeoutMs", [...OAI, "google"]),
|
|
237
|
+
"stream-idle-timeout-ms": wire("streamIdleTimeoutMs", [...OAI, "anthropic", "bedrock", "google"]),
|
|
238
|
+
"strip-image-input": wire("stripImageInput", [...OAI, "anthropic", "google"]),
|
|
239
|
+
"supports-forced-tool-choice": wire("supportsForcedToolChoice", [...OAI, "anthropic"]),
|
|
240
|
+
"supports-sampling-params": wire("supportsSamplingParams", [...OAI, "anthropic"]),
|
|
241
|
+
"thinking-loop-guard": wire("thinkingLoopGuard", [...OAI, "anthropic", "google"], "scalar", [
|
|
242
|
+
"gemini",
|
|
243
|
+
"deepseek",
|
|
244
|
+
"xai",
|
|
245
|
+
]),
|
|
246
|
+
|
|
247
|
+
// ── thinking control surface ──
|
|
248
|
+
"thinking-default-level": { key: "defaultLevel", set: "thinking", shape: "scalar", values: EFFORTS },
|
|
249
|
+
"thinking-effort-budgets": { key: "effortBudgets", set: "thinking", shape: "object" },
|
|
250
|
+
"thinking-effort-map": { key: "effortMap", set: "thinking", shape: "object" },
|
|
251
|
+
"thinking-efforts": { key: "efforts", set: "thinking", shape: "array", values: EFFORTS },
|
|
252
|
+
"thinking-mode": {
|
|
253
|
+
key: "mode",
|
|
254
|
+
set: "thinking",
|
|
255
|
+
shape: "scalar",
|
|
256
|
+
values: THINKING_MODES,
|
|
257
|
+
},
|
|
258
|
+
"thinking-requires-effort": { key: "requiresEffort", set: "thinking", shape: "scalar" },
|
|
259
|
+
"thinking-prefix-binding": { key: "prefixBinding", set: "thinking", shape: "scalar" },
|
|
260
|
+
"thinking-suppress-when-off": { key: "suppressWhenOff", set: "thinking", shape: "scalar" },
|
|
261
|
+
"thinking-supports-display": { key: "supportsDisplay", set: "thinking", shape: "scalar" },
|
|
262
|
+
|
|
263
|
+
// ── catalog metadata ──
|
|
264
|
+
"apply-patch-tool-type": {
|
|
265
|
+
key: "applyPatchToolType",
|
|
266
|
+
set: "catalog",
|
|
267
|
+
shape: "scalar",
|
|
268
|
+
values: ["freeform", "function"],
|
|
269
|
+
},
|
|
270
|
+
"context-promotion-target": { key: "contextPromotionTarget", set: "catalog", shape: "scalar" },
|
|
271
|
+
"context-window-floor": { key: "contextWindowFloor", set: "catalog", shape: "scalar" },
|
|
272
|
+
"cost-patch": { key: "costPatch", set: "catalog", shape: "object" },
|
|
273
|
+
"delegation-bias": { key: "delegationBias", set: "catalog", shape: "scalar", values: DELEGATION_BIASES },
|
|
274
|
+
"edit-revision": { key: "editRevision", set: "catalog", shape: "scalar" },
|
|
275
|
+
"input-modalities": { key: "inputModalities", set: "catalog", shape: "array", values: ["text", "image"] },
|
|
276
|
+
"limits-patch": { key: "limitsPatch", set: "catalog", shape: "object" },
|
|
277
|
+
"long-context-cost": { key: "longContext", set: "catalog", shape: "object" },
|
|
278
|
+
"long-usage-limit-fallback": { key: "longUsageLimitFallback", set: "catalog", shape: "scalar" },
|
|
279
|
+
"requires-cursor-tool-schema-projection": {
|
|
280
|
+
key: "requiresCursorToolSchemaProjection",
|
|
281
|
+
set: "catalog",
|
|
282
|
+
shape: "scalar",
|
|
283
|
+
},
|
|
284
|
+
priority: { key: "priority", set: "catalog", shape: "scalar" },
|
|
285
|
+
"service-tier-cost": { key: "serviceTierCost", set: "catalog", shape: "object" },
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/** Records applicable to each API family; used by `resolve.ts` when applying wire axes. */
|
|
289
|
+
export const API_COMPAT_RECORDS: Readonly<Record<string, readonly CompatRecordName[]>> = {
|
|
290
|
+
"openai-completions": ["openai"],
|
|
291
|
+
openrouter: ["openai", "openai-responses"],
|
|
292
|
+
"openai-responses": ["openai-responses"],
|
|
293
|
+
"azure-openai-responses": ["openai-responses"],
|
|
294
|
+
"openai-codex-responses": ["openai-responses"],
|
|
295
|
+
"anthropic-messages": ["anthropic"],
|
|
296
|
+
"bedrock-converse-stream": ["bedrock"],
|
|
297
|
+
"devin-agent": ["devin"],
|
|
298
|
+
"google-generative-ai": ["google"],
|
|
299
|
+
"google-vertex": ["google"],
|
|
300
|
+
"google-gemini-cli": ["google"],
|
|
301
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed accessors over the compiled runtime-behavior vocabulary
|
|
3
|
+
* (`rules/runtime/behavior.kdl`): provider/model heuristics that run before
|
|
4
|
+
* or outside exact bundled-model lookup — responses routing, API routing,
|
|
5
|
+
* quota tiers, plan requirements, model limits, roster exclusions, hosted
|
|
6
|
+
* defaults, and pricing peers.
|
|
7
|
+
*/
|
|
8
|
+
import { globMatch } from "./cascade";
|
|
9
|
+
import rules from "./rules.json";
|
|
10
|
+
import type { CompiledMatchList } from "./types";
|
|
11
|
+
|
|
12
|
+
const behavior = rules.behavior;
|
|
13
|
+
|
|
14
|
+
function matchesList(match: CompiledMatchList, model: string, modelLower: string): boolean {
|
|
15
|
+
if (match.exact?.some(candidate => candidate === model)) return true;
|
|
16
|
+
if (match.prefix?.some(prefix => model.startsWith(prefix))) return true;
|
|
17
|
+
if (match.substring?.some(sub => model.includes(sub))) return true;
|
|
18
|
+
if (match.token !== undefined) {
|
|
19
|
+
const parts = modelLower.split(/[^a-z0-9]+/);
|
|
20
|
+
if (match.token.some(token => parts.includes(token))) return true;
|
|
21
|
+
}
|
|
22
|
+
if (match.glob?.some(pattern => globMatch(pattern, modelLower))) return true;
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Conservative heuristic for a normalized, lowercase discovered model id with
|
|
28
|
+
* no exact bundled record: whether it likely rides the OpenAI Responses API.
|
|
29
|
+
*/
|
|
30
|
+
export function isLikelyOpenAIResponsesId(model: string): boolean {
|
|
31
|
+
const rule = behavior.openaiResponsesHeuristic;
|
|
32
|
+
if (!rule) return false;
|
|
33
|
+
if (rule.excludePrefixes.some(prefix => model.startsWith(prefix))) return false;
|
|
34
|
+
if (rule.excludeSubstrings.some(sub => model.includes(sub))) return false;
|
|
35
|
+
return rule.includePrefixes.some(prefix => model.startsWith(prefix));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Additional catalog-declared operations for a discovered provider/model pair
|
|
40
|
+
* (e.g. `generate_image`), augmenting provider discovery metadata.
|
|
41
|
+
*/
|
|
42
|
+
export function modelOperationOverrides(provider: string, model: string): readonly string[] {
|
|
43
|
+
const lower = model.toLowerCase();
|
|
44
|
+
const out: string[] = [];
|
|
45
|
+
for (const rule of behavior.modelOperations) {
|
|
46
|
+
if (rule.provider !== provider || !matchesList(rule.models, lower, lower)) continue;
|
|
47
|
+
for (const operation of rule.operations) {
|
|
48
|
+
if (!out.includes(operation)) out.push(operation);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Splits a Cursor effort-suffixed OpenAI sibling id into its base id and
|
|
56
|
+
* declared effort tier. The family gate requires the declared marker
|
|
57
|
+
* (`gpt-`) followed immediately by an ASCII digit; matching stays
|
|
58
|
+
* case-sensitive to preserve Cursor wire-id behavior.
|
|
59
|
+
*/
|
|
60
|
+
export function cursorEffortSuffix(model: string): { base: string; tier: string } | undefined {
|
|
61
|
+
const rule = behavior.cursorEffort;
|
|
62
|
+
if (!rule) return undefined;
|
|
63
|
+
for (const tier of rule.tiers) {
|
|
64
|
+
if (!model.endsWith(tier)) continue;
|
|
65
|
+
const prefix = model.slice(0, model.length - tier.length);
|
|
66
|
+
if (!prefix.endsWith("-")) continue;
|
|
67
|
+
const base = prefix.slice(0, -1);
|
|
68
|
+
let family = false;
|
|
69
|
+
let index = base.indexOf(rule.familyMarker);
|
|
70
|
+
while (index !== -1) {
|
|
71
|
+
const next = base.charCodeAt(index + rule.familyMarker.length);
|
|
72
|
+
if (next >= 48 && next <= 57) {
|
|
73
|
+
family = true;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
index = base.indexOf(rule.familyMarker, index + 1);
|
|
77
|
+
}
|
|
78
|
+
if (!family) return undefined;
|
|
79
|
+
return { base, tier };
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Fixed Cursor `requestedModel` parameters declared for an exact wire model. */
|
|
85
|
+
export function cursorModelParameters(model: string): readonly { id: string; value: string }[] {
|
|
86
|
+
return behavior.cursorParameters.filter(parameter => parameter.model === model);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The catalog-declared quota scope or display tier for a provider model id.
|
|
91
|
+
* Exact authored memberships win; provider-authored substring fallbacks
|
|
92
|
+
* preserve quota semantics for newly discovered ids.
|
|
93
|
+
*/
|
|
94
|
+
export function quotaTierFor(provider: string, model: string): string | undefined {
|
|
95
|
+
const rule = behavior.quotaTiers.find(candidate => candidate.provider === provider);
|
|
96
|
+
if (!rule) return undefined;
|
|
97
|
+
for (const tier of rule.tiers) {
|
|
98
|
+
if (tier.models.includes(model)) return tier.label;
|
|
99
|
+
}
|
|
100
|
+
for (const fallback of rule.fallbacks) {
|
|
101
|
+
if (model.includes(fallback.substring)) return fallback.label;
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Whether a provider has catalog-authored model quota scopes. */
|
|
107
|
+
export function hasQuotaTierPolicy(provider: string): boolean {
|
|
108
|
+
return behavior.quotaTiers.some(rule => rule.provider === provider);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The provider-default wire model for a model-less hosted operation. */
|
|
112
|
+
export function hostedDefaultModel(provider: string): string | undefined {
|
|
113
|
+
return behavior.hostedDefaults.find(entry => entry.provider === provider)?.model;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** One resolved API route for a provider model id. */
|
|
117
|
+
export interface ApiRouteMatch {
|
|
118
|
+
/** Transport API the id rides. */
|
|
119
|
+
api: string;
|
|
120
|
+
/** Wire id after prefix stripping, when the route declares `strip-prefix`. */
|
|
121
|
+
requestModelId?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Resolves the declared API route for a provider model id. Routes match in
|
|
126
|
+
* declaration order; a `strip-prefix` route with a prefix matcher strips the
|
|
127
|
+
* matched prefix off the wire id. Falls back to the node's `default` API.
|
|
128
|
+
*/
|
|
129
|
+
export function apiRouteFor(provider: string, model: string): ApiRouteMatch | undefined {
|
|
130
|
+
const table = behavior.apiRoutes.find(candidate => candidate.provider === provider);
|
|
131
|
+
if (!table) return undefined;
|
|
132
|
+
const lower = model.toLowerCase();
|
|
133
|
+
for (const route of table.routes) {
|
|
134
|
+
if (!matchesList(route.match, model, lower)) continue;
|
|
135
|
+
const out: ApiRouteMatch = { api: route.api };
|
|
136
|
+
if (route.stripPrefix) {
|
|
137
|
+
const prefix = route.match.prefix?.find(candidate => model.startsWith(candidate));
|
|
138
|
+
if (prefix) out.requestModelId = model.slice(prefix.length);
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
return table.default !== undefined ? { api: table.default } : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Exact model ids named by a provider's api-routes rules (cache-migration drop lists). */
|
|
146
|
+
export function apiRouteExactModelIds(provider: string): string[] {
|
|
147
|
+
const table = behavior.apiRoutes.find(candidate => candidate.provider === provider);
|
|
148
|
+
if (!table) return [];
|
|
149
|
+
const ids: string[] = [];
|
|
150
|
+
for (const route of table.routes) {
|
|
151
|
+
for (const id of route.match.exact ?? []) {
|
|
152
|
+
if (!ids.includes(id)) ids.push(id);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return ids;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Declared context-window / max-token pins for a provider model id. */
|
|
159
|
+
export function modelLimitsFor(provider: string, model: string): { context?: number; maxTokens?: number } | undefined {
|
|
160
|
+
for (const table of behavior.modelLimits) {
|
|
161
|
+
if (table.provider !== provider) continue;
|
|
162
|
+
const limit = table.limits.find(entry => entry.model === model);
|
|
163
|
+
if (limit) {
|
|
164
|
+
const out: { context?: number; maxTokens?: number } = {};
|
|
165
|
+
if (limit.context !== undefined) out.context = limit.context;
|
|
166
|
+
if (limit.maxTokens !== undefined) out.maxTokens = limit.maxTokens;
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Whether a provider roster entry is a declared non-chat/unsupported SKU. */
|
|
174
|
+
export function isExcludedModel(provider: string, model: string): boolean {
|
|
175
|
+
const lower = model.toLowerCase();
|
|
176
|
+
return behavior.excludeModels.some(rule => rule.provider === provider && matchesList(rule.match, lower, lower));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Whether a provider is retired: its entire roster is excluded from the
|
|
181
|
+
* generated bundle and must never be resurrected from the previous snapshot.
|
|
182
|
+
*/
|
|
183
|
+
export function isRetiredProvider(provider: string): boolean {
|
|
184
|
+
return behavior.retiredProviders.includes(provider);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The declared subscription tier required to use a provider model id, if any. */
|
|
188
|
+
export function planRequirementFor(provider: string, model: string): string | undefined {
|
|
189
|
+
const rule = behavior.planRequirements.find(candidate => candidate.provider === provider);
|
|
190
|
+
if (!rule) return undefined;
|
|
191
|
+
const lower = model.toLowerCase();
|
|
192
|
+
for (const tier of rule.tiers) {
|
|
193
|
+
if (matchesList(tier.match, model, lower)) return tier.tier;
|
|
194
|
+
}
|
|
195
|
+
return undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Cross-provider pricing-peer resolution for one provider model id. */
|
|
199
|
+
export function pricingPeerFor(
|
|
200
|
+
provider: string,
|
|
201
|
+
model: string,
|
|
202
|
+
): { peers: readonly string[]; peerId: string } | undefined {
|
|
203
|
+
const rule = behavior.pricingPeers.find(candidate => candidate.provider === provider);
|
|
204
|
+
if (!rule) return undefined;
|
|
205
|
+
const alias = rule.aliases.find(candidate => candidate.model === model);
|
|
206
|
+
return { peers: rule.peers, peerId: alias?.peerId ?? model };
|
|
207
|
+
}
|