From 4003963838facfedc20051c64d4d61a87ad65e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9F=A9=E7=BF=94=E5=AE=87?= Date: Tue, 20 Jan 2026 05:04:59 +0800 Subject: [PATCH] fix(backend): implement weighted load balancing in selectModel The selectModel function in api-helpers.ts had a TODO comment but was only returning the first candidate, ignoring the weight configuration. This fix implements proper weighted random selection algorithm that distributes requests across multiple providers based on their configured weights. The same algorithm was already implemented in utils/model.ts (used by embeddings endpoint) but not in api-helpers.ts (used by chat completions, messages, and responses endpoints). Affected endpoints: - /v1/chat/completions - /v1/messages - /v1/responses Co-Authored-By: Claude Opus 4.5 --- backend/src/utils/api-helpers.ts | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/backend/src/utils/api-helpers.ts b/backend/src/utils/api-helpers.ts index 1e21da6..10dcde1 100644 --- a/backend/src/utils/api-helpers.ts +++ b/backend/src/utils/api-helpers.ts @@ -81,6 +81,7 @@ export function extractUpstreamHeaders( /** * Select the best model/provider combination based on target provider and weights + * Uses weighted random selection for load balancing across multiple providers */ export function selectModel( modelsWithProviders: ModelWithProvider[], @@ -105,8 +106,33 @@ export function selectModel( } } - // TODO: implement weighted load balancing - return candidates[0] || null; + // Single candidate, return directly + if (candidates.length === 1) { + // oxlint-disable-next-line no-unnecessary-type-assertion + return candidates[0]!; // TypeScript needs assertion here + } + + // Weighted random selection for load balancing + const totalWeight = candidates.reduce((sum, c) => sum + c.model.weight, 0); + const random = Math.random() * totalWeight; + + let cumulative = 0; + for (const candidate of candidates) { + cumulative += candidate.model.weight; + if (random < cumulative) { + logger.debug("Selected model via weighted random", { + modelId: candidate.model.id, + providerId: candidate.provider.id, + providerName: candidate.provider.name, + weight: candidate.model.weight, + totalWeight, + }); + return candidate; + } + } + + // Fallback (should not happen) + return candidates[0] ?? null; } // =============================================================================