Compare commits

...

3 Commits

Author SHA1 Message Date
OpenCode Slack 8abd0f9c1b fix(app): align project picker menu 2026-08-20 16:45:59 +00:00
Adam ad192a59b5 fix(stats): clarify market share providers (#43647) 2026-08-20 10:46:24 -05:00
opencode-agent[bot] 71d08e94d5 fix(opencode): retry xAI capacity stream errors (#43640)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-08-20 09:29:30 -05:00
9 changed files with 216 additions and 229 deletions
@@ -9,9 +9,8 @@ import {
type ComponentProps,
} from "solid-js"
import { createStore } from "solid-js/store"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { Icon } from "@opencode-ai/ui/icon"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2"
import { ProjectAvatar } from "@opencode-ai/ui/v2/project-avatar-v2"
import { getProjectAvatarVariant } from "@/context/layout"
import { useLanguage } from "@/context/language"
@@ -197,6 +196,7 @@ export function PromptProjectSelector(props: {
placement?: "bottom" | "bottom-start"
}) {
const [triggerReady, setTriggerReady] = createSignal(false)
const [showActive, setShowActive] = createSignal(true)
let contentRef: HTMLDivElement | undefined
const dismiss = createMenuDismissController(() => contentRef)
let triggerFrame: number | undefined
@@ -277,166 +277,173 @@ export function PromptProjectSelector(props: {
})
return (
<DropdownMenu
<MenuV2
open={triggerReady() && props.controller.open()}
placement={props.placement ?? "bottom"}
gutter={4}
modal={false}
onOpenChange={(open) => {
if (open) dismiss.allowTriggerRestore()
if (open) setShowActive(true)
props.controller.setOpen(open)
}}
>
<DropdownMenu.Trigger as={ProjectTrigger} ref={setTriggerRef} controller={props.controller} />
<DropdownMenu.Portal>
<DropdownMenu.Content
<MenuV2.Trigger as={ProjectTrigger} ref={setTriggerRef} controller={props.controller} />
<MenuV2.Portal>
<MenuV2.Content
ref={contentRef}
id="prompt-project-menu"
class="w-[243px] overflow-hidden rounded-md border-0 bg-v2-background-bg-layer-01 p-0 shadow-[var(--v2-elevation-floating)] focus:outline-none [&[data-closed]]:!animate-none"
class="w-[243px] overflow-hidden [&[data-closed]]:!animate-none"
onOpenAutoFocus={(event) => event.preventDefault()}
onPointerDownOutside={dismiss.preventTriggerRestore}
onFocusOutside={dismiss.preventTriggerRestore}
onCloseAutoFocus={dismiss.onCloseAutoFocus}
onMouseEnter={() => setShowActive(false)}
>
<div class="flex flex-col p-0.5">
<div class="flex h-7 items-center gap-2 rounded-sm pl-3 pr-2.5 text-v2-icon-icon-muted">
<Icon name="magnifying-glass" size="small" class="shrink-0" />
<input
ref={(el) => props.controller.setSearchRef(el)}
value={props.controller.search()}
placeholder={props.controller.labels.search()}
aria-autocomplete="list"
aria-controls="prompt-project-menu"
aria-activedescendant={props.controller.active() || undefined}
class="h-7 min-w-0 flex-1 border-0 bg-transparent text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-base outline-none placeholder:text-v2-text-text-faint"
onInput={(event) => props.controller.setSearch(event.currentTarget.value)}
onKeyDown={(event) => {
if (event.key === "Tab") {
event.preventDefault()
event.stopPropagation()
if (event.shiftKey) {
focusPreviousControl()
return
}
activeItem()?.focus()
return
}
<div class="flex h-7 items-center gap-2 rounded-sm pl-3 pr-2.5 text-v2-icon-icon-muted">
<Icon name="magnifying-glass" size="small" class="shrink-0" />
<input
ref={(el) => props.controller.setSearchRef(el)}
value={props.controller.search()}
placeholder={props.controller.labels.search()}
aria-autocomplete="list"
aria-controls="prompt-project-menu"
aria-activedescendant={props.controller.active() || undefined}
class="h-7 min-w-0 flex-1 border-0 bg-transparent text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-base outline-none placeholder:text-v2-text-text-faint"
onInput={(event) => {
setShowActive(true)
props.controller.setSearch(event.currentTarget.value)
}}
onKeyDown={(event) => {
if (event.key === "Tab") {
event.preventDefault()
event.stopPropagation()
if (event.key === "Escape") {
event.preventDefault()
props.controller.setOpen(false)
if (event.shiftKey) {
focusPreviousControl()
return
}
if (event.altKey || event.metaKey) return
if (event.key === "ArrowDown") {
event.preventDefault()
moveActive(1)
return
}
if (event.key === "ArrowUp") {
event.preventDefault()
moveActive(-1)
return
}
if (event.key === "Enter" && !event.isComposing) {
event.preventDefault()
selectActive()
}
}}
/>
<Show when={props.controller.search().trim()}>
<button
type="button"
class="flex size-5 items-center justify-center rounded-sm text-v2-icon-icon-muted hover:bg-v2-overlay-simple-overlay-hover"
onPointerDown={(event) => event.preventDefault()}
onClick={() => props.controller.clearSearch()}
aria-label={props.controller.labels.clear()}
>
<Icon name="close-small" size="small" />
</button>
</Show>
</div>
<div class="max-h-[224px] overflow-y-auto">
<Show
when={props.controller.servers().length > 1}
fallback={
<DropdownMenu.RadioGroup value={selectedValue()}>
<For each={props.controller.projects()}>
{(project) => (
<ProjectItem project={project} controller={props.controller} onSelect={selectProject} />
)}
</For>
</DropdownMenu.RadioGroup>
activeItem()?.focus()
return
}
event.stopPropagation()
if (event.key === "Escape") {
event.preventDefault()
props.controller.setOpen(false)
return
}
if (event.altKey || event.metaKey) return
if (event.key === "ArrowDown") {
event.preventDefault()
setShowActive(true)
moveActive(1)
return
}
if (event.key === "ArrowUp") {
event.preventDefault()
setShowActive(true)
moveActive(-1)
return
}
if (event.key === "Enter" && !event.isComposing) {
event.preventDefault()
selectActive()
}
}}
/>
<Show when={props.controller.search().trim()}>
<button
type="button"
class="flex size-5 items-center justify-center rounded-sm text-v2-icon-icon-muted hover:bg-v2-overlay-simple-overlay-hover"
onPointerDown={(event) => event.preventDefault()}
onClick={() => props.controller.clearSearch()}
aria-label={props.controller.labels.clear()}
>
<For
each={props.controller
.servers()
.filter((server) =>
props.controller.projects().some((project) => project.server?.key === server!.key),
)}
>
{(server) => (
<div>
<div class="flex h-7 select-none items-center pl-1.5 pr-3 text-[11px] font-[530] leading-none tracking-[0.05px] text-v2-text-text-faint">
{server!.name}
</div>
<DropdownMenu.RadioGroup value={selectedValue()}>
<For
each={props.controller.projects().filter((project) => project.server?.key === server!.key)}
>
{(project) => (
<ProjectItem project={project} controller={props.controller} onSelect={selectProject} />
)}
</For>
</DropdownMenu.RadioGroup>
</div>
)}
</For>
</Show>
</div>
<Icon name="close-small" size="small" />
</button>
</Show>
</div>
<div class="h-px bg-v2-border-border-muted" />
<div class="flex flex-col p-0.5">
<div class="max-h-[224px] overflow-y-auto">
<Show
when={props.controller.servers().length > 1}
fallback={
<ProjectAction
server={props.controller.servers()[0]?.key}
controller={props.controller}
onSelect={selectAction}
/>
<MenuV2.RadioGroup value={selectedValue()}>
<For each={props.controller.projects()}>
{(project) => (
<ProjectItem
project={project}
controller={props.controller}
showActive={showActive}
onSelect={selectProject}
/>
)}
</For>
</MenuV2.RadioGroup>
}
>
<DropdownMenu.Sub>
<DropdownMenu.SubTrigger
id={props.controller.actionKey()}
data-option-key={props.controller.actionKey()}
class={projectActionClass}
classList={{
"!bg-v2-overlay-simple-overlay-hover": props.controller.active() === props.controller.actionKey(),
}}
onMouseEnter={() => props.controller.setActive(props.controller.actionKey())}
>
<Icon name="plus" size="small" />
<span data-slot="dropdown-menu-item-label" class="min-w-0 flex-1 truncate leading-5">
{props.controller.labels.add()}
</span>
<Icon name="chevron-right" size="small" class="shrink-0 text-v2-icon-icon-muted" />
</DropdownMenu.SubTrigger>
<DropdownMenu.Portal>
<DropdownMenu.SubContent class="min-w-[180px] overflow-hidden rounded-md border-0 bg-v2-background-bg-layer-01 p-0.5 shadow-[var(--v2-elevation-floating)] focus:outline-none">
<For each={props.controller.servers()}>
{(server) => <ServerAction server={server!} onSelect={selectAction} />}
</For>
</DropdownMenu.SubContent>
</DropdownMenu.Portal>
</DropdownMenu.Sub>
<For
each={props.controller
.servers()
.filter((server) =>
props.controller.projects().some((project) => project.server?.key === server!.key),
)}
>
{(server) => (
<div>
<MenuV2.GroupLabel>{server!.name}</MenuV2.GroupLabel>
<MenuV2.RadioGroup value={selectedValue()}>
<For each={props.controller.projects().filter((project) => project.server?.key === server!.key)}>
{(project) => (
<ProjectItem
project={project}
controller={props.controller}
showActive={showActive}
onSelect={selectProject}
/>
)}
</For>
</MenuV2.RadioGroup>
</div>
)}
</For>
</Show>
</div>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu>
<MenuV2.Separator />
<Show
when={props.controller.servers().length > 1}
fallback={
<ProjectAction
server={props.controller.servers()[0]?.key}
controller={props.controller}
showActive={showActive}
onSelect={selectAction}
/>
}
>
<MenuV2.Sub>
<MenuV2.SubTrigger
id={props.controller.actionKey()}
data-option-key={props.controller.actionKey()}
classList={{
"!bg-v2-overlay-simple-overlay-hover":
showActive() && props.controller.active() === props.controller.actionKey(),
}}
onMouseEnter={() => props.controller.setActive(props.controller.actionKey())}
>
<Icon name="plus" size="small" />
<span class="min-w-0 flex-1 truncate">{props.controller.labels.add()}</span>
</MenuV2.SubTrigger>
<MenuV2.Portal>
<MenuV2.SubContent class="min-w-[180px]">
<For each={props.controller.servers()}>
{(server) => <ServerAction server={server!} onSelect={selectAction} />}
</For>
</MenuV2.SubContent>
</MenuV2.Portal>
</MenuV2.Sub>
</Show>
</MenuV2.Content>
</MenuV2.Portal>
</MenuV2>
)
}
@@ -503,24 +510,17 @@ function ProjectTrigger(props: ComponentProps<"button"> & { controller: PromptPr
function ProjectItem(props: {
project: PromptProject
controller: PromptProjectController
showActive: Accessor<boolean>
onSelect: (project: PromptProject) => void
}) {
const key = () => props.controller.projectKey(props.project)
return (
<DropdownMenu.RadioItem
<MenuV2.RadioItem
id={key()}
value={key()}
data-option-key={key()}
class="h-7 gap-2 rounded-sm px-3 text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-base data-[highlighted]:!bg-v2-overlay-simple-overlay-hover"
classList={{ "!bg-v2-overlay-simple-overlay-hover": props.controller.active() === key() }}
style={{
"font-family": "var(--v2-font-family-sans)",
"font-size": "13px",
"font-weight": 440,
"line-height": "20px",
"letter-spacing": "-0.04px",
color: "var(--v2-text-text-base)",
padding: "0 12px",
classList={{
"!bg-v2-overlay-simple-overlay-hover": props.showActive() && props.controller.active() === key(),
}}
closeOnSelect
onMouseEnter={() => {
@@ -534,37 +534,24 @@ function ProjectItem(props: {
src={getProjectAvatarSource(props.project.id, props.project.icon)}
variant={getProjectAvatarVariant(props.project.icon?.color)}
/>
<DropdownMenu.ItemLabel class="min-w-0 truncate leading-5">{displayName(props.project)}</DropdownMenu.ItemLabel>
<DropdownMenu.ItemIndicator style={{ width: "14px", height: "14px", right: "12px" }}>
<IconV2 name="check" size="small" class="shrink-0 text-v2-icon-icon-base" />
</DropdownMenu.ItemIndicator>
</DropdownMenu.RadioItem>
<span class="min-w-0 truncate">{displayName(props.project)}</span>
</MenuV2.RadioItem>
)
}
const projectActionClass =
"h-7 gap-2 rounded-sm px-3 text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-base [font-family:var(--v2-font-family-sans)] data-[highlighted]:!bg-v2-overlay-simple-overlay-hover"
function ProjectAction(props: {
server?: string
controller: PromptProjectController
showActive: Accessor<boolean>
onSelect: (server?: string) => void
}) {
const key = () => props.controller.actionKey(props.server)
return (
<DropdownMenu.Item
<MenuV2.Item
id={key()}
data-option-key={key()}
class="h-7 gap-2 rounded-sm px-3 text-[13px] font-[440] leading-5 tracking-[-0.04px] text-v2-text-text-base data-[highlighted]:!bg-v2-overlay-simple-overlay-hover"
classList={{ "!bg-v2-overlay-simple-overlay-hover": props.controller.active() === key() }}
style={{
"font-family": "var(--v2-font-family-sans)",
"font-size": "13px",
"font-weight": 440,
"line-height": "20px",
"letter-spacing": "-0.04px",
color: "var(--v2-text-text-base)",
padding: "0 12px",
classList={{
"!bg-v2-overlay-simple-overlay-hover": props.showActive() && props.controller.active() === key(),
}}
onMouseEnter={() => {
props.controller.setActive(key())
@@ -573,17 +560,15 @@ function ProjectAction(props: {
onSelect={() => props.onSelect(props.server)}
>
<Icon name="plus" size="small" />
<DropdownMenu.ItemLabel class="min-w-0 truncate leading-5">
{props.controller.labels.add()}
</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<span class="min-w-0 truncate">{props.controller.labels.add()}</span>
</MenuV2.Item>
)
}
function ServerAction(props: { server: { key: string; name: string }; onSelect: (server: string) => void }) {
return (
<DropdownMenu.Item class={projectActionClass} onSelect={() => props.onSelect(props.server.key)}>
<DropdownMenu.ItemLabel class="min-w-0 flex-1 truncate leading-5">{props.server.name}</DropdownMenu.ItemLabel>
</DropdownMenu.Item>
<MenuV2.Item onSelect={() => props.onSelect(props.server.key)}>
<span class="min-w-0 flex-1 truncate">{props.server.name}</span>
</MenuV2.Item>
)
}
+7
View File
@@ -144,6 +144,13 @@ export function parseStreamError(input: unknown): ParsedStreamError | undefined
responseBody,
}
}
return {
type: "api_error",
message: typeof body?.error?.message === "string" ? body.error.message : "Server error.",
isRetryable: true,
responseBody,
}
}
export type ParsedAPICallError =
+1
View File
@@ -37,6 +37,7 @@ const RETRYABLE_MESSAGE_PATTERNS = [
/terminated|fetch failed|failed to fetch|network error|upstream connect|connection error|connection refused|connection lost|socket connection was closed|socket hang up|reset before headers|getaddrinfo|enotfound|eai_again|econnrefused|econnreset|etimedout/i,
/^timeout$|\b(?:request|response|connection|network|stream|read) (?:timeout|timed out|time out)\b/i,
/try your request again|retry your request|resource exhausted|resource_exhausted/i,
/\btry again (?:later|in\b)|\b(?:currently|temporarily) at capacity\b/i,
]
function cap(ms: number) {
@@ -0,0 +1,24 @@
import { describe, expect, test } from "bun:test"
import { ProviderError } from "@/provider/error"
describe("provider stream errors", () => {
test("retries provider stream errors without a code", () => {
const messages = [
"The model is currently at capacity due to high demand. Please try again in a few minutes, or use a higher service tier for priority processing: https://docs.x.ai/developers/advanced-api-usage/priority-processing",
"The model is temporarily unavailable.",
]
for (const message of messages)
expect(
ProviderError.parseStreamError({
type: "error",
error: { message },
}),
).toEqual({
type: "api_error",
message,
isRetryable: true,
responseBody: JSON.stringify({ type: "error", error: { message } }),
})
})
})
@@ -215,6 +215,9 @@ describe("session.retry.retryable", () => {
"response timed out",
"Please retry your request",
"try your request again",
"Please try again in a few minutes",
"The model is currently at capacity due to high demand",
"The service is temporarily at capacity",
"upstream returned status 524",
])("retries matching API error text: %s", (message) => {
expect(SessionRetry.retryable(wrap(message), retryProvider)).toEqual({ message })
+1
View File
@@ -2170,6 +2170,7 @@ body {
font: inherit;
font-size: 11px;
line-height: 1;
text-align: left;
cursor: pointer;
outline: none;
text-decoration: none;
+20 -59
View File
@@ -4,7 +4,7 @@ import { Resource } from "sst/resource"
import { DatabaseError } from "../database"
import type { GeoStatMetric } from "./geo"
import { ModelStatRepo, type ModelStatMetric } from "./model"
import type { ProviderStatMetric } from "./provider"
import { statProvider } from "./model-normalization"
import { DATA_SITE_TIERS, normalizeTier } from "./stat"
export type UsageProduct = "All Users" | "Zen" | "Go" | "Enterprise"
@@ -140,10 +140,6 @@ type StatMetricRow = Omit<ModelStatMetric, "updatedAt"> & {
periodStart: number
updatedAt: number
}
type ProviderMetricRow = Omit<ProviderStatMetric, "updatedAt"> & {
periodStart: number
updatedAt: number
}
type GeoMetricRow = Omit<GeoStatMetric, "updatedAt"> & {
periodStart: number
updatedAt: number
@@ -171,12 +167,8 @@ type RawRow = Record<string, unknown>
export function getStatsHomeData(): Effect.Effect<StatsHomeData, StatsDataError> {
return Effect.tryPromise({
try: async () => {
const [modelRows, providerRows, geoRows] = await Promise.all([
listModelDaily(),
listProviderDaily(),
listGeoDaily(),
])
return buildStatsHomeData(modelRows, providerRows, geoRows)
const [modelRows, geoRows] = await Promise.all([listModelDaily(), listGeoDaily()])
return buildStatsHomeData(modelRows, geoRows)
},
catch: (cause) => new StatsDataError(cause),
})
@@ -241,23 +233,6 @@ async function listModelDaily(): Promise<ModelStatMetric[]> {
}))
}
async function listProviderDaily(): Promise<ProviderStatMetric[]> {
return (
await queryRows(
`select period_key, updated_at, tier, provider, total_tokens from provider_stat
where grain = 'day' and client = 'all' and source = 'all'
and tier in (${SITE_TIER_PLACEHOLDERS}) order by period_key`,
DATA_SITE_TIERS,
)
).map((row) => ({
periodKey: stringValue(row.period_key),
updatedAt: dateValue(row.updated_at),
tier: stringValue(row.tier),
provider: stringValue(row.provider),
totalTokens: numberValue(row.total_tokens),
}))
}
async function listGeoDaily(opts?: { provider?: string; model?: string }): Promise<GeoStatMetric[]> {
const scope =
opts?.model && opts.provider
@@ -334,15 +309,10 @@ export const getStatsModelComparisonData = (
{ provider: secondProvider, model: secondModel },
])
function buildStatsHomeData(
modelRows: ModelStatMetric[],
providerRows: ProviderStatMetric[],
geoRows: GeoStatMetric[],
): StatsHomeData {
function buildStatsHomeData(modelRows: ModelStatMetric[], geoRows: GeoStatMetric[]): StatsHomeData {
const normalized = modelRows.flatMap(normalizeStatRow)
const providers = providerRows.flatMap(normalizeProviderRow)
const geo = geoRows.flatMap(normalizeGeoRow)
const periods = [...normalized, ...providers, ...geo]
const periods = [...normalized, ...geo]
if (periods.length === 0) return emptyStatsHomeData()
const earliest = Math.min(...periods.map((row) => row.periodStart))
@@ -377,7 +347,7 @@ function buildStatsHomeData(
leaderboard: createUsageProductRecord((product) =>
createRangeRecord((range) => buildLeaderboard(normalized, product, getWindow("1W", earliest, latest))),
),
market: createRangeRecord((range) => buildMarketShare(providers, "Go", range, getWindow(range, earliest, latest))),
market: createRangeRecord((range) => buildMarketShare(normalized, "Go", range, getWindow(range, earliest, latest))),
tokenCost: createTokenProductRecord((product) =>
buildTokenCost(normalized, product, getWindow("1W", earliest, latest)),
),
@@ -602,15 +572,20 @@ function buildLeaderboard(rows: StatMetricRow[], product: UsageProduct, rankWind
}))
}
function buildMarketShare(rows: ProviderMetricRow[], product: UsageProduct, range: UsageRange, window: DateWindow) {
function buildMarketShare(rows: StatMetricRow[], product: UsageProduct, range: UsageRange, window: DateWindow) {
const providerOrder = aggregateByProvider(rowsForProduct(rows, product, window.start, window.end))
.filter((item) => item.provider !== "unknown")
.toSorted((a, b) => b.tokens - a.tokens || a.provider.localeCompare(b.provider))
.slice(0, 8)
.map((item) => item.provider)
return createBuckets(window, range).flatMap((bucket) => {
const total = aggregateByProvider(rowsForProduct(rows, product, bucket.start, bucket.end)).toSorted(
(a, b) => b.tokens - a.tokens,
)
const total = aggregateByProvider(rowsForProduct(rows, product, bucket.start, bucket.end))
const totalTokens = total.reduce((sum, item) => sum + item.tokens, 0)
if (totalTokens === 0) return []
const authors = total.slice(0, 8)
const byProvider = new Map(total.map((item) => [item.provider, item.tokens]))
const authors = providerOrder.map((provider) => ({ provider, tokens: byProvider.get(provider) ?? 0 }))
const knownTokens = authors.reduce((sum, item) => sum + item.tokens, 0)
const withOther = [...authors, { provider: "Other", tokens: Math.max(totalTokens - knownTokens, 0) }].filter(
(item) => item.tokens > 0,
@@ -773,7 +748,7 @@ function aggregateByModelName(rows: StatMetricRow[]) {
)
}
function aggregateByProvider(rows: ProviderMetricRow[]) {
function aggregateByProvider(rows: { provider: string; totalTokens: number }[]) {
return Object.values(
rows.reduce<Record<string, { provider: string; tokens: number }>>((result, row) => {
result[row.provider] = {
@@ -917,27 +892,12 @@ function normalizeStatRow(row: ModelStatMetric): StatMetricRow[] {
periodStart,
updatedAt,
tier: normalizeTier(row.tier),
provider: row.provider || "unknown",
provider: statProvider(row.model, undefined, row.provider) || "unknown",
model: row.model || "unknown",
},
]
}
function normalizeProviderRow(row: ProviderStatMetric): ProviderMetricRow[] {
const periodStart = periodKeyTime(row.periodKey)
const updatedAt = dateTime(row.updatedAt)
if (!Number.isFinite(periodStart) || !Number.isFinite(updatedAt)) return []
return [
{
...row,
periodStart,
updatedAt,
tier: normalizeTier(row.tier),
provider: row.provider || "unknown",
},
]
}
function normalizeGeoRow(row: GeoStatMetric): GeoMetricRow[] {
const periodStart = periodKeyTime(row.periodKey)
const updatedAt = dateTime(row.updatedAt)
@@ -948,7 +908,7 @@ function normalizeGeoRow(row: GeoStatMetric): GeoMetricRow[] {
periodStart,
updatedAt,
tier: normalizeTier(row.tier),
provider: row.provider || "all",
provider: row.provider === "all" ? "all" : statProvider(row.model, undefined, row.provider) || "unknown",
model: row.model || "all",
country: row.country || "ZZ",
continent: row.continent || "",
@@ -987,6 +947,7 @@ function formatProvider(provider: string) {
deepseek: "DeepSeek",
google: "Google",
minimax: "MiniMax",
meta: "Meta",
moonshot: "Moonshot",
moonshotai: "Moonshot",
nvidia: "NVIDIA",
@@ -24,6 +24,7 @@ describe("inference stat normalization", () => {
expect(modelAuthor("kimi-k2.6")).toBe("moonshot")
expect(modelAuthor("mimo-v2-omni")).toBe("xiaomi")
expect(modelAuthor("minimax-m2.7")).toBe("minimax")
expect(modelAuthor("muse-spark-1.2-contributor")).toBe("meta")
expect(modelAuthor("nemotron-3-super-free")).toBe("nvidia")
expect(modelAuthor("qwen3.7-max")).toBe("qwen")
expect(modelAuthor("alpha-gpt-next")).toBeUndefined()
@@ -67,6 +68,9 @@ describe("inference stat normalization", () => {
{ provider: "openai" },
])
expect(toProviderAggregate(aggregate("big-pickle", "opencode"))).toMatchObject([{ provider: "unknown" }])
expect(toProviderAggregate(aggregate("muse-spark-1.2-contributor", "unknown"))).toMatchObject([
{ provider: "meta" },
])
})
test("geo aggregates never keep opencode or big-pickle dimensions", () => {
@@ -9,6 +9,7 @@ export const MODEL_AUTHOR_RULES = [
{ match: "kimi", author: "moonshot" },
{ match: "mimo", author: "xiaomi" },
{ match: "minimax", author: "minimax" },
{ match: "muse-spark", author: "meta" },
{ match: "nemotron", author: "nvidia" },
{ match: "qwen", author: "qwen" },
] as const