fix(stats): refine lab page tooltips
This commit is contained in:
parent
5668d36477
commit
beb586a383
3 changed files with 39 additions and 8 deletions
|
|
@ -469,6 +469,12 @@ function LabUsageSection(props: { lab: ModelCatalogLab; data: StatsLabData | nul
|
||||||
</span>
|
</span>
|
||||||
<b>{formatTokens(active.point.tokens)}</b>
|
<b>{formatTokens(active.point.tokens)}</b>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<span data-slot="tooltip-label">
|
||||||
|
<i data-kind="users" /> {i18n.t("model.uniqueUsers")}
|
||||||
|
</span>
|
||||||
|
<b>{formatUsers(active.point.users)}</b>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|
@ -759,7 +765,7 @@ function relatedLabStats(labs: ModelCatalogLab[], market: MarketDay[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function labRelatedDescription(lab: ModelCatalogLab) {
|
function labRelatedDescription(lab: ModelCatalogLab) {
|
||||||
return lab.description ?? `OpenCode Go usage across ${lab.name} models, recent token volume, and model limits.`
|
return lab.description ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function relatedTone(share: number) {
|
function relatedTone(share: number) {
|
||||||
|
|
|
||||||
|
|
@ -4218,7 +4218,7 @@
|
||||||
|
|
||||||
[data-page="stats"] [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"] {
|
[data-page="stats"] [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"] {
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
top: clamp(12px, calc(var(--lab-usage-user-y) * 1% - 38px), calc(100% - 92px));
|
top: clamp(12px, calc(var(--lab-usage-user-y) * 1% - 46px), calc(100% - 108px));
|
||||||
width: 192px;
|
width: 192px;
|
||||||
min-width: 192px;
|
min-width: 192px;
|
||||||
background: var(--stats-bg);
|
background: var(--stats-bg);
|
||||||
|
|
@ -4240,7 +4240,7 @@
|
||||||
|
|
||||||
[data-page="stats"] [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"] p {
|
[data-page="stats"] [data-component="model-usage-chart"][data-variant="lab-usage"] [data-component="chart-tooltip"] p {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
margin: 8px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-page="stats"]
|
[data-page="stats"]
|
||||||
|
|
@ -4250,6 +4250,13 @@
|
||||||
background: var(--lab-usage-active);
|
background: var(--lab-usage-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-page="stats"]
|
||||||
|
[data-component="model-usage-chart"][data-variant="lab-usage"]
|
||||||
|
[data-component="chart-tooltip"]
|
||||||
|
i[data-kind="users"] {
|
||||||
|
background: var(--lab-usage-line);
|
||||||
|
}
|
||||||
|
|
||||||
[data-page="stats"] [data-component="model-peer-list"] {
|
[data-page="stats"] [data-component="model-peer-list"] {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { query } from "@solidjs/router"
|
||||||
|
|
||||||
export const modelCatalogSourceUrl = "https://models.dev/catalog.json"
|
export const modelCatalogSourceUrl = "https://models.dev/catalog.json"
|
||||||
export const modelCatalogPricingUrl = "https://models.dev/api.json"
|
export const modelCatalogPricingUrl = "https://models.dev/api.json"
|
||||||
|
export const modelCatalogLabSourceUrl = "https://models.dev/labs"
|
||||||
|
|
||||||
export type ModelCatalogCost = {
|
export type ModelCatalogCost = {
|
||||||
input: number
|
input: number
|
||||||
|
|
@ -57,11 +58,12 @@ export type ModelCatalog = {
|
||||||
|
|
||||||
export const getModelCatalog = query(async () => {
|
export const getModelCatalog = query(async () => {
|
||||||
"use server"
|
"use server"
|
||||||
const [models, pricing] = await Promise.all([
|
const [models, pricing, labs] = await Promise.all([
|
||||||
fetchCatalogPayload(modelCatalogSourceUrl),
|
fetchCatalogPayload(modelCatalogSourceUrl),
|
||||||
fetchCatalogPayload(modelCatalogPricingUrl),
|
fetchCatalogPayload(modelCatalogPricingUrl),
|
||||||
|
fetchLabCatalogPayload(modelCatalogLabSourceUrl),
|
||||||
])
|
])
|
||||||
return buildModelCatalog(models, pricing)
|
return buildModelCatalog(models, pricing, labs)
|
||||||
}, "getModelCatalog")
|
}, "getModelCatalog")
|
||||||
|
|
||||||
export function findModelCatalogEntry(catalog: ModelCatalog, model: string, lab?: string) {
|
export function findModelCatalogEntry(catalog: ModelCatalog, model: string, lab?: string) {
|
||||||
|
|
@ -113,9 +115,9 @@ export function catalogSlug(value: string) {
|
||||||
.replace(/-{2,}/g, "-")
|
.replace(/-{2,}/g, "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildModelCatalog(payload: unknown, pricingPayload?: unknown): ModelCatalog {
|
function buildModelCatalog(payload: unknown, pricingPayload?: unknown, labPayload?: unknown): ModelCatalog {
|
||||||
const costs = readCatalogCosts(pricingPayload)
|
const costs = readCatalogCosts(pricingPayload)
|
||||||
const labDescriptions = readCatalogLabDescriptions(payload, pricingPayload)
|
const labDescriptions = readCatalogLabDescriptions(payload, pricingPayload, labPayload)
|
||||||
const models = readCatalogModels(payload)
|
const models = readCatalogModels(payload)
|
||||||
.flatMap(readModelCatalogEntry)
|
.flatMap(readModelCatalogEntry)
|
||||||
.map((model) => ({
|
.map((model) => ({
|
||||||
|
|
@ -191,7 +193,8 @@ function readCatalogLabDescriptions(...payloads: unknown[]) {
|
||||||
if (!description) return
|
if (!description) return
|
||||||
const id = stringValue(value.id) ?? fallbackId
|
const id = stringValue(value.id) ?? fallbackId
|
||||||
const name = stringValue(value.name)
|
const name = stringValue(value.name)
|
||||||
const keys = [id, name]
|
const title = stringValue(value.title)
|
||||||
|
const keys = [id, name, title]
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
if (!key) return
|
if (!key) return
|
||||||
descriptions.set(catalogLabSlug(key), description)
|
descriptions.set(catalogLabSlug(key), description)
|
||||||
|
|
@ -226,6 +229,21 @@ async function fetchCatalogPayload(url: string) {
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchLabCatalogPayload(url: string) {
|
||||||
|
return fetch(url)
|
||||||
|
.then((response) => (response.ok ? response.text() : Promise.resolve("")))
|
||||||
|
.then(readLabSearchIndex)
|
||||||
|
.catch(() => undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
function readLabSearchIndex(html: string) {
|
||||||
|
const match = /<script[^>]*id=["']search-index["'][^>]*>([\s\S]*?)<\/script>/.exec(html)
|
||||||
|
if (!match) return undefined
|
||||||
|
const parsed = JSON.parse(match[1]) as unknown
|
||||||
|
if (!Array.isArray(parsed)) return undefined
|
||||||
|
return parsed.filter((item) => isRecord(item) && item.type === "lab")
|
||||||
|
}
|
||||||
|
|
||||||
function readCatalogCosts(payload: unknown) {
|
function readCatalogCosts(payload: unknown) {
|
||||||
const costs = new Map<string, ModelCatalogCost>()
|
const costs = new Map<string, ModelCatalogCost>()
|
||||||
const add = (model: unknown, provider?: string) => {
|
const add = (model: unknown, provider?: string) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue