Spaces:
Running
Running
| /** | |
| * 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> | |
| ); | |
| } | |