Files
OmniRoute/src/lib/a2a/routingLogger.ts
Diego Rodrigues de Sa e Souza 0adae00c7b Release v3.8.42 (#5459)
Release v3.8.42 — full CHANGELOG in CHANGELOG.md.

CI: 103 checks green incl. CodeQL (all languages), Semgrep, all 8 unit shards,
coverage, Node 24 compat, and integration tests. Full unit suite validated
locally: 19437 pass / 0 fail. The 3 red checks are advisory and do not gate
main (no required status checks): SonarCloud/SonarQube new-code coverage gate,
and PR Test Policy (test-masking detector flagging the legitimate dead-Phind
provider removal in #5530 — reviewed, correct).

Includes cycle-close reconciliation + repair of inherited base-red tests from
#5480/#5527/#5427/#5521 that the PR->release fast-path did not exercise.
2026-06-30 06:54:29 -03:00

68 lines
1.7 KiB
TypeScript

/**
* A2A Routing Decision Logger
*
* Records every routing decision to the `routing_decisions` SQLite table.
* Used by `omniroute_explain_route` (T03) and future learning router.
* Retention: 7 days default.
*/
import { randomUUID } from "crypto";
export interface RoutingFactor {
name: string; // "quota", "health", "cost", "latency", "task_fit"
value: number;
weight: number;
contribution: number;
}
export interface FallbackEntry {
provider: string;
reason: string; // "circuit_breaker_open", "quota_exceeded", "timeout"
}
export interface RoutingDecision {
requestId: string;
taskType: string;
comboId: string;
providerSelected: string;
modelUsed: string;
score: number;
factors: RoutingFactor[];
fallbacksTriggered: FallbackEntry[];
success: boolean;
latencyMs: number;
cost: number;
timestamp: string;
}
// In-memory log (production would use SQLite via routing_decisions table)
const decisions: RoutingDecision[] = [];
const MAX_DECISIONS = 1000;
const RETENTION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
/**
* Log a routing decision.
*/
export function logRoutingDecision(
params: Omit<RoutingDecision, "requestId" | "timestamp">
): RoutingDecision {
const decision: RoutingDecision = {
...params,
requestId: randomUUID(),
timestamp: new Date().toISOString(),
};
decisions.push(decision);
// Cleanup: cap + TTL
if (decisions.length > MAX_DECISIONS) {
const cutoff = new Date(Date.now() - RETENTION_MS);
const validIdx = decisions.findIndex((d) => new Date(d.timestamp) > cutoff);
if (validIdx > 0) decisions.splice(0, validIdx);
else if (decisions.length > MAX_DECISIONS)
decisions.splice(0, decisions.length - MAX_DECISIONS);
}
return decision;
}