mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-08 00:02:20 +03:00
Add an `endpoint` column to `usage_history` (migration 103) so the proxy records which API entry point a request came through — typically `/v1/chat/completions`, `/v1/messages`, or `/v1/responses`. Every `saveRequestUsage(...)` call site in `open-sse/handlers/chatCore.ts` (success, stream, failure) and the codex-responses-ws route now plumbs the endpoint through, and a new `getEndpointUsageRows(...)` analytics query in `src/lib/db/usageAnalytics.ts` aggregates per endpoint × provider × model with NULL → 'unknown' folding for backward compatibility with rows from before the migration. The aggregation reads directly from `usage_history` (matching the `getAutoRoutingVariantBreakdown` pattern) so the existing unified-source CTE — and the `daily_usage_summary` rollup it joins — stay untouched. TDD: tests/unit/usage-endpoint-dimension.test.ts covers persistence round-trip, NULL fallback, and the sinceIso filter (3/3 passing). Co-authored-by: toanalien <toanalien@gmail.com> Inspired-by: https://github.com/decolua/9router/pull/152
42 lines
1.6 KiB
TypeScript
42 lines
1.6 KiB
TypeScript
/**
|
|
* chatCore failed-request usage record builder (Quality Gate v2 / Fase 9 — chatCore god-file
|
|
* decomposition, #3501).
|
|
*
|
|
* Pure core of handleChatCore's persistFailureUsage closure: builds the usage-history entry for a
|
|
* failed request (zeroed tokens/timing, success:false, the unknown/undefined fallbacks, and the
|
|
* combo-strategy gate). The handler keeps the impure parts byte-identically: it computes
|
|
* `latencyMs` (Date.now() - startTime) and fires the fire-and-forget saveRequestUsage(...).catch().
|
|
*/
|
|
|
|
export function buildFailureUsageRecord(opts: {
|
|
provider: string | null | undefined;
|
|
model: string | null | undefined;
|
|
connectionId: string | null | undefined;
|
|
apiKeyInfo: { id?: string; name?: string } | null | undefined;
|
|
effectiveServiceTier: string;
|
|
isCombo: boolean;
|
|
comboStrategy: string | null | undefined;
|
|
statusCode: number;
|
|
errorCode: string | null | undefined;
|
|
latencyMs: number;
|
|
endpoint?: string | null | undefined;
|
|
}) {
|
|
return {
|
|
provider: opts.provider || "unknown",
|
|
model: opts.model || "unknown",
|
|
tokens: { input: 0, output: 0, cacheRead: 0, cacheCreation: 0, reasoning: 0 },
|
|
status: String(opts.statusCode),
|
|
success: false,
|
|
latencyMs: opts.latencyMs,
|
|
timeToFirstTokenMs: 0,
|
|
errorCode: opts.errorCode || String(opts.statusCode),
|
|
timestamp: new Date().toISOString(),
|
|
connectionId: opts.connectionId || undefined,
|
|
apiKeyId: opts.apiKeyInfo?.id || undefined,
|
|
apiKeyName: opts.apiKeyInfo?.name || undefined,
|
|
serviceTier: opts.effectiveServiceTier,
|
|
comboStrategy: opts.isCombo ? opts.comboStrategy || undefined : undefined,
|
|
endpoint: opts.endpoint || undefined,
|
|
};
|
|
}
|