From 1e3f83fb983650ba92bac97fe3c4ae0b8b068006 Mon Sep 17 00:00:00 2001 From: steebchen-bot Date: Wed, 1 Apr 2026 10:50:00 +0000 Subject: [PATCH 1/2] Routing: retry model attribution fix + provider match update --- apps/gateway/src/chat/chat.ts | 28 +++++++++------- ee/admin/src/components/log-card.tsx | 48 +++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/apps/gateway/src/chat/chat.ts b/apps/gateway/src/chat/chat.ts index ddb895854e..a6efe7090c 100644 --- a/apps/gateway/src/chat/chat.ts +++ b/apps/gateway/src/chat/chat.ts @@ -3920,7 +3920,7 @@ chat.openapi(completions, async (c) => { if (willRetryTimeout) { routingAttempts.push({ provider: usedProvider, - model: baseModelName, + model: usedModelMapping, ...(usedRegion && { region: usedRegion }), status_code: 0, error_type: getErrorType(0), @@ -4210,7 +4210,7 @@ chat.openapi(completions, async (c) => { if (willRetryFetch) { routingAttempts.push({ provider: usedProvider, - model: baseModelName, + model: usedModelMapping, ...(usedRegion && { region: usedRegion }), status_code: 0, error_type: getErrorType(0), @@ -4399,7 +4399,7 @@ chat.openapi(completions, async (c) => { if (willRetryHttpError) { routingAttempts.push({ provider: usedProvider, - model: baseModelName, + model: usedModelMapping, ...(usedRegion && { region: usedRegion }), status_code: res.status, error_type: getErrorType(res.status), @@ -4478,7 +4478,7 @@ chat.openapi(completions, async (c) => { if (res && res.ok && usedProvider) { routingAttempts.push({ provider: usedProvider, - model: baseModelName, + model: usedModelMapping, ...(usedRegion && { region: usedRegion }), status_code: res.status, error_type: "none", @@ -4492,13 +4492,15 @@ chat.openapi(completions, async (c) => { const failedMap = new Map( routingAttempts .filter((a) => !a.succeeded) - .map((f) => [f.provider, f]), + .map((f) => [providerRetryKey(f.provider, f.region), f]), ); routingMetadata = { ...routingMetadata, routing: routingAttempts, providerScores: routingMetadata.providerScores.map((score) => { - const failure = failedMap.get(score.providerId); + const failure = failedMap.get( + providerRetryKey(score.providerId, score.region), + ); if (failure) { return { ...score, @@ -6984,7 +6986,7 @@ chat.openapi(completions, async (c) => { if (willRetryFetchNonStreaming) { routingAttempts.push({ provider: usedProvider, - model: baseModelName, + model: usedModelMapping, ...(usedRegion && { region: usedRegion }), status_code: 0, error_type: getErrorType(0), @@ -7421,7 +7423,7 @@ chat.openapi(completions, async (c) => { if (willRetryHttpNonStreaming) { routingAttempts.push({ provider: usedProvider, - model: baseModelName, + model: usedModelMapping, ...(usedRegion && { region: usedRegion }), status_code: res.status, error_type: getErrorType(res.status), @@ -7501,7 +7503,7 @@ chat.openapi(completions, async (c) => { if (res && res.ok && usedProvider) { routingAttempts.push({ provider: usedProvider, - model: baseModelName, + model: usedModelMapping, ...(usedRegion && { region: usedRegion }), status_code: res.status, error_type: "none", @@ -7513,13 +7515,17 @@ chat.openapi(completions, async (c) => { if (routingMetadata) { // Enrich providerScores with failure info from routing attempts const failedMap = new Map( - routingAttempts.filter((a) => !a.succeeded).map((f) => [f.provider, f]), + routingAttempts + .filter((a) => !a.succeeded) + .map((f) => [providerRetryKey(f.provider, f.region), f]), ); routingMetadata = { ...routingMetadata, routing: routingAttempts, providerScores: routingMetadata.providerScores.map((score) => { - const failure = failedMap.get(score.providerId); + const failure = failedMap.get( + providerRetryKey(score.providerId, score.region), + ); if (failure) { return { ...score, diff --git a/ee/admin/src/components/log-card.tsx b/ee/admin/src/components/log-card.tsx index 83744dc352..77745bb96e 100644 --- a/ee/admin/src/components/log-card.tsx +++ b/ee/admin/src/components/log-card.tsx @@ -37,6 +37,7 @@ import type { ProjectLogEntry } from "@/lib/types"; interface RoutingMetadata { selectionReason?: string; + selectedProvider?: string; availableProviders?: string[]; providerScores?: Array<{ providerId: string; @@ -141,6 +142,25 @@ export function LogCard({ log }: { log: ProjectLogEntry }) { log.dataStorageCost !== undefined && Number(log.dataStorageCost) > 0; const [isExpanded, setIsExpanded] = useState(false); + const selectedRoutingProvider = + routingMetadata?.selectedProvider ?? log.usedProvider; + const sortedProviderScores = routingMetadata?.providerScores + ? [...routingMetadata.providerScores].sort((a, b) => { + const aSelected = a.providerId === selectedRoutingProvider; + const bSelected = b.providerId === selectedRoutingProvider; + if (aSelected !== bSelected) { + return aSelected ? -1 : 1; + } + + const aScore = a.score >= 0 ? a.score : Number.POSITIVE_INFINITY; + const bScore = b.score >= 0 ? b.score : Number.POSITIVE_INFINITY; + if (aScore !== bScore) { + return aScore - bScore; + } + + return a.providerId.localeCompare(b.providerId); + }) + : []; const formattedTime = formatDistanceToNow(new Date(log.createdAt), { addSuffix: true, @@ -356,6 +376,14 @@ export function LogCard({ log }: { log: ProjectLogEntry }) { )} + {selectedRoutingProvider && ( +
+ Selected + + {selectedRoutingProvider} + +
+ )} {routingMetadata.availableProviders && routingMetadata.availableProviders.length > 0 && (
@@ -367,20 +395,30 @@ export function LogCard({ log }: { log: ProjectLogEntry }) {
)} - {routingMetadata.providerScores && - routingMetadata.providerScores.length > 0 && ( + {sortedProviderScores.length > 0 && (
-
- Scores +
+
Scores
+
+ lower is better +
- {routingMetadata.providerScores.map((score) => ( + {sortedProviderScores.map((score) => (
{score.providerId} + {score.providerId === selectedRoutingProvider && ( + + selected + + )} {score.region && ( ({score.region}) From a20aff1158ca0b1a2f9e74b2fb1a39465a9e0c37 Mon Sep 17 00:00:00 2001 From: steebchen-bot Date: Wed, 1 Apr 2026 11:00:37 +0000 Subject: [PATCH 2/2] fix: clarify selected routing entry in admin logs --- ee/admin/src/components/log-card.tsx | 189 +++++++++++++++------------ 1 file changed, 104 insertions(+), 85 deletions(-) diff --git a/ee/admin/src/components/log-card.tsx b/ee/admin/src/components/log-card.tsx index 77745bb96e..eb3b4fa02e 100644 --- a/ee/admin/src/components/log-card.tsx +++ b/ee/admin/src/components/log-card.tsx @@ -96,6 +96,10 @@ function formatDuration(ms: number) { return `${(ms / 1000).toFixed(2)}s`; } +function getRoutingEntryKey(provider: string, region?: string) { + return region ? `${provider}:${region}` : provider; +} + function copyToClipboard(text: string) { void navigator.clipboard.writeText(text); } @@ -142,12 +146,24 @@ export function LogCard({ log }: { log: ProjectLogEntry }) { log.dataStorageCost !== undefined && Number(log.dataStorageCost) > 0; const [isExpanded, setIsExpanded] = useState(false); + const selectedRoutingAttempt = routingMetadata?.routing + ?.slice() + .reverse() + .find((attempt) => attempt.succeeded); const selectedRoutingProvider = - routingMetadata?.selectedProvider ?? log.usedProvider; + selectedRoutingAttempt?.provider ?? + routingMetadata?.selectedProvider ?? + log.usedProvider; + const selectedRoutingKey = getRoutingEntryKey( + selectedRoutingAttempt?.provider ?? selectedRoutingProvider, + selectedRoutingAttempt?.region, + ); const sortedProviderScores = routingMetadata?.providerScores ? [...routingMetadata.providerScores].sort((a, b) => { - const aSelected = a.providerId === selectedRoutingProvider; - const bSelected = b.providerId === selectedRoutingProvider; + const aSelected = + getRoutingEntryKey(a.providerId, a.region) === selectedRoutingKey; + const bSelected = + getRoutingEntryKey(b.providerId, b.region) === selectedRoutingKey; if (aSelected !== bSelected) { return aSelected ? -1 : 1; } @@ -396,94 +412,97 @@ export function LogCard({ log }: { log: ProjectLogEntry }) {
)} {sortedProviderScores.length > 0 && ( -
-
-
Scores
-
- lower is better -
+
+
+
Scores
+
+ lower is better
-
- {sortedProviderScores.map((score) => ( -
- - {score.providerId} - {score.providerId === selectedRoutingProvider && ( - - selected - - )} - {score.region && ( - - ({score.region}) - - )} - {score.failed && ( - - - - {score.status_code} - {score.error_type && ( - - {score.error_type} - - )} - - - )} - {score.rate_limited && ( - - - rpm capped - - )} - {score.excludedByContentFilter && ( - - - content filter - - )} - - - {score.score.toFixed(2)} - {score.uptime !== undefined && ( - - ↑{score.uptime?.toFixed(0)}% - - )} - {score.throughput !== undefined && ( - - {score.throughput?.toFixed(0)}t/s - - )} - {score.latency !== undefined && ( - - {score.latency?.toFixed(0)}ms +
+
+ {sortedProviderScores.map((score) => ( +
+ + {score.providerId} + {getRoutingEntryKey( + score.providerId, + score.region, + ) === selectedRoutingKey && ( + + selected + + )} + {score.region && ( + + ({score.region}) + + )} + {score.failed && ( + + + + {score.status_code} + {score.error_type && ( + + {score.error_type} + + )} - )} - {score.price !== undefined && ( + + )} + {score.rate_limited && ( + + + rpm capped + + )} + {score.excludedByContentFilter && ( + + + content filter + + )} + + + {score.score.toFixed(2)} + {score.uptime !== undefined && ( + + ↑{score.uptime?.toFixed(0)}% + + )} + {score.throughput !== undefined && ( + + {score.throughput?.toFixed(0)}t/s + + )} + {score.latency !== undefined && ( + + {score.latency?.toFixed(0)}ms + + )} + {score.price !== undefined && ( + + ${score.price.toFixed(6)} + + )} + {score.priority !== undefined && + score.priority !== 1 && ( - ${score.price.toFixed(6)} + p:{score.priority} )} - {score.priority !== undefined && - score.priority !== 1 && ( - - p:{score.priority} - - )} - -
- ))} -
+ +
+ ))}
- )} +
+ )} {routingMetadata.routing && routingMetadata.routing.length > 0 && (