mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-21 22:52:19 +03:00
* fix(catalog): hash API key in buildCatalogCacheKey so raw credentials never live in the key string (#10313) * fix(api): yield event loop and bulk-load override tables in catalog build (#9147) * fix(api): keep bulk hidden-model load inside catalog builder's error boundary Post-sync-merge fixup for #9147/#10313 against release/v3.8.50: - Resolve the catalog.ts/catalogCache.ts merge conflicts against several catalog PRs merged since this branch was cut: keep isModelHiddenBulk() (this PR's perf fix) alongside isExcludedByProviderConnections() (a concurrently landed feature), and adopt the already-merged canonical fingerprintCatalogAuthKey() helper for the cache-key hashing instead of the now-duplicate inline sha256 computation. - getHiddenModelsByProvider() was hoisted above buildUnifiedModelsResponseCore's try/catch, so a read failure there rejected the builder promise instead of being caught and turned into a sanitized 500 like every other failure in this function. Combined with the pre-existing promise.finally() dangling chain in catalogCache.ts's in-flight coalescing, that produced a genuine unhandled rejection. Move the bulk-load call back inside the try block. - Align tests/unit/models-catalog-route.test.ts and tests/unit/10313-catalog-cache-key-hashing.test.ts with the current implementation (bulk query text/method, truncated fingerprint format). * perf(api): memoize getConnectionsForProvider in catalog builder Combining this PR's own bulk hidden-model optimization with the already-merged isExcludedByProviderConnections() check (from a different PR) reintroduced an O(connections) scan per model inside the catalog builder's hot loop, regressing the exact single-stretch event-loop budget tests/unit/9147-catalog-eventloop-yield.test.ts enforces (was passing on this PR's own commit before the merge). Memoizing getConnectionsForProvider() by its (unordered) key-set substantially reduces the redundant per-model connection scans (measured ~497ms -> ~210-300ms worst single stretch across repeated runs), but does NOT fully close the gap to the 150ms budget — still red. Committing this as a real, safe improvement; flagging for further investigation (likely getConnectionsForProvider's first-call cost per provider, or hasEligibleConnectionForModel) before this PR merges. NOT deciding to relax the test threshold myself. --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com>
107 lines
3.4 KiB
TypeScript
107 lines
3.4 KiB
TypeScript
import { test, after } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
applyCatalogPostFilters,
|
|
filterUnauthorizedFunctionalGatewayMirrors,
|
|
} from "../../src/app/api/v1/models/catalogResponse.ts";
|
|
import {
|
|
removeFeatureFlagOverride,
|
|
setFeatureFlagOverride,
|
|
} from "../../src/lib/db/featureFlags.ts";
|
|
import { setFunctionalGatewayProviderSetting } from "../../src/lib/db/functionalGatewayMirrors.ts";
|
|
import { resetDbInstance } from "../../src/lib/db/core.ts";
|
|
|
|
const FLAG_KEY = "EXPOSE_FUNCTIONAL_GATEWAY_MIRRORS";
|
|
|
|
after(() => {
|
|
removeFeatureFlagOverride(FLAG_KEY);
|
|
setFunctionalGatewayProviderSetting("agentrouter", null);
|
|
resetDbInstance();
|
|
});
|
|
|
|
// Minimal Request shim for applyCatalogPostFilters.
|
|
function makeRequest(query = ""): Request {
|
|
return new Request(`http://localhost/v1/models${query}`);
|
|
}
|
|
|
|
test("catalog post-filters do not add mirrors when gate off (default)", async () => {
|
|
const models = [
|
|
{ id: "deepseek/deepseek-v4-flash", owned_by: "deepseek", root: "deepseek-v4-flash" },
|
|
];
|
|
const out = await applyCatalogPostFilters(makeRequest(), models, {
|
|
connections: [],
|
|
prefixMode: "dual",
|
|
aliasToProviderId: {},
|
|
});
|
|
assert.deepEqual(out, models);
|
|
});
|
|
|
|
test("final catalog permission filtering does not let a mirror inherit base access", async () => {
|
|
setFeatureFlagOverride(FLAG_KEY, "true");
|
|
setFunctionalGatewayProviderSetting("agentrouter", "on");
|
|
|
|
const models = [{ id: "kmc/k3", owned_by: "kimi-coding", root: "k3" }];
|
|
const withMirror = await applyCatalogPostFilters(makeRequest(), models, {
|
|
connections: [
|
|
{
|
|
id: "conn-1",
|
|
provider: "agentrouter",
|
|
isActive: true,
|
|
providerSpecificData: {},
|
|
},
|
|
],
|
|
prefixMode: "dual",
|
|
aliasToProviderId: {},
|
|
});
|
|
const allowed = await filterUnauthorizedFunctionalGatewayMirrors(
|
|
withMirror,
|
|
"restricted-key",
|
|
async (_key, modelId) => modelId === "kmc/k3"
|
|
);
|
|
|
|
assert.deepEqual(
|
|
allowed.map((model) => model.id),
|
|
["kmc/k3"],
|
|
"a synthesized gateway mirror must authorize its own public ID"
|
|
);
|
|
|
|
const gatewayAllowed = await filterUnauthorizedFunctionalGatewayMirrors(
|
|
withMirror,
|
|
"gateway-key",
|
|
async (_key, modelId) => modelId === "agentrouter/kmc/k3"
|
|
);
|
|
assert.deepEqual(
|
|
gatewayAllowed.map((model) => model.id),
|
|
["kmc/k3", "agentrouter/kmc/k3"],
|
|
"an independently authorized gateway mirror must remain visible"
|
|
);
|
|
});
|
|
|
|
test("catalog post-filters synthesize a gateway mirror when gate on and gateway has a connection", async () => {
|
|
setFeatureFlagOverride(FLAG_KEY, "true");
|
|
setFunctionalGatewayProviderSetting("agentrouter", "on");
|
|
|
|
const models = [
|
|
{ id: "deepseek/deepseek-v4-flash", owned_by: "deepseek", root: "deepseek-v4-flash" },
|
|
];
|
|
const out = await applyCatalogPostFilters(makeRequest(), models, {
|
|
connections: [
|
|
{
|
|
id: "conn-1",
|
|
provider: "agentrouter",
|
|
isActive: true,
|
|
providerSpecificData: {},
|
|
},
|
|
],
|
|
prefixMode: "dual",
|
|
aliasToProviderId: {},
|
|
});
|
|
// The mirror pass is wired and synthesizes agentrouter/deepseek/deepseek-v4-flash
|
|
// when the gate is on AND agentrouter (a passthrough gateway) has an active
|
|
// connection covering the model.
|
|
assert.ok(
|
|
out.some((m) => m.id === "agentrouter/deepseek/deepseek-v4-flash"),
|
|
`expected mirror to be synthesized, got: ${out.map((m) => m.id).join(", ")}`
|
|
);
|
|
});
|