first commit
This commit is contained in:
212
ui/app/workspace/dashboard/utils/exportUtils.ts
Normal file
212
ui/app/workspace/dashboard/utils/exportUtils.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Dashboard-specific CSV data converters.
|
||||
*
|
||||
* Each function takes the dashboard data and returns { headers, rows } ready
|
||||
* for `buildCSV()`.
|
||||
*/
|
||||
|
||||
import type {
|
||||
CostHistogramResponse,
|
||||
LatencyHistogramResponse,
|
||||
LogsHistogramResponse,
|
||||
MCPCostHistogramResponse,
|
||||
MCPHistogramResponse,
|
||||
MCPTopToolsResponse,
|
||||
ModelHistogramResponse,
|
||||
ModelRankingsResponse,
|
||||
ProviderCostHistogramResponse,
|
||||
ProviderLatencyHistogramResponse,
|
||||
ProviderTokenHistogramResponse,
|
||||
TokenHistogramResponse,
|
||||
} from "@/lib/types/logs";
|
||||
|
||||
type CSVData = { headers: string[]; rows: unknown[][] };
|
||||
|
||||
export function overviewVolumeToCSV(data: LogsHistogramResponse | null): CSVData {
|
||||
const headers = ["Timestamp", "Total Requests", "Success", "Error"];
|
||||
const rows = (data?.buckets ?? []).map((b) => [b.timestamp, b.count, b.success, b.error]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function overviewTokensToCSV(data: TokenHistogramResponse | null): CSVData {
|
||||
const headers = ["Timestamp", "Prompt Tokens", "Completion Tokens", "Total Tokens", "Cached Read Tokens"];
|
||||
const rows = (data?.buckets ?? []).map((b) => [b.timestamp, b.prompt_tokens, b.completion_tokens, b.total_tokens, b.cached_read_tokens]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function overviewCostToCSV(data: CostHistogramResponse | null): CSVData {
|
||||
const models = data?.models ?? [];
|
||||
const headers = ["Timestamp", "Total Cost", ...models];
|
||||
const rows = (data?.buckets ?? []).map((b) => [b.timestamp, b.total_cost, ...models.map((m) => b.by_model?.[m] ?? 0)]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function overviewModelUsageToCSV(data: ModelHistogramResponse | null): CSVData {
|
||||
const models = data?.models ?? [];
|
||||
const modelHeaders = models.flatMap((m) => [`${m} Total`, `${m} Success`, `${m} Error`]);
|
||||
const headers = ["Timestamp", ...modelHeaders];
|
||||
const rows = (data?.buckets ?? []).map((b) => [
|
||||
b.timestamp,
|
||||
...models.flatMap((m) => {
|
||||
const stats = b.by_model?.[m];
|
||||
return [stats?.total ?? 0, stats?.success ?? 0, stats?.error ?? 0];
|
||||
}),
|
||||
]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function overviewLatencyToCSV(data: LatencyHistogramResponse | null): CSVData {
|
||||
const headers = ["Timestamp", "Avg Latency (ms)", "P90 (ms)", "P95 (ms)", "P99 (ms)", "Total Requests"];
|
||||
const rows = (data?.buckets ?? []).map((b) => [
|
||||
b.timestamp,
|
||||
b.avg_latency,
|
||||
b.p90_latency,
|
||||
b.p95_latency,
|
||||
b.p99_latency,
|
||||
b.total_requests,
|
||||
]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function providerCostToCSV(data: ProviderCostHistogramResponse | null): CSVData {
|
||||
const providers = data?.providers ?? [];
|
||||
const headers = ["Timestamp", "Total Cost", ...providers];
|
||||
const rows = (data?.buckets ?? []).map((b) => [b.timestamp, b.total_cost, ...providers.map((p) => b.by_provider?.[p] ?? 0)]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function providerTokensToCSV(data: ProviderTokenHistogramResponse | null): CSVData {
|
||||
const providers = data?.providers ?? [];
|
||||
const provHeaders = providers.flatMap((p) => [`${p} Prompt`, `${p} Completion`, `${p} Total`]);
|
||||
const headers = ["Timestamp", ...provHeaders];
|
||||
const rows = (data?.buckets ?? []).map((b) => [
|
||||
b.timestamp,
|
||||
...providers.flatMap((p) => {
|
||||
const stats = b.by_provider?.[p];
|
||||
return [stats?.prompt_tokens ?? 0, stats?.completion_tokens ?? 0, stats?.total_tokens ?? 0];
|
||||
}),
|
||||
]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function providerLatencyToCSV(data: ProviderLatencyHistogramResponse | null): CSVData {
|
||||
const providers = data?.providers ?? [];
|
||||
const provHeaders = providers.flatMap((p) => [`${p} Avg (ms)`, `${p} P90 (ms)`, `${p} P95 (ms)`, `${p} P99 (ms)`]);
|
||||
const headers = ["Timestamp", ...provHeaders];
|
||||
const rows = (data?.buckets ?? []).map((b) => [
|
||||
b.timestamp,
|
||||
...providers.flatMap((p) => {
|
||||
const stats = b.by_provider?.[p];
|
||||
return [stats?.avg_latency ?? 0, stats?.p90_latency ?? 0, stats?.p95_latency ?? 0, stats?.p99_latency ?? 0];
|
||||
}),
|
||||
]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function modelRankingsToCSV(data: ModelRankingsResponse | null): CSVData {
|
||||
const headers = [
|
||||
"Model",
|
||||
"Provider",
|
||||
"Total Requests",
|
||||
"Success Count",
|
||||
"Success Rate (%)",
|
||||
"Total Tokens",
|
||||
"Total Cost ($)",
|
||||
"Avg Latency (ms)",
|
||||
"Requests Trend (%)",
|
||||
"Tokens Trend (%)",
|
||||
"Cost Trend (%)",
|
||||
"Latency Trend (%)",
|
||||
];
|
||||
const rows = (data?.rankings ?? []).map((r) => [
|
||||
r.model,
|
||||
r.provider,
|
||||
r.total_requests,
|
||||
r.success_count,
|
||||
r.success_rate,
|
||||
r.total_tokens,
|
||||
r.total_cost,
|
||||
r.avg_latency,
|
||||
r.trend.has_previous_period ? r.trend.requests_trend : "N/A",
|
||||
r.trend.has_previous_period ? r.trend.tokens_trend : "N/A",
|
||||
r.trend.has_previous_period ? r.trend.cost_trend : "N/A",
|
||||
r.trend.has_previous_period ? r.trend.latency_trend : "N/A",
|
||||
]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function mcpVolumeToCSV(data: MCPHistogramResponse | null): CSVData {
|
||||
const headers = ["Timestamp", "Total Executions", "Success", "Error"];
|
||||
const rows = (data?.buckets ?? []).map((b) => [b.timestamp, b.count, b.success, b.error]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function mcpCostToCSV(data: MCPCostHistogramResponse | null): CSVData {
|
||||
const headers = ["Timestamp", "Total Cost ($)"];
|
||||
const rows = (data?.buckets ?? []).map((b) => [b.timestamp, b.total_cost]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export function mcpTopToolsToCSV(data: MCPTopToolsResponse | null): CSVData {
|
||||
const headers = ["Tool Name", "Execution Count", "Cost ($)"];
|
||||
const rows = (data?.tools ?? []).map((t) => [t.tool_name, t.count, t.cost]);
|
||||
return { headers, rows };
|
||||
}
|
||||
|
||||
export interface DashboardData {
|
||||
// Overview
|
||||
histogramData: LogsHistogramResponse | null;
|
||||
tokenData: TokenHistogramResponse | null;
|
||||
costData: CostHistogramResponse | null;
|
||||
modelData: ModelHistogramResponse | null;
|
||||
latencyData: LatencyHistogramResponse | null;
|
||||
// Provider Usage
|
||||
providerCostData: ProviderCostHistogramResponse | null;
|
||||
providerTokenData: ProviderTokenHistogramResponse | null;
|
||||
providerLatencyData: ProviderLatencyHistogramResponse | null;
|
||||
// Rankings
|
||||
rankingsData: ModelRankingsResponse | null;
|
||||
// MCP
|
||||
mcpHistogramData: MCPHistogramResponse | null;
|
||||
mcpCostData: MCPCostHistogramResponse | null;
|
||||
mcpTopToolsData: MCPTopToolsResponse | null;
|
||||
}
|
||||
|
||||
export type ExportTab = "all" | "overview" | "provider-usage" | "rankings" | "mcp";
|
||||
|
||||
/** Return all CSV sections for the selected scope. Each entry becomes its own sheet / file section. */
|
||||
export function getCSVSections(data: DashboardData, tab: ExportTab): { name: string; csv: CSVData }[] {
|
||||
const sections: { name: string; csv: CSVData }[] = [];
|
||||
|
||||
if (tab === "all" || tab === "overview") {
|
||||
sections.push(
|
||||
{ name: "overview-volume", csv: overviewVolumeToCSV(data.histogramData) },
|
||||
{ name: "overview-tokens", csv: overviewTokensToCSV(data.tokenData) },
|
||||
{ name: "overview-cost", csv: overviewCostToCSV(data.costData) },
|
||||
{ name: "overview-model-usage", csv: overviewModelUsageToCSV(data.modelData) },
|
||||
{ name: "overview-latency", csv: overviewLatencyToCSV(data.latencyData) },
|
||||
);
|
||||
}
|
||||
|
||||
if (tab === "all" || tab === "provider-usage") {
|
||||
sections.push(
|
||||
{ name: "provider-cost", csv: providerCostToCSV(data.providerCostData) },
|
||||
{ name: "provider-tokens", csv: providerTokensToCSV(data.providerTokenData) },
|
||||
{ name: "provider-latency", csv: providerLatencyToCSV(data.providerLatencyData) },
|
||||
);
|
||||
}
|
||||
|
||||
if (tab === "all" || tab === "rankings") {
|
||||
sections.push({ name: "model-rankings", csv: modelRankingsToCSV(data.rankingsData) });
|
||||
}
|
||||
|
||||
if (tab === "all" || tab === "mcp") {
|
||||
sections.push(
|
||||
{ name: "mcp-volume", csv: mcpVolumeToCSV(data.mcpHistogramData) },
|
||||
{ name: "mcp-cost", csv: mcpCostToCSV(data.mcpCostData) },
|
||||
{ name: "mcp-top-tools", csv: mcpTopToolsToCSV(data.mcpTopToolsData) },
|
||||
);
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
Reference in New Issue
Block a user