#17348

fix(api): reconcile custom-provider catalog identities for client enrichment

Openmorpheus9393opened 2d ago

Environment

  • OmniRoute 3.8.52, image digest sha256:90f903d7e9245973dc23f7f0a3afc0c63502c9bd25961097843361689df6474b
  • Observed through the OpenCode v2 client plugin @omniroute/opencode-plugin-v2@0.1.0, but the defect is gateway-side (endpoint inconsistency), not plugin-side.

Summary

For custom openai-compatible-chat-* connections the enrichment overlay (/api/pricing/models) cannot be joined to the served catalog (/v1/models), for two independent reasons:

  1. Key mismatch. Enrichment entries are keyed by the connection's internal provider string (e.g. openai-compatible-chat-8d3c46c4-20bc-40ad-a837-c475f578dc8a), while the served models carry routing prefixes (ih/..., owned_by: "ih"). Built-in providers work only because their key equals owned_by (e.g. kilocode).
  2. Id-shape mismatch / stale sync. Per connection the model-id shapes disagree between the two endpoints:
    • connection ...8d3c46c4... serves ih/** on /v1/models (606 models, owned_by: ih) but its pricing entry lists ag/** ids (384 models, 206 prefixed, 0 ih/) - this looks like a stale sync from a previous prefix.
    • connection ...e7cc3ed0... serves prefixed si/** on /v1/models (520 models) but its pricing entry lists 449 bare ids.
    • The pricing catalog contains no si and no ih key at all.

Consequence

  • lookupEnrichment() misses for every model of these connections: no providerDisplayName, no providerAlias, no pricing, no freeType.
  • Client catalogs therefore show raw ids (si/kimi-k3, or bare kimi-k3 for unprefixed serves) with no provider attribution and cost 0/0 - even though owned_by is set correctly on all 4,497 served models.
  • The free-tier summary reports large unmatched counts; plugin refresh log: catalog refresh: served=4659 default=59 presets=59 published=61 unmatched-free=471.
  • /api/providers exposes neither prefix nor a prefix->connection mapping, so clients cannot repair the join locally from public endpoints.

Evidence

  • /v1/models owned_by histogram: kilocode 1446, tokenrouter 750, ih 606, si 520, zenmux 396, aihorde 219, ...
  • /api/pricing/models keys include openai-compatible-chat-8d3c46c4-... (first ids: ag/claude-opus-4-6-thinking, ag/claude-opus-5-5, ...) and openai-compatible-chat-e7cc3ed0-... (first ids: openai-gpt-oss-120b, minimax-m2.1, ...); no si, no ih key.
  • Working counter-example: key kilocode with kilocode/... / kc/... ids joins fine and yields labels + pricing in clients.

Expected

  • Key (or alias) enrichment entries by routing prefix / owned_by in addition to the connection id, or expose the prefix <-> connection mapping on /api/providers.
  • Keep id shapes consistent between /v1/models and /api/pricing/models per connection; re-sync the pricing catalog when a connection prefix changes (the ag/ list of ...8d3c46c4... appears to be a leftover of an earlier prefix).

Workaround in place (client-side, local only)

The OpenCode plugin was locally patched to fall back to a provider label derived from owned_by when the enrichment lookup misses. Pricing and free markers remain unavailable for these connections until the gateway side is fixed.

Activity

1
diegosouzapw2d ago

Thanks for the concrete examples. The two endpoints currently expose different identity contracts: /v1/models uses the public routing prefix, while /api/pricing/models retains internal node IDs for the management UI. The current development branch also exposes displayPrefix and, where applicable, pricingKey on those catalog entries, so we need to preserve that compatibility rather than simply rename the existing keys.

The stale ag/... entries under a node now routed as ih need a separate prefix-change reproduction. #17334 addresses repeated copies of the current prefix, and #17259 adds prices directly to /v1/models; neither alone establishes full coverage of this report. /api/pricing/models is a management catalog and does not itself return a full price/free-tier overlay. The next step is to test the cross-endpoint join and prefix-change behavior with custom-provider fixtures, including upstream IDs that legitimately contain /. Unknown prices must remain unknown rather than being displayed as free. Keeping this open for that diagnosis; the exact migration failure is not yet reproduced.