fix: token tracking pipeline + premium usage dashboard redesign

Backend fixes:
- Fix totalTokens defaulting to 0 when API omits totalTokenCount (now computes prompt+completion)
- Fix streaming metadata extraction: unwrap response wrapper in Anthropic/Gemini handlers
- Fix Zod schemas: handle SQL SUM(null) with nullable().transform()
- Add is_estimated column migration to token_usage table

Frontend redesign:
- Rewrite Usage page with area charts (nivo/line), horizontal bar model ranking
- StatCard: add trend indicators (% vs previous period) and inline SVG sparklines
- Premium color palette, prompt/completion ratio bar, top models mini-list
- Live indicator with 15s auto-refresh

Design system migration:
- Standardize all components on Gemini Nexus tokens (rounded-xl, border-white/[0.06])
- Refine UI components, proxy tabs, cloud account list, provider groups
This commit is contained in:
Gemini Nexus Bot committed 2026-04-29 01:43:48 -03:00
1 parent b1c76f3b69
commit a648f2a281
44 files changed
+8920 -14566

No files matched your search

+14 -9
View File
@@ -77,12 +77,15 @@ export class ProxyService {
if (!usage) {
return;
}
const prompt = usage.promptTokenCount ?? 0;
const completion = usage.candidatesTokenCount ?? 0;
const total = usage.totalTokenCount ?? (prompt + completion);
this.tokenUsageService.recordUsage({
accountId,
model,
promptTokens: usage.promptTokenCount ?? 0,
completionTokens: usage.candidatesTokenCount ?? 0,
totalTokens: usage.totalTokenCount ?? 0,
promptTokens: prompt,
completionTokens: completion,
totalTokens: total,
timestamp: Date.now(),
requestType,
isEstimated,
@@ -319,14 +322,15 @@ export class ProxyService {
if (startMsg) subscriber.next(startMsg);
}
const candidate = json.candidates?.[0];
const responsePayload = json.response ?? json;
const candidate = responsePayload.candidates?.[0];
const part = candidate?.content?.parts?.[0];
if (candidate?.finishReason) {
lastFinishReason = candidate.finishReason;
}
if (json.usageMetadata) {
lastUsageMetadata = json.usageMetadata;
if (responsePayload.usageMetadata) {
lastUsageMetadata = responsePayload.usageMetadata;
}
if (this.isGeminiPart(part)) {
@@ -605,10 +609,11 @@ export class ProxyService {
if (dataStr === '[DONE]') continue;
try {
const parsed = JSON.parse(dataStr);
if (parsed?.usageMetadata) {
lastUsageMetadata = parsed.usageMetadata;
const responsePayload = parsed?.response ?? parsed;
if (responsePayload?.usageMetadata) {
lastUsageMetadata = responsePayload.usageMetadata;
}
const text = parsed?.candidates?.[0]?.content?.parts?.[0]?.text;
const text = responsePayload?.candidates?.[0]?.content?.parts?.[0]?.text;
if (typeof text === 'string') {
accumulatedText += text;
}