feat(stats): add cache ratio section
This commit is contained in:
@@ -1648,6 +1648,7 @@
|
|||||||
[data-section="leaderboard"],
|
[data-section="leaderboard"],
|
||||||
[data-section="market-share"],
|
[data-section="market-share"],
|
||||||
[data-section="token-cost"],
|
[data-section="token-cost"],
|
||||||
|
[data-section="cache-ratio"],
|
||||||
[data-section="session-cost"]
|
[data-section="session-cost"]
|
||||||
) {
|
) {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -2122,7 +2123,8 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-page="stats"] [data-component="token-cost"] {
|
[data-page="stats"] [data-component="token-cost"],
|
||||||
|
[data-page="stats"] [data-component="cache-ratio"] {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
@@ -2611,6 +2613,7 @@
|
|||||||
[data-page="stats"] [data-section="leaderboard"],
|
[data-page="stats"] [data-section="leaderboard"],
|
||||||
[data-page="stats"] [data-section="market-share"],
|
[data-page="stats"] [data-section="market-share"],
|
||||||
[data-page="stats"] [data-section="token-cost"],
|
[data-page="stats"] [data-section="token-cost"],
|
||||||
|
[data-page="stats"] [data-section="cache-ratio"],
|
||||||
[data-page="stats"] [data-section="session-cost"] {
|
[data-page="stats"] [data-section="session-cost"] {
|
||||||
padding: 64px 32px;
|
padding: 64px 32px;
|
||||||
}
|
}
|
||||||
@@ -2742,6 +2745,7 @@
|
|||||||
[data-page="stats"] [data-section="leaderboard"],
|
[data-page="stats"] [data-section="leaderboard"],
|
||||||
[data-page="stats"] [data-section="market-share"],
|
[data-page="stats"] [data-section="market-share"],
|
||||||
[data-page="stats"] [data-section="token-cost"],
|
[data-page="stats"] [data-section="token-cost"],
|
||||||
|
[data-page="stats"] [data-section="cache-ratio"],
|
||||||
[data-page="stats"] [data-section="session-cost"] {
|
[data-page="stats"] [data-section="session-cost"] {
|
||||||
padding: 48px 24px;
|
padding: 48px 24px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import opencodeWordmarkDark from "../asset/logo-ornate-dark.svg"
|
|||||||
import statsUnfurlRankings from "../asset/unfurl-rankings.png?url"
|
import statsUnfurlRankings from "../asset/unfurl-rankings.png?url"
|
||||||
import {
|
import {
|
||||||
getStatsHomeData,
|
getStatsHomeData,
|
||||||
|
type CacheRatioEntry,
|
||||||
type LeaderboardEntry,
|
type LeaderboardEntry,
|
||||||
type MarketDay,
|
type MarketDay,
|
||||||
type StatsHomeData,
|
type StatsHomeData,
|
||||||
@@ -38,8 +39,9 @@ const statsUnfurlAlt = "OpenCode Stats wordmark on a dark patterned background"
|
|||||||
const headerLinks = [
|
const headerLinks = [
|
||||||
{ href: "#top-models", label: "Top Models" },
|
{ href: "#top-models", label: "Top Models" },
|
||||||
{ href: "#leaderboard", label: "Leaderboard" },
|
{ href: "#leaderboard", label: "Leaderboard" },
|
||||||
{ href: "#token-cost", label: "Token Cost" },
|
|
||||||
{ href: "#session-cost", label: "Session Cost" },
|
{ href: "#session-cost", label: "Session Cost" },
|
||||||
|
{ href: "#token-cost", label: "Token Cost" },
|
||||||
|
{ href: "#cache-ratio", label: "Cache Ratio" },
|
||||||
{ href: "#market-share", label: "Market Share" },
|
{ href: "#market-share", label: "Market Share" },
|
||||||
] as const
|
] as const
|
||||||
const githubLink = {
|
const githubLink = {
|
||||||
@@ -160,8 +162,9 @@ export default function StatsHome() {
|
|||||||
<>
|
<>
|
||||||
<Hero updatedAt={stats().updatedAt} />
|
<Hero updatedAt={stats().updatedAt} />
|
||||||
<TopModelsSection data={stats().usage} leaderboard={stats().leaderboard} />
|
<TopModelsSection data={stats().usage} leaderboard={stats().leaderboard} />
|
||||||
<TokenCostSection data={stats().tokenCost} />
|
|
||||||
<SessionCostSection data={stats().sessionCost} />
|
<SessionCostSection data={stats().sessionCost} />
|
||||||
|
<TokenCostSection data={stats().tokenCost} />
|
||||||
|
<CacheRatioSection data={stats().cacheRatio} />
|
||||||
<MarketShareSection data={stats().market} />
|
<MarketShareSection data={stats().market} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -1000,7 +1003,7 @@ function MarketShareSection(props: { data: StatsHomeData["market"] }) {
|
|||||||
setInspecting(false)
|
setInspecting(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SectionBridge label="SESSION COST" href="#session-cost" />
|
<SectionBridge label="CACHE RATIO" href="#cache-ratio" />
|
||||||
<SectionTitle title="Market Share" description="Compare token share by model author." />
|
<SectionTitle title="Market Share" description="Compare token share by model author." />
|
||||||
<Show
|
<Show
|
||||||
when={activeDay()}
|
when={activeDay()}
|
||||||
@@ -1280,7 +1283,7 @@ function TokenCostSection(props: { data: StatsHomeData["tokenCost"] }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="token-cost" data-section="token-cost">
|
<section id="token-cost" data-section="token-cost">
|
||||||
<SectionBridge label="LEADERBOARD" href="#leaderboard" />
|
<SectionBridge label="SESSION COST" href="#session-cost" />
|
||||||
<SectionTitle title="Token Cost" description="Price per 1M tokens." />
|
<SectionTitle title="Token Cost" description="Price per 1M tokens." />
|
||||||
<Show
|
<Show
|
||||||
when={visible().length > 0}
|
when={visible().length > 0}
|
||||||
@@ -1351,6 +1354,90 @@ function TokenCostChart(props: {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CacheRatioSection(props: { data: StatsHomeData["cacheRatio"] }) {
|
||||||
|
const [product, setProduct] = createSignal<TokenProduct>("Go")
|
||||||
|
const [activeIndex, setActiveIndex] = createSignal(2)
|
||||||
|
const data = createMemo(() => props.data[product()])
|
||||||
|
const visible = createMemo(() => data().slice(0, 16))
|
||||||
|
const selectedIndex = createMemo(() => Math.min(activeIndex(), Math.max(visible().length - 1, 0)))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="cache-ratio" data-section="cache-ratio">
|
||||||
|
<SectionBridge label="TOKEN COST" href="#token-cost" />
|
||||||
|
<SectionTitle title="Cache Ratio" description="Share of input tokens served from cache." />
|
||||||
|
<Show
|
||||||
|
when={visible().length > 0}
|
||||||
|
fallback={
|
||||||
|
<EmptyState title="No cache ratio data" description="No input-token model_stat rows matched this product." />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CacheRatioChart data={visible()} activeIndex={selectedIndex()} onActiveIndexChange={setActiveIndex} />
|
||||||
|
</Show>
|
||||||
|
<div data-slot="token-footer" hidden>
|
||||||
|
<FilterPills
|
||||||
|
items={tokenProducts}
|
||||||
|
selected={product()}
|
||||||
|
label="Product filter"
|
||||||
|
variant="product"
|
||||||
|
onSelect={setProduct}
|
||||||
|
/>
|
||||||
|
<LiveIndicator />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CacheRatioChart(props: {
|
||||||
|
data: CacheRatioEntry[]
|
||||||
|
activeIndex: number
|
||||||
|
onActiveIndexChange: (index: number) => void
|
||||||
|
}) {
|
||||||
|
const max = createMemo(() => Math.max(0, ...props.data.map((item) => item.ratio)) || 100)
|
||||||
|
const active = createMemo(() => props.data[props.activeIndex] ?? props.data[0])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-component="cache-ratio">
|
||||||
|
<For each={props.data}>
|
||||||
|
{(item, index) => (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-component="token-row"
|
||||||
|
data-active={props.activeIndex === index() ? "true" : undefined}
|
||||||
|
onClick={() => props.onActiveIndexChange(index())}
|
||||||
|
onPointerEnter={() => props.onActiveIndexChange(index())}
|
||||||
|
>
|
||||||
|
<strong>{formatRatio(item.ratio)}</strong>
|
||||||
|
<span>{item.model}</span>
|
||||||
|
<MetricBar value={item.ratio} max={max()} active={props.activeIndex === index()} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
<Show when={active()}>
|
||||||
|
{(item) => (
|
||||||
|
<div data-component="token-tooltip" style={{ top: `${props.activeIndex * 36 + 2}px` }}>
|
||||||
|
<p>
|
||||||
|
<span>Cache Ratio</span>
|
||||||
|
<strong>{formatRatio(item().ratio)}</strong>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>Cached</span>
|
||||||
|
<strong>{formatBillions(item().cached)}</strong>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>Uncached</span>
|
||||||
|
<strong>{formatBillions(item().uncached)}</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRatio(value: number) {
|
||||||
|
return `${value.toFixed(value > 0 && value < 10 ? 1 : 0)}%`
|
||||||
|
}
|
||||||
|
|
||||||
function formatDollars(value: number) {
|
function formatDollars(value: number) {
|
||||||
return `$${value.toFixed(2)}`
|
return `$${value.toFixed(2)}`
|
||||||
}
|
}
|
||||||
@@ -1378,7 +1465,7 @@ function SessionCostSection(props: { data: StatsHomeData["sessionCost"] }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="session-cost" data-section="session-cost">
|
<section id="session-cost" data-section="session-cost">
|
||||||
<SectionBridge label="TOKEN COST" href="#token-cost" />
|
<SectionBridge label="LEADERBOARD" href="#leaderboard" />
|
||||||
<SectionTitle title="Session Cost" description="Average cost per session." />
|
<SectionTitle title="Session Cost" description="Average cost per session." />
|
||||||
<Show
|
<Show
|
||||||
when={visible().length > 0}
|
when={visible().length > 0}
|
||||||
@@ -1635,8 +1722,9 @@ function Footer(props: {
|
|||||||
const modelStats = [
|
const modelStats = [
|
||||||
{ href: "#top-models", label: "Top Models" },
|
{ href: "#top-models", label: "Top Models" },
|
||||||
{ href: "#leaderboard", label: "Leaderboard" },
|
{ href: "#leaderboard", label: "Leaderboard" },
|
||||||
{ href: "#token-cost", label: "Token Cost" },
|
|
||||||
{ href: "#session-cost", label: "Session Cost" },
|
{ href: "#session-cost", label: "Session Cost" },
|
||||||
|
{ href: "#token-cost", label: "Token Cost" },
|
||||||
|
{ href: "#cache-ratio", label: "Cache Ratio" },
|
||||||
{ href: "#market-share", label: "Market Share" },
|
{ href: "#market-share", label: "Market Share" },
|
||||||
]
|
]
|
||||||
const legal = [
|
const legal = [
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export type UsagePoint = { date: string; segments: { model: string; value: numbe
|
|||||||
export type MarketDay = { date: string; total: number; authors: { author: string; share: number; tokens: number }[] }
|
export type MarketDay = { date: string; total: number; authors: { author: string; share: number; tokens: number }[] }
|
||||||
export type LeaderboardEntry = { model: string; author: string; tokens: number; change: number; rank: number }
|
export type LeaderboardEntry = { model: string; author: string; tokens: number; change: number; rank: number }
|
||||||
export type TokenCostEntry = { model: string; total: number; input: number; output: number; cached: number }
|
export type TokenCostEntry = { model: string; total: number; input: number; output: number; cached: number }
|
||||||
|
export type CacheRatioEntry = { model: string; ratio: number; cached: number; uncached: number; total: number }
|
||||||
export type SessionCostEntry = { model: string; cost: number; tokens: number }
|
export type SessionCostEntry = { model: string; cost: number; tokens: number }
|
||||||
export type CountryEntry = { country: string; continent: string; tokens: number; share: number; rank: number }
|
export type CountryEntry = { country: string; continent: string; tokens: number; share: number; rank: number }
|
||||||
export type StatsHomeData = {
|
export type StatsHomeData = {
|
||||||
@@ -19,6 +20,7 @@ export type StatsHomeData = {
|
|||||||
leaderboard: Record<UsageProduct, Record<UsageRange, LeaderboardEntry[]>>
|
leaderboard: Record<UsageProduct, Record<UsageRange, LeaderboardEntry[]>>
|
||||||
market: Record<UsageRange, MarketDay[]>
|
market: Record<UsageRange, MarketDay[]>
|
||||||
tokenCost: Record<TokenProduct, TokenCostEntry[]>
|
tokenCost: Record<TokenProduct, TokenCostEntry[]>
|
||||||
|
cacheRatio: Record<TokenProduct, CacheRatioEntry[]>
|
||||||
sessionCost: Record<TokenProduct, SessionCostEntry[]>
|
sessionCost: Record<TokenProduct, SessionCostEntry[]>
|
||||||
country: Record<UsageRange, CountryEntry[]>
|
country: Record<UsageRange, CountryEntry[]>
|
||||||
}
|
}
|
||||||
@@ -99,6 +101,9 @@ function buildStatsHomeData(
|
|||||||
tokenCost: createTokenProductRecord((product) =>
|
tokenCost: createTokenProductRecord((product) =>
|
||||||
buildTokenCost(normalized, product, getWindow("1W", earliest, latest)),
|
buildTokenCost(normalized, product, getWindow("1W", earliest, latest)),
|
||||||
),
|
),
|
||||||
|
cacheRatio: createTokenProductRecord((product) =>
|
||||||
|
buildCacheRatio(normalized, product, getWindow("1W", earliest, latest)),
|
||||||
|
),
|
||||||
sessionCost: createTokenProductRecord((product) =>
|
sessionCost: createTokenProductRecord((product) =>
|
||||||
buildSessionCost(normalized, product, getWindow("1W", earliest, latest)),
|
buildSessionCost(normalized, product, getWindow("1W", earliest, latest)),
|
||||||
),
|
),
|
||||||
@@ -113,6 +118,7 @@ function emptyStatsHomeData(): StatsHomeData {
|
|||||||
leaderboard: createUsageProductRecord(() => createRangeRecord(() => [])),
|
leaderboard: createUsageProductRecord(() => createRangeRecord(() => [])),
|
||||||
market: createRangeRecord(() => []),
|
market: createRangeRecord(() => []),
|
||||||
tokenCost: createTokenProductRecord(() => []),
|
tokenCost: createTokenProductRecord(() => []),
|
||||||
|
cacheRatio: createTokenProductRecord(() => []),
|
||||||
sessionCost: createTokenProductRecord(() => []),
|
sessionCost: createTokenProductRecord(() => []),
|
||||||
country: createRangeRecord(() => []),
|
country: createRangeRecord(() => []),
|
||||||
}
|
}
|
||||||
@@ -224,6 +230,25 @@ function buildTokenCost(rows: StatMetricRow[], product: TokenProduct, window: Da
|
|||||||
.slice(0, 17)
|
.slice(0, 17)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildCacheRatio(rows: StatMetricRow[], product: TokenProduct, window: DateWindow) {
|
||||||
|
return aggregateByModel(rowsForProduct(rows, product, window.start, window.end))
|
||||||
|
.flatMap((item) => {
|
||||||
|
const total = item.inputTokens + item.cacheReadTokens
|
||||||
|
if (total === 0) return []
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
model: item.model,
|
||||||
|
ratio: round((item.cacheReadTokens / total) * 100, 1),
|
||||||
|
cached: round(item.cacheReadTokens / 1_000_000_000, 1),
|
||||||
|
uncached: round(item.inputTokens / 1_000_000_000, 1),
|
||||||
|
total: round(total / 1_000_000_000, 1),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.toSorted((a, b) => b.ratio - a.ratio || b.cached - a.cached)
|
||||||
|
.slice(0, 17)
|
||||||
|
}
|
||||||
|
|
||||||
function buildSessionCost(rows: StatMetricRow[], product: TokenProduct, window: DateWindow) {
|
function buildSessionCost(rows: StatMetricRow[], product: TokenProduct, window: DateWindow) {
|
||||||
return aggregateByModel(rowsForProduct(rows, product, window.start, window.end))
|
return aggregateByModel(rowsForProduct(rows, product, window.start, window.end))
|
||||||
.flatMap((item) => {
|
.flatMap((item) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user