mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-29 09:02:16 +00:00
fix(stats): map ox alpha to glm 5.3 flash (#45542)
This commit is contained in:
parent
6568a82455
commit
1120d0704e
5 changed files with 64 additions and 24 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) ??
|
||||
|
|
|
|||
|
|
@ -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'")
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue