mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +03:00
On dashboard/memory?tab=engine the Embedding Model quick-select (and the rerank selector) built their lists from a keyword heuristic over the CHAT catalog (AI_MODELS) plus OpenRouter live discovery. Providers whose embedding models are not in that catalog never appeared — mistral, gemini, nvidia nim, groq, vercel-ai-gateway and others that serve embeddings on a standard OpenAI-compatible /embeddings endpoint — and typing such a model by hand failed at runtime with "Unknown embedding provider". The fix is one generic mechanism rather than a list of per-provider patches: deriveEmbeddingProviderForChatProvider() turns any chat-registry entry with a /chat/completions base into an OpenAI-compatible /embeddings config, with curated EMBEDDING_PROVIDERS entries always winning; the embeddings service resolves a derived config for unknown-but-configured providers instead of rejecting them; deriveRerankProviderForChatProvider() does the same for Cohere-compatible /rerank; and both memory selectors fall back to a free-text provider/model input when no static catalog exists. No provider is special-cased by name, so adding one to the chat registry now makes it embedding- and rerank-capable here automatically. Verified on the current release tip: merged clean, typecheck:core clean, check:cycles OK across 417 files, and 35/35 across the PR's five new suites (qdrant-quick-select-catalog, memory-provider-listings, rerank-provider-listings, embedding-generic-provider-fallback, rerank-generic-provider-fallback) plus the updated hard-session-lease-bypass-inventory and embeddings-handler. Note: the base-red disclaimer in the description referenced #9985 against release/v3.8.50 — that window is closed and the current tip carries no open base-red, so nothing was inherited here. Thanks @rqzbeh — deriving the capability instead of enumerating providers is the version of this that stays correct as the registry grows.
70 lines
3.0 KiB
TypeScript
70 lines
3.0 KiB
TypeScript
/**
|
|
* Issue: rerank model strings outside the curated RERANK_PROVIDERS registry were
|
|
* rejected with "No rerank provider found" even when the provider was configured
|
|
* in OmniRoute with a working Cohere-compatible /rerank endpoint (e.g. groq,
|
|
* siliconflow-style hosts). The memory Rerank selector fed by the curated list
|
|
* had the same blind spot.
|
|
*
|
|
* These tests pin: parseRerankModel keeps returning null provider for unknown
|
|
* prefixes (registry semantics unchanged), and the new generic fallback builder
|
|
* derives a Cohere-compatible config for any configured OpenAI-compatible chat
|
|
* provider without shadowing curated entries.
|
|
*/
|
|
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { REGISTRY } from "@omniroute/open-sse/config/providerRegistry.ts";
|
|
import {
|
|
getRerankProvider,
|
|
parseRerankModel,
|
|
} from "../../open-sse/config/rerankRegistry.ts";
|
|
import { deriveRerankProviderForChatProvider } from "../../open-sse/config/rerankRegistry.ts";
|
|
|
|
describe("rerank registry: unknown providers stay rejected at parse level", () => {
|
|
it("parseRerankModel returns provider null for an unregistered prefix", () => {
|
|
const parsed = parseRerankModel("groq/some-reranker");
|
|
assert.equal(parsed.provider, null);
|
|
// Registry semantics: when no curated provider matches, model keeps its
|
|
// full original string (provider prefix included).
|
|
assert.equal(parsed.model, "groq/some-reranker");
|
|
assert.equal(getRerankProvider("groq"), null);
|
|
});
|
|
});
|
|
|
|
describe("deriveRerankProviderForChatProvider (generic Cohere-compatible fallback)", () => {
|
|
it("derives a /rerank endpoint from a chat-completions base URL", () => {
|
|
const derived = deriveRerankProviderForChatProvider("groq", {
|
|
id: "groq",
|
|
baseUrl: "https://api.groq.com/openai/v1/chat/completions",
|
|
});
|
|
assert.ok(derived, "groq should derive a rerank provider");
|
|
assert.equal(derived.baseUrl, "https://api.groq.com/openai/v1/rerank");
|
|
assert.deepEqual(derived.models, []);
|
|
});
|
|
|
|
it("returns null for dynamic-URL providers and missing entries", () => {
|
|
assert.equal(
|
|
deriveRerankProviderForChatProvider("account-scoped", {
|
|
id: "account-scoped",
|
|
baseUrl: "https://api.example.com/client/v4/accounts",
|
|
}),
|
|
null
|
|
);
|
|
assert.equal(deriveRerankProviderForChatProvider("ghost", undefined), null);
|
|
});
|
|
|
|
it("does not shadow curated rerank registries", () => {
|
|
for (const id of ["cohere", "together", "siliconflow", "voyage-ai", "jina-ai"]) {
|
|
const entry = REGISTRY[id] as { baseUrl?: string } | undefined;
|
|
if (!entry) continue;
|
|
const derived = deriveRerankProviderForChatProvider(id, entry);
|
|
if (derived) {
|
|
assert.ok(getRerankProvider(id), `${id} remains curated; derivation is fallback-only`);
|
|
}
|
|
}
|
|
// cohere IS curated — helper still derives mechanically, but callers must
|
|
// check the curated registry first. Pin that ordering here:
|
|
const curated = getRerankProvider("cohere");
|
|
assert.ok(curated?.models.length, "curated cohere entry has models");
|
|
});
|
|
});
|