AgentGraph / frontend /src /lib /visualization-utils.ts
wu981526092's picture
🚀 Deploy AgentGraph: Complete agent monitoring and knowledge graph system
c2ea5ed
// Color schemes for different visualization types
export const VISUALIZATION_COLORS = {
// Entity type colors
entityTypes: {
person: "#3b82f6",
organization: "#10b981",
location: "#f59e0b",
concept: "#8b5cf6",
event: "#ef4444",
default: "#6b7280",
},
// Processing method colors
processingMethods: {
production: "#059669",
baseline: "#dc2626",
hybrid: "#7c3aed",
sequential: "#ea580c",
default: "#6b7280",
},
// Chart color palettes
sequential: [
"#f7fafc",
"#edf2f7",
"#e2e8f0",
"#cbd5e0",
"#a0aec0",
"#718096",
"#4a5568",
"#2d3748",
"#1a202c",
],
categorical: [
"#3b82f6",
"#10b981",
"#f59e0b",
"#8b5cf6",
"#ef4444",
"#06b6d4",
"#84cc16",
"#f97316",
"#ec4899",
"#6366f1",
],
diverging: [
"#dc2626",
"#ef4444",
"#f87171",
"#fca5a5",
"#fed7d7",
"#f3f4f6",
"#d1fae5",
"#86efac",
"#34d399",
"#10b981",
],
};
// Text processing utilities
export function removeStopwords(
text: string,
customStopwords: string[] = []
): string {
const defaultStopwords = [
"the",
"be",
"to",
"of",
"and",
"a",
"in",
"that",
"have",
"i",
"it",
"for",
"not",
"on",
"with",
"he",
"as",
"you",
"do",
"at",
"this",
"but",
"his",
"by",
"from",
"they",
"we",
"say",
"her",
"she",
"or",
"an",
"will",
"my",
"one",
"all",
"would",
"there",
"their",
"what",
"so",
"up",
"out",
"if",
"about",
"who",
"get",
"which",
"go",
"me",
"when",
"make",
"can",
"like",
"time",
"no",
"just",
"him",
"know",
"take",
"people",
"into",
"year",
"your",
"good",
"some",
"could",
"them",
"see",
"other",
"than",
"then",
"now",
"look",
"only",
"come",
"its",
"over",
"think",
"also",
"back",
"after",
"use",
"two",
"how",
"our",
"work",
"first",
"well",
"way",
"even",
"new",
"want",
"because",
"any",
"these",
"give",
"day",
"most",
"us",
"is",
"was",
"are",
"been",
"has",
"had",
"were",
"said",
"each",
];
const allStopwords = new Set([...defaultStopwords, ...customStopwords]);
return text
.toLowerCase()
.replace(/[^\w\s]/g, " ")
.split(/\s+/)
.filter((word) => word.length > 2 && !allStopwords.has(word))
.join(" ");
}
export function extractKeywords(
text: string,
maxKeywords: number = 20
): string[] {
const processedText = removeStopwords(text);
const words = processedText.split(/\s+/);
// Calculate word frequency
const frequency: Record<string, number> = {};
words.forEach((word) => {
frequency[word] = (frequency[word] || 0) + 1;
});
// Return top keywords by frequency
return Object.entries(frequency)
.sort(([, a], [, b]) => b - a)
.slice(0, maxKeywords)
.map(([word]) => word);
}
export function calculateTfIdf(
documents: string[],
maxTerms: number = 100
): Array<{ word: string; score: number }> {
// Calculate TF-IDF scores for term extraction
const documentWords = documents.map((doc) =>
removeStopwords(doc).split(/\s+/)
);
const vocabulary = new Set(documentWords.flat());
const tfidfScores: Record<string, number> = {};
vocabulary.forEach((term) => {
let tfidfSum = 0;
documentWords.forEach((docWords) => {
const tf =
docWords.filter((word) => word === term).length / docWords.length;
const df = documentWords.filter((doc) => doc.includes(term)).length;
const idf = Math.log(documents.length / df);
tfidfSum += tf * idf;
});
tfidfScores[term] = tfidfSum;
});
return Object.entries(tfidfScores)
.sort(([, a], [, b]) => b - a)
.slice(0, maxTerms)
.map(([word, score]) => ({ word, score }));
}
// Color utility functions
export function getColorForType(
type: string,
colorScheme: "entity" | "processing" = "entity"
): string {
const schemes = {
entity: VISUALIZATION_COLORS.entityTypes,
processing: VISUALIZATION_COLORS.processingMethods,
};
return (
schemes[colorScheme][type as keyof (typeof schemes)[typeof colorScheme]] ||
schemes[colorScheme].default
);
}
export function getColorScale(
count: number,
type: "sequential" | "categorical" | "diverging" = "categorical"
): string[] {
const palette = VISUALIZATION_COLORS[type];
if (count <= palette.length) {
return palette.slice(0, count);
}
// Generate interpolated colors for larger counts
const step = Math.floor(palette.length / count);
const colors: string[] = [];
for (let i = 0; i < count; i++) {
const index = Math.min(i * step, palette.length - 1);
colors.push(palette[index] || "#6b7280");
}
return colors;
}
export function hexToRgba(hex: string, alpha: number = 1): string {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
// Text analysis utilities
export function analyzeTextReadability(text: string): {
readingTime: number;
complexity: number;
wordCount: number;
sentenceCount: number;
avgWordsPerSentence: number;
avgSyllablesPerWord: number;
} {
const words = text.split(/\s+/).filter((word) => word.length > 0);
const sentences = text.split(/[.!?]+/).filter((s) => s.trim().length > 0);
const wordCount = words.length;
const sentenceCount = sentences.length;
const avgWordsPerSentence = wordCount / sentenceCount;
// Simple syllable counting (approximation)
const avgSyllablesPerWord =
words.reduce((sum, word) => {
const syllables = word
.toLowerCase()
.replace(/[^a-z]/g, "")
.replace(/[aeiou]{2,}/g, "a")
.match(/[aeiou]/g);
return sum + (syllables ? syllables.length : 1);
}, 0) / wordCount;
// Flesch-Kincaid Grade Level (simplified)
const complexity = Math.max(
0,
Math.min(
100,
206.835 - 1.015 * avgWordsPerSentence - 84.6 * avgSyllablesPerWord
)
);
// Reading time (200 words per minute)
const readingTime = Math.ceil(wordCount / 200);
return {
readingTime,
complexity,
wordCount,
sentenceCount,
avgWordsPerSentence,
avgSyllablesPerWord,
};
}
// Data transformation utilities
export function normalizeValue(
value: number,
min: number,
max: number
): number {
return (value - min) / (max - min);
}
export function scaleValue(
value: number,
oldMin: number,
oldMax: number,
newMin: number,
newMax: number
): number {
return newMin + ((value - oldMin) * (newMax - newMin)) / (oldMax - oldMin);
}
export function formatNumber(num: number, decimals: number = 0): string {
if (num >= 1000000) {
return (num / 1000000).toFixed(decimals) + "M";
} else if (num >= 1000) {
return (num / 1000).toFixed(decimals) + "K";
} else {
return num.toFixed(decimals);
}
}
export function formatPercentage(value: number, total: number): string {
return `${((value / total) * 100).toFixed(1)}%`;
}
// Debounce utility for performance
export function debounce<T extends (...args: any[]) => void>(
func: T,
delay: number
): (...args: Parameters<T>) => void {
let timeoutId: NodeJS.Timeout;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
// DOM utilities for visualization
export function getElementDimensions(element: HTMLElement | null): {
width: number;
height: number;
} {
if (!element) return { width: 0, height: 0 };
const rect = element.getBoundingClientRect();
return {
width: rect.width,
height: rect.height,
};
}
export function createSvgElement(width: number, height: number): SVGSVGElement {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", width.toString());
svg.setAttribute("height", height.toString());
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
return svg;
}
// Accessibility utilities
export function generateAccessibleLabel(
chartType: string,
dataPoints: number,
summary?: string
): string {
const baseLabel = `${chartType} with ${dataPoints} data points`;
return summary ? `${baseLabel}. ${summary}` : baseLabel;
}
export function getContrastColor(backgroundColor: string): string {
// Simple contrast color calculation
const hex = backgroundColor.replace("#", "");
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
return brightness > 128 ? "#000000" : "#ffffff";
}