diff --git a/packages/stats/core/src/domain/inference.test.ts b/packages/stats/core/src/domain/inference.test.ts index 0f8248ad1..d8937ff28 100644 --- a/packages/stats/core/src/domain/inference.test.ts +++ b/packages/stats/core/src/domain/inference.test.ts @@ -41,6 +41,17 @@ describe("inference stat normalization", () => { expect(statProvider("unknown", "", "custom-provider")).toBe("custom-provider") }) + test("merges renamed models under their current name", () => { + expect(statModel("x-preview-f", "")).toBe("ox-alpha") + expect(statModel("xiaomi/mimo-v2.5", "")).toBe("mimo-v2.5") + expect(toModelAggregate(aggregate("x-preview-f", "openai"))).toMatchObject([ + { + provider: "openai", + model: "ox-alpha", + }, + ]) + }) + test("model aggregates prefer provider.model and use normalized model", () => { expect(toModelAggregate(aggregate("alpha-gpt-next", "openai"))).toEqual([]) diff --git a/packages/stats/core/src/domain/inference.ts b/packages/stats/core/src/domain/inference.ts index 8c488c327..ee0468407 100644 --- a/packages/stats/core/src/domain/inference.ts +++ b/packages/stats/core/src/domain/inference.ts @@ -6,6 +6,7 @@ import { EXCLUDED_MODELS, FREE_MODELS, MODEL_AUTHOR_RULES, + MODEL_NAME_ALIASES, RETIRED_STAT_PROVIDERS, statModel, statProvider, @@ -302,6 +303,9 @@ function statPeriods(grain: "day" | "week", periodStart: Date, periodEnd: Date) function statModelSql(model: string, providerModel: string) { return `COALESCE(NULLIF(regexp_replace(CASE WHEN lower(${model}) = 'big-pickle' THEN regexp_replace(NULLIF(${providerModel}, ''), '^.*/', '') +${Object.entries(MODEL_NAME_ALIASES) + .map(([from, to]) => ` WHEN lower(${model}) = ${sqlString(from)} THEN ${sqlString(to)}`) + .join("\n")} ELSE ${model} END, '(-free|:free|:global)+$', ''), ''), 'unknown')` } diff --git a/packages/stats/core/src/domain/model-normalization.ts b/packages/stats/core/src/domain/model-normalization.ts index 4b6f47406..778e5edb8 100644 --- a/packages/stats/core/src/domain/model-normalization.ts +++ b/packages/stats/core/src/domain/model-normalization.ts @@ -15,7 +15,11 @@ export const MODEL_AUTHOR_RULES = [ ] as const export const EXCLUDED_MODELS = new Set(["alpha-gpt-next"]) export const FREE_MODELS = new Set(["gpt-5-nano", "grok-code", "big-pickle"]) -export const RETIRED_STAT_MODELS = ["big-pickle"] +export const MODEL_NAME_ALIASES: Record = { + "x-preview-f": "ox-alpha", + "xiaomi/mimo-v2.5": "mimo-v2.5", +} +export const RETIRED_STAT_MODELS = ["big-pickle", ...Object.keys(MODEL_NAME_ALIASES)] export const RETIRED_STAT_PROVIDERS = ["opencode"] export function normalizeInferenceModel(value: string | undefined) { @@ -31,6 +35,8 @@ export function modelAuthor(value: string | undefined) { export function statModel(model: string | undefined, providerModel: string | undefined) { const normalized = normalizeInferenceModel(model) + const alias = MODEL_NAME_ALIASES[normalized.toLowerCase()] + if (alias) return alias if (RETIRED_STAT_MODELS.includes(normalized.toLowerCase())) return normalizeInferenceModel(providerModel?.split("/").at(-1)) return normalized