mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-15 03:12:36 +03:00
The weight table said stability accounts for "low latency stdDev / error rate". Grep errorRate in scoring.ts and you find it declared on ProviderCandidate and read nowhere — while combo.ts pulls 24 hours of usage history behind a ten-sample floor, falls back to real-time metrics, and hands every candidate an errorRate the scorer ignores. Two candidates, one failing 1% of calls and one failing 99%, scored identically at 0.459486. This declares reliability as a sixteenth factor: 1 - failureRate, using the same formula, field precedence and rate-bounding speedRanking.ts already applies, so a corrupt reading means "nothing observed" rather than "fails every call". It ships at weight 0, leaving the ranking unchanged to the digit — the honest default, since which weight this deserves is a product call backed by traffic the author does not have. Two declared-but-silent factors already ship (cacheAffinity, resetWindowAffinity), so the pattern is not new. The stability row now describes what that factor actually computes: latency variance. The rest is the mechanical 15 → 16 across nineteen documents and the forty-two llm.txt mirrors — sourced from check:docs-counts rather than a grep, the first real use of the gate #12316 extended. Protected-surface note: this PR touches AGENTS.md, llm.txt and its 42 mirrors, and skills/omni-combos-routing/SKILL.md. Every changed line in those 45 files is a digit substitution and nothing else — masking all digits makes the removed and added lines identical, with no sentence added, removed or reworded. Reviewed and approved on that basis before merging. Verified on the author's rebased head: check:docs-counts green (the gate that now enforces the count this PR moves), typecheck:core clean, and 71/71 focused tests across scoring-reliability-factor, combo-scoring-weights-schema-coverage, check-docs-counts-sync, lkgp-enabled-context, intelligent-routing-options and the combo-matrix auto integration suite. Thanks @maxmad64bis — shipping the factor at weight 0 and saying plainly that the weight is someone else's call is the right way to land this.
231 lines
8.9 KiB
TypeScript
231 lines
8.9 KiB
TypeScript
type JsonRecord = Record<string, unknown>;
|
|
|
|
export const INTELLIGENT_STRATEGIES = ["auto", "lkgp"] as const;
|
|
export const INTELLIGENT_ROUTING_FILTERS = ["all", "intelligent", "deterministic"] as const;
|
|
|
|
export type IntelligentRoutingFilter = (typeof INTELLIGENT_ROUTING_FILTERS)[number];
|
|
|
|
export type IntelligentRoutingWeights = {
|
|
quota: number;
|
|
health: number;
|
|
costInv: number;
|
|
latencyInv: number;
|
|
taskFit: number;
|
|
stability: number;
|
|
tierPriority: number;
|
|
tierAffinity: number;
|
|
specificityMatch: number;
|
|
contextAffinity: number;
|
|
cacheAffinity: number;
|
|
sessionAvailability: number;
|
|
resetWindowAffinity: number;
|
|
connectionDensity: number;
|
|
quality: number;
|
|
reliability: number;
|
|
};
|
|
|
|
export type IntelligentRoutingConfig = {
|
|
candidatePool: string[];
|
|
explorationRate: number;
|
|
modePack: string;
|
|
budgetCap?: number;
|
|
weights: IntelligentRoutingWeights;
|
|
routerStrategy: string;
|
|
slaTargetP95Ms?: number;
|
|
slaMaxErrorRate?: number;
|
|
slaMaxCostPer1MTokens?: number;
|
|
slaHardConstraints: boolean;
|
|
};
|
|
|
|
export type IntelligentProviderScore = {
|
|
provider: string;
|
|
model: string;
|
|
score: number;
|
|
factors: IntelligentRoutingWeights;
|
|
};
|
|
|
|
// Kept in sync with DEFAULT_WEIGHTS by
|
|
// tests/unit/combo-scoring-weights-schema-coverage.test.ts. This is a copy on
|
|
// purpose: this module is imported by a client component, and importing the
|
|
// scorer would pull the tier resolver and per-provider cost data into the
|
|
// browser bundle. The test is what makes the copy safe.
|
|
export const DEFAULT_INTELLIGENT_WEIGHTS: IntelligentRoutingWeights = {
|
|
quota: 0.1429,
|
|
health: 0.1605,
|
|
costInv: 0.1429,
|
|
latencyInv: 0.1143,
|
|
taskFit: 0.0762,
|
|
stability: 0.0476,
|
|
tierPriority: 0.0476,
|
|
tierAffinity: 0.0476,
|
|
specificityMatch: 0.0476,
|
|
contextAffinity: 0.0476,
|
|
cacheAffinity: 0,
|
|
sessionAvailability: 0.0476,
|
|
resetWindowAffinity: 0,
|
|
connectionDensity: 0.0476,
|
|
quality: 0.03,
|
|
reliability: 0,
|
|
};
|
|
|
|
export const MODE_PACK_OPTIONS = [
|
|
{ id: "custom", label: "Custom / None (Use Sliders)", emoji: "tune" },
|
|
{ id: "ship-fast", label: "Ship Fast", emoji: "rocket_launch" },
|
|
{ id: "cost-saver", label: "Cost Saver", emoji: "savings" },
|
|
{ id: "quality-first", label: "Quality First", emoji: "target" },
|
|
{ id: "offline-friendly", label: "Offline Friendly", emoji: "cloud_off" },
|
|
{ id: "reliability-first", label: "Reliability First", emoji: "shield" },
|
|
// Named for what it does: `modePacks.ts` ships it as the fault-injection
|
|
// profile behind `auto/chaos`. It belongs in the list — the engine offers it —
|
|
// but not under a label that reads like a routing preference.
|
|
{ id: "chaos-mode", label: "Chaos Mode (fault injection — testing)", emoji: "science" },
|
|
] as const;
|
|
|
|
export const ROUTER_STRATEGY_OPTIONS = [
|
|
{ id: "rules", label: "Rules (Weighted Scoring)" },
|
|
{ id: "score", label: "Highest Weighted Score" },
|
|
{ id: "cost", label: "Cost Optimized" },
|
|
{ id: "latency", label: "Latency Optimized" },
|
|
{ id: "sla-aware", label: "SLA-aware" },
|
|
{ id: "lkgp", label: "Last Known Good Provider" },
|
|
] as const;
|
|
|
|
export const FACTOR_LABELS: Record<keyof IntelligentRoutingWeights, string> = {
|
|
quota: "Quota",
|
|
health: "Health",
|
|
costInv: "Cost",
|
|
latencyInv: "Latency",
|
|
taskFit: "Task Fit",
|
|
stability: "Stability",
|
|
tierPriority: "Tier",
|
|
tierAffinity: "Tier Affinity",
|
|
specificityMatch: "Specificity",
|
|
contextAffinity: "Context Affinity",
|
|
cacheAffinity: "Cache Hit Affinity",
|
|
sessionAvailability: "Session Availability",
|
|
resetWindowAffinity: "Reset Window",
|
|
connectionDensity: "Connection Spread",
|
|
quality: "Observed Quality",
|
|
reliability: "Observed Reliability",
|
|
};
|
|
|
|
function isRecord(value: unknown): value is JsonRecord {
|
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
}
|
|
|
|
function toFiniteNumber(value: unknown): number | null {
|
|
const numericValue = Number(value);
|
|
return Number.isFinite(numericValue) ? numericValue : null;
|
|
}
|
|
|
|
function toPositiveNumber(value: unknown): number | undefined {
|
|
const numericValue = toFiniteNumber(value);
|
|
return numericValue !== null && numericValue > 0 ? numericValue : undefined;
|
|
}
|
|
|
|
export function isIntelligentStrategy(strategy: unknown): boolean {
|
|
return typeof strategy === "string" && INTELLIGENT_STRATEGIES.includes(strategy as never);
|
|
}
|
|
|
|
export function getStrategyCategory(strategy: unknown): "intelligent" | "deterministic" {
|
|
return isIntelligentStrategy(strategy) ? "intelligent" : "deterministic";
|
|
}
|
|
|
|
export function normalizeIntelligentRoutingFilter(value: unknown): IntelligentRoutingFilter {
|
|
if (typeof value === "string" && INTELLIGENT_ROUTING_FILTERS.includes(value as never)) {
|
|
return value as IntelligentRoutingFilter;
|
|
}
|
|
return "all";
|
|
}
|
|
|
|
export function filterCombosByStrategyCategory<T extends { strategy?: unknown }>(
|
|
combos: T[],
|
|
filter: IntelligentRoutingFilter
|
|
): T[] {
|
|
if (filter === "all") return combos;
|
|
return combos.filter((combo) => getStrategyCategory(combo?.strategy) === filter);
|
|
}
|
|
|
|
export function normalizeIntelligentRoutingConfig(config: unknown): IntelligentRoutingConfig {
|
|
const configRecord = isRecord(config) ? config : {};
|
|
const rawWeights = isRecord(configRecord.weights) ? configRecord.weights : {};
|
|
const rawSla = isRecord(configRecord.sla) ? configRecord.sla : {};
|
|
const slaTargetP95Ms = configRecord.slaTargetP95Ms ?? rawSla.targetP95Ms;
|
|
const slaMaxErrorRate = toFiniteNumber(configRecord.slaMaxErrorRate ?? rawSla.maxErrorRate);
|
|
const slaMaxCostPer1MTokens = configRecord.slaMaxCostPer1MTokens ?? rawSla.maxCostPer1MTokens;
|
|
const slaHardConstraints = configRecord.slaHardConstraints ?? rawSla.hardConstraints;
|
|
|
|
return {
|
|
candidatePool: Array.isArray(configRecord.candidatePool)
|
|
? configRecord.candidatePool.filter((value): value is string => typeof value === "string")
|
|
: [],
|
|
explorationRate: Math.min(1, Math.max(0, toFiniteNumber(configRecord.explorationRate) ?? 0.05)),
|
|
modePack:
|
|
typeof configRecord.modePack === "string" && configRecord.modePack.trim().length > 0
|
|
? configRecord.modePack
|
|
: "ship-fast",
|
|
budgetCap: toPositiveNumber(configRecord.budgetCap),
|
|
weights: {
|
|
quota: toFiniteNumber(rawWeights.quota) ?? DEFAULT_INTELLIGENT_WEIGHTS.quota,
|
|
health: toFiniteNumber(rawWeights.health) ?? DEFAULT_INTELLIGENT_WEIGHTS.health,
|
|
costInv: toFiniteNumber(rawWeights.costInv) ?? DEFAULT_INTELLIGENT_WEIGHTS.costInv,
|
|
latencyInv: toFiniteNumber(rawWeights.latencyInv) ?? DEFAULT_INTELLIGENT_WEIGHTS.latencyInv,
|
|
taskFit: toFiniteNumber(rawWeights.taskFit) ?? DEFAULT_INTELLIGENT_WEIGHTS.taskFit,
|
|
stability: toFiniteNumber(rawWeights.stability) ?? DEFAULT_INTELLIGENT_WEIGHTS.stability,
|
|
tierPriority:
|
|
toFiniteNumber(rawWeights.tierPriority) ?? DEFAULT_INTELLIGENT_WEIGHTS.tierPriority,
|
|
tierAffinity:
|
|
toFiniteNumber(rawWeights.tierAffinity) ?? DEFAULT_INTELLIGENT_WEIGHTS.tierAffinity,
|
|
specificityMatch:
|
|
toFiniteNumber(rawWeights.specificityMatch) ?? DEFAULT_INTELLIGENT_WEIGHTS.specificityMatch,
|
|
contextAffinity:
|
|
toFiniteNumber(rawWeights.contextAffinity) ?? DEFAULT_INTELLIGENT_WEIGHTS.contextAffinity,
|
|
cacheAffinity:
|
|
toFiniteNumber(rawWeights.cacheAffinity) ?? DEFAULT_INTELLIGENT_WEIGHTS.cacheAffinity,
|
|
sessionAvailability:
|
|
toFiniteNumber(rawWeights.sessionAvailability) ??
|
|
DEFAULT_INTELLIGENT_WEIGHTS.sessionAvailability,
|
|
resetWindowAffinity:
|
|
toFiniteNumber(rawWeights.resetWindowAffinity) ??
|
|
DEFAULT_INTELLIGENT_WEIGHTS.resetWindowAffinity,
|
|
connectionDensity:
|
|
toFiniteNumber(rawWeights.connectionDensity) ??
|
|
DEFAULT_INTELLIGENT_WEIGHTS.connectionDensity,
|
|
quality: toFiniteNumber(rawWeights.quality) ?? DEFAULT_INTELLIGENT_WEIGHTS.quality,
|
|
reliability:
|
|
toFiniteNumber(rawWeights.reliability) ?? DEFAULT_INTELLIGENT_WEIGHTS.reliability,
|
|
},
|
|
routerStrategy:
|
|
typeof configRecord.routerStrategy === "string" &&
|
|
configRecord.routerStrategy.trim().length > 0
|
|
? configRecord.routerStrategy
|
|
: "rules",
|
|
slaTargetP95Ms: toPositiveNumber(slaTargetP95Ms),
|
|
slaMaxErrorRate:
|
|
slaMaxErrorRate !== null ? Math.min(1, Math.max(0, slaMaxErrorRate)) : undefined,
|
|
slaMaxCostPer1MTokens: toPositiveNumber(slaMaxCostPer1MTokens),
|
|
slaHardConstraints: slaHardConstraints === true,
|
|
};
|
|
}
|
|
|
|
export function buildIntelligentProviderScores(combo: {
|
|
config?: unknown;
|
|
weights?: unknown;
|
|
}): IntelligentProviderScore[] {
|
|
const configRecord = normalizeIntelligentRoutingConfig(combo?.config);
|
|
const comboWeights = isRecord(combo?.weights) ? combo.weights : combo?.config;
|
|
const weights = normalizeIntelligentRoutingConfig({
|
|
...(isRecord(comboWeights) ? comboWeights : {}),
|
|
weights: isRecord(combo?.weights) ? combo.weights : configRecord.weights,
|
|
}).weights;
|
|
const pool = configRecord.candidatePool;
|
|
const baseScore = pool.length > 0 ? 1 / pool.length : 0;
|
|
|
|
return pool.map((provider) => ({
|
|
provider,
|
|
model: "auto",
|
|
score: baseScore,
|
|
factors: weights,
|
|
}));
|
|
}
|