| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
| |
| const roundRobinCounters = new Map(); |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| export function resolveComboModel(combo: any, context: any = {}) { |
| const models = combo.models || []; |
| if (models.length === 0) { |
| throw new Error(`Combo "${combo.name}" has no models configured`); |
| } |
|
|
| |
| const normalized = models.map((m) => (typeof m === "string" ? { model: m, weight: 1 } : m)); |
|
|
| const strategy = combo.strategy || "priority"; |
|
|
| switch (strategy) { |
| case "priority": |
| return { model: normalized[0].model, index: 0 }; |
|
|
| case "round-robin": { |
| |
| const comboKey = combo.id || combo.name || "default"; |
| if (!roundRobinCounters.has(comboKey)) { |
| roundRobinCounters.set(comboKey, 0); |
| } |
| const counter = roundRobinCounters.get(comboKey); |
| const index = counter % normalized.length; |
| roundRobinCounters.set(comboKey, counter + 1); |
| return { model: normalized[index].model, index }; |
| } |
|
|
| case "random": { |
| |
| const totalWeight = normalized.reduce((sum, m) => sum + (m.weight || 1), 0); |
| let rand = Math.random() * totalWeight; |
|
|
| for (let i = 0; i < normalized.length; i++) { |
| rand -= normalized[i].weight || 1; |
| if (rand <= 0) { |
| return { model: normalized[i].model, index: i }; |
| } |
| } |
| return { model: normalized[0].model, index: 0 }; |
| } |
|
|
| case "least-used": { |
| const usageCounts = context.modelUsageCounts || {}; |
| let minUsage = Infinity; |
| let minIndex = 0; |
|
|
| for (let i = 0; i < normalized.length; i++) { |
| const usage = usageCounts[normalized[i].model] || 0; |
| if (usage < minUsage) { |
| minUsage = usage; |
| minIndex = i; |
| } |
| } |
|
|
| return { model: normalized[minIndex].model, index: minIndex }; |
| } |
|
|
| default: |
| return { model: normalized[0].model, index: 0 }; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function getComboFallbacks(combo, primaryIndex) { |
| const models = (combo.models || []).map((m) => (typeof m === "string" ? m : m.model)); |
| return [...models.slice(primaryIndex + 1), ...models.slice(0, primaryIndex)]; |
| } |
|
|