fix(api): stop leaking the internal provider UUID in /v1/models and honor the configured prefix (#8327) - #8361
Merged
Conversation
…onor the configured prefix (#8327)
4 tasks
6 of 7 tasks
6 tasks
korvin2000
pushed a commit
to korvin2000/OmniRoute
that referenced
this pull request
Aug 2, 2026
GET /v1/models was assembled by many independent push loops (auto-combos, named combos, static registry, codex-native, synced, OpenRouter, specialty, custom, alias-backed, connection-fallback), so one provider's models landed in several separated, interleaved blocks. Apply ONE stable, provider-grouped sort at serialization in finalizeCatalogResponse, keyed by owned_by (canonical owner identity) rather than the model-id prefix — so a single routable public prefix that differs from its owner (e.g. no-auth OpenCode publishing oc/<model> while keeping owned_by "opencode") stays contiguous. Combos are pinned first (preserving diegosouzapw#4164); then providers in registry precedence (OAuth -> NoAuth -> API-key); then unknown providers in locale-independent code-unit order. The sort is stable and pure (reorders rows only, no mutation, no DB/IO), preserving combo sort_order, connection priority, custom append-order, and equal-id audio twins. Identity, alias mapping, effort variants, dedupe, and Claude-mirror gating are untouched. This is the one enumerated Model-Identity behavior with no upstream equivalent on release/v3.8.50: single public prefix / UUID-leak (diegosouzapw#8327/diegosouzapw#8361) and Combo Builder effort variants (diegosouzapw#8072/diegosouzapw#8165) are already merged there.
HouMinXi
pushed a commit
to HouMinXi/OmniRoute
that referenced
this pull request
Aug 2, 2026
…onor the configured prefix (diegosouzapw#8327) (diegosouzapw#8361)
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…onor the configured prefix (diegosouzapw#8327) (diegosouzapw#8361)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #8327
Root cause
src/app/api/v1/models/catalog.tsbuilds two different identifiers per modelentry for compatible-provider nodes (openai-compatible / anthropic-compatible):
alias— DOES receiveproviderIdToPrefix(built from provider nodes) andcorrectly becomes the operator's configured prefix for the published
idfield.canonicalProviderId(viaresolveCanonicalProviderId()) — used forowned_by,but only knows the static
AI_PROVIDERS/PROVIDER_MODELSalias maps. Acompatible-provider node's
idis an internal UUID (e.g.openai-compatible-chat-<uuid>, percreateProviderNode()'sid: data.id || uuidv4()) that is never present in those static maps, so itfalls through every lookup and returns the raw UUID verbatim — leaking the
internal provider-node id into the public
owned_byfield for every emitsite that uses it: the synced-models block (the one matching the reporter's
scenario), the custom-models block, the model-alias block, and the
managed-fallback block (which additionally skipped
canonicalProviderIdresolution entirely, using the raw
providerIddirectly).Fix
canonicalProviderIdis still required, unmodified, by internalconnection/hidden-model/registry lookups that are keyed on the raw provider-node
id (
getConnectionsForProvider,getModelIsHidden, etc.) — so I did not changewhat it resolves to. Instead I added a dedicated
resolvePublicOwnerId(providerId, canonicalProviderId)helper that checksproviderIdToPrefixfirst and appliedit at every
owned_byemit site (previouslyowned_by: canonicalProviderId/owned_by: providerId). Built-in providers are unaffected —providerIdToPrefixonly has entries for actual provider nodes with a configured prefix, never for
static
AI_PROVIDERSids.Scope
This PR fixes symptom (a) — the internal provider UUID leaking as
owned_by—and symptom (b) — the configured prefix being ignored for
owned_by. It doesnot touch effort-variant multiplication, which the triage plan confirmed is
intentional, documented design (
open-sse/utils/claudeEffortVariants.ts,open-sse/utils/syncedEffortVariants.ts), not a defect. UsingRefs #8327rather than
Closes #8327since the issue as filed also raises that symptom.Regression test (Hard Rule #18 — TDD)
New file
tests/unit/8327-models-owned-by-prefix.test.ts, reusing the triageplan's proven repro scenario (compatible provider node + configured prefix +
synced/custom models).
catalog.tsviagit checkout HEAD -- <file>in-worktree, no stash used):models, and a built-in-provider contract-preservation check).
Gates run (all green)
node scripts/check/check-file-size.mjs— OKnode scripts/check/check-complexity.mjs— pre-existing baseline drift(2167 violations), confirmed identical on a disposable probe worktree pinned
to
origin/release/v3.8.49— not introduced by this diff.node scripts/check/check-cognitive-complexity.mjs— same pre-existingdrift (956), confirmed identical on the same probe — not introduced by this diff.
npm run typecheck:core— cleannpx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files>— 0 errorsapi/v1/models/catalog) — all green, includingmodels-catalog-route.test.ts(43 tests)node scripts/check/check-changelog-integrity.mjs— OKChangelog
changelog.d/fixes/8327-models-owned-by-prefix.md