@oh-my-pi/pi-catalog 18.2.7 → 18.2.8

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,6 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.2.8] - 2026-09-21
6
+
7
+ ### Added
8
+
9
+ - Expanded OpenRouter provider support with embedding, reranking, video generation, text-to-speech, and speech-to-text capabilities, including five new speech-to-text models.
10
+ - Added speech-to-text support to the OpenAI provider.
11
+
5
12
  ## [18.2.7] - 2026-09-21
6
13
 
7
14
  ### Added
@@ -181,9 +188,9 @@
181
188
  ### Fixed
182
189
 
183
190
  - Fixed OpenCode Go/Zen live model discovery (`GET /v1/models`) missing `x-opencode-session` and omp's `User-Agent`: discovery requests now attribute with the stable install id so the requests OpenCode flags as `Bun fetch` carry the required session header.
184
- - Fixed GPT-6 Astra requests through GitHub Copilot failing with an unsupported endpoint error ([#10874](https://github.com/can1357/oh-my-pi/pull/10874) by [@xpcmdshell](https://github.com/xpcmdshell)).
185
- - Fixed GPT-6 Astra showing as free with a 272K-token window in the OpenAI Codex catalog by applying its documented pricing; `/extended-context` enables the wire-advertised 872K-token maximum ([#10980](https://github.com/can1357/oh-my-pi/pull/10980) by [@H4vC](https://github.com/H4vC)).
186
- - Made extended-context catalog rebuilds faster by resolving each model's maximum window once per process ([#11039](https://github.com/can1357/oh-my-pi/pull/11039) by [@H4vC](https://github.com/H4vC)).
191
+ - Fixed GPT-6 Astra requests through GitHub Copilot failing with an unsupported endpoint error ([#10874](https://github.com/can1357/oh-my-pi/pull/10874) by [@xpcmdshell](https://github.com/xpcmdshell)).
192
+ - Fixed GPT-6 Astra showing as free with a 272K-token window in the OpenAI Codex catalog by applying its documented pricing; `/extended-context` enables the wire-advertised 872K-token maximum ([#10980](https://github.com/can1357/oh-my-pi/pull/10980) by [@H4vC](https://github.com/H4vC)).
193
+ - Made extended-context catalog rebuilds faster by resolving each model's maximum window once per process ([#11039](https://github.com/can1357/oh-my-pi/pull/11039) by [@H4vC](https://github.com/H4vC)).
187
194
 
188
195
  ## [18.1.9] - 2026-09-04
189
196
 
package/README.md CHANGED
@@ -4,18 +4,18 @@ Model catalog for [oh-my-pi](https://github.com/can1357/oh-my-pi): bundled model
4
4
 
5
5
  ## What's inside
6
6
 
7
- | Module | Purpose |
8
- | ------------------------------- | ----------------------------------------------------------------------------------------------------------- |
9
- | `models.json` + `models` | Bundled model database (pricing, context windows, modalities, thinking support) |
10
- | `provider-models` | Provider catalog descriptors (`CATALOG_PROVIDERS`), per-provider model resolution rules |
11
- | `discovery` | Runtime model discovery for OpenAI-compatible endpoints, Gemini, Codex, Cursor, Antigravity, Ollama |
12
- | `compat/rules` | Checked-in KDL policy tree: taxonomy (classes/families/revisions), class/provider cascade rules, runtime behavior vocabulary; compiled by `bun run gen:compat` into the committed `rules.json` |
7
+ | Module | Purpose |
8
+ | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
9
+ | `models.json` + `models` | Bundled model database (pricing, context windows, modalities, thinking support) |
10
+ | `provider-models` | Provider catalog descriptors (`CATALOG_PROVIDERS`), per-provider model resolution rules |
11
+ | `discovery` | Runtime model discovery for OpenAI-compatible endpoints, Gemini, Codex, Cursor, Antigravity, Ollama |
12
+ | `compat/rules` | Checked-in KDL policy tree: taxonomy (classes/families/revisions), class/provider cascade rules, runtime behavior vocabulary; compiled by `bun run gen:compat` into the committed `rules.json` |
13
13
  | `compat` | The rule engine: `classifyModel` (taxonomy), `resolveModelPolicy` (cascade), behavior accessors (`api-routes`, `model-limits`, `exclude-models`, `pricing-peer`), collapse, and OpenAI/Anthropic wire builders that consume resolved records |
14
- | `identity` | Mechanical id utilities: reference resolution against the bundled index, dialects, selection priority, tokenizer families |
15
- | `model-thinking` | Runtime thinking helpers (`getSupportedEfforts`, effort clamping/mapping, wire-id routing) over resolved model records |
16
- | `model-manager` / `model-cache` | Runtime model registry with discovery refresh and on-disk caching |
17
- | `wire` | Wire-level helpers: Codex, Gemini headers, GitHub Copilot |
18
- | `effort` | Reasoning-effort level definitions |
14
+ | `identity` | Mechanical id utilities: reference resolution against the bundled index, dialects, selection priority, tokenizer families |
15
+ | `model-thinking` | Runtime thinking helpers (`getSupportedEfforts`, effort clamping/mapping, wire-id routing) over resolved model records |
16
+ | `model-manager` / `model-cache` | Runtime model registry with discovery refresh and on-disk caching |
17
+ | `wire` | Wire-level helpers: Codex, Gemini headers, GitHub Copilot |
18
+ | `effort` | Reasoning-effort level definitions |
19
19
 
20
20
  Import from subpaths (`@oh-my-pi/pi-catalog/<module>`) or the root barrel.
21
21
 
@@ -42,13 +42,13 @@ Ships TypeScript source directly (no build step); requires Bun ≥ 1.3.14.
42
42
 
43
43
  The `models` subpath (also exported from the root) provides timestamp-aware pricing helpers:
44
44
 
45
- | API | Result |
46
- | --- | --- |
47
- | `calculateCost(model, usage, timestamp?)` | Updates and returns `usage.cost` using `model.cost`. |
48
- | `calculateUsageCost(cost, usage, timestamp?)` | Updates and returns `usage.cost` using a `ModelCost`. |
49
- | `calculateUncachedInputCost(cost, promptInputTokens, timestamp?)` | Returns the cost of a fully uncached prompt. |
50
- | `getTimeBasedPricingPeriod(cost, timestamp?)` | Returns `"peak"`, `"off-peak"`, or `undefined` without a schedule. |
51
- | `getNextTimeBasedPricingTransition(cost, timestamp?)` | Returns the next actual peak/off-peak change strictly after the timestamp, or `undefined` if none exists. |
45
+ | API | Result |
46
+ | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
47
+ | `calculateCost(model, usage, timestamp?)` | Updates and returns `usage.cost` using `model.cost`. |
48
+ | `calculateUsageCost(cost, usage, timestamp?)` | Updates and returns `usage.cost` using a `ModelCost`. |
49
+ | `calculateUncachedInputCost(cost, promptInputTokens, timestamp?)` | Returns the cost of a fully uncached prompt. |
50
+ | `getTimeBasedPricingPeriod(cost, timestamp?)` | Returns `"peak"`, `"off-peak"`, or `undefined` without a schedule. |
51
+ | `getNextTimeBasedPricingTransition(cost, timestamp?)` | Returns the next actual peak/off-peak change strictly after the timestamp, or `undefined` if none exists. |
52
52
 
53
53
  Timestamps are Unix milliseconds; omitted timestamps use the current time for scheduled pricing. Flat token prices are unaffected. Pricing selects the latest applicable effective rate card, then its long-context tier, then the peak/off-peak multiplier. A transition query concerns the recurring tariff, not dated rate-card changes.
54
54
 
@@ -5,7 +5,7 @@
5
5
  * `behavior.ts`, `resolve.ts`) exposes to consumers.
6
6
  */
7
7
  import type { Effort } from "../effort.js";
8
- import type { Api, ThinkingControlMode, TokenCost } from "../types.js";
8
+ import type { Api, KindApiKind, ThinkingControlMode, TokenCost } from "../types.js";
9
9
  import type { RevisionOp } from "./revision.js";
10
10
  /** Class-membership matcher kinds, most to least specific. */
11
11
  export type MatcherKind = "exact" | "bounded" | "namespace" | "prefix" | "glob";
@@ -620,7 +620,7 @@ export interface CompiledProvider {
620
620
  /** Present only for providers enrolled in `generate-models.ts` discovery. */
621
621
  discovery?: CompiledProviderDiscovery;
622
622
  /** Non-chat model kinds mapped to their runtime transport APIs. */
623
- kindApis?: Partial<Record<"image" | "tts" | "stt", Api>>;
623
+ kindApis?: Partial<Record<KindApiKind, Api>>;
624
624
  /** Authored bundled rows, when the provider cannot be discovered at generation time. */
625
625
  seed?: CompiledSeed;
626
626
  }
@@ -5,13 +5,16 @@ export type { KnownProvider } from "./provider-models/descriptors.js";
5
5
  export type KnownApi = "openai-completions" | "openai-responses" | "openrouter" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "gitlab-duo-agent" | "devin-agent";
6
6
  export type Api = KnownApi | (string & {});
7
7
  /** Catalog kinds used to isolate role-specific runners from session chat models. */
8
- export declare const MODEL_KINDS: readonly ["chat", "tiny", "image", "tts", "stt", "search", "judge"];
8
+ export declare const MODEL_KINDS: readonly ["chat", "tiny", "image", "tts", "stt", "search", "judge", "embedding", "rerank", "video"];
9
9
  /** Technical capability of a catalog model; absent model kinds mean chat. */
10
10
  export type ModelKind = (typeof MODEL_KINDS)[number];
11
+ /** Kinds a provider maps to a runner transport through `kind-apis` in its KDL; discovery drops rows of these kinds when the provider declares no API. */
12
+ export declare const KIND_API_KINDS: readonly ["image", "tts", "stt", "embedding", "rerank", "video"];
13
+ export type KindApiKind = (typeof KIND_API_KINDS)[number];
11
14
  /** Grounding transport available to chat models selected by the web role. */
12
15
  export type WebSearchGrounding = "gemini" | "anthropic" | "codex" | "xai" | "openrouter";
13
16
  /** Non-chat runner protocols accepted by catalog seeds, outside the chat dispatch union. */
14
- export declare const RUNNER_APIS: readonly ["local-inference", "web-search", "typesafe", "openrouter-decisions", "openai-images", "openrouter-images", "xai-tts", "openai-speech"];
17
+ export declare const RUNNER_APIS: readonly ["local-inference", "web-search", "typesafe", "openrouter-decisions", "openai-images", "openrouter-images", "xai-tts", "openai-speech", "openai-embeddings", "openrouter-rerank", "openrouter-video", "openai-transcriptions"];
15
18
  /** Resolve a model's kind while preserving chat semantics for existing catalog rows. */
16
19
  export declare function modelKind(model: Pick<Model, "kind">): ModelKind;
17
20
  /** Canonical thinking transport used by a model. */
package/package.json CHANGED
@@ -1,51 +1,25 @@
1
1
  {
2
- "type": "module",
3
2
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "18.2.7",
3
+ "version": "18.2.8",
5
4
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
5
+ "keywords": [
6
+ "ai",
7
+ "catalog",
8
+ "discovery",
9
+ "llm",
10
+ "models"
11
+ ],
6
12
  "homepage": "https://omp.sh",
7
- "author": "Stencil Labs, Inc.",
13
+ "bugs": {
14
+ "url": "https://github.com/can1357/oh-my-pi/issues"
15
+ },
8
16
  "license": "MIT",
17
+ "author": "Stencil Labs, Inc.",
9
18
  "repository": {
10
19
  "type": "git",
11
20
  "url": "git+https://github.com/can1357/oh-my-pi.git",
12
21
  "directory": "packages/catalog"
13
22
  },
14
- "bugs": {
15
- "url": "https://github.com/can1357/oh-my-pi/issues"
16
- },
17
- "keywords": [
18
- "ai",
19
- "llm",
20
- "models",
21
- "catalog",
22
- "discovery"
23
- ],
24
- "main": "./src/index.ts",
25
- "types": "./dist/types/index.d.ts",
26
- "scripts": {
27
- "check": "oxlint . && oxfmt --check --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts' && bun run check:types",
28
- "check:types": "tsgo -p tsconfig.json --noEmit",
29
- "lint": "oxlint .",
30
- "test": "bun test --parallel",
31
- "fix": "oxlint --fix --fix-suggestions . && bun run fmt",
32
- "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'",
33
- "gen:models": "bun scripts/generate-models.ts",
34
- "gen:compat": "bun scripts/compile-compat.ts",
35
- "gen:proto": "bun scripts/generate-protocols.ts"
36
- },
37
- "dependencies": {
38
- "@oh-my-pi/omptype": "18.2.7",
39
- "@oh-my-pi/pi-utils": "18.2.7"
40
- },
41
- "devDependencies": {
42
- "@bgotink/kdl": "0.4.0",
43
- "@oh-my-pi/pi-ai": "18.2.7",
44
- "@types/bun": "^1.3.14"
45
- },
46
- "engines": {
47
- "bun": ">=1.3.14"
48
- },
49
23
  "files": [
50
24
  "src",
51
25
  "README.md",
@@ -54,6 +28,9 @@
54
28
  "THIRD-PARTY-NOTICES.txt",
55
29
  "dist/types"
56
30
  ],
31
+ "type": "module",
32
+ "main": "./src/index.ts",
33
+ "types": "./dist/types/index.d.ts",
57
34
  "exports": {
58
35
  ".": {
59
36
  "types": "./dist/types/index.d.ts",
@@ -100,5 +77,28 @@
100
77
  "import": "./src/*.ts"
101
78
  },
102
79
  "./*.js": "./src/*.ts"
80
+ },
81
+ "scripts": {
82
+ "check": "oxlint . && oxfmt --check --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts' && bun run check:types",
83
+ "check:types": "tsgo -p tsconfig.json --noEmit",
84
+ "lint": "oxlint .",
85
+ "test": "bun test --parallel",
86
+ "fix": "oxlint --fix --fix-suggestions . && bun run fmt",
87
+ "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'",
88
+ "gen:models": "bun scripts/generate-models.ts",
89
+ "gen:compat": "bun scripts/compile-compat.ts",
90
+ "gen:proto": "bun scripts/generate-protocols.ts"
91
+ },
92
+ "dependencies": {
93
+ "@oh-my-pi/omptype": "18.2.8",
94
+ "@oh-my-pi/pi-utils": "18.2.8"
95
+ },
96
+ "devDependencies": {
97
+ "@bgotink/kdl": "0.4.0",
98
+ "@oh-my-pi/pi-ai": "18.2.8",
99
+ "@types/bun": "^1.3.14"
100
+ },
101
+ "engines": {
102
+ "bun": ">=1.3.14"
103
103
  }
104
104
  }
@@ -50,14 +50,14 @@ class "anthropic" {
50
50
 
51
51
  Classification trims and lowercases the full model identifier. The **bare name** is the segment after its final `/`. Matcher tokens are also lowercased while parsing.
52
52
 
53
- | Node | Rank | Match |
54
- | --- | ---: | --- |
55
- | `exact "token"` | 4 | The whole bare name equals `token`. |
56
- | `bounded "token"` | 3 | The bare name equals `token`, or starts with it followed by `-`, `_`, `.`, `:`, or an ASCII digit. |
57
- | `namespace "token"` | 2 | A non-empty `/`-separated segment of the full identifier equals `token`. |
58
- | `namespace "token" bounded=#true` | 2 | Split the full identifier on `/`, `.`, and `:`; a segment must satisfy the bounded rule above. This is the only matcher property. |
59
- | `prefix "token"` | 1 | The bare name starts with `token`. |
60
- | `glob "pattern"` | 0 | An anchored `*` wildcard match over the bare name. `*` spans any substring; all non-wildcard text remains anchored in order. |
53
+ | Node | Rank | Match |
54
+ | --------------------------------- | ---: | --------------------------------------------------------------------------------------------------------------------------------- |
55
+ | `exact "token"` | 4 | The whole bare name equals `token`. |
56
+ | `bounded "token"` | 3 | The bare name equals `token`, or starts with it followed by `-`, `_`, `.`, `:`, or an ASCII digit. |
57
+ | `namespace "token"` | 2 | A non-empty `/`-separated segment of the full identifier equals `token`. |
58
+ | `namespace "token" bounded=#true` | 2 | Split the full identifier on `/`, `.`, and `:`; a segment must satisfy the bounded rule above. This is the only matcher property. |
59
+ | `prefix "token"` | 1 | The bare name starts with `token`. |
60
+ | `glob "pattern"` | 0 | An anchored `*` wildcard match over the bare name. `*` spans any substring; all non-wildcard text remains anchored in order. |
61
61
 
62
62
  A class match is ranked by `(matcher-kind rank, token byte length)`. The greatest tuple wins. Equal tuples from different classes are an ambiguity error; source order is not a tiebreak. If nothing matches, classification returns class `unknown` with no family or revision.
63
63
 
@@ -93,16 +93,16 @@ revision skip-bare "o1" "o3" "o4"
93
93
 
94
94
  Optional properties are:
95
95
 
96
- | Property | Shape and meaning |
97
- | --- | --- |
98
- | `provider` | Exact provider key, compared case-insensitively. A matching provider-specific override wins over a provider-agnostic one. |
99
- | `logical` | Corrected logical model identifier. |
100
- | `class` | Corrected class ID; a non-empty string. |
101
- | `family` | Corrected product-family ID; a non-empty string. |
102
- | `revision` | One to three unsigned 8-bit components separated by `.` or `-`. |
103
- | `effort` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`. |
104
- | `thinking-variant` | Boolean marker for a separately exposed thinking sibling. |
105
- | `expires-at-ms` | Non-negative Unix time in milliseconds. The override is inactive when the observation time is at or after this value. |
96
+ | Property | Shape and meaning |
97
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
98
+ | `provider` | Exact provider key, compared case-insensitively. A matching provider-specific override wins over a provider-agnostic one. |
99
+ | `logical` | Corrected logical model identifier. |
100
+ | `class` | Corrected class ID; a non-empty string. |
101
+ | `family` | Corrected product-family ID; a non-empty string. |
102
+ | `revision` | One to three unsigned 8-bit components separated by `.` or `-`. |
103
+ | `effort` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`. |
104
+ | `thinking-variant` | Boolean marker for a separately exposed thinking sibling. |
105
+ | `expires-at-ms` | Non-negative Unix time in milliseconds. The override is inactive when the observation time is at or after this value. |
106
106
 
107
107
  The tuple `(provider, selector-kind, selector)` must also be unique, including provider-agnostic selectors. Active exact overrides take precedence over every glob. Within each selector kind, provider-scoped overrides precede provider-agnostic ones; matching globs rank by non-wildcard byte count. Equal-ranked globs are an ambiguity error, never resolved by declaration order. When no observation time is supplied, an expiring override remains active.
108
108
 
@@ -124,6 +124,7 @@ collapse {
124
124
  provider-alias "devin" "opus" "claude-opus-5"
125
125
  }
126
126
  ```
127
+
127
128
  `variant-family` declares one reviewed provider-scoped collapsed family: positional provider and logical id, `name=` display name, and a body of `members "a" "b" …` (wire ids in priority order), `route "<tier>" "<wire-id>"` per effort tier (`off` included), and optional `mode`, `efforts`, `default-level`, `default-member`, `retired-members`, `effort-budget "<tier>" <n>`, `requires-effort`, `suppress-when-off`, `no-thinking`, `preserve-absent-effort-routes`, and `extra-aliases`. A `{rev}` placeholder in the logical id makes the node a **template**: it is instantiated once per revision found in live ids (`gemini-{rev}-flash` matches `gemini-3.8-flash-low` → family `gemini-3.8-flash`), every wire id in the body and the `name=` carry the same placeholder, and an optional `revision=` constraint (`">=3.6"`) bounds the generations it applies to. A concrete family with the same instantiated id wins over the template. `provider-alias` maps one provider-scoped selector spelling onto a logical id without making it a family member.
128
129
 
129
130
  `thinking-suffix` accepts one non-empty suffix and no properties. `pair-token` declares bounded (possibly infix) tokens naming the thinking sibling of a live bare twin (`sonar-reasoning-pro` beside `sonar-pro`); it drives thinking-pair derivation only — never identity suffix collapse — and negated `no-`/`non-` forms never match. `effort-suffix` additionally requires `tier` with one of the effort values above, and may have `except-bare-prefix`. `routing-variant-suffix` takes one non-empty suffix followed by one or more provider IDs: a wire identifier carrying the suffix on one of those providers is a **routing variant** of its plain identifier — discovery derives base-model metadata from the plain bundled SKU while keeping the suffixed wire identifier for requests; routing variants never participate in effort collapse. `effort-lane-suffix` takes one non-empty lane suffix followed by one or more provider IDs, plus an optional `bare-prefix` gate: on a declared provider, an identifier ending in the lane suffix collapses the effort suffix wedged before the lane token while keeping the lane on the logical id. `effort-family` takes a provider, the canonical logical id, and zero or more exact aliases that fold onto it.
@@ -178,15 +179,15 @@ provider "openrouter" {
178
179
 
179
180
  ### Selectors and nesting
180
181
 
181
- | Selector | Form | Matching semantics |
182
- | --- | --- | --- |
183
- | `class` | `class "id" { ... }` | Exact class ID. At document root it may contain `on`, `on-api`, `family`, `revision`, and `models`. Under `provider` it may contain `family`, `revision`, and `models`. |
184
- | `provider` | `provider "id" { ... }` | Exact provider ID. It is root-only and may contain `class` and `models`. |
185
- | `on` | `on "provider-a" "provider-b" { ... }` | One or more provider IDs, combined as OR. It is allowed only under a root `class`, and may contain `family`, `revision`, and `models`. |
186
- | `on-api` | `on-api "adapter-a" "adapter-b" { ... }` | One or more request adapter IDs, combined as OR. It is allowed only under a root `class`, and may contain `family`, `revision`, and `models`. |
187
- | `family` | `family "id" { ... }` | Exact classified family ID. It may contain `revision` and `models`. A target with no family does not match. |
188
- | `revision` | `revision ">=2.5 <4" { ... }` | A non-empty, whitespace-separated conjunction of comparisons. It may contain `models`. A target with no revision does not match. |
189
- | `models` | `models "id" "vendor/*" { ... }` | One or more alternatives, combined as OR. It cannot contain another selector. `token="name"` matches an ASCII-case-insensitive token bounded by non-alphanumerics. |
182
+ | Selector | Form | Matching semantics |
183
+ | ---------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
184
+ | `class` | `class "id" { ... }` | Exact class ID. At document root it may contain `on`, `on-api`, `family`, `revision`, and `models`. Under `provider` it may contain `family`, `revision`, and `models`. |
185
+ | `provider` | `provider "id" { ... }` | Exact provider ID. It is root-only and may contain `class` and `models`. |
186
+ | `on` | `on "provider-a" "provider-b" { ... }` | One or more provider IDs, combined as OR. It is allowed only under a root `class`, and may contain `family`, `revision`, and `models`. |
187
+ | `on-api` | `on-api "adapter-a" "adapter-b" { ... }` | One or more request adapter IDs, combined as OR. It is allowed only under a root `class`, and may contain `family`, `revision`, and `models`. |
188
+ | `family` | `family "id" { ... }` | Exact classified family ID. It may contain `revision` and `models`. A target with no family does not match. |
189
+ | `revision` | `revision ">=2.5 <4" { ... }` | A non-empty, whitespace-separated conjunction of comparisons. It may contain `models`. A target with no revision does not match. |
190
+ | `models` | `models "id" "vendor/*" { ... }` | One or more alternatives, combined as OR. It cannot contain another selector. `token="name"` matches an ASCII-case-insensitive token bounded by non-alphanumerics. |
190
191
 
191
192
  Class, provider/`on`, `on-api`, and family selector values are compared exactly and case-sensitively to the structured resolve target. Revision operators are `>=`, `>`, `<=`, `<`, and `=`; operands have one to three dot-separated unsigned 8-bit components, omitted components zero.
192
193
 
@@ -401,17 +402,17 @@ Only `discovery` enrolls a provider in `generate-models.ts`; providers without i
401
402
 
402
403
  ### Seed rows
403
404
 
404
- A `seed` *defines* bundled rows for providers whose catalog cannot be discovered at generation time — credential-scoped rosters, unauthenticated regens, or models ahead of upstream catalogs. Every other stratum patches rows; this one authors them. Runtime model managers hand the rows to `staticModels` through `seedModels(provider)`; the generator bundles them per the seed's `bundle` policy. Values are literal — a seed never derives from another provider's row, and pricing is never borrowed.
405
+ A `seed` _defines_ bundled rows for providers whose catalog cannot be discovered at generation time — credential-scoped rosters, unauthenticated regens, or models ahead of upstream catalogs. Every other stratum patches rows; this one authors them. Runtime model managers hand the rows to `staticModels` through `seedModels(provider)`; the generator bundles them per the seed's `bundle` policy. Values are literal — a seed never derives from another provider's row, and pricing is never borrowed.
405
406
 
406
407
  `seed` properties: `api` and `base-url` are per-row defaults (a `model` may override either with the same property names); `bundle` defaults to `always`; `precedence="seed"` is optional. `model` takes the wire id positionally, requires `name=`, and its body MUST declare `reasoning`, `input` (`"text"` and/or `"image"`), `cost` (all four per-million rates), and `limits` (`context=` / `max-tokens=`, an omitted limit is `null`); `supports-tools #true` is optional. Any other directive is an axis from the cascade vocabulary: thinking axes become the row's explicit `thinking` (then `thinking-mode` and `thinking-efforts` are both required), wire axes become its explicit `compat` and must apply to the row's API, and catalog axes are rejected because they stay rule-owned in the cascade block. Explicit `thinking`/`compat` on a seed row win over the cascade exactly as they do for any authored spec.
407
408
 
408
409
  `bundle` decides when the generator includes the rows:
409
410
 
410
- | Policy | Rows enter the bundle |
411
- | --- | --- |
412
- | `always` | Every regeneration. Same-id upstream/discovery rows win dedup. |
411
+ | Policy | Rows enter the bundle |
412
+ | ---------- | ------------------------------------------------------------------------- |
413
+ | `always` | Every regeneration. Same-id upstream/discovery rows win dedup. |
413
414
  | `fallback` | Only when the provider's authoritative catalog discovery did not succeed. |
414
- | `empty` | Only when no other source produced a row for the provider. |
415
+ | `empty` | Only when no other source produced a row for the provider. |
415
416
 
416
417
  `precedence="seed"` prepends the rows after the previous-snapshot merge and cross-provider reference fills, so the authored row wins dedup and same-id rows on other hosts never overwrite its name or capabilities (QwenCloud Token Plan, Meta). The default `upstream` precedence appends before the snapshot merge, so the current seed — not a stale snapshot copy — is the fallback row.
417
418
 
@@ -4,7 +4,9 @@ provider "openai" {
4
4
  default-model "gpt-5.5"
5
5
  env "OPENAI_API_KEY"
6
6
  kind-apis {
7
+ embedding "openai-embeddings"
7
8
  image "openai-responses"
9
+ stt "openai-transcriptions"
8
10
  }
9
11
 
10
12
  // Daybreak models are approval-gated first-party Responses models that are
@@ -33,6 +35,62 @@ provider "openai" {
33
35
  cost input=12.5 output=75 cache-read=1.25 cache-write=15.625
34
36
  limits context=400000 max-tokens=128000
35
37
  }
38
+
39
+ // Catalog cost fields are per 1M tokens: GPT transcribers map documented
40
+ // audio-input/output-token rates directly. Whisper's $0.006/min duration
41
+ // tariff has no token-cost representation, so it remains zero and the
42
+ // transcription response's provider-reported usage/cost is authoritative.
43
+ model "whisper-1" name="Whisper 1" api="openai-transcriptions" base-url="https://api.openai.com/v1" {
44
+ reasoning #false
45
+ input "text"
46
+ cost input=0 output=0 cache-read=0 cache-write=0
47
+ limits
48
+ supports-tools #false
49
+ }
50
+ model "gpt-4o-transcribe" name="GPT-4o Transcribe" api="openai-transcriptions" base-url="https://api.openai.com/v1" {
51
+ reasoning #false
52
+ input "text"
53
+ cost input=2.5 output=10 cache-read=0 cache-write=0
54
+ limits context=16000 max-tokens=2000
55
+ supports-tools #false
56
+ }
57
+ model "gpt-4o-mini-transcribe" name="GPT-4o Mini Transcribe" api="openai-transcriptions" base-url="https://api.openai.com/v1" {
58
+ reasoning #false
59
+ input "text"
60
+ cost input=1.25 output=5 cache-read=0 cache-write=0
61
+ limits context=16000 max-tokens=2000
62
+ supports-tools #false
63
+ }
64
+ // OpenAI publishes embedding prices per million input tokens; embedding
65
+ // responses have no output-token charge.
66
+ model "text-embedding-3-small" name="Text Embedding 3 Small" api="openai-embeddings" base-url="https://api.openai.com/v1" {
67
+ reasoning #false
68
+ input "text"
69
+ cost input=0.02 output=0 cache-read=0 cache-write=0
70
+ limits context=8192
71
+ supports-tools #false
72
+ }
73
+ model "text-embedding-3-large" name="Text Embedding 3 Large" api="openai-embeddings" base-url="https://api.openai.com/v1" {
74
+ reasoning #false
75
+ input "text"
76
+ cost input=0.13 output=0 cache-read=0 cache-write=0
77
+ limits context=8192
78
+ supports-tools #false
79
+ }
80
+ model "text-embedding-ada-002" name="Text Embedding Ada 002" api="openai-embeddings" base-url="https://api.openai.com/v1" {
81
+ reasoning #false
82
+ input "text"
83
+ cost input=0.1 output=0 cache-read=0 cache-write=0
84
+ limits context=8192
85
+ supports-tools #false
86
+ }
87
+ }
88
+
89
+ models "whisper-1" "gpt-4o-transcribe" "gpt-4o-mini-transcribe" {
90
+ kind "stt"
91
+ }
92
+ models "text-embedding-3-small" "text-embedding-3-large" "text-embedding-ada-002" {
93
+ kind "embedding"
36
94
  }
37
95
 
38
96
  // Replaces the strict-mode provider whitelist entry.
@@ -5,7 +5,12 @@ provider "openrouter" {
5
5
  env "OPENROUTER_API_KEY"
6
6
  discovery label="OpenRouter" allow-unauthenticated=#true
7
7
  kind-apis {
8
+ embedding "openai-embeddings"
8
9
  image "openrouter-images"
10
+ rerank "openrouter-rerank"
11
+ video "openrouter-video"
12
+ tts "openai-speech"
13
+ stt "openai-transcriptions"
9
14
  }
10
15
  web-search "openrouter"
11
16
 
@@ -19,10 +24,113 @@ provider "openrouter" {
19
24
  cost input=0.042 output=0 cache-read=0 cache-write=0
20
25
  limits context=32000 max-tokens=28800
21
26
  }
27
+
28
+ // OpenRouter's STT roster mixes per-token and duration-based prices.
29
+ // Token-priced GPT rows map per-token rates to catalog per-million costs;
30
+ // duration-priced rows remain zero because ModelCost has no seconds axis.
31
+ model "openai/whisper-1" name="OpenAI: Whisper 1" api="openai-transcriptions" base-url="https://openrouter.ai/api/v1" {
32
+ reasoning #false
33
+ input "text"
34
+ cost input=0 output=0 cache-read=0 cache-write=0
35
+ limits
36
+ supports-tools #false
37
+ }
38
+ model "openai/whisper-large-v3" name="OpenAI: Whisper Large V3" api="openai-transcriptions" base-url="https://openrouter.ai/api/v1" {
39
+ reasoning #false
40
+ input "text"
41
+ cost input=0 output=0 cache-read=0 cache-write=0
42
+ limits
43
+ supports-tools #false
44
+ }
45
+ model "openai/gpt-4o-transcribe" name="OpenAI: GPT-4o Transcribe" api="openai-transcriptions" base-url="https://openrouter.ai/api/v1" {
46
+ reasoning #false
47
+ input "text"
48
+ cost input=2.5 output=10 cache-read=0 cache-write=0
49
+ limits context=128000 max-tokens=115200
50
+ supports-tools #false
51
+ }
52
+ model "microsoft/mai-transcribe-1.5" name="Microsoft AI: MAI-Transcribe 1.5" api="openai-transcriptions" base-url="https://openrouter.ai/api/v1" {
53
+ reasoning #false
54
+ input "text"
55
+ cost input=0 output=0 cache-read=0 cache-write=0
56
+ limits
57
+ supports-tools #false
58
+ }
59
+ model "microsoft/mai-transcribe-2" name="Microsoft AI: MAI-Transcribe 2" api="openai-transcriptions" base-url="https://openrouter.ai/api/v1" {
60
+ reasoning #false
61
+ input "text"
62
+ cost input=0 output=0 cache-read=0 cache-write=0
63
+ limits
64
+ supports-tools #false
65
+ }
66
+
67
+ // OpenRouter bills reranking per 1,000 searches, which has no catalog cost
68
+ // axis. Keep token costs at zero; the provider-reported response cost is authoritative.
69
+ model "cohere/rerank-v3.5" name="Cohere: Rerank v3.5" api="openrouter-rerank" base-url="https://openrouter.ai/api/v1" {
70
+ reasoning #false
71
+ input "text"
72
+ cost input=0 output=0 cache-read=0 cache-write=0
73
+ limits context=4096 max-tokens=3686
74
+ supports-tools #false
75
+ }
76
+ // Bundled fallbacks keep the gateway useful offline; live
77
+ // `/embeddings/models` discovery refreshes this roster and its per-token
78
+ // pricing. Catalog token costs are per million input tokens.
79
+ model "openai/text-embedding-3-small" name="OpenAI: Text Embedding 3 Small" api="openai-embeddings" base-url="https://openrouter.ai/api/v1" {
80
+ reasoning #false
81
+ input "text"
82
+ cost input=0.02 output=0 cache-read=0 cache-write=0
83
+ limits context=8192
84
+ supports-tools #false
85
+ }
86
+ model "qwen/qwen3-embedding-8b" name="Qwen: Qwen3 Embedding 8B" api="openai-embeddings" base-url="https://openrouter.ai/api/v1" {
87
+ reasoning #false
88
+ input "text"
89
+ cost input=0.01 output=0 cache-read=0 cache-write=0
90
+ limits context=32768
91
+ supports-tools #false
92
+ }
93
+ // OpenRouter bills generated video by output second and resolution/SKU,
94
+ // which ModelCost cannot represent. Keep token costs at zero; poll-reported
95
+ // cost is authoritative. Live `/videos/models` discovery refreshes the roster.
96
+ model "google/veo-3.1" name="Google: Veo 3.1" api="openrouter-video" base-url="https://openrouter.ai/api/v1" {
97
+ reasoning #false
98
+ input "text" "image"
99
+ cost input=0 output=0 cache-read=0 cache-write=0
100
+ limits
101
+ supports-tools #false
102
+ }
103
+ model "minimax/hailuo-3" name="MiniMax: H3" api="openrouter-video" base-url="https://openrouter.ai/api/v1" {
104
+ reasoning #false
105
+ input "text" "image"
106
+ cost input=0 output=0 cache-read=0 cache-write=0
107
+ limits
108
+ supports-tools #false
109
+ }
110
+ model "alibaba/wan-2.7" name="Alibaba: Wan 2.7" api="openrouter-video" base-url="https://openrouter.ai/api/v1" {
111
+ reasoning #false
112
+ input "text" "image"
113
+ cost input=0 output=0 cache-read=0 cache-write=0
114
+ limits
115
+ supports-tools #false
116
+ }
22
117
  }
23
118
  models "~typesafe/*" "typesafe/*" {
24
119
  kind "judge"
25
120
  }
121
+ models "cohere/rerank-v3.5" {
122
+ kind "rerank"
123
+ }
124
+ models "openai/text-embedding-3-small" "qwen/qwen3-embedding-8b" {
125
+ kind "embedding"
126
+ }
127
+ models "google/veo-3.1" "minimax/hailuo-3" "alibaba/wan-2.7" {
128
+ kind "video"
129
+ }
130
+ models "openai/whisper-1" "openai/whisper-large-v3" "openai/gpt-4o-transcribe" \
131
+ "microsoft/mai-transcribe-1.5" "microsoft/mai-transcribe-2" {
132
+ kind "stt"
133
+ }
26
134
 
27
135
  // Replaces the OpenRouter provider wire-model-id dispatch branch.
28
136
  wire-model-id-mode "openrouter"