mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-11 17:52:31 +03:00
Compare commits
3 Commits
chloeassis
...
feat/5501-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26a2067f21 | ||
|
|
5018712a4b | ||
|
|
0c2aa35aee |
@@ -0,0 +1,2 @@
|
||||
- **feat(sse):** combo `system_message` supports server-side `{{MODEL_ID}}`, `{{PROVIDER_ID}}`, `{{ACCOUNT}}` and `{{FINGERPRINT}}` template expansion from the actually-routed target ([#5501](https://github.com/diegosouzapw/OmniRoute/issues/5501))
|
||||
- **feat(sse):** template expansion covers the standard dispatch loop, round-robin and pinned context-cache sessions; fusion, chaos, pipeline and nested-execute strategies do not expand yet ([#5501](https://github.com/diegosouzapw/OmniRoute/issues/5501))
|
||||
@@ -69,6 +69,17 @@ Recent combo improvements:
|
||||
- **Repeated provider support** — reuse the same provider many times in one combo as long as the `(provider, model, connection)` tuple is unique
|
||||
- **Combo target health** — analytics and health surfaces now distinguish individual combo targets/steps instead of collapsing everything into model strings
|
||||
- **Composite tier ordering** — `defaultTier -> fallbackTier` now influences runtime execution/fallback order for top-level combo steps
|
||||
- **System prompt templates** — combo `system_message` supports server-side
|
||||
`{{MODEL_ID}}`, `{{PROVIDER_ID}}`, `{{ACCOUNT}}` and `{{FINGERPRINT}}`
|
||||
placeholders, expanded from the actually-routed target right before dispatch.
|
||||
Allowlisted and non-recursive; unknown placeholders stay literal; empty values
|
||||
expand to empty; client system prompts are never rewritten. `{{FINGERPRINT}}`
|
||||
resolves only for fingerprint-based free providers with a pinned or
|
||||
auto-rotated fingerprint — it expands to empty elsewhere (e.g.
|
||||
single-fingerprint connections, non-fp providers). Expansion covers the
|
||||
standard dispatch loop, round-robin, and pinned context-cache sessions;
|
||||
fusion, chaos, pipeline and nested-execute strategies do not expand
|
||||
placeholders yet.
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ import {
|
||||
import { buildNoUpstreamResponseDiagnostics, buildRecoveryHint } from "./combo/pinRecovery.ts";
|
||||
import { buildTargetTimeoutRunner } from "./combo/targetTimeoutRunner.ts";
|
||||
import { recordComboRequest, recordComboShadowRequest, getComboMetrics } from "./comboMetrics.ts";
|
||||
import {
|
||||
expandComboSystemPromptIfPresent,
|
||||
resolveTargetFingerprint,
|
||||
} from "./comboAgentMiddleware.ts";
|
||||
import {
|
||||
resolveComboConfig,
|
||||
getDefaultComboConfig,
|
||||
@@ -1169,6 +1173,18 @@ export async function handleComboChat({
|
||||
}
|
||||
}
|
||||
}
|
||||
// #5501: server-side template expansion for the combo system_message —
|
||||
// resolved per-target, scoped to combo-injected content only (never
|
||||
// client-owned system messages). Gate: a non-empty combo system_message.
|
||||
attemptBody = expandComboSystemPromptIfPresent(attemptBody, combo, {
|
||||
modelId: modelStr,
|
||||
providerId: provider !== "unknown" ? provider : "",
|
||||
account:
|
||||
typeof target.label === "string" && target.label.trim().length > 0
|
||||
? target.label.trim()
|
||||
: "",
|
||||
fingerprint: resolveTargetFingerprint(target) ?? "",
|
||||
});
|
||||
const result = await handleSingleModelWithTimeout(attemptBody, modelStr, {
|
||||
...targetForAttempt,
|
||||
effectiveComboStrategy: strategy,
|
||||
@@ -2568,6 +2584,18 @@ async function handleRoundRobinCombo({
|
||||
}
|
||||
}
|
||||
|
||||
// #5501: combo system_message template expansion per target (same gate
|
||||
// as the main iteration loop — round-robin branches here, not executeTarget).
|
||||
attemptBody = expandComboSystemPromptIfPresent(attemptBody, combo, {
|
||||
modelId: modelStr,
|
||||
providerId: provider !== "unknown" ? provider : "",
|
||||
account:
|
||||
typeof target.label === "string" && target.label.trim().length > 0
|
||||
? target.label.trim()
|
||||
: "",
|
||||
fingerprint: resolveTargetFingerprint(target) ?? "",
|
||||
});
|
||||
|
||||
const result = await handleSingleModel(attemptBody, modelStr, {
|
||||
...targetForAttempt,
|
||||
effectiveComboStrategy: "round-robin",
|
||||
|
||||
@@ -22,6 +22,10 @@ import type { resolveComboSetupConfig } from "../comboConfig.ts";
|
||||
import { clampComboDepth, MAX_GLOBAL_ATTEMPTS, resolveDelayMs } from "./comboPredicates.ts";
|
||||
import { resolveComboRuntimeUnits, resolveComboTargets } from "./comboStructure.ts";
|
||||
import { buildFusionHandleSingleModel, extractFusionPanelSpec } from "./fusionPanel.ts";
|
||||
import {
|
||||
expandComboSystemPromptIfPresent,
|
||||
resolveTargetFingerprint,
|
||||
} from "../comboAgentMiddleware.ts";
|
||||
import {
|
||||
clampStickyWeightedTargetLimit,
|
||||
getStickyRoundRobinStartIndex,
|
||||
@@ -254,11 +258,16 @@ export async function tryPinnedModelDispatch(args: {
|
||||
// when allCombos is authoritative (non-empty) so we can resolve combo-refs;
|
||||
// the auto-combo redirect path passes an empty list and keeps prior behavior.
|
||||
const haveFullCombos = Array.isArray(allCombos) ? allCombos.length > 0 : !!allCombos;
|
||||
const pinInCombo =
|
||||
!haveFullCombos ||
|
||||
resolveComboTargets(combo, allCombos, clampComboDepth(config.maxComboDepth)).some(
|
||||
(t) => t.modelStr === pinnedModel
|
||||
);
|
||||
// Eagerly resolve the combo's targets once (used for the pin-validity check AND
|
||||
// #5501 template expansion). A non-authoritative allCombos (empty/missing)
|
||||
// resolves to the combo's direct targets only — same semantics as the original
|
||||
// `!haveFullCombos ||` short-circuit, without feeding `[]` to the nested resolver.
|
||||
const comboTargets = resolveComboTargets(
|
||||
combo,
|
||||
haveFullCombos ? allCombos : undefined,
|
||||
clampComboDepth(config.maxComboDepth)
|
||||
);
|
||||
const pinInCombo = !haveFullCombos || comboTargets.some((t) => t.modelStr === pinnedModel);
|
||||
// Honor the pin only if it is still a combo target AND its provider is not
|
||||
// DURABLY down. Without the health gate a pin keeps routing a session to a
|
||||
// dead/credits-exhausted/throttled account forever (strategy bypassed, no
|
||||
@@ -273,7 +282,21 @@ export async function tryPinnedModelDispatch(args: {
|
||||
);
|
||||
let pinnedResult: Response | null = null;
|
||||
try {
|
||||
pinnedResult = await handleSingleModelWithTimeout(body, pinnedModel, {
|
||||
// #5501: the combo system_message also expands on the pinned context path —
|
||||
// a session pin bypasses the main loop, so without this the template would
|
||||
// go literal from the second in-session request on. Target context comes
|
||||
// from the pinned model's resolved combo target when available.
|
||||
const pinnedTarget = comboTargets.find((t) => t.modelStr === pinnedModel);
|
||||
const pinnedBody = expandComboSystemPromptIfPresent(body, combo, {
|
||||
modelId: pinnedModel,
|
||||
providerId: pinnedTarget && pinnedTarget.provider !== "unknown" ? pinnedTarget.provider : "",
|
||||
account:
|
||||
typeof pinnedTarget?.label === "string" && pinnedTarget.label.trim().length > 0
|
||||
? pinnedTarget.label.trim()
|
||||
: "",
|
||||
fingerprint: pinnedTarget ? resolveTargetFingerprint(pinnedTarget) ?? "" : "",
|
||||
});
|
||||
pinnedResult = await handleSingleModelWithTimeout(pinnedBody, pinnedModel, {
|
||||
modelPinned: true,
|
||||
} as SingleModelTarget);
|
||||
} catch (pinErr) {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
* All features are opt-in per combo and backward compatible with existing setups.
|
||||
*/
|
||||
|
||||
import { isFingerprintProvider } from "./combo/fingerprintExpansion.ts";
|
||||
|
||||
interface ComboConfig {
|
||||
system_message?: string | null;
|
||||
tool_filter_regex?: string | null;
|
||||
@@ -221,3 +223,122 @@ export function applyComboAgentMiddleware(
|
||||
pinnedModel,
|
||||
};
|
||||
}
|
||||
|
||||
// ── System Prompt Template Expansion (#5501) ─────────────────────────────────
|
||||
|
||||
export interface ComboSystemPromptTemplateContext {
|
||||
modelId: string;
|
||||
providerId: string;
|
||||
account: string;
|
||||
fingerprint: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace allowlisted `{{TOKEN}}` placeholders in a single left-to-right scan.
|
||||
* No regex (ReDoS-averse, cf. #3870) and no recursion: an expanded value is
|
||||
* appended to the output and never re-scanned. Unknown tokens ({{FOO}}) and
|
||||
* dangling "{{" stay literal.
|
||||
*/
|
||||
function expandStringTemplates(value: string, values: Record<string, string>): string {
|
||||
let out = "";
|
||||
let rest = value;
|
||||
while (rest.length > 0) {
|
||||
const start = rest.indexOf("{{");
|
||||
if (start === -1) {
|
||||
out += rest;
|
||||
break;
|
||||
}
|
||||
const end = rest.indexOf("}}", start + 2);
|
||||
if (end === -1) {
|
||||
out += rest;
|
||||
break;
|
||||
}
|
||||
const token = rest.slice(start, end + 2);
|
||||
out += rest.slice(0, start);
|
||||
out += token in values ? values[token] : token;
|
||||
rest = rest.slice(end + 2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand allowlisted placeholders in the combo-injected system prompt (#5501).
|
||||
*
|
||||
* Strictly scoped to the content the combo override produced — never
|
||||
* client-owned system content:
|
||||
* - Responses API body (has `instructions`) → expand `body.instructions`.
|
||||
* - messages body → expand `body.messages[0]` when it is the injected combo
|
||||
* system message (the override filters all system messages and injects its
|
||||
* own at index 0 with string content).
|
||||
* - otherwise → body unchanged.
|
||||
*/
|
||||
export function expandComboSystemPromptTemplates(
|
||||
body: Record<string, unknown>,
|
||||
ctx: ComboSystemPromptTemplateContext
|
||||
): Record<string, unknown> {
|
||||
const values: Record<string, string> = {
|
||||
"{{MODEL_ID}}": ctx.modelId,
|
||||
"{{PROVIDER_ID}}": ctx.providerId,
|
||||
"{{ACCOUNT}}": ctx.account,
|
||||
"{{FINGERPRINT}}": ctx.fingerprint,
|
||||
};
|
||||
const result = { ...body };
|
||||
if (typeof result.instructions === "string") {
|
||||
result.instructions = expandStringTemplates(result.instructions, values);
|
||||
return result;
|
||||
}
|
||||
const messages = result.messages;
|
||||
if (Array.isArray(messages)) {
|
||||
const first = messages[0] as Record<string, unknown> | undefined;
|
||||
if (
|
||||
first &&
|
||||
(first.role === "system" || first.role === "developer") &&
|
||||
typeof first.content === "string"
|
||||
) {
|
||||
const next = [...messages];
|
||||
next[0] = { ...first, content: expandStringTemplates(first.content, values) };
|
||||
result.messages = next;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gate + expand: expand the combo `system_message` template placeholders only
|
||||
* when the combo actually defines a non-empty `system_message`. Client-owned
|
||||
* content passes through untouched (single gate shared by every dispatch path).
|
||||
*/
|
||||
export function expandComboSystemPromptIfPresent(
|
||||
body: Record<string, unknown>,
|
||||
combo: { system_message?: string | null },
|
||||
ctx: ComboSystemPromptTemplateContext
|
||||
): Record<string, unknown> {
|
||||
if (typeof combo.system_message === "string" && combo.system_message.trim()) {
|
||||
return expandComboSystemPromptTemplates(body, ctx);
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the device fingerprint for a combo target (#5501, #6087).
|
||||
* Only fingerprint-based providers carry fingerprints (see isFingerprintProvider).
|
||||
* Priority: explicit pin (`pinnedFingerprint`, combo builder) → the `@fp:`
|
||||
* suffix in `executionKey` (auto-rotation).
|
||||
* Returns null when none is knowable (the first fingerprint of an auto-rotated
|
||||
* set keeps the bare execution key — documented limitation).
|
||||
*/
|
||||
export function resolveTargetFingerprint(target: {
|
||||
provider: string;
|
||||
pinnedFingerprint?: string;
|
||||
executionKey?: string;
|
||||
}): string | null {
|
||||
if (!isFingerprintProvider(target.provider)) return null;
|
||||
if (target.pinnedFingerprint) return target.pinnedFingerprint;
|
||||
const key = target.executionKey;
|
||||
if (key) {
|
||||
const marker = "@fp:";
|
||||
const idx = key.lastIndexOf(marker);
|
||||
if (idx !== -1) return key.slice(idx + marker.length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -181,6 +181,7 @@
|
||||
"tests/unit/combo-stream-readiness-fallback.test.ts",
|
||||
"tests/unit/combo-streaming-empty-content-failover.test.ts",
|
||||
"tests/unit/combo-strict-random-distribution-3959.test.ts",
|
||||
"tests/unit/combo-system-prompt-templates-5501.test.ts",
|
||||
"tests/unit/combo-target-defensive-modelstr.test.ts",
|
||||
"tests/unit/combo/auto-quota-cutoff.test.ts",
|
||||
"tests/unit/combo/auto-status-penalty-4540.test.ts",
|
||||
|
||||
@@ -457,6 +457,39 @@ test("tryPinnedModelDispatch: serves the pinned response when the pin is healthy
|
||||
);
|
||||
});
|
||||
|
||||
test("tryPinnedModelDispatch: expands the combo system_message template on the pinned path (#5501)", async () => {
|
||||
const ctx = setup({
|
||||
name: "pinned-combo",
|
||||
strategy: "priority",
|
||||
models: [{ model: `${HEALTHY_PROVIDER}/live` }],
|
||||
config: {},
|
||||
system_message: "Model: {{MODEL_ID}}",
|
||||
});
|
||||
ctx.body = {
|
||||
messages: [
|
||||
{ role: "system", content: "Model: {{MODEL_ID}}" },
|
||||
{ role: "user", content: "hi" },
|
||||
],
|
||||
};
|
||||
await seedHealthyPinProvider();
|
||||
const seen: string[] = [];
|
||||
const res = await tryPinnedModelDispatch({
|
||||
body: ctx.body,
|
||||
combo: ctx.combo,
|
||||
pinnedModel: `${HEALTHY_PROVIDER}/live`,
|
||||
allCombos: [],
|
||||
config: ctx.config,
|
||||
clientRequestedStream: false,
|
||||
handleSingleModelWithTimeout: async (received: Record<string, unknown>) => {
|
||||
seen.push((received.messages as { content: string }[])[0].content);
|
||||
return okResponse("pinned answer");
|
||||
},
|
||||
log: ctx.log,
|
||||
});
|
||||
assert.ok(res, "the healthy pin must be served");
|
||||
assert.deepEqual(seen, [`Model: ${HEALTHY_PROVIDER}/live`]);
|
||||
});
|
||||
|
||||
test("tryPinnedModelDispatch: fails over when the pinned model returns a transient status", async () => {
|
||||
for (const status of [408, 429, 500, 502, 503, 504]) {
|
||||
const ctx = pinCtx();
|
||||
|
||||
271
tests/unit/combo-system-prompt-templates-5501.test.ts
Normal file
271
tests/unit/combo-system-prompt-templates-5501.test.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
// Node's test runner stops registering tests past a top-level `await import`, so
|
||||
// every module import — and the DATA_DIR pin that must precede combo.ts — happens
|
||||
// here, before any `test()` call. Mirrors the combo-attempt-body-isolation harness.
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-combo-tpl-"));
|
||||
const ORIGINAL_DATA_DIR = process.env.DATA_DIR;
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const {
|
||||
expandComboSystemPromptIfPresent,
|
||||
expandComboSystemPromptTemplates,
|
||||
resolveTargetFingerprint,
|
||||
} = await import("../../open-sse/services/comboAgentMiddleware.ts");
|
||||
const { handleComboChat } = await import("../../open-sse/services/combo.ts");
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const { resetAllComboMetrics } = await import("../../open-sse/services/comboMetrics.ts");
|
||||
const { resetAllCircuitBreakers } = await import("../../src/shared/utils/circuitBreaker.ts");
|
||||
const { resetAll: resetAllSemaphores } =
|
||||
await import("../../open-sse/services/rateLimitSemaphore.ts");
|
||||
const { _resetAllDecks } = await import("../../src/shared/utils/shuffleDeck.ts");
|
||||
const { clearSessions } = await import("../../open-sse/services/sessionManager.ts");
|
||||
|
||||
const CTX = {
|
||||
modelId: "openrouter/owl-alpha",
|
||||
providerId: "openrouter",
|
||||
account: "my-key",
|
||||
fingerprint: "fp-123",
|
||||
};
|
||||
|
||||
function createLog() {
|
||||
const entries: unknown[] = [];
|
||||
const push = (level: string) => (tag: unknown, msg: unknown) => entries.push({ level, tag, msg });
|
||||
return {
|
||||
info: push("info"),
|
||||
warn: push("warn"),
|
||||
error: push("error"),
|
||||
debug: push("debug"),
|
||||
entries,
|
||||
};
|
||||
}
|
||||
|
||||
const okResponse = () =>
|
||||
new Response(JSON.stringify({ choices: [{ message: { content: "ok" } }] }), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
|
||||
const MODELS = ["openai/gpt-4o-mini", "claude/sonnet", "gemini/flash"];
|
||||
|
||||
function comboOf(name: string, systemMessage?: string, strategy = "priority") {
|
||||
return {
|
||||
name,
|
||||
strategy,
|
||||
models: MODELS,
|
||||
config: { maxRetries: 0, retryDelayMs: 0, fallbackDelayMs: 0 },
|
||||
...(systemMessage ? { system_message: systemMessage } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function bodyWithSystem(content: string) {
|
||||
return {
|
||||
model: "openai/gpt-4o-mini",
|
||||
max_tokens: 100,
|
||||
messages: [{ role: "system", content }, { role: "user", content: "hi" }],
|
||||
};
|
||||
}
|
||||
|
||||
test("messages format: expands all placeholders in messages[0] system content", () => {
|
||||
const body = {
|
||||
messages: [
|
||||
{ role: "system", content: "M={{MODEL_ID}} P={{PROVIDER_ID}} A={{ACCOUNT}} F={{FINGERPRINT}}" },
|
||||
{ role: "user", content: "hi" },
|
||||
],
|
||||
};
|
||||
const out = expandComboSystemPromptTemplates(body, CTX);
|
||||
assert.equal(out.messages[0].content, "M=openrouter/owl-alpha P=openrouter A=my-key F=fp-123");
|
||||
assert.equal(out.messages[1].content, "hi");
|
||||
});
|
||||
|
||||
test("instructions (Responses API): expands instructions, leaves client messages untouched", () => {
|
||||
const body = {
|
||||
instructions: "Model: {{MODEL_ID}}",
|
||||
input: "hi",
|
||||
messages: [{ role: "system", content: "client {{MODEL_ID}}" }],
|
||||
};
|
||||
const out = expandComboSystemPromptTemplates(body, CTX);
|
||||
assert.equal(out.instructions, "Model: openrouter/owl-alpha");
|
||||
assert.equal(out.messages[0].content, "client {{MODEL_ID}}");
|
||||
});
|
||||
|
||||
test("unknown placeholder stays literal", () => {
|
||||
const body = { messages: [{ role: "system", content: "{{FOO}} {{MODEL_ID}}" }] };
|
||||
const out = expandComboSystemPromptTemplates(body, CTX);
|
||||
assert.equal(out.messages[0].content, "{{FOO}} openrouter/owl-alpha");
|
||||
});
|
||||
|
||||
test("no recursion: expanded value is never re-scanned", () => {
|
||||
const body = { messages: [{ role: "system", content: "{{MODEL_ID}}" }] };
|
||||
const out = expandComboSystemPromptTemplates(body, { ...CTX, modelId: "{{PROVIDER_ID}}" });
|
||||
assert.equal(out.messages[0].content, "{{PROVIDER_ID}}");
|
||||
});
|
||||
|
||||
test("empty value expands to empty string", () => {
|
||||
const body = { messages: [{ role: "system", content: "[{{FINGERPRINT}}]" }] };
|
||||
const out = expandComboSystemPromptTemplates(body, { ...CTX, fingerprint: "" });
|
||||
assert.equal(out.messages[0].content, "[]");
|
||||
});
|
||||
|
||||
test("no placeholders: body unchanged (deep equal)", () => {
|
||||
const body = {
|
||||
messages: [{ role: "system", content: "plain" }, { role: "user", content: "hi" }],
|
||||
};
|
||||
const out = expandComboSystemPromptTemplates(body, CTX);
|
||||
assert.deepEqual(out, body);
|
||||
});
|
||||
|
||||
test("messages[0] non-system role: unchanged", () => {
|
||||
const body = { messages: [{ role: "user", content: "{{MODEL_ID}}" }] };
|
||||
const out = expandComboSystemPromptTemplates(body, CTX);
|
||||
assert.equal(out.messages[0].content, "{{MODEL_ID}}");
|
||||
});
|
||||
|
||||
test("expandComboSystemPromptIfPresent: absent system_message passes body through", () => {
|
||||
const body = { messages: [{ role: "system", content: "keep {{MODEL_ID}}" }] };
|
||||
const out = expandComboSystemPromptIfPresent(body, { system_message: null }, CTX);
|
||||
assert.equal(out, body);
|
||||
const out2 = expandComboSystemPromptIfPresent(body, {}, CTX);
|
||||
assert.equal(out2, body);
|
||||
});
|
||||
|
||||
test("expandComboSystemPromptIfPresent: blank system_message passes body through", () => {
|
||||
const body = { messages: [{ role: "system", content: "keep {{MODEL_ID}}" }] };
|
||||
const out = expandComboSystemPromptIfPresent(body, { system_message: " " }, CTX);
|
||||
assert.equal(out, body);
|
||||
});
|
||||
|
||||
test("expandComboSystemPromptIfPresent: non-empty system_message expands", () => {
|
||||
const body = { messages: [{ role: "system", content: "M={{MODEL_ID}}" }] };
|
||||
const out = expandComboSystemPromptIfPresent(body, { system_message: "M={{MODEL_ID}}" }, CTX);
|
||||
assert.equal(out.messages[0].content, "M=openrouter/owl-alpha");
|
||||
});
|
||||
|
||||
test("resolveTargetFingerprint: non-fp provider returns null", () => {
|
||||
assert.equal(resolveTargetFingerprint({ provider: "openai", executionKey: "k@fp:abc" }), null);
|
||||
});
|
||||
|
||||
test("resolveTargetFingerprint: pinned fingerprint wins", () => {
|
||||
assert.equal(
|
||||
resolveTargetFingerprint({ provider: "opencode", pinnedFingerprint: "pin1", executionKey: "k@fp:abc" }),
|
||||
"pin1"
|
||||
);
|
||||
});
|
||||
|
||||
test("resolveTargetFingerprint: parses @fp: suffix from executionKey", () => {
|
||||
assert.equal(resolveTargetFingerprint({ provider: "mcode", executionKey: "k@fp:abc" }), "abc");
|
||||
});
|
||||
|
||||
test("resolveTargetFingerprint: null when no source", () => {
|
||||
assert.equal(resolveTargetFingerprint({ provider: "opencode", executionKey: "k" }), null);
|
||||
assert.equal(resolveTargetFingerprint({ provider: "mimocode" }), null);
|
||||
});
|
||||
|
||||
// ── Integration: hook + gate through handleComboChat (#5501) ──────────────────
|
||||
|
||||
test.beforeEach(() => {
|
||||
resetAllComboMetrics();
|
||||
resetAllCircuitBreakers();
|
||||
resetAllSemaphores();
|
||||
_resetAllDecks();
|
||||
clearSessions();
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
if (ORIGINAL_DATA_DIR === undefined) delete process.env.DATA_DIR;
|
||||
else process.env.DATA_DIR = ORIGINAL_DATA_DIR;
|
||||
});
|
||||
|
||||
test("combo system_message: {{MODEL_ID}} expands to the resolved target model", async () => {
|
||||
const seen: string[] = [];
|
||||
await handleComboChat({
|
||||
body: bodyWithSystem("placeholder"),
|
||||
combo: comboOf("cow-tpl-expand", "Model: {{MODEL_ID}}"),
|
||||
handleSingleModel: async (received: Record<string, unknown>) => {
|
||||
seen.push((received.messages as { content: string }[])[0].content);
|
||||
return okResponse();
|
||||
},
|
||||
isModelAvailable: async () => true,
|
||||
log: createLog(),
|
||||
settings: null,
|
||||
allCombos: null,
|
||||
});
|
||||
assert.deepEqual(seen, ["Model: openai/gpt-4o-mini"]);
|
||||
});
|
||||
|
||||
test("gate: without combo system_message, client system content is NOT expanded", async () => {
|
||||
const seen: string[] = [];
|
||||
await handleComboChat({
|
||||
body: bodyWithSystem("keep {{MODEL_ID}} literal"),
|
||||
combo: comboOf("cow-tpl-gate"),
|
||||
handleSingleModel: async (received: Record<string, unknown>) => {
|
||||
seen.push((received.messages as { content: string }[])[0].content);
|
||||
return okResponse();
|
||||
},
|
||||
isModelAvailable: async () => true,
|
||||
log: createLog(),
|
||||
settings: null,
|
||||
allCombos: null,
|
||||
});
|
||||
assert.deepEqual(seen, ["keep {{MODEL_ID}} literal"]);
|
||||
});
|
||||
|
||||
test("{{FINGERPRINT}} on a non-fp target expands to empty string, not literal 'null'", async () => {
|
||||
const seen: string[] = [];
|
||||
await handleComboChat({
|
||||
body: bodyWithSystem("placeholder"),
|
||||
combo: comboOf("cow-tpl-fp", "F=[{{FINGERPRINT}}]"),
|
||||
handleSingleModel: async (received: Record<string, unknown>) => {
|
||||
seen.push((received.messages as { content: string }[])[0].content);
|
||||
return okResponse();
|
||||
},
|
||||
isModelAvailable: async () => true,
|
||||
log: createLog(),
|
||||
settings: null,
|
||||
allCombos: null,
|
||||
});
|
||||
assert.deepEqual(seen, ["F=[]"]);
|
||||
});
|
||||
|
||||
test("round-robin combo: {{MODEL_ID}} expands to the resolved target model", async () => {
|
||||
const seen: string[] = [];
|
||||
const routed: string[] = [];
|
||||
await handleComboChat({
|
||||
body: bodyWithSystem("placeholder"),
|
||||
combo: comboOf("cow-tpl-rr", "Model: {{MODEL_ID}}", "round-robin"),
|
||||
handleSingleModel: async (received: Record<string, unknown>, modelStr: string) => {
|
||||
seen.push((received.messages as { content: string }[])[0].content);
|
||||
routed.push(modelStr);
|
||||
return okResponse();
|
||||
},
|
||||
isModelAvailable: async () => true,
|
||||
log: createLog(),
|
||||
settings: null,
|
||||
allCombos: null,
|
||||
});
|
||||
assert.equal(seen.length, 1, "round-robin must dispatch exactly one target");
|
||||
assert.deepEqual(seen, [`Model: ${routed[0]}`]);
|
||||
});
|
||||
|
||||
test("round-robin gate: without combo system_message, client system content stays literal", async () => {
|
||||
const seen: string[] = [];
|
||||
await handleComboChat({
|
||||
body: bodyWithSystem("keep {{MODEL_ID}} literal"),
|
||||
combo: comboOf("cow-tpl-rr-gate", undefined, "round-robin"),
|
||||
handleSingleModel: async (received: Record<string, unknown>) => {
|
||||
seen.push((received.messages as { content: string }[])[0].content);
|
||||
return okResponse();
|
||||
},
|
||||
isModelAvailable: async () => true,
|
||||
log: createLog(),
|
||||
settings: null,
|
||||
allCombos: null,
|
||||
});
|
||||
assert.deepEqual(seen, ["keep {{MODEL_ID}} literal"]);
|
||||
});
|
||||
Reference in New Issue
Block a user