@oh-my-pi/pi-catalog 18.1.17 → 18.1.19

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 CHANGED
@@ -2,10 +2,30 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.1.19] - 2026-09-12
6
+
7
+ ### Added
8
+
9
+ - Added Charm Hyper as a built-in provider with API-key login, live model discovery, and per-model pricing, effort ladders, and limits read straight from its catalog ([#11656](https://github.com/can1357/oh-my-pi/pull/11656) by [@oldschoola](https://github.com/oldschoola)).
10
+
11
+ ## [18.1.18] - 2026-09-11
12
+
13
+ ### Added
14
+
15
+ - `supports-server-compaction` compat axis (`compat.supportsServerCompaction`): whether a model line accepts Anthropic server-side compaction (`compact-2026-01-12`). Class rules enable it for Opus 4.6+, Sonnet 4.6+, and Fable/Mythos 5 on every Anthropic-messages host; the default is `false`.
16
+
17
+ ### Fixed
18
+
19
+ - OpenCode Go's DeepSeek Flash lanes (`deepseek-flash`, `deepseek-v4.1-flash`) now declare image input. The gateway serves them with vision despite the IDs carrying no vision suffix, so the class-wide `strip-image-input` rule was dropping attachments the endpoint reads; the modality is declared too, since live discovery seeds these lanes text-only ([#11774](https://github.com/can1357/oh-my-pi/pull/11774) by [@STRML](https://github.com/STRML)).
20
+ - Amazon Bedrock OpenAI models, plus unclassified profiles such as opaque application-inference-profile ARNs, now carry the compatibility policy required to preserve image-bearing tool results ([#11681](https://github.com/can1357/oh-my-pi/issues/11681)).
21
+ - DeepSeek V4.1 Flash requests now honor the documented 384K output maximum instead of being capped at 64K ([#11769](https://github.com/can1357/oh-my-pi/issues/11769)).
22
+ - Fixed the first-party `deepseek-flash` alias missing the V4.1 Flash wire contract: it now sends `max_tokens` with `reasoning_content` and replays reasoning and assistant content on tool calls with no tool choice ([#11799](https://github.com/can1357/oh-my-pi/pull/11799) by [@brit](https://github.com/brit)).
23
+
5
24
  ## [18.1.17] - 2026-09-10
6
25
 
7
26
  ### Added
8
27
 
28
+ - Added DeepSeek V4.1 Flash on OpenRouter with image input and low/high/max reasoning levels ([#11592](https://github.com/can1357/oh-my-pi/pull/11592) by [@mazzanfar](https://github.com/mazzanfar)).
9
29
  - Added DeepSeek cost estimates that follow published peak/off-peak rates.
10
30
  - Added dated, announced price changes to the catalog, so rates switch on their effective date (e.g. DeepSeek Pro moving to Flash rates).
11
31
  - Added Command Code as a built-in provider with API-key login, live model discovery, per-model pricing, native OpenAI/Anthropic-compatible routing, cache-aware token usage, and TTFT metrics ([#11391](https://github.com/can1357/oh-my-pi/pull/11391) by [@CherkaSSH](https://github.com/CherkaSSH)).
@@ -1,4 +1,4 @@
1
1
  /** Every provider with an `auth/<id>.kdl` policy. */
2
- export type AuthProviderId = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "cline-pass" | "cloudflare-ai-gateway" | "commandcode" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "synthetic" | "tavily" | "together" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
2
+ export type AuthProviderId = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "commandcode" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "synthetic" | "tavily" | "together" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
3
3
  /** Providers whose policy declares a `login` flow (the `/login` roster). */
4
- export type LoginProviderId = "abliteration" | "aiand" | "alibaba-coding-plan" | "alibaba-token-plan" | "anthropic" | "baseten" | "cerebras" | "cline-pass" | "cloudflare-ai-gateway" | "commandcode" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google-antigravity" | "google-gemini-cli" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "synthetic" | "tavily" | "together" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
4
+ export type LoginProviderId = "abliteration" | "aiand" | "alibaba-coding-plan" | "alibaba-token-plan" | "anthropic" | "baseten" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "commandcode" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google-antigravity" | "google-gemini-cli" | "huggingface" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "siliconflow" | "siliconflow-cn" | "synthetic" | "tavily" | "together" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
@@ -88,6 +88,14 @@ export declare const CATALOG_PROVIDERS: readonly [{
88
88
  readonly catalogDiscovery: {
89
89
  readonly label: "Cerebras";
90
90
  };
91
+ }, {
92
+ readonly id: "charm-hyper";
93
+ readonly defaultModel: "glm-5.3";
94
+ readonly envVars: readonly ["CHARM_HYPER_API_KEY", "HYPER_API_KEY"];
95
+ readonly createModelManagerOptions: (config: ModelManagerConfig) => import("../index.js").ModelManagerOptions<"openai-completions", unknown>;
96
+ readonly allowUnauthenticated: true;
97
+ readonly dynamicModelsAuthoritative: true;
98
+ readonly skipCrossProviderReferenceFills: true;
91
99
  }, {
92
100
  readonly id: "cloudflare-ai-gateway";
93
101
  readonly defaultModel: "anthropic/claude-opus-4-8";
@@ -745,3 +745,22 @@ export interface CommandCodeModelManagerConfig {
745
745
  * or context window.
746
746
  */
747
747
  export declare function commandCodeModelManagerOptions(config?: CommandCodeModelManagerConfig): ModelManagerOptions<Api>;
748
+ /**
749
+ * Configuration for the Charm Hyper model manager.
750
+ *
751
+ * `baseUrl` overrides the gateway root for tests and self-hosted proxies; a
752
+ * value that omits the `/v1` surface gains one, so a host-only override
753
+ * behaves like every sibling provider's.
754
+ */
755
+ export interface CharmHyperModelManagerConfig {
756
+ apiKey?: string;
757
+ baseUrl?: string;
758
+ fetch?: FetchImpl;
759
+ }
760
+ /**
761
+ * Charm Hyper's gateway catalog. `/v1/models` is public and carries the live
762
+ * tariff, so discovery runs with or without a key and the snapshot is
763
+ * authoritative: a model the gateway stops serving is pruned rather than kept
764
+ * alive by a stale bundled row.
765
+ */
766
+ export declare function charmHyperModelManagerOptions(config?: CharmHyperModelManagerConfig): ModelManagerOptions<"openai-completions">;
@@ -409,6 +409,20 @@ export interface OpenAICompat {
409
409
  export interface AnthropicCompat {
410
410
  /** Whether thinking requests may include `context_management` and its beta header. Default: true. */
411
411
  supportsContextManagement?: boolean;
412
+ /**
413
+ * Whether the model lineage supports Anthropic server-side compaction
414
+ * (`compact-2026-01-12`: the `compact_20260112` edit and replayed
415
+ * `compaction` blocks). Rule-owned per model line; the beta covers the
416
+ * adaptive-thinking generation onward and rejects older lines. Default: false.
417
+ */
418
+ supportsServerCompaction?: boolean;
419
+ /**
420
+ * Whether the model is served by the first-party Anthropic provider (its
421
+ * default route is the official API). Rule-owned on the provider; the
422
+ * compaction transport pairs it with a per-request effective-URL check
423
+ * because reroutes leave it stale-true. Default: false.
424
+ */
425
+ firstPartyProvider?: boolean;
412
426
  /**
413
427
  * Whether requests may carry `output_config.effort` (and its effort beta
414
428
  * header). Vertex AI rejects the field/header. Default: true.
@@ -756,6 +770,13 @@ export type ResolvedAnthropicCompat = Required<Omit<AnthropicCompat, "streamIdle
756
770
  * env headers, and cache-TTL shaping without per-request URL parsing.
757
771
  */
758
772
  officialEndpoint: boolean;
773
+ /**
774
+ * The model is served by the first-party Anthropic provider. Unlike
775
+ * `officialEndpoint` (URL-derived per deployment), this is provider
776
+ * identity for compaction gating, always paired with a per-request
777
+ * effective-URL check.
778
+ */
779
+ firstPartyProvider: boolean;
759
780
  };
760
781
  /**
761
782
  * Compatibility settings for the devin-agent (Codeium Cascade) API. Cascade
@@ -910,6 +931,8 @@ export interface Model<TApi extends Api = Api> {
910
931
  requiresGlyphTokenization?: boolean;
911
932
  /** Whether this model requires Cursor's tool-schema combiner projection. */
912
933
  requiresCursorToolSchemaProjection?: boolean;
934
+ /** Whether this model requires tool-result images hoisted into sibling user content blocks. */
935
+ requiresToolResultImageHoisting?: boolean;
913
936
  /**
914
937
  * Model id to send on the wire when it differs from `id`. Used by catalog
915
938
  * variants that present one upstream model under several local entries —
@@ -1083,7 +1106,7 @@ export interface Model<TApi extends Api = Api> {
1083
1106
  * vocabulary of `buildModel`. Identical to `Model` except `compat` carries the
1084
1107
  * sparse override shape and nothing is resolved yet.
1085
1108
  */
1086
- export interface ModelSpec<TApi extends Api = Api> extends Omit<Model<TApi>, "compat" | "identity" | "compatConfig" | "requiresGlyphTokenization" | "requiresCursorToolSchemaProjection" | "supportsComputerUseConfig"> {
1109
+ export interface ModelSpec<TApi extends Api = Api> extends Omit<Model<TApi>, "compat" | "identity" | "compatConfig" | "requiresGlyphTokenization" | "requiresCursorToolSchemaProjection" | "requiresToolResultImageHoisting" | "supportsComputerUseConfig"> {
1087
1110
  /** Sparse compatibility overrides; resolved into `Model.compat` by `buildModel`. */
1088
1111
  compat?: CompatConfigOf<TApi>;
1089
1112
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Charm Hyper gateway endpoint, shared so a host migration — or a self-hosted
3
+ * proxy override — touches a single module.
4
+ */
5
+ export declare const CHARM_HYPER_API_BASE_URL = "https://hyper.charm.land/v1";
6
+ /**
7
+ * Resolve a configured Charm Hyper base URL onto the gateway's `/v1` surface.
8
+ *
9
+ * Every consumer must agree on this, because they key different things off the
10
+ * result: inference and discovery target it, `/usage` sends a bearer token to
11
+ * it, and the model cache namespace is hashed from it. Three separate copies
12
+ * previously disagreed for a blank value — the model manager treated it as
13
+ * absent and used the canonical host, while the others produced a bare `/v1` —
14
+ * so a whitespace-only override silently split inference, balance checks and
15
+ * caching across different endpoints.
16
+ *
17
+ * A blank or whitespace-only value therefore means "not configured" and
18
+ * resolves to the canonical host; anything else keeps its host and gains the
19
+ * `/v1` segment if it omits one.
20
+ */
21
+ export declare function normalizeCharmHyperBaseUrl(baseUrl?: string): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "18.1.17",
4
+ "version": "18.1.19",
5
5
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Stencil Labs, Inc.",
@@ -35,12 +35,12 @@
35
35
  "gen:proto": "bun scripts/generate-protocols.ts"
36
36
  },
37
37
  "dependencies": {
38
- "@oh-my-pi/omptype": "18.1.17",
39
- "@oh-my-pi/pi-utils": "18.1.17"
38
+ "@oh-my-pi/omptype": "18.1.19",
39
+ "@oh-my-pi/pi-utils": "18.1.19"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@bgotink/kdl": "0.4.0",
43
- "@oh-my-pi/pi-ai": "18.1.17",
43
+ "@oh-my-pi/pi-ai": "18.1.19",
44
44
  "@types/bun": "^1.3.14"
45
45
  },
46
46
  "engines": {
package/src/build.ts CHANGED
@@ -34,8 +34,8 @@ function isInputModalities(value: unknown): value is ("text" | "image")[] {
34
34
  * corrections (`cost-patch`, `limits-patch`, `long-context-cost`,
35
35
  * `context-window-floor`) overwrite upstream values; selection metadata
36
36
  * (`priority`, `apply-patch-tool-type`, `service-tier-cost`,
37
- * `requires-cursor-tool-schema-projection`) is rule-owned;
38
- * `context-promotion-target` fills only when the spec left it unset.
37
+ * `requires-cursor-tool-schema-projection`, `requires-tool-result-image-hoisting`)
38
+ * is rule-owned; `context-promotion-target` fills only when the spec left it unset.
39
39
  */
40
40
  function applyCatalogAssignments<TApi extends Api>(model: Model<TApi>, catalog: Record<string, unknown>): void {
41
41
  const serviceTierCost = objectPayload(catalog.serviceTierCost);
@@ -63,6 +63,12 @@ function applyCatalogAssignments<TApi extends Api>(model: Model<TApi>, catalog:
63
63
  } else {
64
64
  delete model.requiresCursorToolSchemaProjection;
65
65
  }
66
+ const requiresToolResultImageHoisting = catalog.requiresToolResultImageHoisting;
67
+ if (requiresToolResultImageHoisting === true) {
68
+ model.requiresToolResultImageHoisting = true;
69
+ } else {
70
+ delete model.requiresToolResultImageHoisting;
71
+ }
66
72
  const contextPromotionTarget = catalog.contextPromotionTarget;
67
73
  if (typeof contextPromotionTarget === "string" && model.contextPromotionTarget === undefined) {
68
74
  model.contextPromotionTarget = contextPromotionTarget;
@@ -13,6 +13,7 @@ export type AuthProviderId =
13
13
  | "baseten"
14
14
  | "bedrock-mantle"
15
15
  | "cerebras"
16
+ | "charm-hyper"
16
17
  | "cline-pass"
17
18
  | "cloudflare-ai-gateway"
18
19
  | "commandcode"
@@ -94,6 +95,7 @@ export type LoginProviderId =
94
95
  | "anthropic"
95
96
  | "baseten"
96
97
  | "cerebras"
98
+ | "charm-hyper"
97
99
  | "cline-pass"
98
100
  | "cloudflare-ai-gateway"
99
101
  | "commandcode"
@@ -192,6 +192,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
192
192
  "disable-adaptive-thinking": wire("disableAdaptiveThinking", ["anthropic"]),
193
193
  "disable-strict-tools": wire("disableStrictTools", ["anthropic"]),
194
194
  "escape-builtin-tool-names": wire("escapeBuiltinToolNames", ["anthropic"]),
195
+ "first-party-provider": wire("firstPartyProvider", ["anthropic"]),
195
196
  "inject-claude-code-instruction": wire("injectClaudeCodeInstruction", ["anthropic"]),
196
197
  "official-endpoint": wire("officialEndpoint", ["anthropic", "openai-responses"]),
197
198
  "replay-unsigned-thinking": wire("replayUnsignedThinking", ["anthropic"]),
@@ -205,6 +206,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
205
206
  "supports-mid-conversation-system": wire("supportsMidConversationSystem", ["anthropic"]),
206
207
  "supports-mid-conversation-tool-changes": wire("supportsMidConversationToolChanges", ["anthropic"]),
207
208
  "supports-per-message-effort": wire("supportsPerMessageEffort", ["anthropic"]),
209
+ "supports-server-compaction": wire("supportsServerCompaction", ["anthropic"]),
208
210
  "supports-thinking-binding-controls": wire("supportsThinkingBindingControls", ["anthropic"]),
209
211
  "supports-turn-scoped-system": wire("supportsTurnScopedSystem", ["anthropic"]),
210
212
 
@@ -285,6 +287,11 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
285
287
  set: "catalog",
286
288
  shape: "scalar",
287
289
  },
290
+ "requires-tool-result-image-hoisting": {
291
+ key: "requiresToolResultImageHoisting",
292
+ set: "catalog",
293
+ shape: "scalar",
294
+ },
288
295
  priority: { key: "priority", set: "catalog", shape: "scalar" },
289
296
  "service-tier-cost": { key: "serviceTierCost", set: "catalog", shape: "object" },
290
297
  "time-based-cost": { key: "timeBased", set: "catalog", shape: "object" },
@@ -843,6 +843,8 @@ function resolveAnthropicPolicy(
843
843
  officialEndpoint: official,
844
844
  signingEndpoint,
845
845
  supportsContextManagement: true,
846
+ supportsServerCompaction: false,
847
+ firstPartyProvider: false,
846
848
  supportsOutputEffort: true,
847
849
  disableStrictTools: isAzure,
848
850
  disableAdaptiveThinking: false,
@@ -5,7 +5,7 @@ login-order "openai-codex" "anthropic" "zai" "zai-coding-plan" "kimi-code" "open
5
5
  "cursor" "devin" "google-antigravity" "google-gemini-cli" "openai-codex-device" "xai" "xai-oauth" \
6
6
  "gitlab-duo" "gitlab-duo-agent" "alibaba-coding-plan" "alibaba-token-plan" "aiand" "abliteration" "zhipu-coding-plan" \
7
7
  "umans" "qwen-portal" "sakana" "minimax-code" "minimax-code-cn" "xiaomi" "xiaomi-token-plan-sgp" \
8
- "xiaomi-token-plan-ams" "xiaomi-token-plan-cn" "firepass" "cline-pass" "commandcode" "deepseek" "muse-code" "meta" "moonshot" \
8
+ "xiaomi-token-plan-ams" "xiaomi-token-plan-cn" "firepass" "cline-pass" "commandcode" "charm-hyper" "deepseek" "muse-code" "meta" "moonshot" \
9
9
  "cerebras" "baseten" "fireworks" "together" "nvidia" "novita" "deepinfra" "huggingface" "perplexity" \
10
10
  "qianfan" "venice" "siliconflow" "siliconflow-cn" "synthetic" "nanogpt" "wafer-serverless" "coreweave" \
11
11
  "vercel-ai-gateway" "cloudflare-ai-gateway" "litellm" "kilo" "zenmux" "opencode-zen" "opencode-go" \
@@ -0,0 +1,19 @@
1
+ // Validation targets `/v1/credits`, not `/v1/models`: the models endpoint is
2
+ // public and answers 200 with no credentials or a bogus key (verified
3
+ // 2026-09-11), so a models-endpoint probe against it could never reject a bad
4
+ // key. `/v1/credits` is an authenticated, non-billable GET that returns the
5
+ // account balance on success and 401 `{"error":"authentication failed"}`
6
+ // otherwise — the same trick cline-pass uses to validate against its own
7
+ // `/users/me`, and cheaper than a chat-completions probe that would bill the
8
+ // key on every login.
9
+ auth "charm-hyper" {
10
+ name "Charm Hyper"
11
+ env "CHARM_HYPER_API_KEY" "HYPER_API_KEY"
12
+ login "api-key" {
13
+ auth-url "https://hyper.charm.land/"
14
+ instructions "Create or copy an API key from the Charm Hyper dashboard"
15
+ prompt "Paste your Charm Hyper API key" placeholder="sk-hyper-..."
16
+ normalize "strip-bearer"
17
+ validate "models-endpoint" label="Charm Hyper" url="https://hyper.charm.land/v1/credits"
18
+ }
19
+ }
@@ -7,6 +7,30 @@ class "anthropic" {
7
7
  thinking-prefix-binding #true
8
8
  }
9
9
  }
10
+ // Server-side compaction (compact-2026-01-12) is a lineage capability: the
11
+ // documented model list is the adaptive-thinking generation onward (Opus
12
+ // 4.6+, Sonnet 4.6+, Fable/Mythos 5+). Whether a host delivers the beta is
13
+ // decided at request time from the effective endpoint, not here.
14
+ family "fable" {
15
+ revision ">=5" {
16
+ supports-server-compaction #true
17
+ }
18
+ }
19
+ family "mythos" {
20
+ revision ">=5" {
21
+ supports-server-compaction #true
22
+ }
23
+ }
24
+ family "opus" {
25
+ revision ">=4.6" {
26
+ supports-server-compaction #true
27
+ }
28
+ }
29
+ family "sonnet" {
30
+ revision ">=4.6" {
31
+ supports-server-compaction #true
32
+ }
33
+ }
10
34
  on "anthropic" "cloudflare-ai-gateway" "google-vertex" {
11
35
  family "fable" {
12
36
  revision ">=5" {
@@ -28,8 +28,10 @@ class "minimax" {
28
28
  }
29
29
  // MiniMax-M3: 512K is the standard pricing-tier boundary leaked into
30
30
  // upstream metadata, not the model ceiling (documented: 1M in / 128K out).
31
- on "minimax" "minimax-cn" "minimax-code" "minimax-code-cn" {
32
- models "MiniMax-M3" {
31
+ // Exact selectors are case-sensitive, so both served spellings are listed:
32
+ // MiniMax hosts publish `MiniMax-M3`, Charm Hyper publishes `minimax-m3`.
33
+ on "charm-hyper" "minimax" "minimax-cn" "minimax-code" "minimax-code-cn" {
34
+ models "MiniMax-M3" "minimax-m3" {
33
35
  limits-patch {
34
36
  context-window 1000000
35
37
  max-tokens 128000
@@ -15,6 +15,8 @@ provider "amazon-bedrock" {
15
15
  }
16
16
  class "openai" {
17
17
  thinking-mode "effort"
18
+ // OpenAI models reject Converse image blocks nested inside toolResult content.
19
+ requires-tool-result-image-hoisting #true
18
20
  }
19
21
  class "deepseek" {
20
22
  requires-reasoning-content-for-all-assistant-turns #true
@@ -28,6 +30,11 @@ provider "amazon-bedrock" {
28
30
  class "unknown" {
29
31
  thinking-efforts "minimal" "low" "medium" "high"
30
32
  thinking-mode "budget"
33
+ // Opaque application-inference-profile ARNs carry no vendor signal, so an
34
+ // OpenAI-backed profile classifies here and would otherwise nest images in
35
+ // toolResult content and hit the same rejection. Hoisting is accepted by
36
+ // every Converse model measured, so it is the safe default for unknowns.
37
+ requires-tool-result-image-hoisting #true
31
38
  }
32
39
  // residue: taxonomy ranks and exact globs do not isolate these models.
33
40
  models "moonshot.kimi-k2-thinking" {
@@ -1,6 +1,14 @@
1
1
  // Provider-wire compat for "anthropic"; regenerated from the frozen census using deployment contract selectors.
2
2
 
3
3
  provider "anthropic" {
4
+ // First-party provider: models served through this provider reach the
5
+ // official API unless rerouted. A dedicated axis rather than
6
+ // `official-endpoint`, which stays URL-derived: a custom `baseUrl` on this
7
+ // provider must keep `officialEndpoint: false` (SDK `X-Api-Key`
8
+ // suppression and friends read that flag). The compaction transport
9
+ // combines this fact with a per-request effective-URL check, so it never
10
+ // authorizes a block on its own.
11
+ first-party-provider #true
4
12
  class "anthropic" {
5
13
  revision ">=3.7 <4.6" {
6
14
  thinking-efforts "minimal" "low" "medium" "high" "xhigh"
@@ -0,0 +1,82 @@
1
+ // Charm Hyper gateway deployment contract.
2
+ //
3
+ // Capability data is NOT restated here: `GET /v1/models` publishes each row's
4
+ // context window, output cap, vision flag, effort vocabulary and per-million
5
+ // tariff, and `charmHyperModelManagerOptions` maps all of it verbatim —
6
+ // including `reasoningDisableMode`, which is derived per model from whether
7
+ // the advertised vocabulary contains `none`. Only facts the gateway does not
8
+ // publish, or publishes wrongly, belong in this file.
9
+ provider "charm-hyper" {
10
+ // OpenAI-dialect gateway in front of open-weight and third-party models.
11
+ // `developer` and `store` are accepted (HTTP 200) but the gateway accepts
12
+ // every unknown field it is handed, so acceptance is not evidence the
13
+ // semantics are honored; both stay off and system prompts ride the
14
+ // `system` role, matching every sibling gateway. The wire takes
15
+ // `max_tokens` (verified 2026-09-11).
16
+ supports-developer-role #false
17
+ supports-store #false
18
+ max-tokens-field "max_tokens"
19
+ thinking-mode "effort"
20
+ // Reasoning arrives as `reasoning_content` on both the streamed delta and
21
+ // the final message (verified 2026-09-11).
22
+ reasoning-content-field "reasoning_content"
23
+ // ---------------------------------------------------------------------------
24
+ // Blockless rows are left non-reasoning on purpose.
25
+ //
26
+ // Four ids serve a thinking model while publishing no `reasoning` block:
27
+ // glm-5, glm-5.1, kimi-k2-thinking and minimax-m2.7 each returned
28
+ // populated `reasoning_content` and non-zero `reasoning_tokens` on a
29
+ // multi-step prompt (2026-09-11). The other blockless rows
30
+ // (gemma-4-26b-a4b-it, kimi-k2.5, kimi-k2.7-code, llama-3.3-70b-instruct,
31
+ // llama-4-maverick-17b-128e-instruct-fp8,
32
+ // qwen3-coder-480b-a35b-instruct-int4-mixed-ar, qwen3-next-80b-a3b-instruct)
33
+ // returned none.
34
+ //
35
+ // No rule restores them, because none can be written honestly. Those four
36
+ // expose no effort dial, and `ThinkingConfig.efforts` is documented as
37
+ // never empty — a reasoning model without a controllable surface carries
38
+ // `thinking: undefined`, which no KDL axis can express (the only upgrade
39
+ // path is an exact `thinking-efforts` ladder). Synthesizing a ladder would
40
+ // add silent no-op rungs: the gateway answers 200 to efforts it never
41
+ // advertised and simply ignores them, and a repeat measurement of effort
42
+ // sensitivity on these ids came back non-monotonic, i.e. sampling noise
43
+ // rather than a hidden dial.
44
+ //
45
+ // The cost is narrow and was checked: streamed `reasoning_content` is
46
+ // parsed and displayed with no `model.reasoning` gate, the effort
47
+ // parameter is moot without a dial, and the developer-role and
48
+ // zai-replay paths are already disabled for this provider.
49
+ // ---------------------------------------------------------------------------
50
+ // Residue: misreported output caps. The gateway derives
51
+ // `max_output_tokens` as a flat fraction of the context window rather
52
+ // than reporting a real ceiling — 10% on 9 rows, the dominant
53
+ // convention — and the values are advisory, not enforced: glm-5.1
54
+ // publishes 3276 yet produced 14066 tokens, and minimax-m2.7 publishes
55
+ // 6553 yet produced 8418, both stopping on `finish_reason: "stop"`
56
+ // rather than `length`. An over-stated cap is never rejected either
57
+ // (`max_tokens: 200000` returns 200), so the risk is one-sided:
58
+ // over-stating costs only the context share 10% already reserves
59
+ // everywhere else, while under-stating truncates real output.
60
+ //
61
+ // Both corrections take a same-window peer's published value rather than
62
+ // inventing a ceiling, and both clear the measured floor:
63
+ // glm-5.1 (202750) is 1.6%; sibling glm-5 (202752) publishes 20275.
64
+ // minimax-m2.7 (262100) is 2.5%, the lone outlier in its window class;
65
+ // kimi-k2.5 (262144), kimi-k2.6 (262000) and
66
+ // qwen3-next-80b (262144) all publish 26214.
67
+ //
68
+ // deepseek-v4.1-flash is the only other 2.5% row and is deliberately left
69
+ // alone: its 1M window has no peer group that agrees on a fraction, and a
70
+ // long generation stopped naturally at 8504, well inside the published
71
+ // 26214. Measured 2026-09-11.
72
+ models "glm-5.1" {
73
+ limits-patch {
74
+ max-tokens 20275
75
+ }
76
+ }
77
+ models "minimax-m2.7" {
78
+ limits-patch {
79
+ max-tokens 26214
80
+ }
81
+ }
82
+ }
@@ -16,6 +16,7 @@ provider "deepseek" {
16
16
  // bare alias carries no family, and the retired `v4-flash`/`-vision-exp` ids
17
17
  // are still accepted and billed at the Flash card.
18
18
  models "deepseek-flash" "deepseek-v4-flash" "deepseek-v4-flash-vision-exp" {
19
+ clamp-output-to-model-max #true
19
20
  time-based-cost {
20
21
  off-peak-multiplier 0.5
21
22
  peak-windows {
@@ -81,11 +82,22 @@ provider "deepseek" {
81
82
  }
82
83
  }
83
84
  // residue: taxonomy ranks and exact globs do not isolate these models.
84
- models "deepseek-v4-flash" "deepseek-v4.1-flash-expires-on-0910" {
85
+ // The bare `deepseek-flash` alias serves DeepSeek-V4.1-Flash, so it shares
86
+ // the V4.1 wire contract: max_tokens + reasoning_content, mandatory replay
87
+ // (synthetic placeholders rejected upstream), no tool choice, and the
88
+ // low/high/max effort ladder the bare alias cannot inherit by family.
89
+ models "deepseek-flash" "deepseek-v4-flash" "deepseek-v4.1-flash-expires-on-0910" {
90
+ // Stale source capability data: discovery reports the bare alias as
91
+ // non-reasoning, so opt into the cascade upgrade for the exact ladder.
92
+ thinking-upgrade-neutral #true
85
93
  max-tokens-field "max_tokens"
86
94
  reasoning-content-field "reasoning_content"
87
95
  requires-assistant-content-for-tool-calls #true
88
96
  requires-reasoning-content-for-tool-calls #true
97
+ allows-synthetic-reasoning-content-for-tool-calls #false
89
98
  supports-tool-choice #false
99
+ // The bare alias carries no taxonomy family, so it cannot inherit the
100
+ // flash ladder; the exact rule owns it instead.
101
+ thinking-efforts "low" "high" "max"
90
102
  }
91
103
  }
@@ -1,6 +1,16 @@
1
1
  // Provider-wire compat for "opencode-go"; regenerated from the frozen census using deployment contract selectors.
2
2
 
3
3
  provider "opencode-go" {
4
+ // The Flash lanes accept images despite their IDs lacking a vision suffix;
5
+ // verified against the gateway (unguessable pixel-rendered text read back
6
+ // verbatim). Mirrors the OpenRouter carve-out for deepseek-v4.1-flash.
7
+ // The modality is declared here as well: live gateway discovery seeds
8
+ // `input: ["text"]`, and the wire guard requires the declared modality, so
9
+ // clearing `strip-image-input` alone would leave the lane text-only.
10
+ models "deepseek-flash" "deepseek-v4.1-flash" {
11
+ input-modalities "text" "image"
12
+ strip-image-input #false
13
+ }
4
14
  // Replaces the OpenCode reasoning-model whenThinking fallback object.
5
15
  when-thinking {
6
16
  requires-reasoning-content-for-tool-calls #true
@@ -1,6 +1,10 @@
1
1
  // Provider-wire compat for "openrouter"; regenerated from the frozen census using deployment contract selectors.
2
2
 
3
3
  provider "openrouter" {
4
+ // V4.1 Flash accepts images despite its ID lacking a vision suffix.
5
+ models "deepseek/deepseek-v4.1-flash" {
6
+ strip-image-input #false
7
+ }
4
8
  // Replaces the OpenRouter provider wire-model-id dispatch branch.
5
9
  wire-model-id-mode "openrouter"
6
10
  // Replaces the strict-mode provider whitelist entry.