feat(providers): curated OpenRouter embeddings catalog + specialty merge in live discovery (#6976) - #6994
Conversation
…rge in live discovery (#6976) OpenRouter serves embeddings via a dedicated OpenAI-compatible /api/v1/embeddings endpoint that is omitted from /v1/models, and the embeddingRegistry entry for it was stale (3 legacy ids). Meanwhile providerModelsConfig gives openrouter a live discovery config, so buildApiDiscoveryResponse's success path returned only the live chat catalog verbatim — the specialty (embeddings/rerank) static catalog was only ever merged in on the no-config local_catalog fallback, so OpenRouter embeddings never surfaced through model discovery. Refreshed the curated openrouter embeddingRegistry lineup (ids verified against https://openrouter.ai/docs/api/reference/embeddings and the collections page) and added a scoped, additive merge (mergeSpecialtyCatalogIntoLiveModels, allowlisted to openrouter) that folds embeddings/rerank entries from getStaticModelsForProvider() into the live discovery response, deduped by id. Scoped as an allowlist rather than a blanket merge because some providers (e.g. Gemini) already return embedding models directly from their live /v1/models endpoint, where a blind merge would risk stale/duplicate entries.
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
PR #6994 — aprovado. Probe TDD confirmou fail-without-fix no teste-chave do merge (revertendo route.ts/helpers.ts ele cai pra RED com a mensagem exata do bug original). Fui atrás dos IDs curados no OpenRouter (mistral-embed-2312, bge-m3, qwen3-embedding-4b/8b) — todos existem e batem com as dimensões documentadas. Único ponto de atenção (não bloqueante, não é regressão sua): |
…openrouter-embeddings-catalog
) no-explicit-any is an error under tests/ (#6218), so the 4 `any` usages in the new discovery assertions failed the max-warnings-0 lint gate. Replace them with an explicit ModelsResponseBody shape — type-only change, all 13 assertions unchanged and still passing.
The new #6976 assertion added a 56th explicit `any` to this file, one over the 55 frozen in config/quality/eslint-suppressions.json, tripping the max-warnings-0 lint gate. Type the callback param instead of raising the frozen count — the debt ratchet only decreases. All 59 tests still pass.
Babysit summaryCI is green (13/13 SUCCESS, 2 NEUTRAL = Mergify skips). Not merged — handing off for human review & merge. Fixes
Guardrails honored
Note on the base: Ready for human review & merge. |
…rge in live discovery (diegosouzapw#6976) (diegosouzapw#6994) * feat(providers): curated OpenRouter embeddings catalog + specialty merge in live discovery (diegosouzapw#6976) OpenRouter serves embeddings via a dedicated OpenAI-compatible /api/v1/embeddings endpoint that is omitted from /v1/models, and the embeddingRegistry entry for it was stale (3 legacy ids). Meanwhile providerModelsConfig gives openrouter a live discovery config, so buildApiDiscoveryResponse's success path returned only the live chat catalog verbatim — the specialty (embeddings/rerank) static catalog was only ever merged in on the no-config local_catalog fallback, so OpenRouter embeddings never surfaced through model discovery. Refreshed the curated openrouter embeddingRegistry lineup (ids verified against https://openrouter.ai/docs/api/reference/embeddings and the collections page) and added a scoped, additive merge (mergeSpecialtyCatalogIntoLiveModels, allowlisted to openrouter) that folds embeddings/rerank entries from getStaticModelsForProvider() into the live discovery response, deduped by id. Scoped as an allowlist rather than a blanket merge because some providers (e.g. Gemini) already return embedding models directly from their live /v1/models endpoint, where a blind merge would risk stale/duplicate entries. * test(providers): type the models discovery payload instead of any (diegosouzapw#6976) no-explicit-any is an error under tests/ (diegosouzapw#6218), so the 4 `any` usages in the new discovery assertions failed the max-warnings-0 lint gate. Replace them with an explicit ModelsResponseBody shape — type-only change, all 13 assertions unchanged and still passing. * test(providers): type the openrouter merge assertion callback (diegosouzapw#6976) The new diegosouzapw#6976 assertion added a 56th explicit `any` to this file, one over the 55 frozen in config/quality/eslint-suppressions.json, tripping the max-warnings-0 lint gate. Type the callback param instead of raising the frozen count — the debt ratchet only decreases. All 59 tests still pass.
…rge in live discovery (diegosouzapw#6976) (diegosouzapw#6994) * feat(providers): curated OpenRouter embeddings catalog + specialty merge in live discovery (diegosouzapw#6976) OpenRouter serves embeddings via a dedicated OpenAI-compatible /api/v1/embeddings endpoint that is omitted from /v1/models, and the embeddingRegistry entry for it was stale (3 legacy ids). Meanwhile providerModelsConfig gives openrouter a live discovery config, so buildApiDiscoveryResponse's success path returned only the live chat catalog verbatim — the specialty (embeddings/rerank) static catalog was only ever merged in on the no-config local_catalog fallback, so OpenRouter embeddings never surfaced through model discovery. Refreshed the curated openrouter embeddingRegistry lineup (ids verified against https://openrouter.ai/docs/api/reference/embeddings and the collections page) and added a scoped, additive merge (mergeSpecialtyCatalogIntoLiveModels, allowlisted to openrouter) that folds embeddings/rerank entries from getStaticModelsForProvider() into the live discovery response, deduped by id. Scoped as an allowlist rather than a blanket merge because some providers (e.g. Gemini) already return embedding models directly from their live /v1/models endpoint, where a blind merge would risk stale/duplicate entries. * test(providers): type the models discovery payload instead of any (diegosouzapw#6976) no-explicit-any is an error under tests/ (diegosouzapw#6218), so the 4 `any` usages in the new discovery assertions failed the max-warnings-0 lint gate. Replace them with an explicit ModelsResponseBody shape — type-only change, all 13 assertions unchanged and still passing. * test(providers): type the openrouter merge assertion callback (diegosouzapw#6976) The new diegosouzapw#6976 assertion added a 56th explicit `any` to this file, one over the 55 frozen in config/quality/eslint-suppressions.json, tripping the max-warnings-0 lint gate. Type the callback param instead of raising the frozen count — the debt ratchet only decreases. All 59 tests still pass.
Closes #6976
Root cause
OpenRouter serves embeddings via a dedicated OpenAI-compatible
/api/v1/embeddingsendpoint (omitted from/v1/models).open-sse/config/embeddingRegistry.tsalready had anopenrouterentry, but it was stale (3 legacy ids) and effectively dead code for discovery:providerModelsConfig.tsgivesopenroutera live discovery config, so the live-discovery success path inbuildApiDiscoveryResponse(src/app/api/providers/[id]/models/route.ts) returned the live/v1/modelschat catalog verbatim — the specialty (embeddings/rerank) static catalog was only ever merged in on the no-configlocal_catalogfallback, which OpenRouter never hits.Fix
openrouterembeddingRegistry lineup — ids verified against the API reference (https://openrouter.ai/docs/api/reference/embeddings) and the models collections page, not display names:openai/text-embedding-3-small/-large,qwen/qwen3-embedding-8b/-4b,baai/bge-m3,mistralai/mistral-embed-2312,google/gemini-embedding-001. Dropped the legacyopenai/text-embedding-ada-002entry rather than guess at its current availability.mergeSpecialtyCatalogIntoLiveModels()(discovery/helpers.ts) that folds embeddings/rerank entries fromgetStaticModelsForProvider()into a successful live-discovery response, additively and deduped by id. Scoped via an explicit allowlist (LIVE_DISCOVERY_SPECIALTY_MERGE_PROVIDERS, currently justopenrouter) rather than applied to every provider with an embeddingRegistry/rerankRegistry entry — some providers (Gemini) already return embedding models directly inside their live/v1/modelsresponse, so a blanket merge broke an existing pagination test (gemini-embedding-2/gemini-embedding-001duplicating what Gemini's own live response already returns). Updated one pre-existing OpenRouter test (provider-models-route.test.ts) whose exact-equality assertion encoded the old (buggy) behavior.TDD evidence
New
tests/unit/openrouter-embeddings-catalog-6976.test.ts:live discovery merges curated embeddings into the response even when /v1/models returns nonefailed —AssertionError: curated embedding baai/bge-m3 should be merged into live discovery; got: anthropic/claude-sonnet-5.getStaticModelsForProviderfold, live-discovery merge, live-vs-curated dedup-by-id).Gates run
npm run typecheck:core— cleannpx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files>— 0 errors (pre-existingno-explicit-anywarnings only, all suppressed)node scripts/check/check-file-size.mjs— no new violations (3 pre-existing base-red files untouched)node scripts/check/check-complexity.mjs/check-cognitive-complexity.mjs— bothOKat baseline (2056 / 890, unchanged)node scripts/check/check-changelog-integrity.mjs—OKtests/unit/provider-models-route.test.ts(59 tests),provider-models-discovery-split.test.ts,provider-models-custom-merge-6247.test.ts,provider-models-route-codex.test.ts,provider-models-v1-route.test.ts,provider-scoped-models-route.test.ts,openrouter-registry.test.ts,rerank-openrouter-6574.test.ts— all green (regression sweep of the area)Notes
nvidia/llama-nemotron-embed-vl-1b-v2andperplexity/pplx-embed-v1-0.6b(both surfaced in the API reference/collections search) out of the curated list — less confident these fit the plain OpenAI-shaped embeddings request/response contract without further verification (multimodal / different input shape); flagging for a follow-up refresh rather than guessing.google/gemini-embedding-2(128–3072 flexible dims per the collections page) also left out — no single fixed dimension to record for the conflict guard, unlikegemini-embedding-001(768, matching the existinggeminiregistry entry's convention in this file).