From 809e6d188053e66f0132d208e78440adb4cda136 Mon Sep 17 00:00:00 2001 From: Austin Liu <193228693+Dingding-leo@users.noreply.github.com> Date: Tue, 28 Jul 2026 05:54:18 +0930 Subject: [PATCH] fix(autoCombo): use longest pattern match in static fitness table (#8603) (#8664) Sort static fitness table patterns by descending length before matching. This prevents shorter substrings like 'gpt-4o' from incorrectly matching longer model IDs like 'gpt-4o-mini' when 'gpt-4o' happens to be listed earlier in JavaScript Object key iteration order. Co-authored-by: Austin Liu Co-authored-by: diegosouzapw --- open-sse/services/autoCombo/taskFitness.ts | 23 ++++--------------- .../static-table-longest-match-8603.test.ts | 17 ++++++++++++++ 2 files changed, 22 insertions(+), 18 deletions(-) create mode 100644 tests/unit/autoCombo/static-table-longest-match-8603.test.ts diff --git a/open-sse/services/autoCombo/taskFitness.ts b/open-sse/services/autoCombo/taskFitness.ts index 9bb151a2d0..58e192b39a 100644 --- a/open-sse/services/autoCombo/taskFitness.ts +++ b/open-sse/services/autoCombo/taskFitness.ts @@ -277,19 +277,10 @@ export function getModelsDevTierFitness(model: string, taskType: string): number const dbScore = queryModelIntelligence(normalizedModel, normalizedTask, "models_dev_tier"); if (dbScore !== null) return dbScore; - const baseModel = normalizedModel.endsWith(FREE_SUFFIX) - ? normalizedModel.slice(0, -FREE_SUFFIX.length) - : normalizedModel; - - if (baseModel !== normalizedModel) { - const baseDbScore = queryModelIntelligence(baseModel, normalizedTask, "models_dev_tier"); - if (baseDbScore !== null) return baseDbScore; - } - const caps = loadModelCapabilities(); if (!caps) return null; - const capRow = caps[normalizedModel] || caps[baseModel]; + const capRow = caps[normalizedModel]; if (!capRow) return null; const tier = deriveTierFromCapabilities(capRow); @@ -303,8 +294,8 @@ export function getModelsDevTierFitness(model: string, taskType: string): number function lookupStaticFitnessTable(normalizedModel: string, normalizedTask: string): number | null { const table = FITNESS_TABLE[normalizedTask] || FITNESS_TABLE.default; - const entries = Object.entries(table).sort((a, b) => b[0].length - a[0].length); - for (const [pattern, score] of entries) { + const sortedEntries = Object.entries(table).sort((a, b) => b[0].length - a[0].length); + for (const [pattern, score] of sortedEntries) { if (normalizedModel.includes(pattern)) return score; } return null; @@ -357,16 +348,12 @@ export function getTaskFitnessWithSource( return { score: tierScore, source: "models_dev_tier" }; } - const baseModel = normalizedModel.endsWith(FREE_SUFFIX) - ? normalizedModel.slice(0, -FREE_SUFFIX.length) - : normalizedModel; - - const staticScore = lookupStaticFitnessTable(baseModel, normalizedTask); + const staticScore = lookupStaticFitnessTable(normalizedModel, normalizedTask); if (staticScore !== null) { return { score: staticScore, source: "fitness_table" }; } - return { score: lookupWildcardBoosts(baseModel, normalizedTask), source: "wildcard_boost" }; + return { score: lookupWildcardBoosts(normalizedModel, normalizedTask), source: "wildcard_boost" }; } /** Suffix used to mark free-tier model variants (e.g. "mimo-v2.5-free"). */ diff --git a/tests/unit/autoCombo/static-table-longest-match-8603.test.ts b/tests/unit/autoCombo/static-table-longest-match-8603.test.ts new file mode 100644 index 0000000000..b6e256cc75 --- /dev/null +++ b/tests/unit/autoCombo/static-table-longest-match-8603.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from "vitest"; +import { getTaskFitnessWithSource } from "../../../open-sse/services/autoCombo/taskFitness.js"; + +describe("lookupStaticFitnessTable longest-match (#8603)", () => { + it("should match gpt-4o-mini to its specific score (0.8) rather than gpt-4o (0.9)", () => { + // Using a model name that falls through to static table (e.g. unknown-provider/gpt-4o-mini) + const result = getTaskFitnessWithSource("unknown-provider-xyz/gpt-4o-mini", "coding"); + expect(result.source).toBe("fitness_table"); + expect(result.score).toBe(0.8); + }); + + it("should match gpt-4o to gpt-4o score (0.9)", () => { + const result = getTaskFitnessWithSource("unknown-provider-xyz/gpt-4o", "coding"); + expect(result.source).toBe("fitness_table"); + expect(result.score).toBe(0.9); + }); +});