import React, { useState, useEffect } from "react"; import { BarChart, Card, Title, Text, Grid, Col, DateRangePicker, DateRangePickerValue, Table, TableHead, TableRow, TableHeaderCell, TableBody, TableCell, DonutChart, TabPanel, TabGroup, TabList, Tab, TabPanels } from "@tremor/react"; import { Select } from 'antd'; import { ActivityMetrics, processActivityData } from './activity_metrics'; import { DailyData, KeyMetricWithMetadata, EntityMetricWithMetadata } from './usage/types'; import { tagDailyActivityCall, teamDailyActivityCall } from './networking'; import TopKeyView from "./top_key_view"; interface EntityMetrics { metrics: { spend: number; prompt_tokens: number; completion_tokens: number; cache_read_input_tokens: number; cache_creation_input_tokens: number; total_tokens: number; successful_requests: number; failed_requests: number; api_requests: number; }; metadata: Record; } interface BreakdownMetrics { models: Record; providers: Record; api_keys: Record; entities: Record; } interface ExtendedDailyData extends DailyData { breakdown: BreakdownMetrics; } interface EntitySpendData { results: ExtendedDailyData[]; metadata: { total_spend: number; total_api_requests: number; total_successful_requests: number; total_failed_requests: number; total_tokens: number; }; } export interface EntityList { label: string; value: string; } interface EntityUsageProps { accessToken: string | null; entityType: 'tag' | 'team'; entityId?: string | null; userID: string | null; userRole: string | null; entityList: EntityList[] | null; } const EntityUsage: React.FC = ({ accessToken, entityType, entityId, userID, userRole, entityList }) => { const [spendData, setSpendData] = useState({ results: [], metadata: { total_spend: 0, total_api_requests: 0, total_successful_requests: 0, total_failed_requests: 0, total_tokens: 0 } }); const modelMetrics = processActivityData(spendData, "models"); const keyMetrics = processActivityData(spendData, "api_keys"); const [selectedTags, setSelectedTags] = useState([]); const [dateValue, setDateValue] = useState({ from: new Date(Date.now() - 28 * 24 * 60 * 60 * 1000), to: new Date(), }); const fetchSpendData = async () => { if (!accessToken || !dateValue.from || !dateValue.to) return; const startTime = dateValue.from; const endTime = dateValue.to; if (entityType === 'tag') { const data = await tagDailyActivityCall( accessToken, startTime, endTime, 1, selectedTags.length > 0 ? selectedTags : null ); setSpendData(data); } else if (entityType === 'team') { const data = await teamDailyActivityCall( accessToken, startTime, endTime, 1, selectedTags.length > 0 ? selectedTags : null ); setSpendData(data); } else { throw new Error("Invalid entity type"); } }; useEffect(() => { fetchSpendData(); }, [accessToken, dateValue, entityId, selectedTags]); const getTopModels = () => { const modelSpend: { [key: string]: any } = {}; spendData.results.forEach(day => { Object.entries(day.breakdown.models || {}).forEach(([model, metrics]) => { if (!modelSpend[model]) { modelSpend[model] = { spend: 0, requests: 0, successful_requests: 0, failed_requests: 0, tokens: 0 }; } try { modelSpend[model].spend += metrics.metrics.spend; } catch (e) { console.log(`Error adding spend for ${model}: ${e}, got metrics: ${JSON.stringify(metrics)}`); } modelSpend[model].requests += metrics.metrics.api_requests; modelSpend[model].successful_requests += metrics.metrics.successful_requests; modelSpend[model].failed_requests += metrics.metrics.failed_requests; modelSpend[model].tokens += metrics.metrics.total_tokens; }); }); return Object.entries(modelSpend) .map(([model, metrics]) => ({ key: model, ...metrics })) .sort((a, b) => b.spend - a.spend) .slice(0, 5); }; const getTopAPIKeys = () => { const keySpend: { [key: string]: KeyMetricWithMetadata } = {}; spendData.results.forEach(day => { Object.entries(day.breakdown.api_keys || {}).forEach(([key, metrics]) => { if (!keySpend[key]) { keySpend[key] = { metrics: { spend: 0, prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, api_requests: 0, successful_requests: 0, failed_requests: 0, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 }, metadata: { key_alias: metrics.metadata.key_alias } }; } keySpend[key].metrics.spend += metrics.metrics.spend; keySpend[key].metrics.prompt_tokens += metrics.metrics.prompt_tokens; keySpend[key].metrics.completion_tokens += metrics.metrics.completion_tokens; keySpend[key].metrics.total_tokens += metrics.metrics.total_tokens; keySpend[key].metrics.api_requests += metrics.metrics.api_requests; keySpend[key].metrics.successful_requests += metrics.metrics.successful_requests; keySpend[key].metrics.failed_requests += metrics.metrics.failed_requests; keySpend[key].metrics.cache_read_input_tokens += metrics.metrics.cache_read_input_tokens || 0; keySpend[key].metrics.cache_creation_input_tokens += metrics.metrics.cache_creation_input_tokens || 0; }); }); return Object.entries(keySpend) .map(([api_key, metrics]) => ({ api_key, key_alias: metrics.metadata.key_alias || "-", // Using truncated key as alias spend: metrics.metrics.spend, })) .sort((a, b) => b.spend - a.spend) .slice(0, 5); }; const getProviderSpend = () => { const providerSpend: { [key: string]: any } = {}; spendData.results.forEach(day => { Object.entries(day.breakdown.providers || {}).forEach(([provider, metrics]) => { if (!providerSpend[provider]) { providerSpend[provider] = { provider, spend: 0, requests: 0, successful_requests: 0, failed_requests: 0, tokens: 0 }; } try { providerSpend[provider].spend += metrics.metrics.spend; providerSpend[provider].requests += metrics.metrics.api_requests; providerSpend[provider].successful_requests += metrics.metrics.successful_requests; providerSpend[provider].failed_requests += metrics.metrics.failed_requests; providerSpend[provider].tokens += metrics.metrics.total_tokens; } catch (e) { console.log(`Error processing provider ${provider}: ${e}`); } }); }); return Object.values(providerSpend) .filter(provider => provider.spend > 0) .sort((a, b) => b.spend - a.spend); }; const getAllTags = () => { if (entityList) { return entityList; } }; const filterDataByTags = (data: EntityMetricWithMetadata[]) => { if (selectedTags.length === 0) return data; return data.filter(item => selectedTags.includes(item.metadata.id)); }; const getEntityBreakdown = () => { const entitySpend: { [key: string]: EntityMetricWithMetadata } = {}; spendData.results.forEach(day => { Object.entries(day.breakdown.entities || {}).forEach(([entity, data]) => { if (!entitySpend[entity]) { entitySpend[entity] = { metrics: { spend: 0, prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, api_requests: 0, successful_requests: 0, failed_requests: 0, cache_read_input_tokens: 0, cache_creation_input_tokens: 0 }, metadata: { alias: data.metadata.team_alias || entity, id: entity } }; } entitySpend[entity].metrics.spend += data.metrics.spend; entitySpend[entity].metrics.api_requests += data.metrics.api_requests; entitySpend[entity].metrics.successful_requests += data.metrics.successful_requests; entitySpend[entity].metrics.failed_requests += data.metrics.failed_requests; entitySpend[entity].metrics.total_tokens += data.metrics.total_tokens; }); }); const result = Object.values(entitySpend) .sort((a, b) => b.metrics.spend - a.metrics.spend); return filterDataByTags(result); }; return (
Select Time Range {entityList && entityList.length > 0 && ( Filter by {entityType === 'tag' ? 'Tags' : 'Teams'}