fix(stats): bypass worker runtime crash

This commit is contained in:
Adam 2026-06-21 06:48:25 -05:00
parent c6f719e153
commit f96e6aa6ed
No known key found for this signature in database
GPG Key ID: 9CB48779AF150E75
3 changed files with 128 additions and 70 deletions

View File

@ -1,19 +1,3 @@
import { AppConfig } from "@opencode-ai/stats-core/config"
import { Effect } from "effect"
import { runStatsEffect } from "../../stats-runtime"
export async function GET() {
return Response.json(
await runStatsEffect(
Effect.gen(function* () {
const config = yield* AppConfig
return {
ok: true,
app: "stats",
stage: config.stage,
publicUrl: config.publicUrl,
}
}),
),
)
return Response.json({ ok: true, app: "stats" })
}

View File

@ -1,16 +1,5 @@
import { AppConfig } from "@opencode-ai/stats-core/config"
import { layer } from "@opencode-ai/stats-core/database"
import { GeoStatRepo } from "@opencode-ai/stats-core/domain/geo"
import { ModelStatRepo } from "@opencode-ai/stats-core/domain/model"
import { ProviderStatRepo } from "@opencode-ai/stats-core/domain/provider"
import { Effect, Layer } from "effect"
import type { Success } from "effect/Layer"
import { Effect } from "effect"
const repoLayer = Layer.mergeAll(ModelStatRepo.layer, ProviderStatRepo.layer, GeoStatRepo.layer).pipe(
Layer.provide(layer),
)
const statsLayer = Layer.mergeAll(AppConfig.layer, layer, repoLayer)
export function runStatsEffect<A, E>(effect: Effect.Effect<A, E, Success<typeof statsLayer>>) {
return Effect.runPromise(Effect.provide(effect, statsLayer))
export function runStatsEffect<A, E>(effect: Effect.Effect<A, E>) {
return Effect.runPromise(effect)
}

View File

@ -1,8 +1,9 @@
import { Client } from "@planetscale/database"
import { Effect } from "effect"
import { DatabaseError } from "../database"
import { GeoStatRepo, type GeoStatMetric } from "./geo"
import { ModelStatRepo, type ModelStatMetric } from "./model"
import { ProviderStatRepo, type ProviderStatMetric } from "./provider"
import { Resource } from "sst/resource"
import type { GeoStatMetric } from "./geo"
import type { ModelStatMetric } from "./model"
import type { ProviderStatMetric } from "./provider"
export type UsageProduct = "All Users" | "Zen" | "Go" | "Enterprise"
export type TokenProduct = "Zen" | "Go" | "Enterprise"
@ -91,6 +92,14 @@ export type StatsHomeData = {
country: Record<UsageRange, CountryEntry[]>
}
export class StatsDataError extends Error {
override name = "StatsDataError"
constructor(readonly cause: unknown) {
super("Failed to load stats data")
}
}
const DAY_MS = 86_400_000
const TOKEN_SCALE = 1_000_000
const DOLLARS_PER_MICROCENT = 1 / 100_000_000
@ -130,53 +139,129 @@ type ModelAggregate = {
totalCostMicrocents: number
}
export function getStatsHomeData(): Effect.Effect<
StatsHomeData,
DatabaseError,
ModelStatRepo | ProviderStatRepo | GeoStatRepo
> {
return Effect.gen(function* () {
const modelStats = yield* ModelStatRepo
const providerStats = yield* ProviderStatRepo
const geoStats = yield* GeoStatRepo
const [modelRows, providerRows, geoRows] = yield* Effect.all(
[modelStats.listDaily(), providerStats.listDaily(), geoStats.listDaily()],
{ concurrency: "unbounded" },
)
return buildStatsHomeData(modelRows, providerRows, geoRows)
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)
},
catch: (cause) => new StatsDataError(cause),
})
}
export function getStatsModelData(
model: string,
provider?: string,
): Effect.Effect<StatsModelData | null, DatabaseError, ModelStatRepo | GeoStatRepo> {
return Effect.gen(function* () {
const modelStats = yield* ModelStatRepo
const geoStats = yield* GeoStatRepo
const modelRows = yield* modelStats.listDaily()
const normalized = modelRows.flatMap(normalizeStatRow)
const resolvedModel = resolveModelName(model, normalized, provider)
if (!resolvedModel) return null
return buildStatsModelData(
resolvedModel,
modelRows,
yield* geoStats.listDaily({
model: resolvedModel,
provider: resolveModelProvider(resolvedModel, normalized, provider),
}),
provider,
)
): Effect.Effect<StatsModelData | null, StatsDataError> {
return Effect.tryPromise({
try: async () => {
const modelRows = await listModelDaily()
const normalized = modelRows.flatMap(normalizeStatRow)
const resolvedModel = resolveModelName(model, normalized, provider)
if (!resolvedModel) return null
return buildStatsModelData(
resolvedModel,
modelRows,
await listGeoDaily({
model: resolvedModel,
provider: resolveModelProvider(resolvedModel, normalized, provider),
}),
provider,
)
},
catch: (cause) => new StatsDataError(cause),
})
}
export function getStatsLabData(provider: string): Effect.Effect<StatsLabData | null, DatabaseError, ModelStatRepo> {
return Effect.gen(function* () {
const modelStats = yield* ModelStatRepo
return buildStatsLabData(provider, yield* modelStats.listDaily())
export function getStatsLabData(provider: string): Effect.Effect<StatsLabData | null, StatsDataError> {
return Effect.tryPromise({
try: async () => buildStatsLabData(provider, await listModelDaily()),
catch: (cause) => new StatsDataError(cause),
})
}
async function listModelDaily(): Promise<ModelStatMetric[]> {
return (await queryRows(`select period_key, updated_at, tier, provider, model, sessions, unique_users, input_tokens,
output_tokens, reasoning_tokens, cache_read_tokens, total_tokens, input_cost_microcents, output_cost_microcents,
total_cost_microcents from model_stat where grain = 'day' and client = 'all' and source = 'all'
and tier in ('Go', 'go') order by period_key`)).map((row) => ({
periodKey: stringValue(row.period_key),
updatedAt: dateValue(row.updated_at),
tier: stringValue(row.tier),
provider: stringValue(row.provider),
model: stringValue(row.model),
sessions: numberValue(row.sessions),
uniqueUsers: numberValue(row.unique_users),
inputTokens: numberValue(row.input_tokens),
outputTokens: numberValue(row.output_tokens),
reasoningTokens: numberValue(row.reasoning_tokens),
cacheReadTokens: numberValue(row.cache_read_tokens),
totalTokens: numberValue(row.total_tokens),
inputCostMicrocents: numberValue(row.input_cost_microcents),
outputCostMicrocents: numberValue(row.output_cost_microcents),
totalCostMicrocents: numberValue(row.total_cost_microcents),
}))
}
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 ('Go', 'go') order by period_key`)).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
? "and provider = ? and model = ?"
: opts?.model
? "and model = ?"
: "and provider = 'all' and model = 'all'"
const params = opts?.model && opts.provider ? [opts.provider, opts.model] : opts?.model ? [opts.model] : []
return (await queryRows(
`select period_key, updated_at, tier, provider, model, country, continent, total_tokens from geo_stat
where grain = 'day' and client = 'all' and source = 'all' and tier in ('Go', 'go') ${scope} order by period_key`,
params,
)).map((row) => ({
periodKey: stringValue(row.period_key),
updatedAt: dateValue(row.updated_at),
tier: stringValue(row.tier),
provider: stringValue(row.provider),
model: stringValue(row.model),
country: stringValue(row.country),
continent: stringValue(row.continent),
totalTokens: numberValue(row.total_tokens),
}))
}
async function queryRows(query: string, params: string[] = []) {
return (await new Client({ url: databaseUrl() }).execute(query, params)).rows as RawRow[]
}
function databaseUrl() {
return process.env.DATABASE_URL ?? Resource.StatsDatabase.url
}
function stringValue(value: unknown) {
return value == null ? "" : String(value)
}
function numberValue(value: unknown) {
return Number(value ?? 0)
}
function dateValue(value: unknown) {
return value instanceof Date ? value : new Date(stringValue(value))
}
function buildStatsHomeData(
modelRows: ModelStatMetric[],
providerRows: ProviderStatMetric[],