diff --git a/apps/gateway/src/chat/chat.ts b/apps/gateway/src/chat/chat.ts index 52d59a276b..d4c8cbddc7 100644 --- a/apps/gateway/src/chat/chat.ts +++ b/apps/gateway/src/chat/chat.ts @@ -4746,7 +4746,7 @@ chat.openapi(completions, async (c) => { routingAttempts.push( buildRoutingAttempt( usedProvider, - baseModelName, + usedModelMapping, 0, getErrorType(0), false, @@ -5082,7 +5082,7 @@ chat.openapi(completions, async (c) => { routingAttempts.push( buildRoutingAttempt( usedProvider, - baseModelName, + usedModelMapping, 0, getErrorType(0), false, @@ -5322,7 +5322,7 @@ chat.openapi(completions, async (c) => { routingAttempts.push( buildRoutingAttempt( usedProvider, - baseModelName, + usedModelMapping, res.status, getErrorType(res.status), false, @@ -5621,7 +5621,7 @@ chat.openapi(completions, async (c) => { routingAttempts.push( buildRoutingAttempt( usedProvider, - baseModelName, + usedModelMapping, res.status, "none", true, @@ -5640,13 +5640,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, @@ -8325,7 +8327,7 @@ chat.openapi(completions, async (c) => { routingAttempts.push( buildRoutingAttempt( usedProvider, - baseModelName, + usedModelMapping, 0, getErrorType(0), false, @@ -8809,7 +8811,7 @@ chat.openapi(completions, async (c) => { routingAttempts.push( buildRoutingAttempt( usedProvider, - baseModelName, + usedModelMapping, res.status, getErrorType(res.status), false, @@ -8896,7 +8898,7 @@ chat.openapi(completions, async (c) => { routingAttempts.push( buildRoutingAttempt( usedProvider, - baseModelName, + usedModelMapping, res.status, "none", true, @@ -8913,13 +8915,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/packages/shared/src/components/log-card.tsx b/packages/shared/src/components/log-card.tsx index 5e708300e4..617c3a0dbb 100644 --- a/packages/shared/src/components/log-card.tsx +++ b/packages/shared/src/components/log-card.tsx @@ -50,6 +50,7 @@ import { interface RoutingMetadata { selectionReason?: string; + selectedProvider?: string; usedApiKeyHash?: string; availableProviders?: string[]; xNoFallbackHeaderSet?: boolean; @@ -214,6 +215,10 @@ function formatDuration(ms: number) { return `${(ms / 1000).toFixed(2)}s`; } +function getRoutingEntryKey(provider: string, region?: string) { + return region ? `${provider}:${region}` : provider; +} + function formatApiKeyHash(hash: string) { return hash.slice(0, 7); } @@ -369,6 +374,37 @@ export function LogCard({ log.dataStorageCost !== undefined && Number(log.dataStorageCost) > 0; const [isExpanded, setIsExpanded] = useState(false); + + const selectedRoutingAttempt = routingMetadata?.routing + ?.slice() + .reverse() + .find((attempt) => attempt.succeeded); + const selectedRoutingProvider = + 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 = + getRoutingEntryKey(a.providerId, a.region) === selectedRoutingKey; + const bSelected = + getRoutingEntryKey(b.providerId, b.region) === selectedRoutingKey; + 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 [imagePreviewOpen, setImagePreviewOpen] = useState(false); const [imagePreviewSrcs, setImagePreviewSrcs] = useState([]); const [imagePreviewTitle, setImagePreviewTitle] = useState("Generated Image"); @@ -659,86 +695,107 @@ export function LogCard({ )} - {routingMetadata.providerScores && - routingMetadata.providerScores.length > 0 && ( -
-
- Scores + {selectedRoutingProvider && ( +
+ Selected + + {selectedRoutingProvider} + +
+ )} + {sortedProviderScores.length > 0 && ( +
+
+
Scores
+
+ lower is better
-
- {routingMetadata.providerScores.map((score) => ( -
- - {score.providerId} - {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 +
+
+ {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.latency !== 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} + )} + {score.priority !== undefined && + score.priority !== 1 && ( - {score.latency?.toFixed(0)}ms + p:{score.priority} )} - {score.price !== undefined && ( - ${score.price} - )} - {score.priority !== undefined && - score.priority !== 1 && ( - - p:{score.priority} - - )} - {score.cacheSupported && ( - cache - )} - -
- ))} -
+ {score.cacheSupported && ( + cache + )} + +
+ ))}
- )} +
+ )} {routingMetadata.routing && routingMetadata.routing.length > 0 && (