mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-17 12:22:34 +03:00
fix(catalog): keep operator custom models out of the live-sync reader
#12934 unioned customModels into getAllActiveSyncedModels() alongside the dispatch-time readers it was actually fixing (#12597: getActiveSyncedCatalog, reconcileProvidersWithActiveSyncedCatalog, getActiveProvidersWithSyncedModel). getAllActiveSyncedModels() is not a dispatch reader. Its three consumers read it as "what the provider's live sync reported": /v1/models feeds its synced-emission loop (and has a separate custom-model pass right after, which owns the specialty-registry dedupe, hidePaid and the vision overrides), /api/models uses it to decide whether an exclusive-listing provider suppresses a static row, and getSyncedAutoAliases derives tier aliases from it. Blurring custom rows into "synced" made a custom embedding/rerank model on jina-ai come out as the alias row `jina/<id>` (parentless primary) with the registry's canonical `jina-ai/<id>` demoted to its child — the inverse of the identity every other specialty model of that provider carries, and it also dropped the registry's `dimensions`. Drop the union there only; the dispatch trio keeps it, so #12597's tests and the 400-on-picker-model fix are untouched. Locked by a new case in custom-models-live-catalog-12597.test.ts and by both jina cases in models-catalog-route.test.ts ("does not duplicate imported/custom Jina specialty models"), which encode the two identities side by side.
This commit is contained in:
@@ -184,10 +184,12 @@ async function unionCustomModels(
|
||||
*/
|
||||
async function loadConnectionCatalog(storedProviderId: string): Promise<SyncedAvailableModel[]> {
|
||||
const [connections, modelsByConnection] = await Promise.all([
|
||||
getRawProviderConnections({ provider: storedProviderId, isActive: true }, undefined, undefined, [
|
||||
"id",
|
||||
"provider",
|
||||
]),
|
||||
getRawProviderConnections(
|
||||
{ provider: storedProviderId, isActive: true },
|
||||
undefined,
|
||||
undefined,
|
||||
["id", "provider"]
|
||||
),
|
||||
getSyncedAvailableModelsByConnection(storedProviderId),
|
||||
]);
|
||||
|
||||
@@ -248,6 +250,18 @@ export async function getActiveSyncedCatalog(providerId: string): Promise<Active
|
||||
/**
|
||||
* Return non-empty synced catalogs grouped by provider, restricted to active
|
||||
* connections. This is the authoritative live source for /v1/models.
|
||||
*
|
||||
* Deliberately does NOT union `customModels` the way the dispatch-time readers
|
||||
* above do (#12597). Its three consumers all read this as "what the provider's
|
||||
* live sync reported": /v1/models emits synced rows through their own loop and
|
||||
* has a separate custom-model pass right after it (with the specialty-registry
|
||||
* dedupe, hidePaid and vision overrides that pass owns); /api/models uses it to
|
||||
* decide whether an exclusive-listing provider's live catalog suppresses a static
|
||||
* row; getSyncedAutoAliases derives tier aliases from it. Blurring operator custom
|
||||
* rows into "synced" made /v1/models emit a custom specialty model under the
|
||||
* provider's ALIAS as the parentless primary row (`jina/<id>`) and demote the
|
||||
* embedding/rerank registry's canonical `jina-ai/<id>` to a child — the inverse of
|
||||
* the identity every other specialty model of that provider carries.
|
||||
*/
|
||||
export async function getAllActiveSyncedModels(): Promise<Record<string, SyncedAvailableModel[]>> {
|
||||
try {
|
||||
@@ -277,10 +291,7 @@ export async function getAllActiveSyncedModels(): Promise<Record<string, SyncedA
|
||||
|
||||
const models = enrichCursorCatalog(
|
||||
providerId,
|
||||
await unionCustomModels(
|
||||
providerId,
|
||||
collectModelsForConnections(modelsByConnection, connectionIds)
|
||||
)
|
||||
collectModelsForConnections(modelsByConnection, connectionIds)
|
||||
);
|
||||
|
||||
if (models.length > 0) {
|
||||
|
||||
@@ -14,10 +14,14 @@ process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "custom-live-12597-test-secret";
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const { addCustomModel, replaceSyncedAvailableModelsForConnection, getActiveProvidersWithSyncedModel } =
|
||||
await import("../../src/lib/db/models.ts");
|
||||
const {
|
||||
addCustomModel,
|
||||
replaceSyncedAvailableModelsForConnection,
|
||||
getActiveProvidersWithSyncedModel,
|
||||
} = await import("../../src/lib/db/models.ts");
|
||||
const {
|
||||
getActiveSyncedCatalog,
|
||||
getAllActiveSyncedModels,
|
||||
catalogContainsModel,
|
||||
reconcileProvidersWithActiveSyncedCatalog,
|
||||
} = await import("../../src/lib/db/models/activeSyncedCatalog.ts");
|
||||
@@ -124,6 +128,21 @@ test("#12597 sparse custom overlay does not wipe synced capability fields", asyn
|
||||
assert.deepEqual(match?.supportedThinkingEfforts, ["low", "high"]);
|
||||
});
|
||||
|
||||
test("#12597 the union is dispatch-only — getAllActiveSyncedModels stays live-sync-shaped", async () => {
|
||||
await addCustomModel(PROVIDER, PICKER_MODEL, "DeepSeek R1 via picker");
|
||||
|
||||
const allActive = await getAllActiveSyncedModels();
|
||||
const ids = (allActive[PROVIDER] ?? []).map((model) => model.id);
|
||||
|
||||
assert.deepEqual(
|
||||
ids,
|
||||
[SYNCED_MODEL],
|
||||
"getAllActiveSyncedModels reports what the provider's live sync returned; its consumers (/v1/models' synced loop, /api/models' exclusive-listing suppression, getSyncedAutoAliases) each handle custom rows on their own"
|
||||
);
|
||||
// The dispatch-time readers still see it — that is what #12597 fixed.
|
||||
assert.equal(catalogContainsModel(await getActiveSyncedCatalog(PROVIDER), PICKER_MODEL), true);
|
||||
});
|
||||
|
||||
test("#12597 without a custom row the picker id is still absent (lock the old contract)", async () => {
|
||||
const catalog = await getActiveSyncedCatalog(PROVIDER);
|
||||
assert.equal(catalogContainsModel(catalog, PICKER_MODEL), false);
|
||||
|
||||
Reference in New Issue
Block a user