From f08379986ad773ec7bf6cd53485ce72418f4ae56 Mon Sep 17 00:00:00 2001 From: "M.M" Date: Sat, 23 May 2026 01:14:39 +0200 Subject: [PATCH] feat(opencode-plugin): provider-tag becomes a prefix + traffic-light compression intensity emoji (#2604) Integrated into release/v3.8.2 --- @omniroute/opencode-plugin/README.md | 58 +++---- @omniroute/opencode-plugin/src/index.ts | 158 +++++++++++++----- .../opencode-plugin/tests/config-shim.test.ts | 51 ++++-- .../opencode-plugin/tests/features.test.ts | 90 ++++++++-- 4 files changed, 257 insertions(+), 100 deletions(-) diff --git a/@omniroute/opencode-plugin/README.md b/@omniroute/opencode-plugin/README.md index 823e1d554a..e4a6e93c95 100644 --- a/@omniroute/opencode-plugin/README.md +++ b/@omniroute/opencode-plugin/README.md @@ -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/` 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/` 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/` namespace and labelled `Combo: ` 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[].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-.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.` remote entry into the OC config pointing at `/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/` namespace and labelled `Combo: ` 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[].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-.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.` remote entry into the OC config pointing at `/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-.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` diff --git a/@omniroute/opencode-plugin/src/index.ts b/@omniroute/opencode-plugin/src/index.ts index 3ba0fefc0e..e42c73fa11 100644 --- a/@omniroute/opencode-plugin/src/index.ts +++ b/@omniroute/opencode-plugin/src/index.ts @@ -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: ` - ` (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 + * `