/** * Telemetry API Client * Handles telemetry data fetching for the usage dashboard. */ import { apiGet, type ApiResponse } from "./client"; // ─── Types ─────────────────────────────────────────────────────────── export type TimeRange = "7d" | "30d" | "90d"; export interface UsageSummary { totalTokens: number; totalCost: number; taskCount: number; avgQualityGatePassRate: number; } export interface TokenUsagePoint { date: string; inputTokens: number; outputTokens: number; totalTokens: number; } export interface CostBreakdownItem { model: string; provider: string; cost: number; taskCount: number; } export interface TaskOutcomeItem { outcome: string; count: number; color: string; } export interface EstimateParams { taskType: string; model: string; provider: string; complexity: string; } export interface EstimateResponse { prediction: { input_tokens: { median: number; p75: number; p90: number }; output_tokens: { median: number; p75: number; p90: number }; cost_usd_micros: Record; quality: { gate_pass_rate: number; success_rate: number }; } | null; metadata: { sample_size: number; confidence: "none" | "low" | "medium" | "high"; }; } interface ProviderUsageAnalyticsItem { provider: string; calls: number; promptTokens: number; completionTokens: number; totalTokens: number; costCents: number; averageDurationMs: number; } interface ModelUsageAnalyticsItem { model: string; calls: number; promptTokens: number; completionTokens: number; totalTokens: number; costCents: number; averageDurationMs: number; } interface TaskTypeUsageAnalyticsItem { taskType: string; calls: number; promptTokens: number; completionTokens: number; totalTokens: number; costCents: number; averageDurationMs: number; } interface UsageAnalyticsResponse { totalCalls: number; totalPromptTokens: number; totalCompletionTokens: number; totalTokens: number; totalCostCents: number; averageDurationMs: number; byProvider: ProviderUsageAnalyticsItem[]; byModel: ModelUsageAnalyticsItem[]; byTaskType: TaskTypeUsageAnalyticsItem[]; } const TASK_OUTCOME_COLORS = ["#6EBF8B", "#F5C862", "#94A3B8", "#C4A5DE", "#7AA2F7"]; const DAYS_BY_RANGE: Record = { "7d": 7, "30d": 30, "90d": 90, }; const analyticsRequestCache = new Map>(); function buildAnalyticsEndpoint(timeRange: TimeRange): string { const endDate = new Date(); const startDate = new Date(endDate); startDate.setDate(startDate.getDate() - (DAYS_BY_RANGE[timeRange] - 1)); startDate.setHours(0, 0, 0, 0); const query = new URLSearchParams({ startDate: startDate.toISOString(), endDate: endDate.toISOString(), }).toString(); return `/api/llm-usage/analytics?${query}`; } async function fetchUsageAnalytics(timeRange: TimeRange): Promise { const cachedRequest = analyticsRequestCache.get(timeRange); if (cachedRequest) { return cachedRequest; } const request = apiGet>(buildAnalyticsEndpoint(timeRange)) .then((response) => response.data) .finally(() => { analyticsRequestCache.delete(timeRange); }); analyticsRequestCache.set(timeRange, request); return request; } // ─── API Functions ─────────────────────────────────────────────────── /** * Fetch usage summary data (total tokens, cost, task count, quality rate) */ export async function fetchUsageSummary(timeRange: TimeRange): Promise { const analytics = await fetchUsageAnalytics(timeRange); return { totalTokens: analytics.totalTokens, totalCost: analytics.totalCostCents / 100, taskCount: analytics.totalCalls, avgQualityGatePassRate: 0, }; } /** * Fetch token usage time series for charts */ export function fetchTokenUsage(timeRange: TimeRange): Promise { void timeRange; return Promise.resolve([]); } /** * Fetch cost breakdown by model */ export async function fetchCostBreakdown(timeRange: TimeRange): Promise { const analytics = await fetchUsageAnalytics(timeRange); return analytics.byModel .filter((item) => item.calls > 0) .sort((a, b) => b.costCents - a.costCents) .map((item) => ({ model: item.model, provider: "unknown", cost: item.costCents / 100, taskCount: item.calls, })); } /** * Fetch task outcome distribution */ export async function fetchTaskOutcomes(timeRange: TimeRange): Promise { const analytics = await fetchUsageAnalytics(timeRange); return analytics.byTaskType .filter((item) => item.calls > 0) .map((item, index) => ({ outcome: item.taskType, count: item.calls, color: TASK_OUTCOME_COLORS[index % TASK_OUTCOME_COLORS.length] ?? "#94A3B8", })); } /** * Fetch cost/token estimate for a given task configuration. * Uses the real GET /api/telemetry/estimate endpoint from TEL-006. */ export async function fetchEstimate(params: EstimateParams): Promise { const query = new URLSearchParams({ taskType: params.taskType, model: params.model, provider: params.provider, complexity: params.complexity, }).toString(); const response = await apiGet>(`/api/telemetry/estimate?${query}`); return response.data; }