AgentGraph / frontend /src /lib /trend-data-generator.ts
wu981526092's picture
🚀 Deploy AgentGraph: Complete agent monitoring and knowledge graph system
c2ea5ed
import { Trace } from "@/types";
export interface TrendDataPoint {
time: string;
value: number;
value2?: number; // Optional second value for dual-line charts
date: Date;
}
export interface TrendData {
tracesAndGraphs: TrendDataPoint[]; // Combined traces + graphs
entitiesAndRelations: TrendDataPoint[]; // Combined entities + relations
failures: TrendDataPoint[]; // New failures trend
// Legacy single values for backwards compatibility
traces: TrendDataPoint[];
agentGraphs: TrendDataPoint[];
entities: TrendDataPoint[];
relations: TrendDataPoint[];
}
function createRealTrendData(
traces: Trace[],
valueExtractor: (traces: Trace[], date: Date) => number,
valueExtractor2?: (traces: Trace[], date: Date) => number
): TrendDataPoint[] {
const data: TrendDataPoint[] = [];
const now = new Date();
// Get all upload dates from traces
const traceDates = traces
.map((trace) =>
trace.upload_timestamp ? new Date(trace.upload_timestamp) : null
)
.filter((date): date is Date => date !== null)
.sort((a, b) => a.getTime() - b.getTime());
if (traceDates.length === 0) {
// If no traces, return empty data
return [];
}
// Create data points for the last 7 days
for (let i = 6; i >= 0; i--) {
const date = new Date(now);
date.setDate(date.getDate() - i);
date.setHours(23, 59, 59, 999); // End of day for cumulative counting
const value = valueExtractor(traces, date);
const value2 = valueExtractor2 ? valueExtractor2(traces, date) : undefined;
data.push({
time: date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
}),
value: value,
value2: value2,
date: date,
});
}
return data;
}
function createCombinedRealTrendData(
traces: Trace[],
valueExtractor1: (traces: Trace[], date: Date) => number,
valueExtractor2: (traces: Trace[], date: Date) => number
): TrendDataPoint[] {
return createRealTrendData(traces, valueExtractor1, valueExtractor2);
}
// Helper function to extract failure count from traces
export function calculateFailureCount(traces: Trace[]): number {
let totalFailures = 0;
traces.forEach((trace) => {
trace.knowledge_graphs?.forEach((kg: any) => {
if (kg.graph_data) {
try {
const graphData =
typeof kg.graph_data === "string"
? JSON.parse(kg.graph_data)
: kg.graph_data;
// Count failures
if (graphData.failures && Array.isArray(graphData.failures)) {
totalFailures += graphData.failures.length;
}
} catch (error) {
console.warn("Error parsing graph_data for failure count:", error);
}
}
});
});
return totalFailures;
}
// Value extractors for different metrics - CUMULATIVE COUNTS
const getTracesUpToDate = (traces: Trace[], date: Date): number => {
return traces.filter((trace) => {
if (!trace.upload_timestamp) return false;
const uploadDate = new Date(trace.upload_timestamp);
return uploadDate <= date; // Cumulative: count all traces up to this date
}).length;
};
const getKnowledgeGraphsUpToDate = (traces: Trace[], date: Date): number => {
return traces.reduce((count, trace) => {
const finalKGs =
trace.knowledge_graphs?.filter((kg) => {
if (!kg.created_at) return false;
const creationDate = new Date(kg.created_at);
const isBeforeOrOnDate = creationDate <= date; // Cumulative
const isFinal =
kg.is_final === true ||
(kg.window_index === null && kg.window_total !== null);
return isBeforeOrOnDate && isFinal;
}) || [];
return count + finalKGs.length;
}, 0);
};
const getEntitiesUpToDate = (traces: Trace[], date: Date): number => {
return traces.reduce((count, trace) => {
const entityCount =
trace.knowledge_graphs?.reduce((kgTotal, kg) => {
if (!kg.created_at) return kgTotal;
const creationDate = new Date(kg.created_at);
const isBeforeOrOnDate = creationDate <= date; // Cumulative
return isBeforeOrOnDate ? kgTotal + (kg.entity_count || 0) : kgTotal;
}, 0) || 0;
return count + entityCount;
}, 0);
};
const getRelationsUpToDate = (traces: Trace[], date: Date): number => {
return traces.reduce((count, trace) => {
const relationCount =
trace.knowledge_graphs?.reduce((kgTotal, kg) => {
if (!kg.created_at) return kgTotal;
const creationDate = new Date(kg.created_at);
const isBeforeOrOnDate = creationDate <= date; // Cumulative
return isBeforeOrOnDate ? kgTotal + (kg.relation_count || 0) : kgTotal;
}, 0) || 0;
return count + relationCount;
}, 0);
};
const getFailuresUpToDate = (traces: Trace[], date: Date): number => {
return traces.reduce((count, trace) => {
let traceFailures = 0;
trace.knowledge_graphs?.forEach((kg) => {
if (!kg.created_at) return;
const creationDate = new Date(kg.created_at);
const isBeforeOrOnDate = creationDate <= date; // Cumulative
if (isBeforeOrOnDate && kg.graph_data) {
try {
const graphData =
typeof kg.graph_data === "string"
? JSON.parse(kg.graph_data)
: kg.graph_data;
if (graphData.failures && Array.isArray(graphData.failures)) {
traceFailures += graphData.failures.length;
}
} catch (error) {
console.warn("Error parsing graph_data for failure count:", error);
}
}
});
return count + traceFailures;
}, 0);
};
export function generateTrendData(traces: Trace[]): TrendData {
return {
// New combined data for merged cards
tracesAndGraphs: createCombinedRealTrendData(
traces,
getTracesUpToDate,
getKnowledgeGraphsUpToDate
),
entitiesAndRelations: createCombinedRealTrendData(
traces,
getEntitiesUpToDate,
getRelationsUpToDate
),
failures: createRealTrendData(traces, getFailuresUpToDate),
// Legacy single values for backwards compatibility
traces: createRealTrendData(traces, getTracesUpToDate),
agentGraphs: createRealTrendData(traces, getKnowledgeGraphsUpToDate),
entities: createRealTrendData(traces, getEntitiesUpToDate),
relations: createRealTrendData(traces, getRelationsUpToDate),
};
}