"use client"; import { Markdown } from "@copilotkit/react-ui"; import { useState, useCallback } from "react"; interface VisualGroundingState { isOpen: boolean; chunkId: string | null; images: string[]; loading: boolean; error: string | null; } interface InsightRecord { id: string; summary: string; status: string; notes?: string; supporting_sources: string[]; originating_questions: string[]; } interface GapRecord { id: string; description: string; severity: string; blocking: boolean; resolved: boolean; notes?: string; supporting_sources: string[]; resolved_by: string[]; } interface Citation { document_id: string; chunk_id: string; document_uri: string; document_title?: string; page_numbers: number[]; headings?: string[]; content: string; } interface SearchAnswer { query: string; answer: string; confidence: number; cited_chunks: string[]; citations: Citation[]; } interface ResearchContext { original_question: string; sub_questions: string[]; qa_responses: SearchAnswer[]; insights: InsightRecord[]; gaps: GapRecord[]; } interface EvaluationResult { confidence: number; reasoning: string; should_continue: boolean; gaps_identified: string[]; follow_up_questions: string[]; } interface ResearchReport { question: string; summary: string; findings: string[]; conclusions: string[]; insights_used: string[]; methodology: string; } interface ResearchState { context: ResearchContext; iterations: number; max_iterations: number; confidence_threshold: number; max_concurrency: number; last_eval: EvaluationResult | null; last_analysis: { insights_extracted: InsightRecord[]; gaps_identified: GapRecord[]; } | null; result?: ResearchReport; current_activity?: string; current_activity_message?: string; } interface StateDisplayProps { state: ResearchState; } export default function StateDisplay({ state }: StateDisplayProps) { const [expandedSections, setExpandedSections] = useState< Record >({ questions: true, insights: true, gaps: true, report: true, }); const [expandedQuestions, setExpandedQuestions] = useState< Record >({}); const [visualGrounding, setVisualGrounding] = useState({ isOpen: false, chunkId: null, images: [], loading: false, error: null, }); const fetchVisualGrounding = useCallback(async (chunkId: string) => { setVisualGrounding({ isOpen: true, chunkId, images: [], loading: true, error: null, }); try { const response = await fetch( `${process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"}/api/visualize/${chunkId}` ); const data = await response.json(); if (!response.ok) { throw new Error(data.error || "Failed to fetch visual grounding"); } setVisualGrounding((prev) => ({ ...prev, images: data.images || [], loading: false, error: data.images?.length === 0 ? data.message : null, })); } catch (err) { setVisualGrounding((prev) => ({ ...prev, loading: false, error: err instanceof Error ? err.message : "Unknown error", })); } }, []); const closeVisualGrounding = useCallback(() => { setVisualGrounding({ isOpen: false, chunkId: null, images: [], loading: false, error: null, }); }, []); const toggleSection = (section: string) => { setExpandedSections((prev) => ({ ...prev, [section]: !prev[section], })); }; const toggleQuestion = (questionId: string) => { setExpandedQuestions((prev) => ({ ...prev, [questionId]: !prev[questionId], })); }; // Calculate research progress based on iterations const researchProgress = state.max_iterations > 0 ? (state.iterations / state.max_iterations) * 100 : 0; const confidence = state.last_eval?.confidence || 0; return (
{/* Question */} {state.context.original_question && (
Question
{state.context.original_question}
)} {/* Research Progress - only show when research has started */} {(state.iterations > 0 || (state.current_activity && !state.result)) && (
{/* Current Activity - hide when complete */} {state.current_activity && !state.result && (
0 ? "1rem" : 0, }} >
{state.current_activity.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase())}
{state.current_activity_message && (
{state.current_activity_message}
)}
)} {/* Iteration Progress Bar */} {state.iterations > 0 && (
Iterations {state.iterations}/{state.max_iterations}
)}
)} {/* Confidence Meter */} {confidence > 0 && (
Confidence
0.8 ? "#48bb78" : confidence > 0.5 ? "#ed8936" : "#f56565", transition: "width 0.3s ease", }} />
0.8 ? "#48bb78" : confidence > 0.5 ? "#ed8936" : "#f56565", }} > {(confidence * 100).toFixed(0)}%
{state.last_eval?.reasoning && (
)}
)} {/* Sub-Questions and QA Responses */} {(state.context.sub_questions.length > 0 || state.context.qa_responses.length > 0) && (
{expandedSections.questions && (
{/* Show pending sub_questions */} {state.context.sub_questions.map((question, idx) => (
))} {/* Show all qa_responses (each has query + answer) */} {state.context.qa_responses.map((qaResponse, idx) => { const questionId = `q-${idx}`; return (
{/* QA Response nested inside question */} {expandedQuestions[questionId] && (
Answer
{/* Citations with visual grounding info */} {qaResponse.citations && qaResponse.citations.length > 0 && (
Citations ({qaResponse.citations.length})
{qaResponse.citations.map((citation, citIdx) => (
{citation.document_title || citation.document_uri}
{citation.page_numbers && citation.page_numbers.length > 0 && (
{citation.page_numbers.length === 1 ? `p. ${citation.page_numbers[0]}` : `pp. ${citation.page_numbers[0]}-${citation.page_numbers[citation.page_numbers.length - 1]}`}
)}
{citation.headings && citation.headings.length > 0 && (
{citation.headings.join(" › ")}
)}
{citation.content.slice(0, 200)} {citation.content.length > 200 && "…"}
))}
)}
)}
); })}
)}
)} {/* Insights */} {state.context.insights.length > 0 && (
{expandedSections.insights && (
{state.context.insights.map((insight) => (
{insight.status} {insight.supporting_sources.length} sources
{insight.notes && (
)} {insight.supporting_sources.length > 0 && (
Sources: {insight.supporting_sources.map((source, srcIdx) => ( {srcIdx > 0 && ", "} {source} ))}
)}
))}
)}
)} {/* Knowledge Gaps */} {state.context.gaps.length > 0 && (
{expandedSections.gaps && (
{state.context.gaps.map((gap) => (
{gap.severity} {gap.blocking && ( Blocking )} {gap.resolved && ( Resolved )}
{gap.notes && (
)} {gap.resolved && gap.resolved_by.length > 0 && (
Resolved by: {gap.resolved_by.map((source, srcIdx) => ( {srcIdx > 0 && ", "} {source} ))}
)}
))}
)}
)} {/* Final Report */} {state.result && (
{expandedSections.report && (

{state.result.question}

Summary

Key Findings

    {state.result.findings.map((finding, idx) => (
  • ))}

Conclusions

    {state.result.conclusions.map((conclusion, idx) => (
  • ))}

Methodology

Insights Used ({state.result.insights_used.length})

{state.result.insights_used.map((insightId, idx) => { const insight = state.context.insights.find( (i) => i.id === insightId, ); return (
{insight ? (
) : (
Insight ID: {insightId}
)}
); })}
)}
)} {/* Visual Grounding Modal */} {visualGrounding.isOpen && (
e.stopPropagation()} >

Visual Grounding

{visualGrounding.loading && (
Loading...
)} {visualGrounding.error && (
{visualGrounding.error}
)} {!visualGrounding.loading && !visualGrounding.error && visualGrounding.images.length > 0 && (
{visualGrounding.images.map((img, idx) => (
Page {idx + 1} of {visualGrounding.images.length}
{`Page
))}
)}
)}
); }