Files
OmniRoute/open-sse/services/combo/autoConfig.ts
KooshaPari 2cbb47d53c [codex] Keep mode-pack weights consistent in auto fallback ranking (#7008)
* fix(routing): honor modePack weights in combo fallback ranking

* fix(routing): make effective post-override modePack drive fallback weights

parseAutoConfig() already resolves `weights` from a combo's own STORED
modePack, but resolveAutoStrategyOrder() also supports a per-request
X-OmniRoute-Mode header override (#6024/#6025) that can select a
DIFFERENT mode pack than the one stored on the combo, for that single
request only. selectAutoProvider() (engine.ts) already re-derives
weights internally from the modePack it receives, so it correctly
reacts to the override -- but scoreAutoTargets(), which ranks the
fallback tail, had no such re-derivation and only ever saw the stale
pre-override weights from parseAutoConfig(). Net effect: a request
overriding e.g. "quality-first" to "ship-fast" would select its
primary target under ship-fast weights but rank every fallback under
quality-first weights -- the identical "select under one policy, rank
fallbacks under another" bug this module's original fix (honoring the
combo's own stored modePack) set out to close.

Recompute `weights` from the effective (post-override) `modePack`
right after it's resolved, so both selectAutoProvider and
scoreAutoTargets consume the same weight vector.

Adds a regression test proving a request-level X-OmniRoute-Mode
override produces IDENTICAL fallback-ranking weights to a combo
natively configured with that same modePack.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-18 15:15:57 -03:00

72 lines
3.0 KiB
TypeScript

import { DEFAULT_WEIGHTS, type ScoringWeights } from "../autoCombo/scoring.ts";
import { getModePack } from "../autoCombo/modePacks.ts";
import { isRecord } from "./comboData.ts";
import { resolveResetWindowConfig, resolveSlaRoutingPolicy } from "./quotaScoring.ts";
import type { ComboLike, ResolvedComboTarget } from "./types.ts";
/**
* Resolve the auto-strategy routing configuration for a combo.
*
* Pure function of `(combo, eligibleTargets)`: derives the router strategy name,
* candidate provider pool, scoring weights, exploration rate, budget cap, mode
* pack, reset-window config and SLA policy from the combo's `autoConfig`/`config`.
* No side effects, no early returns — extracted verbatim from `handleComboChat`
* so its behavior is byte-identical to the previous inline block.
*/
export function parseAutoConfig(combo: ComboLike, eligibleTargets: ResolvedComboTarget[]) {
const rawAutoConfigSource =
combo?.autoConfig ||
(isRecord(combo?.config?.auto) ? combo.config.auto : null) ||
combo?.config ||
{};
const autoConfigSource: Record<string, unknown> = isRecord(rawAutoConfigSource)
? rawAutoConfigSource
: {};
const routingStrategy =
typeof autoConfigSource.routerStrategy === "string"
? autoConfigSource.routerStrategy
: typeof autoConfigSource.routingStrategy === "string"
? autoConfigSource.routingStrategy
: typeof autoConfigSource.strategyName === "string"
? autoConfigSource.strategyName
: "rules";
const candidatePool = Array.isArray(autoConfigSource.candidatePool)
? autoConfigSource.candidatePool
: [...new Set(eligibleTargets.map((target) => target.provider))];
const configuredWeights =
autoConfigSource.weights && typeof autoConfigSource.weights === "object"
? (autoConfigSource.weights as ScoringWeights)
: DEFAULT_WEIGHTS;
const explorationRate = Number.isFinite(Number(autoConfigSource.explorationRate))
? Number(autoConfigSource.explorationRate)
: 0.05;
const budgetCap = Number.isFinite(Number(autoConfigSource.budgetCap))
? Number(autoConfigSource.budgetCap)
: undefined;
// #3470: persisted fallback policy for when EVERY candidate exceeds budgetCap.
// Any other value (including absent) falls through to the engine's "cheapest" default.
const budgetFallback: "strict" | "cheapest" | undefined =
autoConfigSource.budgetFallback === "strict" || autoConfigSource.budgetFallback === "cheapest"
? (autoConfigSource.budgetFallback as "strict" | "cheapest")
: undefined;
const modePack =
typeof autoConfigSource.modePack === "string" ? autoConfigSource.modePack : undefined;
const weights = modePack ? getModePack(modePack) || configuredWeights : configuredWeights;
const resetWindowConfig = resolveResetWindowConfig(autoConfigSource);
const slaPolicy = resolveSlaRoutingPolicy(autoConfigSource);
return {
routingStrategy,
candidatePool,
weights,
explorationRate,
budgetCap,
budgetFallback,
modePack,
resetWindowConfig,
slaPolicy,
};
}