fix(stats): align big-pickle provider resolution (#30274)

This commit is contained in:
Adam
2026-06-01 14:09:07 -05:00
committed by GitHub
parent 1813256d8e
commit 2bf85b8479
4 changed files with 112 additions and 35 deletions
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test"
import { toModelAggregate } from "./inference"
import { modelAuthor, normalizeInferenceModel } from "./model-normalization"
import { toModelAggregate, toProviderAggregate } from "./inference"
import { modelAuthor, normalizeInferenceModel, statModel, statProvider } from "./model-normalization"
describe("inference stat normalization", () => {
test("normalizes model suffixes used by router/provider variants", () => {
@@ -12,7 +12,7 @@ describe("inference stat normalization", () => {
})
test("maps normalized model ids to public authors", () => {
expect(modelAuthor("big-pickle")).toBe("opencode")
expect(modelAuthor("big-pickle")).toBe("unknown")
expect(modelAuthor("claude-sonnet-4-5")).toBe("anthropic")
expect(modelAuthor("deepseek-v4-pro")).toBe("deepseek")
expect(modelAuthor("gemini-3.5-flash")).toBe("google")
@@ -28,7 +28,15 @@ describe("inference stat normalization", () => {
expect(modelAuthor("alpha-gpt-next")).toBeUndefined()
})
test("model aggregates ignore datalake provider and use normalized author/model", () => {
test("uses provider.model to resolve opencode route providers", () => {
expect(statModel("big-pickle", "claude-sonnet-4-5")).toBe("claude-sonnet-4-5")
expect(statModel("big-pickle", "gpt-5-free")).toBe("gpt-5")
expect(statProvider("big-pickle", "claude-sonnet-4-5", "opencode")).toBe("anthropic")
expect(statProvider("big-pickle", "gpt-5", "opencode")).toBe("openai")
expect(statProvider("big-pickle", "", "opencode")).toBe("unknown")
})
test("model aggregates prefer provider.model and use normalized model", () => {
expect(toModelAggregate(aggregate("alpha-gpt-next", "openai"))).toEqual([])
expect(toModelAggregate(aggregate("deepseek-v4-flash-free", "not-public-provider"))).toMatchObject([
@@ -38,6 +46,23 @@ describe("inference stat normalization", () => {
model: "deepseek-v4-flash",
},
])
expect(
toModelAggregate({ ...aggregate("big-pickle", "opencode"), provider_model: "claude-sonnet-4-5" }),
).toMatchObject([
{
provider: "anthropic",
model: "claude-sonnet-4-5",
provider_model: "claude-sonnet-4-5",
},
])
})
test("provider aggregates never keep opencode as the provider", () => {
expect(toProviderAggregate({ ...aggregate("big-pickle", "opencode"), provider_model: "gpt-5" })).toMatchObject([
{ provider: "openai" },
])
expect(toProviderAggregate(aggregate("big-pickle", "opencode"))).toMatchObject([{ provider: "unknown" }])
})
test("model aggregates use ISO week period keys", () => {
+23 -15
View File
@@ -4,10 +4,9 @@ import type { GeoStatAggregate } from "./geo"
import type { ModelStatAggregate } from "./model"
import {
EXCLUDED_MODELS,
MODEL_AUTHOR_OVERRIDES,
MODEL_AUTHOR_RULES,
modelAuthor,
normalizeInferenceModel,
statModel,
statProvider,
} from "./model-normalization"
import type { ProviderStatAggregate } from "./provider"
import { normalizeCountry, normalizeTier, type StatBaseAggregate } from "./stat"
@@ -64,7 +63,7 @@ WITH normalized AS (
SELECT
from_iso8601_timestamp(event_timestamp) AS event_time,
model AS raw_model,
COALESCE(NULLIF(regexp_replace(model, '(-free|:global)+$', ''), ''), 'unknown') AS model,
${statModelSql("model", "provider_model")} AS model,
COALESCE(NULLIF(provider_model, ''), '') AS provider_model,
UPPER(COALESCE(NULLIF(cf_country, ''), 'ZZ')) AS country,
COALESCE(NULLIF(cf_continent, ''), '') AS continent,
@@ -98,10 +97,10 @@ WITH normalized AS (
event_time,
CASE
WHEN source = 'lite' THEN 'Go'
WHEN model IN ('gpt-5-nano', 'grok-code', 'big-pickle') OR regexp_like(raw_model, '-free(:global)?$') THEN 'Free'
WHEN raw_model IN ('gpt-5-nano', 'grok-code', 'big-pickle') OR regexp_like(raw_model, '-free(:global)?$') THEN 'Free'
ELSE 'Paid'
END AS tier,
${modelAuthorSql("model")} AS provider,
${statProviderSql("model", "provider_model")} AS provider,
provider_model,
model,
country,
@@ -157,25 +156,27 @@ ORDER BY grain, period_key, total_tokens DESC
}
export function toModelAggregate(data: AthenaData): ModelStatAggregate[] {
const model = normalizeInferenceModel(data.model)
const author = modelAuthor(model)
if (!author) return []
const model = statModel(data.model, data.provider_model)
const provider = statProvider(model, data.provider_model, data.provider)
if (!provider) return []
return toStatBaseAggregate(data).flatMap((base) => [
{ ...base, provider: author, model, provider_model: data.provider_model || "" },
{ ...base, provider, model, provider_model: data.provider_model || "" },
])
}
export function toProviderAggregate(data: AthenaData): ProviderStatAggregate[] {
return toStatBaseAggregate(data).flatMap((base) => [{ ...base, provider: data.provider || "unknown" }])
return toStatBaseAggregate(data).flatMap((base) => [
{ ...base, provider: statProvider(data.model, data.provider_model, data.provider) || "unknown" },
])
}
export function toGeoAggregate(data: AthenaData): GeoStatAggregate[] {
return toStatBaseAggregate(data).flatMap((base) => [
{
...base,
provider: data.provider || "all",
model: normalizeInferenceModel(data.model || "all"),
provider: statProvider(data.model, data.provider_model, data.provider) || "all",
model: statModel(data.model || "all", data.provider_model),
country: normalizeCountry(data.country),
continent: data.continent || "",
},
@@ -243,9 +244,16 @@ function sqlString(value: string) {
return `'${value.replace(/'/g, "''")}'`
}
function modelAuthorSql(model: string) {
function statModelSql(model: string, providerModel: string) {
return `COALESCE(NULLIF(regexp_replace(CASE
WHEN ${model} = 'big-pickle' THEN COALESCE(NULLIF(${providerModel}, ''), ${model})
ELSE ${model}
END, '(-free|:global)+$', ''), ''), 'unknown')`
}
function statProviderSql(model: string, providerModel: string) {
return `CASE
${MODEL_AUTHOR_OVERRIDES.map((item) => ` WHEN lower(${model}) = ${sqlString(item.model)} THEN ${sqlString(item.author)}`).join("\n")}
${MODEL_AUTHOR_RULES.map((item) => ` WHEN strpos(lower(${providerModel}), ${sqlString(item.match)}) > 0 THEN ${sqlString(item.author)}`).join("\n")}
${MODEL_AUTHOR_RULES.map((item) => ` WHEN strpos(lower(${model}), ${sqlString(item.match)}) > 0 THEN ${sqlString(item.author)}`).join("\n")}
ELSE 'unknown'
END`
@@ -1,4 +1,3 @@
export const MODEL_AUTHOR_OVERRIDES = [{ model: "big-pickle", author: "opencode" }] as const
export const MODEL_AUTHOR_RULES = [
{ match: "claude", author: "anthropic" },
{ match: "gemini", author: "google" },
@@ -23,8 +22,26 @@ export function modelAuthor(value: string | undefined) {
const model = normalizeInferenceModel(value).toLowerCase()
if (EXCLUDED_MODELS.has(model)) return undefined
const override = MODEL_AUTHOR_OVERRIDES.find((item) => item.model === model)
if (override) return override.author
return MODEL_AUTHOR_RULES.find((item) => model.includes(item.match))?.author ?? "unknown"
}
export function statModel(model: string | undefined, providerModel: string | undefined) {
const normalized = normalizeInferenceModel(model)
if (normalized.toLowerCase() === "big-pickle") return normalizeInferenceModel(providerModel)
return normalized
}
export function statProvider(
model: string | undefined,
providerModel: string | undefined,
provider: string | undefined,
) {
const modelAuthorValue = modelAuthor(statModel(model, providerModel))
if (!modelAuthorValue) return undefined
const providerModelAuthor = modelAuthor(providerModel)
if (providerModelAuthor && providerModelAuthor !== "unknown") return providerModelAuthor
if (modelAuthorValue !== "unknown") return modelAuthorValue
if (provider && provider.toLowerCase() !== "opencode") return provider
return modelAuthorValue
}