fix(stats): map ox alpha to glm 5.3 flash (#45542)

This commit is contained in:
Adam 2026-08-27 06:12:21 -05:00 committed by GitHub
parent 6568a82455
commit 1120d0704e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 64 additions and 24 deletions

View file

@ -9,6 +9,7 @@ import {
type ModelUsagePoint,
type StatsModelData,
} from "@opencode-ai/stats-core/domain/home"
import { statModel } from "@opencode-ai/stats-core/domain/model-normalization"
import { createAsync, query, useParams } from "@solidjs/router"
import { createMemo, createSignal, createUniqueId, For, onMount, Show, type JSX } from "solid-js"
import { getRequestEvent } from "solid-js/web"
@ -40,6 +41,8 @@ import {
} from "../stats-shell"
const statsUnfurlPath = "banner.png"
const glmFlashCatalogId = "zhipuai/glm-5.3-flash"
const glmFlashModel = "glm-5.3-flash"
const shortMonths = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"] as const
type IsoCountryCode = readonly [string, string, string]
@ -89,14 +92,23 @@ export default function StatsModel() {
const stats = createMemo(() => page()?.stats)
const githubStars = createAsync(() => getGitHubStars())
const [themePreference, setThemePreference] = createSignal<ThemePreference>("system")
const modelName = createMemo(() => catalogEntry()?.name ?? stats()?.model ?? modelParam() ?? i18n.t("model.fallback"))
const labName = createMemo(() => formatCatalogLabName(catalogEntry()?.lab ?? stats()?.provider ?? labParam()))
const modelTitle = createMemo(() => i18n.t("model.title", { model: modelName() }))
const modelDescription = createMemo(() => i18n.t("model.description", { model: modelName() }))
const modelPath = createMemo(
() =>
`/data/${catalogEntry()?.id ?? [labParam(), stats()?.slug ?? modelParam()].filter((part) => part.length > 0).join("/")}`,
const canonicalModel = createMemo(() => statModel(stats()?.model ?? modelParam(), undefined))
const modelName = createMemo(
() => catalogEntry()?.name ?? publicModelName(canonicalModel()) ?? i18n.t("model.fallback"),
)
const labName = createMemo(() => formatCatalogLabName(catalogEntry()?.lab ?? stats()?.provider ?? labParam()))
const formerName = createMemo(() => formerModelName(canonicalModel()))
const searchModelName = createMemo(() =>
formerName() ? `${modelName()} (formerly ${formerName()})` : modelName(),
)
const modelTitle = createMemo(() => i18n.t("model.title", { model: searchModelName() }))
const modelDescription = createMemo(() => i18n.t("model.description", { model: searchModelName() }))
const modelPath = createMemo(() => {
const fallback = formerName()
? glmFlashCatalogId
: [labParam(), stats()?.slug ?? canonicalModel()].filter((part) => part.length > 0).join("/")
return `/data/${catalogEntry()?.id ?? fallback}`
})
const modelUrl = createMemo(() => localizedUrl(language.locale(), modelPath()))
const statsUnfurlUrl = new URL(statsUnfurlPath, localizedUrl("en", "/data/")).toString()
const modelHeaderLinks = createMemo<readonly HeaderLink[]>(() => [
@ -167,6 +179,7 @@ export default function StatsModel() {
catalog={catalogEntry() ?? null}
catalogData={page()?.catalog ?? null}
labName={labName()}
formerName={formerName()}
/>
<ModelOverview catalog={catalogEntry() ?? null} />
<ModelMomentumSection data={stats() ?? null} />
@ -255,6 +268,7 @@ function ModelHero(props: {
catalog: ModelCatalogEntry | null
catalogData: ModelPageCatalog | null
labName: string
formerName?: string
}) {
const i18n = useI18n()
const language = useLanguage()
@ -336,6 +350,9 @@ function ModelHero(props: {
when={props.data}
fallback={
<p data-slot="model-hero-state">
<Show when={props.formerName}>
{(name) => <span>{`Formerly ${name()}.`}</span>}
</Show>
<span>Listed</span>
<span>across the shared model catalog.</span>
</p>
@ -343,6 +360,9 @@ function ModelHero(props: {
>
{(data) => (
<p data-slot="model-hero-rankline">
<Show when={props.formerName}>
{(name) => <span>{`Formerly ${name()}.`}</span>}
</Show>
<span>Ranked</span>
<span data-slot="model-hero-rank-group">
<span data-slot="model-hero-pill">{formatHeroRank(data().rank)}</span>
@ -1438,3 +1458,13 @@ function providerSlug(provider: string) {
.replace(/^-+|-+$/g, "")
.replace(/-{2,}/g, "-")
}
function formerModelName(model: string) {
return statModel(model, undefined) === glmFlashModel ? "ox-alpha" : undefined
}
function publicModelName(model: string) {
if (model === "unknown") return undefined
if (model === glmFlashModel) return "GLM-5.3-Flash"
return model
}

View file

@ -1,3 +1,4 @@
import { statModel } from "@opencode-ai/stats-core/domain/model-normalization"
import { query } from "@solidjs/router"
export const modelCatalogSourceUrl = "https://models.opencode.ai/catalog.json"
@ -71,8 +72,9 @@ export const getModelCatalog = query(async () => {
}, "getModelCatalog")
export function findModelCatalogEntry(catalog: ModelCatalog, model: string, lab?: string) {
const normalizedId = lab ? `${catalogLabSlug(lab)}/${catalogSlug(model)}` : model.trim().toLowerCase()
const leaf = catalogSlug(model)
const canonicalModel = statModel(model, undefined)
const normalizedId = lab ? `${catalogLabSlug(lab)}/${catalogSlug(canonicalModel)}` : canonicalModel.trim().toLowerCase()
const leaf = catalogSlug(canonicalModel)
return (
catalog.models.find((entry) => entry.id.toLowerCase() === normalizedId) ??
catalog.models.find((entry) => (lab ? entry.lab === catalogLabSlug(lab) : true) && entry.slug === leaf) ??

View file

@ -50,14 +50,18 @@ describe("inference stat normalization", () => {
})
test("merges renamed models under their current name", () => {
expect(statModel("x-preview-f", "")).toBe("ox-alpha")
expect(statModel("x-preview-f", "")).toBe("glm-5.3-flash")
expect(statModel("ox-alpha", "")).toBe("glm-5.3-flash")
expect(statModel("ox-alpha-free", "")).toBe("glm-5.3-flash")
expect(statModel("big-pickle", "zhipuai/ox-alpha-free")).toBe("glm-5.3-flash")
expect(statModel("xiaomi/mimo-v2.5", "")).toBe("mimo-v2.5")
expect(toModelAggregate(aggregate("x-preview-f", "openai"))).toMatchObject([
expect(toModelAggregate(aggregate("x-preview-f", "unknown"))).toMatchObject([
{
provider: "openai",
model: "ox-alpha",
provider: "zhipu",
model: "glm-5.3-flash",
},
])
expect(toProviderAggregate(aggregate("ox-alpha", "unknown"))).toMatchObject([{ provider: "zhipu" }])
})
test("model aggregates prefer provider.model and use normalized model", () => {
@ -126,6 +130,8 @@ describe("inference stat normalization", () => {
expect(queries[0]).toContain("COALESCE(NULLIF(lower(model_tier), ''), '') AS raw_tier")
expect(queries[0]).toContain("WHEN lower(COALESCE(raw_tier, '')) = 'free'")
expect(queries[0]).toContain("regexp_replace(NULLIF(route_model, ''), '^.*/', '')")
expect(queries[0]).toContain("= 'ox-alpha' THEN 'glm-5.3-flash'")
expect(queries[0]).toContain("= 'x-preview-f' THEN 'glm-5.3-flash'")
expect(queries[0]).toContain("OR lower(raw_model) IN ('gpt-5-nano', 'grok-code', 'big-pickle')")
expect(queries[0]).toContain("OR lower(raw_model) LIKE '%-free'")
expect(queries[0]).toContain("THEN 'Free'")

View file

@ -462,13 +462,16 @@ function retentionPeriods(periodStart: Date, periodEnd: Date) {
}
function statModelSql(model: string, providerModel: string) {
return `COALESCE(NULLIF(regexp_replace(CASE
const normalized = `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')`
END, '(-free|:free|:global)+$', '')`
return `COALESCE(NULLIF(CASE
${Object.entries(MODEL_NAME_ALIASES)
.map(([from, to]) => ` WHEN lower(${normalized}) = ${sqlString(from)} THEN ${sqlString(to)}`)
.join("\n")}
ELSE ${normalized}
END, ''), 'unknown')`
}
function freeTierSql(tier: string, model: string) {

View file

@ -16,7 +16,8 @@ export const MODEL_AUTHOR_RULES = [
export const EXCLUDED_MODELS = new Set(["alpha-gpt-next"])
export const FREE_MODELS = new Set(["gpt-5-nano", "grok-code", "big-pickle"])
export const MODEL_NAME_ALIASES: Record<string, string> = {
"x-preview-f": "ox-alpha",
"ox-alpha": "glm-5.3-flash",
"x-preview-f": "glm-5.3-flash",
"xiaomi/mimo-v2.5": "mimo-v2.5",
}
export const RETIRED_STAT_MODELS = ["big-pickle", ...Object.keys(MODEL_NAME_ALIASES)]
@ -35,11 +36,9 @@ 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
const resolved =
normalized === "big-pickle" ? normalizeInferenceModel(providerModel?.split("/").at(-1)) : normalized
return MODEL_NAME_ALIASES[resolved.toLowerCase()] ?? resolved
}
export function statProvider(