feat(routing): implement Last Known Good Provider (LKGP) strategy (#919)

This commit is contained in:
oyi77
2026-04-03 04:31:02 +07:00
parent a65bca6e49
commit b777b15ee8
4 changed files with 83 additions and 2 deletions

View File

@@ -16,6 +16,7 @@ export interface RoutingContext {
requestHasTools?: boolean;
requestHasVision?: boolean;
estimatedInputTokens?: number;
lastKnownGoodProvider?: string;
}
export interface RoutingDecision {
@@ -116,6 +117,34 @@ class LatencyStrategyImpl implements RouterStrategy {
}
}
// ── LKGPStrategy: tries last known good provider first ───────────────────────
class LKGPStrategyImpl implements RouterStrategy {
readonly name = "lkgp";
readonly description = "Tries last known good provider first, then falls back to rules";
select(pool: ProviderCandidate[], context: RoutingContext): RoutingDecision {
if (context.lastKnownGoodProvider) {
const best = pool.find(
(c) => c.provider === context.lastKnownGoodProvider && c.circuitBreakerState !== "OPEN"
);
if (best) {
return {
provider: best.provider,
model: best.model,
strategy: this.name,
reason: `LKGP: using last known good provider ${best.provider}`,
candidatesConsidered: 1,
finalScore: 1.0,
};
}
}
// Fallback to rules strategy
return getStrategy("rules").select(pool, context);
}
}
// ── Registry ──────────────────────────────────────────────────────────────────
const strategyRegistry = new Map<string, RouterStrategy>();
@@ -123,12 +152,14 @@ const strategyRegistry = new Map<string, RouterStrategy>();
const rulesStrategy = new RulesStrategyImpl();
const costStrategy = new CostStrategyImpl();
const latencyStrategy = new LatencyStrategyImpl();
const lkgpStrategy = new LKGPStrategyImpl();
strategyRegistry.set("rules", rulesStrategy);
strategyRegistry.set("cost", costStrategy);
strategyRegistry.set("eco", costStrategy); // alias
strategyRegistry.set("latency", latencyStrategy);
strategyRegistry.set("fast", latencyStrategy); // alias
strategyRegistry.set("lkgp", lkgpStrategy);
export function getStrategy(name: string): RouterStrategy {
const strategy = strategyRegistry.get(name);

View File

@@ -99,7 +99,10 @@ async function validateResponseQuality(
if (json?.output || json?.result || json?.data || json?.response) return { valid: true };
if (json?.error) {
const err = json.error as Record<string, unknown>;
return { valid: false, reason: `upstream error in 200 body: ${err?.message || JSON.stringify(json.error).substring(0, 200)}` };
return {
valid: false,
reason: `upstream error in 200 body: ${err?.message || JSON.stringify(json.error).substring(0, 200)}`,
};
}
return { valid: true };
}
@@ -809,6 +812,16 @@ export async function handleComboChat({
const modePack =
typeof autoConfigSource.modePack === "string" ? autoConfigSource.modePack : undefined;
// Retrieve last known good provider (LKGP) for this combo/model (#919)
let lastKnownGoodProvider: string | undefined;
try {
const { getLKGP } = await import("../../src/lib/localDb");
const lkgp = await getLKGP(combo.name, combo.id || combo.name);
if (lkgp) lastKnownGoodProvider = lkgp;
} catch {
/* ignore db errors */
}
const candidates = await buildAutoCandidates(eligibleModels, combo.name);
if (candidates.length > 0) {
let selectedProvider = null;
@@ -819,7 +832,7 @@ export async function handleComboChat({
try {
const decision = selectWithStrategy(
candidates,
{ taskType, requestHasTools },
{ taskType, requestHasTools, lastKnownGoodProvider },
routingStrategy
);
selectedProvider = decision.provider;
@@ -980,6 +993,14 @@ export async function handleComboChat({
fallbackCount,
strategy,
});
// Record last known good provider (LKGP) for this combo/model (#919)
if (provider) {
import("../../src/lib/localDb")
.then(({ setLKGP }) => setLKGP(combo.name, combo.id || combo.name, provider))
.catch(() => {});
}
return result;
}

View File

@@ -248,6 +248,31 @@ export async function resetAllPricing() {
return {};
}
// ──────────────── LKGP (Last Known Good Provider) ────────────────
export async function getLKGP(comboName: string, modelId: string): Promise<string | null> {
const db = getDbInstance();
const key = `${comboName}:${modelId}`;
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = 'lkgp' AND key = ?")
.get(key) as { value?: string } | undefined;
if (!row?.value) return null;
try {
return JSON.parse(row.value);
} catch {
return row.value;
}
}
export async function setLKGP(comboName: string, modelId: string, providerId: string) {
const db = getDbInstance();
const key = `${comboName}:${modelId}`;
db.prepare("INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('lkgp', ?, ?)").run(
key,
JSON.stringify(providerId)
);
}
// ──────────────── Proxy Config ────────────────
const DEFAULT_PROXY_CONFIG: ProxyConfig = { global: null, providers: {}, combos: {}, keys: {} };

View File

@@ -92,6 +92,10 @@ export {
updateSettings,
isCloudEnabled,
// LKGP (Last Known Good Provider) (#919)
getLKGP,
setLKGP,
// Pricing
getPricing,
getPricingForModel,