wu981526092's picture
🚀 Deploy AgentGraph: Complete agent monitoring and knowledge graph system
c2ea5ed
/**
* Agent Graphs Section Component
*
* Displays and manages agent graphs for a trace, extracted from TraceKnowledgeGraphView
*/
import React from "react";
import { Button } from "@/components/ui/button";
import { CardTitle } from "@/components/ui/card";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { FileText, Activity, AlertCircle, Plus } from "lucide-react";
import { Trace, KnowledgeGraph } from "@/types";
import { KnowledgeGraphTree } from "@/components/features/traces/KnowledgeGraphTree";
// import { useKGDisplayMode } from "@/context/KGDisplayModeContext";
// import { getDisplayModeDescription } from "@/lib/kg-selection";
import { api } from "@/lib/api";
import { useModal } from "@/context/ModalContext";
import { useNotification } from "@/context/NotificationContext";
import { useNavigation } from "@/context/NavigationContext";
import { useAgentGraph } from "@/context/AgentGraphContext";
interface AgentGraphsSectionProps {
trace: Trace;
kgData: KnowledgeGraph[];
loading: boolean;
error: string | null;
isGenerating: boolean;
isPolling: boolean;
generationProgress: number;
onGenerateKG: () => void;
onRefreshKnowledgeGraphs: () => void;
onOpenContextDocuments: () => void;
}
export function AgentGraphsSection({
trace,
kgData,
loading,
error,
isGenerating,
isPolling,
generationProgress,
onGenerateKG,
onRefreshKnowledgeGraphs,
onOpenContextDocuments,
}: AgentGraphsSectionProps) {
// const { mode: kgDisplayMode } = useKGDisplayMode();
const { openModal } = useModal();
const { showNotification } = useNotification();
const { actions: navigationActions } = useNavigation();
const { actions } = useAgentGraph();
// Helper to show both toast and persistent notifications
const showSystemNotification = React.useCallback(
(notification: {
type: "success" | "error" | "warning" | "info";
title: string;
message: string;
}) => {
showNotification(notification);
navigationActions.addNotification({
type: notification.type,
title: notification.title,
message: notification.message,
});
},
[showNotification, navigationActions]
);
const handleViewKnowledgeGraph = (kgId: string) => {
const kg = kgData.find((k) => k.kg_id === kgId || k.id === kgId);
if (!kg) return;
console.log("Selected KG:", kg);
actions.setSelectedKnowledgeGraph(kg);
actions.setActiveView("kg-visualizer");
};
return (
<div className="h-full">
{/* Agent Graphs Section */}
<div className="border border-border/50 rounded-lg bg-background h-full">
<div className="p-4 pb-3">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 flex-1 min-w-0">
<Activity className="h-5 w-5 text-primary" />
<CardTitle className="text-lg">Agent Graphs</CardTitle>
</div>
{/* Action buttons in top right */}
<div className="flex items-center gap-2">
{/* Context Documents Button */}
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={onOpenContextDocuments}
size="sm"
variant="outline"
className="gap-1"
>
<FileText className="h-4 w-4" />
<span className="hidden sm:inline">Context</span>
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="max-w-sm">
<p className="font-medium mb-1 text-primary-foreground">
Context Documents
</p>
<p className="text-xs text-primary-foreground/80">
Manage domain knowledge, schemas, and guidelines to
improve extraction quality.
</p>
</div>
</TooltipContent>
</Tooltip>
{/* Generate Button */}
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={onGenerateKG}
disabled={isGenerating || isPolling}
size="sm"
className="bg-primary hover:bg-primary/90"
>
{isGenerating || isPolling ? (
<>
<div className="w-3 h-3 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" />
<span className="hidden sm:inline">
{generationProgress > 0
? `Generating... ${Math.round(generationProgress)}%`
: "Generating..."}
</span>
<span className="sm:hidden">
{generationProgress > 0
? `${Math.round(generationProgress)}%`
: "..."}
</span>
</>
) : (
<>
<Plus className="h-4 w-4 mr-2" />
<span className="hidden sm:inline">Generate</span>
<span className="sm:hidden">+</span>
</>
)}
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="max-w-sm">
<p className="font-medium mb-1 text-primary-foreground">
Generate New Agent Graph
</p>
<p className="text-xs text-primary-foreground/80">
Create a new agent graph with different chunking
strategies. You can generate multiple graphs to compare
approaches and insights.
</p>
</div>
</TooltipContent>
</Tooltip>
</div>
</div>
{/* Subtitle */}
<p className="text-sm text-muted-foreground">
Generated knowledge graphs from this trace
</p>
</div>
<div className="p-4 space-y-4 flex-1 overflow-hidden">
{loading ? (
<div className="space-y-4">
<div className="h-8 w-full bg-muted animate-pulse rounded" />
<div className="h-6 w-3/4 bg-muted animate-pulse rounded" />
<div className="h-6 w-1/2 bg-muted animate-pulse rounded" />
</div>
) : error ? (
<div className="border border-destructive/50 bg-destructive/10 rounded-lg p-4">
<div className="flex items-center gap-2 text-destructive">
<AlertCircle className="h-4 w-4" />
<span className="text-sm">{error}</span>
<Button
variant="outline"
size="sm"
onClick={onRefreshKnowledgeGraphs}
className="ml-2"
>
Try Again
</Button>
</div>
</div>
) : kgData.length > 0 ? (
<>
{/* Display mode indicator
<div className="mb-4 p-3 bg-muted/20 rounded-lg border">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Info className="h-4 w-4" />
{getDisplayModeDescription(kgDisplayMode)}
</div>
</div> */}
<KnowledgeGraphTree
knowledgeGraphs={kgData}
_onViewKg={handleViewKnowledgeGraph}
onDeleteKg={async (kgId, name) => {
const confirmMessage = `Are you sure you want to delete the knowledge graph "${name}"?
This will permanently remove:
- All extracted entities and relationships
- Generated insights and analysis
- Historical comparison data
This action cannot be undone.`;
if (!confirm(confirmMessage)) {
return;
}
try {
const response = await fetch(
`/api/knowledge-graphs/${kgId}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
throw new Error(
`API error: ${response.status} ${response.statusText}`
);
}
const data = await response.json();
console.log("Knowledge graph deleted:", data);
// Refresh the knowledge graphs list
onRefreshKnowledgeGraphs();
} catch (error) {
console.error("Error deleting knowledge graph:", error);
alert(
`Error deleting knowledge graph: ${
error instanceof Error ? error.message : String(error)
}`
);
}
}}
onReplayKg={async (kgId, traceId, runId) => {
try {
// Import the temporal API functions
const { fetchTemporalGraphData, validateTemporalData } =
await import("@/lib/temporal-api");
// Validate temporal data availability
const isValid = await validateTemporalData(traceId, runId);
if (!isValid) {
showSystemNotification({
type: "error",
title: "Insufficient Data",
message:
"Need at least 2 window knowledge graphs for temporal replay.",
});
return;
}
// Fetch temporal data
const temporalData = await fetchTemporalGraphData(
traceId,
runId
);
// Set temporal data and navigate to visualizer
actions.setSelectedTemporalData(temporalData);
actions.setActiveView("temporal-visualizer");
showSystemNotification({
type: "success",
title: "Temporal Visualization",
message:
"Loading temporal knowledge graph visualization...",
});
} catch (error) {
console.error("Error starting temporal replay:", error);
showSystemNotification({
type: "error",
title: "Replay Error",
message: "Failed to load temporal data for replay.",
});
}
}}
onViewSegment={async (traceId, start, end, window) => {
try {
const segmentData = await api.traces.extractSegment(
traceId,
start,
end
);
openModal(
"trace-segment",
`Trace Segment - Window ${window}`,
{
trace: trace,
segment: {
content: segmentData.content,
startChar: start,
endChar: end,
windowIndex: window,
},
},
{
size: "xl",
closable: true,
}
);
} catch (error) {
console.error("Error loading trace segment:", error);
showSystemNotification({
type: "error",
title: "Failed to Load Segment",
message:
"Could not load the trace segment. Please try again.",
});
}
}}
currentTraceId={trace.trace_id}
/>
</>
) : (
<div className="flex-1 flex items-center justify-center py-12">
<div className="text-center space-y-4">
<div className="p-4 rounded-full bg-muted/50 w-16 h-16 flex items-center justify-center mx-auto">
<Activity className="h-8 w-8 text-muted-foreground" />
</div>
<div>
<p className="text-sm font-medium text-muted-foreground mb-1">
No Agent Graphs Yet
</p>
<p className="text-xs text-muted-foreground">
Generate your first agent graph to start analyzing this
trace
</p>
</div>
<Button onClick={onGenerateKG} className="mt-4">
<Plus className="h-4 w-4 mr-2" />
Generate First Graph
</Button>
</div>
</div>
)}
</div>
</div>
</div>
);
}