What you see in the Copilot Chat picker is not the raw GET /v1/models payload.
The extension shapes it, and the numbers below come from a real OmniRoute
instance measured on 2026-08-18 (2345 raw catalog entries).
| Stage | Entries | What happened |
|---|---|---|
| Raw catalog, server default | 2345 | every model listed twice (see below) |
?prefix=alias |
1396 | 949 duplicate ids dropped, zero models lost |
| Non-chat models removed | 1319 | 26 image / video / audio / rerank rows |
OmniRoute's MODELS_CATALOG_PREFIX_MODE defaults to dual, which advertises
each model under both its short alias prefix and its canonical provider prefix,
for backward compatibility with client configs that hardcoded either one:
cc/claude-sonnet-4-6 ← alias prefix
claude/claude-sonnet-4-6 ← canonical prefix — same model
Both route fine; they are the same model. In a picker they read as duplicates.
The extension asks for GET /v1/models?prefix=alias, a supported per-request
override (see tests/unit/models-catalog-low-noise-flag.test.ts in the OmniRoute
repo), so the server sends one id per model without changing its global setting
for other clients.
⚠️ Never use?prefix=canonical. In OmniRoute'scatalog.tsthe canonical row is only emitted whencanonicalProviderId !== alias, so providers without a distinct alias would emit nothing — that mode silently loses models.aliasis the safe direction, and was verified as lossless: all 949 dropped ids were mirrors.
A second, server-independent guard drops mirror rows via their parent
back-reference, so an OmniRoute too old to honor ?prefix is covered too. On the
measured instance both paths converge on the same 1319 models.
Specialty registries. The catalog also carries image, video, audio, rerank, embedding and moderation models. OmniRoute rejects those on a chat request:
HTTP 400 — Model 'cheaperinference/nano-banana-pro' is an image-generation model
and cannot be used on /v1/chat/completions. Use POST /v1/images/generations instead.
They are filtered by the catalog's type field, so they never reach the picker.
Responses-API models are not filtered. Every Codex / GPT-5.x entry is listed
as supported_endpoints: ["responses"], but OmniRoute translates those for
/v1/chat/completions — cx/gpt-5.5-low and cx/gpt-5.6-sol-low both answer
HTTP 200. Treating "does not list chat" as "unusable" would drop 26 working
models, so only genuinely non-conversational surfaces are excluded.
Your modelFilter setting. omnicopilot.modelFilter is a regex (falling back
to substring matching if the regex is invalid) applied to the model id. Empty by
default.
OmniRoute advertises models from:
- every provider with an active connection, plus
- every noAuth provider — the ones that need no credential at all. This is the "90+ free providers" part of OmniRoute, and it is intentional.
To hide the keyless ones, add them to blockedProviders in the OmniRoute
dashboard settings. Nothing needs to change in the extension.
The extension caches the catalog per discovery call. OmniRoute: Refresh Models (Command Palette) or the ↻ link in the panel re-queries the server and
tells VS Code the list changed. Changing any omnicopilot.* setting refreshes
automatically.