feat(opencode-plugin): provider-tag becomes a prefix + traffic-light compression intensity emoji (#2604)

Integrated into release/v3.8.2
This commit is contained in:
M.M
2026-05-23 01:14:39 +02:00
committed by GitHub
parent df141c40f7
commit f08379986a
4 changed files with 257 additions and 100 deletions

View File

@@ -119,21 +119,21 @@ npm install --prefix ~/.config/opencode/plugins/omniroute-opencode-plugin-prepro
## Features
| Feature | What it does | Hook |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| Dynamic `/v1/models` | Pulls live catalog (455+ entries on prod) on each refresh, TTL-cached | `provider.models` |
| Variants pass-through | `-low`/`-medium`/`-high`/`-thinking` ship as first-class IDs from OmniRoute (no client synthesis) | `provider.models` |
| Combo LCD aggregation | Combos appear with intersected capabilities + min context/output across members | `provider.models` + `config` |
| `combo/<slug>` namespace + `Combo: ` prefix | Combos surface under `combo/claude-primary` (not the upstream UUID) and the picker shows `Combo: claude-primary` so they stand apart from raw provider/model pairs | both hooks |
| Nice names + cost | `/api/pricing/models` display names AND `/api/pricing` per-million-token cost overlaid onto the live catalog | both hooks |
| Compression pipeline tags | Combo names get tagged with their compression pipeline (e.g. `Combo: claude-primary [rtk:standard → caveman:full]`) when `features.compressionMetadata: true` | both hooks |
| Provider-tag suffix | Append upstream provider label to enriched names (e.g. `Claude Opus 4.7 · Claude` vs `Claude Opus 4.7 · Kiro`) so same-id models routed via different upstream connections are visibly distinct in the picker (default-on, opt-out via `features.providerTag: false`) | both hooks |
| Usable-only filter | Filter to providers with at least one healthy connection in `/api/providers` (opt-in via `features.usableOnly`) | both hooks |
| Disk-cache fallback | Last-known-good catalog persisted to disk; hydrates on a cold start when `/v1/models` is unreachable (default-on, opt-out via `features.diskCache: false`) | `config` |
| Bearer injection + suffix-spoof guard | Adds `Authorization` on baseURL-matched requests only | `auth.loader.fetch` |
| Gemini schema sanitization | Strips `$schema`/`$ref`/`additionalProperties` for `gemini-*`/`google-vertex-gemini/*` | `auth.loader.fetch` wrap |
| Multi-instance | Each plugin entry binds to its own `providerId`; closures isolated | factory |
| Config-hook shim | OC ≤1.15.5 fallback: writes static catalog into `config.provider[id]` (config hook is the only one that fires in `serve` mode on these versions) | `config` |
| Feature | What it does | Hook |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------- |
| Dynamic `/v1/models` | Pulls live catalog (455+ entries on prod) on each refresh, TTL-cached | `provider.models` |
| Variants pass-through | `-low`/`-medium`/`-high`/`-thinking` ship as first-class IDs from OmniRoute (no client synthesis) | `provider.models` |
| Combo LCD aggregation | Combos appear with intersected capabilities + min context/output across members | `provider.models` + `config` |
| `combo/<slug>` namespace + `Combo: ` prefix | Combos surface under `combo/claude-primary` (not the upstream UUID) and the picker shows `Combo: claude-primary` so they stand apart from raw provider/model pairs | both hooks |
| Nice names + cost | `/api/pricing/models` display names AND `/api/pricing` per-million-token cost overlaid onto the live catalog | both hooks |
| Compression pipeline tags | Combo names get tagged with their compression pipeline (e.g. `Combo: claude-primary [rtk🟡 → caveman🟠]`) when `features.compressionMetadata: true`. Intensity tokens render as a traffic-light emoji: 🟢 lite/minimal · 🟡 standard · 🟠 aggressive/full · 🔴 ultra | both hooks |
| Provider-tag prefix | Prepend short upstream-provider label to enriched names (e.g. `Claude - Claude Opus 4.7` vs `Kiro - Claude Opus 4.7`, `GHM - GPT 5`) so same-id models routed via different upstream connections group visibly in the picker (default-on, opt-out via `features.providerTag: false`) | both hooks |
| Usable-only filter | Filter to providers with at least one healthy connection in `/api/providers` (opt-in via `features.usableOnly`) | both hooks |
| Disk-cache fallback | Last-known-good catalog persisted to disk; hydrates on a cold start when `/v1/models` is unreachable (default-on, opt-out via `features.diskCache: false`) | `config` |
| Bearer injection + suffix-spoof guard | Adds `Authorization` on baseURL-matched requests only | `auth.loader.fetch` |
| Gemini schema sanitization | Strips `$schema`/`$ref`/`additionalProperties` for `gemini-*`/`google-vertex-gemini/*` | `auth.loader.fetch` wrap |
| Multi-instance | Each plugin entry binds to its own `providerId`; closures isolated | factory |
| Config-hook shim | OC ≤1.15.5 fallback: writes static catalog into `config.provider[id]` (config hook is the only one that fires in `serve` mode on these versions) | `config` |
## Plugin options
@@ -149,18 +149,18 @@ npm install --prefix ~/.config/opencode/plugins/omniroute-opencode-plugin-prepro
Every field is optional. Defaults mirror v0.1.0 behaviour so existing `opencode.json` files do not need to change.
| Feature | Type | Default | What it does |
| --------------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `combos` | `boolean` | `true` | Discover `/api/combos` and surface them as pseudo-models with LCD capabilities. Combos are keyed under the `combo/<slug>` namespace and labelled `Combo: <name>` in the model picker so they're distinguishable from raw provider/model pairs. |
| `enrichment` | `boolean` | `true` | Pull display names from `/api/pricing/models` AND per-million-token pricing (`input`, `output`, `cached``cacheRead`, `cache_creation``cacheWrite`) from `/api/pricing`, then overlay both onto the live catalog (so the UI shows `Claude 4.7 Opus` with `cost.input: 5`, `cost.output: 25` instead of raw IDs and zeroed cost). |
| `compressionMetadata` | `boolean` | `false` | Pull `/api/context/combos` so combo names get tagged with their compression pipeline, e.g. `Combo: claude-primary [rtk:standard → caveman:full]` |
| `providerTag` | `boolean` | `true` | Append the upstream provider's human label (from `/api/pricing/models[<alias>].name`) to the enriched display name with a middle-dot separator, so `cc/claude-opus-4-7 → Claude Opus 4.7 · Claude` differs visibly from `kr/claude-opus-4-7 → Claude Opus 4.7 · Kiro` in the OC TUI model picker. Idempotent. Combos are intentionally skipped (the `Combo: ` prefix already conveys multi-upstream). No-op when `providerDisplayName` is missing from the enrichment entry. |
| `usableOnly` | `boolean` | `false` | Read `/api/providers` and filter the catalog to providers that have at least one connection with `isActive: true` AND `testStatus: 'active'`. Subtract-filter semantics: providers unknown to BOTH the pricing-models catalog AND the connection table pass through (so synthetic prefixes like `agentrouter/*` survive). On fetch failure the filter is disabled for the refresh — never hides the whole catalog. |
| `diskCache` | `boolean` | `true` | Persist the last successful `/v1/models` + `/api/combos` + enrichment + connections + compression snapshot to `${OPENCODE_DATA_DIR ?? ~/.local/share/opencode}/plugins/omniroute-<providerId>.json`. On a subsequent cold start where `/v1/models` throws (network down / IP whitelist drop / 5xx) the static block hydrates from the snapshot so OC's model picker survives offline. Soft-fail on read/write — never blocks publishing. |
| `geminiSanitization` | `boolean` | `true` | Strip `$schema`/`$ref`/`additionalProperties` from tool params when the model id matches `gemini` |
| `mcpAutoEmit` | `boolean` | `false` | Auto-write an `mcp.<providerId>` remote entry into the OC config pointing at `<baseURL>/api/mcp/stream` with the resolved Bearer token |
| `mcpToken` | `string` | _unset_ | Optional separate Bearer for the auto-emitted MCP entry. Falls back to the provider's `apiKey` (from `auth.json`) when unset |
| `fetchInterceptor` | `boolean` | `true` | Inject `Authorization: Bearer` + default `Content-Type` on every outbound request targeting `baseURL` (suffix-spoof guarded) |
| Feature | Type | Default | What it does |
| --------------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `combos` | `boolean` | `true` | Discover `/api/combos` and surface them as pseudo-models with LCD capabilities. Combos are keyed under the `combo/<slug>` namespace and labelled `Combo: <name>` in the model picker so they're distinguishable from raw provider/model pairs. |
| `enrichment` | `boolean` | `true` | Pull display names from `/api/pricing/models` AND per-million-token pricing (`input`, `output`, `cached``cacheRead`, `cache_creation``cacheWrite`) from `/api/pricing`, then overlay both onto the live catalog (so the UI shows `Claude 4.7 Opus` with `cost.input: 5`, `cost.output: 25` instead of raw IDs and zeroed cost). |
| `compressionMetadata` | `boolean` | `false` | Pull `/api/context/combos` so combo names get tagged with their compression pipeline, e.g. `Combo: claude-primary [rtk🟡 → caveman🟠]`. Intensity tokens render as traffic-light emoji (🟢 lite/minimal · 🟡 standard · 🟠 aggressive/full · 🔴 ultra) so the picker advertises "how compressed" each combo is at a glance. |
| `providerTag` | `boolean` | `true` | Prepend a short upstream-provider label to the enriched display name with `" - "` separator, so `cc/claude-opus-4-7 → Claude - Claude Opus 4.7` differs visibly from `kr/claude-opus-4-7 → Kiro - Claude Opus 4.7` in the OC TUI model picker. Label resolution: use `/api/pricing/models[<alias>].name` verbatim when ≤8 chars (e.g. `Claude`, `Kiro`, `Codex`, `Qwen`), otherwise fall back to `UPPER(alias)` (e.g. `GitHub Models``GHM`, `Gemini-cli``GEMINI-CLI`). Idempotent. Combos intentionally skipped (the `Combo: ` prefix already conveys multi-upstream). |
| `usableOnly` | `boolean` | `false` | Read `/api/providers` and filter the catalog to providers that have at least one connection with `isActive: true` AND `testStatus: 'active'`. Subtract-filter semantics: providers unknown to BOTH the pricing-models catalog AND the connection table pass through (so synthetic prefixes like `agentrouter/*` survive). On fetch failure the filter is disabled for the refresh — never hides the whole catalog. |
| `diskCache` | `boolean` | `true` | Persist the last successful `/v1/models` + `/api/combos` + enrichment + connections + compression snapshot to `${OPENCODE_DATA_DIR ?? ~/.local/share/opencode}/plugins/omniroute-<providerId>.json`. On a subsequent cold start where `/v1/models` throws (network down / IP whitelist drop / 5xx) the static block hydrates from the snapshot so OC's model picker survives offline. Soft-fail on read/write — never blocks publishing. |
| `geminiSanitization` | `boolean` | `true` | Strip `$schema`/`$ref`/`additionalProperties` from tool params when the model id matches `gemini` |
| `mcpAutoEmit` | `boolean` | `false` | Auto-write an `mcp.<providerId>` remote entry into the OC config pointing at `<baseURL>/api/mcp/stream` with the resolved Bearer token |
| `mcpToken` | `string` | _unset_ | Optional separate Bearer for the auto-emitted MCP entry. Falls back to the provider's `apiKey` (from `auth.json`) when unset |
| `fetchInterceptor` | `boolean` | `true` | Inject `Authorization: Bearer` + default `Content-Type` on every outbound request targeting `baseURL` (suffix-spoof guarded) |
#### Example — enrichment + compression tags + MCP auto-emit
@@ -224,8 +224,8 @@ If you want a narrower-scoped Bearer for MCP (different from the chat/inference
- `usableOnly: true` drops models whose canonical provider has no healthy connection in your OmniRoute instance — your `/models` picker stays focused on what you can actually call.
- `diskCache: true` (default) writes a snapshot to `${OPENCODE_DATA_DIR}/plugins/omniroute-<providerId>.json` on every healthy refresh. On a cold start where `/v1/models` is unreachable (laptop offline, IP whitelist drop), the snapshot hydrates the static block so OC still shows the catalog instead of a stub.
- `compressionMetadata: true` annotates combo display names with their pipeline (e.g. `Combo: claude-primary [rtk:standard → caveman:full]`) so the picker advertises which compression each combo applies.
- `providerTag: true` (default) appends the upstream provider label after the model's friendly name, so the picker shows `Claude Opus 4.7 · Claude` for `cc/claude-opus-4-7` and `Claude Opus 4.7 · Kiro` for `kr/claude-opus-4-7`. Critical when the same model id is sold through multiple upstream connections with different cost/auth/rate-limit profiles. Set to `false` to keep the pre-v3.8.3 unsuffixed format.
- `compressionMetadata: true` annotates combo display names with their pipeline using traffic-light emoji for intensity (e.g. `Combo: claude-primary [rtk🟡 → caveman🟠]`) so the picker advertises which compression each combo applies and how heavy it is at a glance. Palette: 🟢 lite/minimal · 🟡 standard · 🟠 aggressive/full · 🔴 ultra. Unknown intensities fall through to raw text (`[rtk:custom-thing]`) so the plugin never hides a value OmniRoute knows but the plugin doesn't.
- `providerTag: true` (default) prepends a short upstream-provider label so the picker shows `Claude - Claude Opus 4.7` for `cc/claude-opus-4-7`, `Kiro - Claude Opus 4.7` for `kr/claude-opus-4-7`, and `GHM - GPT 5` for `ghm/gpt-5` (slot.name `GitHub Models` > 8 chars → abbreviated). Critical when the same model id is sold through multiple upstream connections with different cost/auth/rate-limit profiles. Set to `false` to keep the pre-v3.8.3 unsuffixed format.
## Comparison vs `@omniroute/opencode-provider`

View File

@@ -1068,38 +1068,82 @@ export const defaultOmniRouteEnrichmentFetcher: OmniRouteEnrichmentFetcher = asy
};
/**
* Separator used by `applyProviderTag` between the enriched model name
* and the upstream provider label. Middle-dot (U+00B7) chosen for visual
* compactness in narrow TUI columns; trivially distinct from any model
* id character so it survives a roundtrip through `model.name`.
* Separator used by `applyProviderTag` between the upstream provider
* label (prefix) and the enriched model name. ASCII hyphen with
* surrounding spaces — terminal-safe everywhere, never collides with
* a model id (those use slashes / dots / underscores).
*
* Layout: `<short-label> - <model name>` (label leads so column scans
* group by provider — e.g. `Claude - Claude Opus 4.7`,
* `Kiro - Claude Opus 4.7`).
*/
export const PROVIDER_TAG_SEPARATOR = " · ";
export const PROVIDER_TAG_SEPARATOR = " - ";
/** Threshold beyond which `providerDisplayName` is abbreviated to UPPER(alias). */
const PROVIDER_LABEL_MAX_CHARS = 8;
/**
* Append the upstream provider label to `model.name` so the OC TUI picker
* can differentiate the same model id sold through different upstream
* connections (e.g. `cc/claude-opus-4-7` via Anthropic vs `kr/claude-opus-4-7`
* via Kiro). Mutates the model in place and is idempotent — running twice
* never double-suffixes. No-op when:
* Pick the short label for an upstream provider that goes into the
* `<label> · <model>` prefix.
*
* Rule (matches user spec — no hardcoded registry, fully data-driven):
*
* 1. Trim `enrichment.providerDisplayName` (= `/api/pricing/models[<alias>].name`).
* 2. If the trimmed label is non-empty AND ≤ {@link PROVIDER_LABEL_MAX_CHARS},
* use it verbatim (e.g. `Claude`, `Kiro`, `Codex`, `Qwen`).
* 3. Otherwise fall back to `UPPER(enrichment.providerAlias)` so long
* slot.names (`GitHub Models`, `Gemini-cli`) compress to `GHM`,
* `GEMINI-CLI`.
* 4. If neither field is usable, return `undefined` (caller should
* skip the prefix decoration).
*/
export function shortProviderLabel(
enrichment: OmniRouteEnrichmentEntry | undefined
): string | undefined {
if (!enrichment) return undefined;
const raw =
typeof enrichment.providerDisplayName === "string" ? enrichment.providerDisplayName.trim() : "";
if (raw.length > 0 && raw.length <= PROVIDER_LABEL_MAX_CHARS) return raw;
const alias = typeof enrichment.providerAlias === "string" ? enrichment.providerAlias.trim() : "";
if (alias.length > 0) return alias.toUpperCase();
// Tolerate "label too long + no alias" by falling back to the long
// label itself — better than dropping the prefix entirely. Rare case.
return raw.length > 0 ? raw : undefined;
}
/**
* Prepend the upstream provider label to `model.name` so the OC TUI
* picker can differentiate the same model id sold through different
* upstream connections (e.g. `cc/claude-opus-4-7` via Anthropic
* vs `kr/claude-opus-4-7` via Kiro). Result shape:
*
* `<label>${PROVIDER_TAG_SEPARATOR}<enriched name>`
* → `Claude - Claude Opus 4.7`
* → `Kiro - Claude Opus 4.7`
* → `GHM - GPT 5` (slot.name "GitHub Models" > 8 chars → UPPER(alias))
*
* Mutates the model in place and is idempotent — running twice never
* double-prefixes. No-op when:
*
* - `enrichment` is undefined,
* - `enrichment.providerDisplayName` is missing/empty,
* - the current `model.name` already ends with the suffix.
* - {@link shortProviderLabel} returns `undefined`
* (no `providerDisplayName` AND no `providerAlias`),
* - the current `model.name` already starts with the prefix.
*
* Combos are intentionally skipped by callers (they're multi-upstream by
* definition; the `Combo: ` prefix conveys that). Raw models call this
* after `applyEnrichment` so the tag layers on top of the friendly name.
* Combos are intentionally skipped by callers (they're multi-upstream
* by definition; the `Combo: ` prefix conveys that). Raw models call
* this after `applyEnrichment` so the tag layers on top of the
* friendly name.
*/
export function applyProviderTag(
model: ModelV2,
enrichment: OmniRouteEnrichmentEntry | undefined
): ModelV2 {
if (!enrichment) return model;
const tag = enrichment.providerDisplayName;
if (typeof tag !== "string" || tag.trim().length === 0) return model;
const suffix = `${PROVIDER_TAG_SEPARATOR}${tag}`;
if (model.name.endsWith(suffix)) return model;
model.name = `${model.name}${suffix}`;
const label = shortProviderLabel(enrichment);
if (!label) return model;
const prefix = `${label}${PROVIDER_TAG_SEPARATOR}`;
if (model.name.startsWith(prefix)) return model;
model.name = `${prefix}${model.name}`;
return model;
}
@@ -1230,14 +1274,52 @@ export const defaultOmniRouteCompressionMetaFetcher: OmniRouteCompressionMetaFet
};
/**
* Format a compression pipeline as a short human-readable string for combo
* `name` decoration. Example: `[rtk:standard → caveman:full]`.
* Map of well-known compression-intensity tokens to a single emoji
* conveying "how much" compression is applied. Traffic-light palette:
*
* 🟢 minimal / lite — almost no loss
* 🟡 standard — balanced
* 🟠 aggressive / full — heavy
* 🔴 ultra — extreme
*
* Lookup is case-insensitive. Unknown intensities fall through to the
* raw text form (`engine:<intensity>`) so we never hide a value that
* OmniRoute knows but the plugin doesn't.
*
* Exported for callers (and tests) that want to assemble their own
* pipeline strings.
*/
export const COMPRESSION_INTENSITY_EMOJI: Record<string, string> = {
minimal: "🟢",
lite: "🟢",
standard: "🟡",
aggressive: "🟠",
full: "🟠",
ultra: "🔴",
};
/**
* Format a compression pipeline as a short human-readable string for
* combo `name` decoration. Intensity tokens render as a traffic-light
* emoji so a column scan reveals "how compressed" the combo is at a
* glance:
*
* `[rtk🟡 → caveman🟠]` (rtk:standard → caveman:full)
* `[rtk🔴]` (rtk:ultra, single-step)
* `[caveman]` (engine without intensity, no emoji)
* `[rtk:custom-thing]` (unknown intensity, raw-text fallback)
*/
export function formatCompressionPipeline(pipeline: OmniRouteCompressionStep[]): string {
if (!pipeline || pipeline.length === 0) return "";
return (
"[" +
pipeline.map((s) => (s.intensity ? `${s.engine}:${s.intensity}` : s.engine)).join(" → ") +
pipeline
.map((s) => {
if (!s.intensity) return s.engine;
const emoji = COMPRESSION_INTENSITY_EMOJI[s.intensity.toLowerCase()];
return emoji ? `${s.engine}${emoji}` : `${s.engine}:${s.intensity}`;
})
.join(" → ") +
"]"
);
}
@@ -1789,10 +1871,10 @@ export function createOmniRouteProviderHook(
});
const enrichEntry = rawEnrichment.get(entry.id);
applyEnrichment(model, enrichEntry);
// Append upstream provider label (e.g. `Claude Opus 4.7 · Claude`)
// so the picker can differentiate same-id models routed through
// different upstream connections. Idempotent + gated by
// `features.providerTag` (default-on). Combos skip this on purpose.
// Prepend upstream provider label (e.g. `Claude - Claude Opus 4.7`)
// so the picker groups same-model rows by upstream connection.
// Idempotent + gated by `features.providerTag` (default-on).
// Combos skip this on purpose.
if (wantProviderTag) applyProviderTag(model, enrichEntry);
models[entry.id] = model;
}
@@ -2400,8 +2482,8 @@ export function buildStaticProviderEntry(
? usableProviderAliasSet(connections, enrichment)
: undefined;
// Provider-tag suffix — default-on, opt-out via `features.providerTag: false`.
// Appends e.g. ` · Claude` to enriched raw-model names so the picker can
// tell `cc/claude-opus-4-7` (Anthropic) apart from `kr/claude-opus-4-7`
// Prepends e.g. `Claude - ` to enriched raw-model names so the picker
// can tell `cc/claude-opus-4-7` (Anthropic) apart from `kr/claude-opus-4-7`
// (Kiro). Combos skip this by design.
const wantProviderTag = opts.features?.providerTag !== false;
@@ -2435,15 +2517,15 @@ export function buildStaticProviderEntry(
const enrichmentEntry = enrichment?.get(raw.id);
const enrichmentName = enrichmentEntry?.name;
let displayName = enrichmentName && enrichmentName.length > 0 ? enrichmentName : raw.id;
// Provider-tag suffix — append upstream provider label so the picker
// can differentiate same-id models routed through different upstream
// connections (mirrors `applyProviderTag` used in the dynamic hook).
// Idempotent: skip when the name already ends with the suffix.
// Provider-tag PREFIX — `<label> - <name>` so the picker groups by
// upstream provider when scanning a column of model names. Mirrors
// `applyProviderTag` used in the dynamic hook. Idempotent: skip
// when the name already starts with the prefix.
if (wantProviderTag) {
const tag = enrichmentEntry?.providerDisplayName;
if (typeof tag === "string" && tag.trim().length > 0) {
const suffix = `${PROVIDER_TAG_SEPARATOR}${tag}`;
if (!displayName.endsWith(suffix)) displayName = `${displayName}${suffix}`;
const label = shortProviderLabel(enrichmentEntry);
if (label) {
const prefix = `${label}${PROVIDER_TAG_SEPARATOR}`;
if (!displayName.startsWith(prefix)) displayName = `${prefix}${displayName}`;
}
}
const entry: OmniRouteStaticModelEntry = { name: displayName };

View File

@@ -1081,7 +1081,7 @@ test("config: cached rawEnrichment from earlier provider hook is reused (no refe
// (Anthropic) from `kr/claude-opus-4-7` (Kiro) etc.
// ─────────────────────────────────────────────────────────────────────
test("config: providerTag (default-on) appends ' · <provider>' to enriched raw-model names", async () => {
test("config: providerTag (default-on) prepends '<provider> - ' to enriched raw-model names", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
@@ -1121,8 +1121,8 @@ test("config: providerTag (default-on) appends ' · <provider>' to enriched raw-
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.ok(entry);
assert.equal(entry.models["claude-sonnet-4-6"].name, "Claude Sonnet 4.6 · Claude");
assert.equal(entry.models["gemini-3-flash"].name, "Gemini 3 Flash · Gemini-cli");
assert.equal(entry.models["claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
assert.equal(entry.models["gemini-3-flash"].name, "GEMINI-CLI - Gemini 3 Flash");
// Combos stay untouched — `Combo: ` prefix already conveys multi-upstream.
assert.equal(entry.models["combo/claude-tier"].name, "Combo: Claude Tier");
});
@@ -1135,10 +1135,7 @@ test("config: providerTag=false suppresses the suffix", async () => {
const combosFetcher = stubCombosFetcher([]);
const enrichmentFetcher = stubEnrichmentFetcher(
new Map<string, OmniRouteEnrichmentEntry>([
[
"claude-sonnet-4-6",
{ name: "Claude Sonnet 4.6", providerDisplayName: "Claude" },
],
["claude-sonnet-4-6", { name: "Claude Sonnet 4.6", providerDisplayName: "Claude" }],
])
);
const logger = captureWarn();
@@ -1159,14 +1156,15 @@ test("config: providerTag=false suppresses the suffix", async () => {
);
});
test("config: providerTag skipped when providerDisplayName missing (falls back to enriched name only)", async () => {
test("config: providerTag falls back to UPPER(alias) when providerDisplayName missing", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
// Enrichment has the friendly name but NO providerDisplayName — e.g.
// a slot OmniRoute hasn't curated a human label for yet.
// a slot OmniRoute hasn't curated a human label for yet. We still
// have the alias though, so the prefix uses UPPER(alias) = "CC".
const enrichmentFetcher = stubEnrichmentFetcher(
new Map<string, OmniRouteEnrichmentEntry>([
["claude-sonnet-4-6", { name: "Claude Sonnet 4.6", providerAlias: "cc" }],
@@ -1181,6 +1179,32 @@ test("config: providerTag skipped when providerDisplayName missing (falls back t
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entry.models["claude-sonnet-4-6"].name, "CC - Claude Sonnet 4.6");
});
test("config: providerTag skipped entirely when neither providerDisplayName nor providerAlias set", async () => {
const readAuthJson = stubReadAuthJson({
omniroute: { type: "api", key: "sk-test", baseURL: "https://or.example/v1" },
});
const fetcher = stubModelsFetcher([MODEL_CLAUDE]);
const combosFetcher = stubCombosFetcher([]);
// No metadata at all — defensive case, e.g. legacy enrichment payload.
const enrichmentFetcher = stubEnrichmentFetcher(
new Map<string, OmniRouteEnrichmentEntry>([
["claude-sonnet-4-6", { name: "Claude Sonnet 4.6" }],
])
);
const logger = captureWarn();
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute" },
{ readAuthJson, fetcher, combosFetcher, enrichmentFetcher, logger }
);
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entry.models["claude-sonnet-4-6"].name, "Claude Sonnet 4.6");
@@ -1194,10 +1218,7 @@ test("config: providerTag is idempotent — second hook call doesn't double-suff
const combosFetcher = stubCombosFetcher([]);
const enrichmentFetcher = stubEnrichmentFetcher(
new Map<string, OmniRouteEnrichmentEntry>([
[
"claude-sonnet-4-6",
{ name: "Claude Sonnet 4.6", providerDisplayName: "Claude" },
],
["claude-sonnet-4-6", { name: "Claude Sonnet 4.6", providerDisplayName: "Claude" }],
])
);
const logger = captureWarn();
@@ -1212,12 +1233,12 @@ test("config: providerTag is idempotent — second hook call doesn't double-suff
await hook(inputA);
const entryA = (inputA as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entryA.models["claude-sonnet-4-6"].name, "Claude Sonnet 4.6 · Claude");
assert.equal(entryA.models["claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
// Second invocation (cache hit) — name must still be single-suffixed.
const inputB = makeInput();
await hook(inputB);
const entryB = (inputB as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider
.omniroute;
assert.equal(entryB.models["claude-sonnet-4-6"].name, "Claude Sonnet 4.6 · Claude");
assert.equal(entryB.models["claude-sonnet-4-6"].name, "Claude - Claude Sonnet 4.6");
});

View File

@@ -176,8 +176,8 @@ test("applyEnrichment: partial pricing preserves untouched fields", () => {
// applyProviderTag (Option E)
// ─────────────────────────────────────────────────────────────────────────
test("applyProviderTag: PROVIDER_TAG_SEPARATOR is middle-dot with surrounding spaces", () => {
assert.equal(PROVIDER_TAG_SEPARATOR, " \u00b7 ");
test("applyProviderTag: PROVIDER_TAG_SEPARATOR is hyphen with surrounding spaces", () => {
assert.equal(PROVIDER_TAG_SEPARATOR, " - ");
});
test("applyProviderTag: undefined enrichment → no-op", () => {
@@ -187,46 +187,63 @@ test("applyProviderTag: undefined enrichment → no-op", () => {
assert.equal(m.name, "Claude Sonnet 4.6");
});
test("applyProviderTag: providerDisplayName present → suffix appended", () => {
test("applyProviderTag: providerDisplayName present (short) → label prefix prepended", () => {
const m = baseModel();
m.name = "Claude Sonnet 4.6";
applyProviderTag(m as never, { providerDisplayName: "Claude" });
assert.equal(m.name, "Claude Sonnet 4.6 \u00b7 Claude");
assert.equal(m.name, "Claude - Claude Sonnet 4.6");
});
test("applyProviderTag: missing providerDisplayName → no-op", () => {
test("applyProviderTag: providerDisplayName too long → falls back to UPPER(alias) prefix", () => {
const m = baseModel();
m.name = "GPT 5";
applyProviderTag(m as never, {
providerDisplayName: "GitHub Models",
providerAlias: "ghm",
});
assert.equal(m.name, "GHM - GPT 5");
});
test("applyProviderTag: long displayName + no alias → uses long label rather than dropping prefix", () => {
const m = baseModel();
m.name = "GPT 5";
applyProviderTag(m as never, { providerDisplayName: "GitHub Models" });
assert.equal(m.name, "GitHub Models - GPT 5");
});
test("applyProviderTag: only providerAlias known → UPPER(alias) prefix", () => {
const m = baseModel();
m.name = "Claude Sonnet 4.6";
applyProviderTag(m as never, { providerAlias: "cc" });
assert.equal(m.name, "Claude Sonnet 4.6");
assert.equal(m.name, "CC - Claude Sonnet 4.6");
});
test("applyProviderTag: empty/whitespace providerDisplayName → no-op", () => {
test("applyProviderTag: empty/whitespace providerDisplayName + no alias → no-op", () => {
const m = baseModel();
m.name = "Claude Sonnet 4.6";
applyProviderTag(m as never, { providerDisplayName: " " });
assert.equal(m.name, "Claude Sonnet 4.6");
});
test("applyProviderTag: idempotent — second call doesn't double-suffix", () => {
test("applyProviderTag: idempotent — second call doesn't double-prefix", () => {
const m = baseModel();
m.name = "Claude Sonnet 4.6";
applyProviderTag(m as never, { providerDisplayName: "Claude" });
applyProviderTag(m as never, { providerDisplayName: "Claude" });
applyProviderTag(m as never, { providerDisplayName: "Claude" });
assert.equal(m.name, "Claude Sonnet 4.6 \u00b7 Claude");
assert.equal(m.name, "Claude - Claude Sonnet 4.6");
});
test("applyProviderTag: distinct providers for same model id → two separate suffixes", () => {
test("applyProviderTag: distinct providers for same model id → two separate prefixes", () => {
const a = baseModel();
a.name = "Claude Opus 4.7";
applyProviderTag(a as never, { providerDisplayName: "Claude" });
assert.equal(a.name, "Claude Opus 4.7 \u00b7 Claude");
assert.equal(a.name, "Claude - Claude Opus 4.7");
const b = baseModel();
b.name = "Claude Opus 4.7";
applyProviderTag(b as never, { providerDisplayName: "Kiro" });
assert.equal(b.name, "Claude Opus 4.7 \u00b7 Kiro");
assert.equal(b.name, "Kiro - Claude Opus 4.7");
});
// ─────────────────────────────────────────────────────────────────────────
@@ -237,27 +254,60 @@ test("formatCompressionPipeline: empty pipeline → empty string", () => {
assert.equal(formatCompressionPipeline([]), "");
});
test("formatCompressionPipeline: single step with intensity", () => {
test("formatCompressionPipeline: single step with intensity → emoji", () => {
assert.equal(
formatCompressionPipeline([{ engine: "caveman", intensity: "full" }]),
"[caveman:full]"
"[caveman\u{1F7E0}]"
);
});
test("formatCompressionPipeline: multi-step pipeline", () => {
test("formatCompressionPipeline: multi-step pipeline → emoji per step", () => {
assert.equal(
formatCompressionPipeline([
{ engine: "rtk", intensity: "standard" },
{ engine: "caveman", intensity: "full" },
]),
"[rtk:standard → caveman:full]"
"[rtk\u{1F7E1} → caveman\u{1F7E0}]"
);
});
test("formatCompressionPipeline: step without intensity", () => {
test("formatCompressionPipeline: step without intensity → engine bare", () => {
assert.equal(formatCompressionPipeline([{ engine: "rtk" }]), "[rtk]");
});
test("formatCompressionPipeline: ultra → red", () => {
assert.equal(
formatCompressionPipeline([{ engine: "caveman", intensity: "ultra" }]),
"[caveman\u{1F534}]"
);
});
test("formatCompressionPipeline: lite/minimal → green", () => {
assert.equal(formatCompressionPipeline([{ engine: "rtk", intensity: "lite" }]), "[rtk\u{1F7E2}]");
assert.equal(
formatCompressionPipeline([{ engine: "rtk", intensity: "minimal" }]),
"[rtk\u{1F7E2}]"
);
});
test("formatCompressionPipeline: intensity case-insensitive", () => {
assert.equal(
formatCompressionPipeline([{ engine: "caveman", intensity: "ULTRA" }]),
"[caveman\u{1F534}]"
);
assert.equal(
formatCompressionPipeline([{ engine: "caveman", intensity: "Standard" }]),
"[caveman\u{1F7E1}]"
);
});
test("formatCompressionPipeline: unknown intensity falls back to raw text", () => {
assert.equal(
formatCompressionPipeline([{ engine: "rtk", intensity: "custom-thing" }]),
"[rtk:custom-thing]"
);
});
// ─────────────────────────────────────────────────────────────────────────
// Provider hook — enrichment applied via injected fetcher
// ─────────────────────────────────────────────────────────────────────────
@@ -384,7 +434,11 @@ test("provider hook: compression metadata fetcher called when opted in", async (
assert.equal(called, 1, "compression metadata fetcher called");
const combo = out["combo/claude-primary"];
assert.ok(combo, "combo entry present");
assert.match(combo.name, /\[rtk:standard → caveman:full\]/, "combo name decorated with pipeline");
assert.match(
combo.name,
/\[rtk\u{1F7E1} caveman\u{1F7E0}\]/u,
"combo name decorated with emoji pipeline (rtk:standard=🟡, caveman:full=🟠)"
);
});
// ─────────────────────────────────────────────────────────────────────────