From aaaea96bfbec7d8926cb00c8d565b174833aec3b Mon Sep 17 00:00:00 2001 From: Luca Steeb Date: Thu, 27 Nov 2025 15:52:51 +0800 Subject: [PATCH] feat(routing): add provider metrics to routing metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include uptime, latency, and throughput metrics in routing metadata for direct provider selection and low-uptime fallback cases, providing full visibility into provider scores and performance characteristics. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/gateway/src/chat/chat.ts | 44 ++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/apps/gateway/src/chat/chat.ts b/apps/gateway/src/chat/chat.ts index b522f89b9c..5318f2c541 100644 --- a/apps/gateway/src/chat/chat.ts +++ b/apps/gateway/src/chat/chat.ts @@ -1186,6 +1186,25 @@ chat.openapi(completions, async (c) => { allMetricsMap, ); + // Get price info for the original requested provider to include in scores + const originalProviderInfo = modelInfo.providers.find( + (p) => p.providerId === requestedProvider, + ); + const originalProviderPrice = originalProviderInfo + ? (originalProviderInfo.inputPrice ?? 0) + + (originalProviderInfo.outputPrice ?? 0) + : 0; + + // Create score entry for the original requested provider + const originalProviderScore = { + providerId: requestedProvider, + score: -1, // Negative score indicates this provider was skipped due to low uptime + price: originalProviderPrice, + uptime: metrics.uptime, + latency: metrics.averageLatency, + throughput: metrics.throughput, + }; + if (cheapestResult) { usedProvider = cheapestResult.provider.providerId; usedModel = cheapestResult.provider.modelName; @@ -1194,6 +1213,11 @@ chat.openapi(completions, async (c) => { selectionReason: "low-uptime-fallback", originalProvider: requestedProvider, originalProviderUptime: metrics.uptime, + // Add the original provider's score to the scores array + providerScores: [ + originalProviderScore, + ...cheapestResult.metadata.providerScores, + ], }; } else { // Use first available provider as fallback @@ -1205,7 +1229,7 @@ chat.openapi(completions, async (c) => { ), selectedProvider: usedProvider, selectionReason: "low-uptime-fallback", - providerScores: [], + providerScores: [originalProviderScore], originalProvider: requestedProvider, originalProviderUptime: metrics.uptime, }; @@ -1320,6 +1344,21 @@ chat.openapi(completions, async (c) => { (selectedProviderInfo.outputPrice ?? 0) : 0; + // Fetch metrics for the selected provider to include in routing metadata + // This provides visibility into uptime/latency/throughput even for direct provider selection + const baseModelId = (modelInfo as ModelDefinition).id; + let providerMetrics: + | { uptime: number; averageLatency: number; throughput: number } + | undefined; + + if (baseModelId && usedProvider !== "custom") { + const directMetricsMap = await getProviderMetricsForCombinations( + [{ modelId: baseModelId, providerId: usedProvider }], + 5, + ); + providerMetrics = directMetricsMap.get(`${baseModelId}:${usedProvider}`); + } + routingMetadata = { availableProviders: [usedProvider], selectedProvider: usedProvider, @@ -1329,6 +1368,9 @@ chat.openapi(completions, async (c) => { providerId: usedProvider, score: 1, price, + uptime: providerMetrics?.uptime, + latency: providerMetrics?.averageLatency, + throughput: providerMetrics?.throughput, }, ], };