import React, { useState, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { AlertCircle, CheckCircle, Clock, Play, Eye, Zap, Shield, GitBranch, Sparkles, Settings, } from "lucide-react"; import { KnowledgeGraph, PerturbationConfig } from "@/types"; import { useNotification } from "@/context/NotificationContext"; import { api } from "@/lib/api"; import { EnrichResults } from "./EnrichResults"; import { PerturbResults } from "./PerturbResults"; import { PerturbationTestConfig } from "./PerturbationTestConfig"; interface OptionalPipelineSectionProps { knowledgeGraph: KnowledgeGraph; onStageComplete?: (stage: string) => void; } interface StageState { status: "idle" | "running" | "completed" | "error"; progress: number; taskId?: string; error?: string; } interface PipelineStageConfig { id: string; name: string; description: string; icon: React.ComponentType<{ className?: string }>; apiEndpoint: (kgId: string) => string; benefit: string; } const PIPELINE_STAGES: PipelineStageConfig[] = [ { id: "enrich", name: "Prompt Reconstruction", description: "Reconstruct original prompts from conversation patterns", icon: Sparkles, apiEndpoint: (kgId) => `/knowledge-graphs/${kgId}/enrich`, benefit: "Understand conversation context and intent", }, { id: "perturb", name: "Perturbation Testing", description: "Test graph robustness through systematic variations", icon: Shield, apiEndpoint: (kgId) => `/knowledge-graphs/${kgId}/perturb`, benefit: "Validate relationship reliability and stability", }, { id: "causal", name: "Causal Analysis", description: "Analyze causal relationships and dependencies", icon: GitBranch, apiEndpoint: (kgId) => `/knowledge-graphs/${kgId}/analyze`, benefit: "Discover cause-and-effect patterns", }, ]; export function OptionalPipelineSection({ knowledgeGraph, onStageComplete, }: OptionalPipelineSectionProps) { const { showNotification } = useNotification(); const [stageStates, setStageStates] = useState>({ enrich: { status: knowledgeGraph.is_enriched ? "completed" : "idle", progress: knowledgeGraph.is_enriched ? 100 : 0, }, perturb: { status: knowledgeGraph.is_perturbed ? "completed" : "idle", progress: knowledgeGraph.is_perturbed ? 100 : 0, }, causal: { status: knowledgeGraph.is_analyzed ? "completed" : "idle", progress: knowledgeGraph.is_analyzed ? 100 : 0, }, }); const [selectedStageResults, setSelectedStageResults] = useState<{ stage: string; data: any; } | null>(null); const [showPerturbConfig, setShowPerturbConfig] = useState(false); const getStageStatusColor = (status: StageState["status"]) => { switch (status) { case "completed": return "bg-green-500/10 text-green-700 border-green-200"; case "running": return "bg-blue-500/10 text-blue-700 border-blue-200"; case "error": return "bg-red-500/10 text-red-700 border-red-200"; default: return "bg-gray-500/10 text-gray-700 border-gray-200"; } }; const pollTaskStatus = useCallback( async (stageId: string, taskId: string) => { const maxAttempts = 60; // 5 minutes at 5-second intervals let attempts = 0; const poll = async () => { try { const status = await api.tasks.get(taskId); setStageStates((prev) => { const currentState = prev[stageId] || { status: "idle" as const, progress: 0, }; return { ...prev, [stageId]: { ...currentState, progress: status.progress || currentState.progress, }, }; }); if (status.status === "completed" || status.status === "COMPLETED") { setStageStates((prev) => ({ ...prev, [stageId]: { status: "completed", progress: 100 }, })); onStageComplete?.(stageId); showNotification({ type: "success", title: "Stage Completed", message: `${ PIPELINE_STAGES.find((s) => s.id === stageId)?.name } has finished successfully.`, }); return; } else if (status.status === "failed" || status.status === "FAILED") { setStageStates((prev) => ({ ...prev, [stageId]: { status: "error", progress: 0, error: status.error || "Unknown error", }, })); showNotification({ type: "error", title: "Stage Failed", message: status.error || "Processing failed", }); return; } // Continue polling attempts++; if (attempts < maxAttempts) { setTimeout(poll, 5000); // Poll every 5 seconds } else { // Timeout setStageStates((prev) => ({ ...prev, [stageId]: { status: "error", progress: 0, error: "Processing timeout", }, })); } } catch (error) { console.error("Error polling task status:", error); // Continue polling on error, might be temporary attempts++; if (attempts < maxAttempts) { setTimeout(poll, 5000); } } }; // Start polling setTimeout(poll, 2000); // First check after 2 seconds }, [onStageComplete, showNotification] ); const runPerturbWithConfig = useCallback( async (config: PerturbationConfig) => { const stageConfig = PIPELINE_STAGES.find((s) => s.id === "perturb")!; setStageStates((prev) => ({ ...prev, perturb: { status: "running", progress: 0 }, })); try { const response = await api.knowledgeGraphs.perturb( knowledgeGraph.kg_id, config ); if (response.task_id) { pollTaskStatus("perturb", response.task_id); showNotification({ type: "info", title: `${stageConfig.name} Started`, message: "Processing has begun with custom configuration.", }); } } catch (error) { console.error("Error running perturb stage:", error); setStageStates((prev) => ({ ...prev, perturb: { status: "error", progress: 0, error: error instanceof Error ? error.message : "Unknown error", }, })); showNotification({ type: "error", title: `${stageConfig.name} Failed`, message: error instanceof Error ? error.message : "An error occurred", }); } }, [knowledgeGraph.kg_id, showNotification, pollTaskStatus] ); const runStage = useCallback( async (stageConfig: PipelineStageConfig) => { const { id } = stageConfig; // For perturb stage, open the configuration dialog instead if (id === "perturb") { setShowPerturbConfig(true); return; } setStageStates((prev) => ({ ...prev, [id]: { status: "running", progress: 0 }, })); try { // Call the API endpoint based on stage let response; if (id === "enrich") { response = await api.knowledgeGraphs.enrich(knowledgeGraph.kg_id); } else if (id === "causal") { response = await api.knowledgeGraphs.analyze(knowledgeGraph.kg_id); } else { throw new Error(`Unknown stage: ${id}`); } if (response.task_id) { // Start polling for task status pollTaskStatus(id, response.task_id); showNotification({ type: "info", title: `${stageConfig.name} Started`, message: "Processing has begun. This may take several minutes.", }); } else { // Mark as completed immediately if no task ID setStageStates((prev) => ({ ...prev, [id]: { status: "completed", progress: 100 }, })); onStageComplete?.(id); } } catch (error) { console.error(`Error running ${id} stage:`, error); setStageStates((prev) => ({ ...prev, [id]: { status: "error", progress: 0, error: error instanceof Error ? error.message : "Unknown error", }, })); showNotification({ type: "error", title: `${stageConfig.name} Failed`, message: error instanceof Error ? error.message : "An error occurred", }); } }, [knowledgeGraph.kg_id, showNotification, onStageComplete, pollTaskStatus] ); const viewStageResults = useCallback( async (stageId: string) => { try { const results = await api.knowledgeGraphs.getStageResults( knowledgeGraph.kg_id, stageId ); setSelectedStageResults({ stage: stageId, data: results, }); } catch (error) { showNotification({ type: "error", title: "Failed to Load Results", message: "Could not retrieve stage results", }); } }, [knowledgeGraph.kg_id, showNotification] ); const anyStageRunning = Object.values(stageStates).some( (state) => state.status === "running" ); return ( <>
{/* Advanced Processing - Unified */} Advanced Processing
{/* Description */}

Enhance your knowledge graph with advanced analysis techniques. These optional stages provide deeper insights but are not required for basic graph functionality.

{/* Horizontal Stepper */}
{/* Step Circles - Evenly Distributed */}
{PIPELINE_STAGES.map((stage, index) => { const state = stageStates[stage.id] || { status: "idle" as const, progress: 0, }; const StageIcon = stage.icon; return (
{/* Step Circle */}
{state.status === "completed" ? ( ) : state.status === "running" ? ( ) : state.status === "error" ? ( ) : ( )}
{/* Step Number */} Step {index + 1}
); })}
{/* Connector Lines - Positioned Absolutely */}
{PIPELINE_STAGES.slice(0, -1).map((stage, _index) => { const state = stageStates[stage.id] || { status: "idle" as const, progress: 0, }; return (
); })}
{/* Stage Labels */}
{PIPELINE_STAGES.map((stage, _index) => { const state = stageStates[stage.id] || { status: "idle" as const, progress: 0, }; return (

{stage.name}

{stage.description}

{state.status === "idle" ? "Ready" : state.status === "running" ? "Running" : state.status === "completed" ? "Completed" : "Error"}
); })}
{/* Error Display */} {Object.values(stageStates).some((s) => s.error) && (

{Object.values(stageStates).find((s) => s.error)?.error}

)} {/* Action Buttons - Aligned with Step Circles */}
{PIPELINE_STAGES.map((stage) => { const state = stageStates[stage.id] || { status: "idle" as const, progress: 0, }; return (
{state.status === "idle" && ( )} {state.status === "completed" && ( )} {state.status === "error" && ( )} {state.status === "running" && ( )}
); })}
{/* Stage Results Dialog */} {selectedStageResults && ( setSelectedStageResults(null)} > { PIPELINE_STAGES.find( (s) => s.id === selectedStageResults.stage )?.name }{" "} Results Analysis results for knowledge graph {knowledgeGraph.kg_id}
{/* Render appropriate results component based on stage */} {selectedStageResults.stage === "enrich" && ( )} {selectedStageResults.stage === "perturb" && ( )} {selectedStageResults.stage === "causal" && (

Causal Analysis Results

Rich causal analysis visualization is in development.

                    {JSON.stringify(selectedStageResults.data, null, 2)}
                  
)}
)} {/* Perturbation Test Configuration Dialog */} ); }