Files
OmniRoute/tests/unit/chatcore-semantic-cache-store.test.ts
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

138 lines
4.7 KiB
TypeScript

// Characterization of storeSemanticCacheResponse — the Phase 9.1 non-streaming semantic-cache
// store extracted from handleChatCore (chatCore god-file decomposition, #3501). Deps are injected
// so the gating logic + signature derivation are observable without the real cache backend.
// Locks: the 3-way gate (enabled + cacheable-for-write + small-enough), the signature args
// (messages ?? input, temperature, top_p, apiKeyId), and the `prompt + completion || 0` token count.
import { test } from "node:test";
import assert from "node:assert/strict";
const { storeSemanticCacheResponse } =
await import("../../open-sse/handlers/chatCore/semanticCacheStore.ts");
type Stored = { sig: unknown; model: string; response: unknown; tokens: number };
function makeDeps(overrides: Record<string, unknown> = {}) {
const stored: Stored[] = [];
const calls = { cacheable: 0, small: 0, signature: 0 };
const deps = {
isCacheableForWrite: (..._a: unknown[]) => {
calls.cacheable++;
return true;
},
isSmallEnoughForSemanticCache: (..._a: unknown[]) => {
calls.small++;
return true;
},
generateSignature: (...a: unknown[]) => {
calls.signature++;
return `sig:${JSON.stringify(a)}`;
},
setCachedResponse: (sig: unknown, model: string, response: unknown, tokens: number) => {
stored.push({ sig, model, response, tokens });
},
...overrides,
} as Parameters<typeof storeSemanticCacheResponse>[1];
return { deps, stored, calls };
}
function baseArgs(overrides: Record<string, unknown> = {}) {
return {
enabled: true,
body: { messages: [{ role: "user", content: "hi" }], temperature: 0, top_p: 1 },
headers: undefined,
translatedResponse: { id: "resp-1" },
model: "gpt-x",
apiKeyId: "key-1",
usage: { prompt_tokens: 10, completion_tokens: 5 },
log: undefined,
...overrides,
} as Parameters<typeof storeSemanticCacheResponse>[0];
}
function assertNumericSignatureInputs(
deps: Parameters<typeof storeSemanticCacheResponse>[1]
): void {
if (process.env.NODE_ENV === "__semantic_cache_type_contract__") {
storeSemanticCacheResponse(
{
enabled: true,
body: {
messages: [],
// @ts-expect-error temperature is a numeric producer field
temperature: "0",
top_p: 1,
},
headers: undefined,
translatedResponse: {},
model: "gpt-x",
},
deps
);
}
}
test("signature input contract keeps temperature numeric", () => {
const { deps } = makeDeps();
assertNumericSignatureInputs(deps);
});
test("happy path → stores under a signature, tokensSaved = prompt + completion", () => {
const { deps, stored } = makeDeps();
storeSemanticCacheResponse(baseArgs(), deps);
assert.equal(stored.length, 1);
assert.equal(stored[0].model, "gpt-x");
assert.deepEqual(stored[0].response, { id: "resp-1" });
assert.equal(stored[0].tokens, 15);
const signatureArgs = JSON.parse(String(stored[0].sig).slice("sig:".length)) as unknown[];
assert.deepEqual(signatureArgs.slice(2, 4), [0, 1]);
});
test("disabled → no store, no gate calls past enabled", () => {
const { deps, stored, calls } = makeDeps();
storeSemanticCacheResponse(baseArgs({ enabled: false }), deps);
assert.equal(stored.length, 0);
assert.equal(calls.cacheable, 0);
});
test("not cacheable-for-write → no store", () => {
const { deps, stored } = makeDeps({ isCacheableForWrite: () => false });
storeSemanticCacheResponse(baseArgs(), deps);
assert.equal(stored.length, 0);
});
test("too large → no store", () => {
const { deps, stored } = makeDeps({ isSmallEnoughForSemanticCache: () => false });
storeSemanticCacheResponse(baseArgs(), deps);
assert.equal(stored.length, 0);
});
test("signature uses messages when present, with model/temperature/top_p/apiKeyId", () => {
const { deps, stored } = makeDeps();
storeSemanticCacheResponse(baseArgs(), deps);
const sig = stored[0].sig as string;
assert.ok(sig.includes("gpt-x"));
assert.ok(sig.includes("key-1"));
});
test("falls back to body.input when messages absent", () => {
let captured: unknown[] = [];
const { deps } = makeDeps({
generateSignature: (...a: unknown[]) => {
captured = a;
return "sig";
},
});
storeSemanticCacheResponse(
baseArgs({ body: { input: "the-input", temperature: 0, top_p: 1 } }),
deps
);
// args: (model, messages ?? input, temperature, top_p, apiKeyId)
assert.equal(captured[1], "the-input");
});
test("missing usage → tokensSaved coerces to 0 (NaN || 0)", () => {
const { deps, stored } = makeDeps();
storeSemanticCacheResponse(baseArgs({ usage: undefined }), deps);
assert.equal(stored[0].tokens, 0);
});