feat(memory): implement stability stage
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
61
apps/web/server/api/v1/usage.get.ts
Normal file
61
apps/web/server/api/v1/usage.get.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { requireStageTwoUser, stageTwoApiError, stageTwoDatabase } from '~/server/utils/stage-two-supabase'
|
||||
|
||||
interface UsageRow {
|
||||
campaign_id: string | null
|
||||
request_kind: string
|
||||
input_tokens: number
|
||||
output_tokens: number
|
||||
cost_usd: number | string
|
||||
latency_ms: number | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const user = await requireStageTwoUser(event)
|
||||
const since = new Date()
|
||||
since.setUTCDate(since.getUTCDate() - 30)
|
||||
const today = new Date()
|
||||
today.setUTCHours(0, 0, 0, 0)
|
||||
const [usage, quotaEvents] = await Promise.all([
|
||||
stageTwoDatabase<UsageRow[]>(
|
||||
`ai_usage?select=campaign_id,request_kind,input_tokens,output_tokens,cost_usd,latency_ms,created_at&user_id=eq.${user.id}&created_at=gte.${encodeURIComponent(since.toISOString())}&order=created_at.desc&limit=1000`,
|
||||
),
|
||||
stageTwoDatabase<Array<{ created_at: string }>>(
|
||||
`ai_quota_events?select=created_at&user_id=eq.${user.id}&created_at=gte.${encodeURIComponent(today.toISOString())}`,
|
||||
),
|
||||
])
|
||||
const campaignIds = [...new Set(usage.flatMap(row => row.campaign_id ? [row.campaign_id] : []))]
|
||||
const campaigns = campaignIds.length
|
||||
? await stageTwoDatabase<Array<{ id: string; title: string }>>(`campaigns?select=id,title&id=in.(${campaignIds.join(',')})`)
|
||||
: []
|
||||
const campaignNames = new Map(campaigns.map(campaign => [campaign.id, campaign.title]))
|
||||
const summarize = (rows: UsageRow[]) => {
|
||||
const latencies = rows.flatMap(row => row.latency_ms === null ? [] : [Number(row.latency_ms)])
|
||||
return {
|
||||
requests: rows.length,
|
||||
inputTokens: rows.reduce((sum, row) => sum + Number(row.input_tokens), 0),
|
||||
outputTokens: rows.reduce((sum, row) => sum + Number(row.output_tokens), 0),
|
||||
costUsd: rows.reduce((sum, row) => sum + Number(row.cost_usd), 0),
|
||||
averageLatencyMs: latencies.length ? Math.round(latencies.reduce((sum, value) => sum + value, 0) / latencies.length) : 0,
|
||||
}
|
||||
}
|
||||
const grouped = new Map<string, UsageRow[]>()
|
||||
for (const row of usage) {
|
||||
const key = row.campaign_id ?? 'coauthor'
|
||||
grouped.set(key, [...(grouped.get(key) ?? []), row])
|
||||
}
|
||||
return {
|
||||
periodDays: 30,
|
||||
quota: { usedToday: quotaEvents.length, dailyRequestLimit: 60, dailyTokenLimit: 250000 },
|
||||
totals: summarize(usage),
|
||||
groups: [...grouped].map(([id, rows]) => ({
|
||||
id,
|
||||
label: id === 'coauthor' ? 'World coauthor' : campaignNames.get(id) ?? 'Private campaign',
|
||||
...summarize(rows),
|
||||
})).sort((left, right) => right.costUsd - left.costUsd),
|
||||
}
|
||||
} catch (error) {
|
||||
stageTwoApiError(error)
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user