Compare commits

...

3 Commits

Author SHA1 Message Date
diegosouzapw
26a2067f21 fix(quality-gates): register combo-system-prompt-templates-5501 test in stryker tap.testFiles
check:mutation-test-coverage --strict flagged tests/unit/combo-system-prompt-templates-5501.test.ts
as covering src/shared/utils/circuitBreaker.ts without being listed in stryker.conf.json
tap.testFiles, so its mutant kills wouldn't count.

Co-authored-by: maxmad64bis <maxmad64bis@users.noreply.github.com>
2026-08-05 19:51:15 -03:00
Diego Rodrigues de Sa e Souza
5018712a4b Merge branch 'release/v3.8.50' into feat/5501-combo-system-prompt-templates 2026-08-05 13:22:15 -03:00
Max
0c2aa35aee feat(sse): server-side template expansion for combo system prompts (#5501) 2026-08-04 11:56:04 +02:00
8 changed files with 496 additions and 6 deletions

View File

@@ -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))

View File

@@ -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.
![Combos Dashboard](../screenshots/02-combos.png)

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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",

View File

@@ -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();

View 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"]);
});