import React, { useState, useEffect } from 'react'; import { Search, Database, MessageSquare, Zap, FileText, ChevronRight, Clock, RefreshCw, Terminal, Activity, BookOpen, Play, Layers, Shield, Globe } from 'lucide-react'; import { apiUrl } from '../api'; const CATEGORY_OPTIONS = [ { value: '', label: 'All Categories' }, { value: 'report', label: 'Report' }, { value: 'summary', label: 'Summary' }, { value: 'architecture', label: 'Architecture' }, { value: 'authentication', label: 'Authentication' }, { value: 'api', label: 'API Endpoints' }, { value: 'dependency', label: 'Dependencies' }, { value: 'business_flow', label: 'Business Flows' }, { value: 'concept', label: 'Concepts' }, { value: 'file', label: 'Source Files' }, ]; const TOOLS = [ { name: 'repository_search', icon: , color: '#2E9E9E', bg: '#E6F7F7', border: '#A8D8D8', desc: 'Semantic search over indexed repo chunks' }, { name: 'graph_query', icon: , color: '#7C3AED', bg: '#F5F3FF', border: '#C4B5FD', desc: 'Query architecture graph, entry points, and flows' }, { name: 'dependency_lookup', icon: , color: '#D97706', bg: '#FFFBEB', border: '#FDE68A', desc: 'Lookup packages, frameworks, and databases' }, { name: 'file_reader', icon: , color: '#4338CA', bg: '#EEF2FF', border: '#C7D2FE', desc: 'Retrieve specific source file content' }, { name: 'architecture_lookup', icon: , color: '#059669', bg: '#ECFDF5', border: '#A7F3D0', desc: 'Query architecture pattern and key modules' }, { name: 'api_lookup', icon: , color: '#E11D48', bg: '#FFF1F2', border: '#FECDD3', desc: 'Lookup HTTP routes and authentication methods' }, ]; const CATEGORY_COLORS = { report: { color: '#4338CA', bg: '#EEF2FF', border: '#C7D2FE' }, summary: { color: '#2E9E9E', bg: '#E6F7F7', border: '#A8D8D8' }, architecture: { color: '#7C3AED', bg: '#F5F3FF', border: '#C4B5FD' }, authentication: { color: '#E11D48', bg: '#FFF1F2', border: '#FECDD3' }, api: { color: '#059669', bg: '#ECFDF5', border: '#A7F3D0' }, dependency: { color: '#D97706', bg: '#FFFBEB', border: '#FDE68A' }, business_flow: { color: '#2E9E9E', bg: '#E6F7F7', border: '#A8D8D8' }, concept: { color: '#7C3AED', bg: '#F5F3FF', border: '#C4B5FD' }, file: { color: '#475569', bg: '#F8FAFC', border: '#CBD5E1' }, }; const getCatStyle = (cat) => CATEGORY_COLORS[cat] || { color: '#64748B', bg: '#F8FAFC', border: '#CBD5E1' }; const TABS = [ { id: 'search', icon: , label: 'Semantic Search' }, { id: 'memory', icon: , label: 'Memory Inspector' }, { id: 'conversations', icon: , label: 'Conversations' }, { id: 'tools', icon: , label: 'Tool Catalog' }, ]; export default function KnowledgeExplorer({ repo_id, apiKey }) { const [activeTab, setActiveTab] = useState('search'); const [searchQuery, setSearchQuery] = useState(''); const [searchCategory, setSearchCategory] = useState(''); const [searchTopK, setSearchTopK] = useState(5); const [searchResults, setSearchResults] = useState([]); const [searchLoading, setSearchLoading] = useState(false); const [searchLatency, setSearchLatency] = useState(null); const [searchError, setSearchError] = useState(null); const [memoryInfo, setMemoryInfo] = useState(null); const [memoryLoading, setMemoryLoading] = useState(false); const [conversations, setConversations] = useState([]); const [convsLoading, setConvsLoading] = useState(false); const [selectedSession, setSelectedSession] = useState(null); const [sessionHistory, setSessionHistory] = useState([]); const [historyLoading, setHistoryLoading] = useState(false); const headers = () => { const h = { 'Content-Type': 'application/json' }; if (apiKey) h['x-gemini-key'] = apiKey; return h; }; useEffect(() => { if (activeTab === 'memory') loadMemory(); if (activeTab === 'conversations') loadConversations(); }, [activeTab]); const loadMemory = async () => { setMemoryLoading(true); try { const r = await fetch(apiUrl(`/api/memory?repo_id=${repo_id}`)); setMemoryInfo(await r.json()); } catch { setMemoryInfo(null); } setMemoryLoading(false); }; const loadConversations = async () => { setConvsLoading(true); try { const r = await fetch(apiUrl(`/api/conversations?repo_id=${repo_id}`)); const d = await r.json(); setConversations(d.sessions || []); } catch { setConversations([]); } setConvsLoading(false); }; const loadSessionHistory = async (sessionId) => { setHistoryLoading(true); try { const r = await fetch(apiUrl(`/api/conversations/${sessionId}`)); const data = await r.json(); if (!r.ok) throw new Error(data.detail); setSessionHistory(data.history || []); } catch { setSessionHistory([]); } finally { setHistoryLoading(false); } }; const handleSelectSession = (sessionId) => { if (selectedSession === sessionId) { setSelectedSession(null); setSessionHistory([]); return; } setSelectedSession(sessionId); loadSessionHistory(sessionId); }; const handleSearch = async (e) => { e.preventDefault(); if (!searchQuery.trim()) return; setSearchLoading(true); setSearchError(null); setSearchResults([]); setSearchLatency(null); try { const body = { repo_id, query: searchQuery.trim(), top_k: searchTopK }; if (searchCategory) body.category = searchCategory; const r = await fetch(apiUrl('/api/search'), { method: 'POST', headers: headers(), body: JSON.stringify(body) }); const d = await r.json(); if (!r.ok) throw new Error(d.detail || 'Search failed'); setSearchResults(d.results || []); setSearchLatency(d.latency_ms); } catch (e) { setSearchError(e.message); } setSearchLoading(false); }; return (
{/* Sub-tabs */}
{TABS.map(t => ( ))}
{/* ── SEMANTIC SEARCH ── */} {activeTab === 'search' && (
Semantic Knowledge Search

Search across all indexed repository knowledge using natural language. Results are ranked by cosine similarity score.

setSearchQuery(e.target.value)} id="semantic-search-input" />
{searchError &&
{searchError}
} {searchLatency != null && !searchLoading && (
{searchResults.length} results · {searchLatency}ms
)}
{searchResults.map((r, i) => { const cs = getCatStyle(r.metadata?.category); const sim = r.similarity; const fillColor = sim > 0.8 ? 'var(--accent-green)' : sim > 0.6 ? 'var(--accent-teal)' : 'var(--accent-amber)'; return (
{r.metadata?.category || 'general'} {r.metadata?.path && {r.metadata.path}}
{Math.round(sim * 100)}%
{r.content}
); })} {!searchLoading && searchResults.length === 0 && searchLatency != null && (
No results found. Try different search terms or select a different category.
)}
)} {/* ── MEMORY INSPECTOR ── */} {activeTab === 'memory' && (
Vector Memory Inspector

Inspect the repository's semantic knowledge stored in ChromaDB. Each chunk is tagged with category metadata for precise retrieval.

{memoryLoading && (
Loading memory info…
)} {memoryInfo && !memoryLoading && (
Indexed Chunks
{memoryInfo.indexed_chunks?.toLocaleString()}
ChromaDB documents
Repository ID
{memoryInfo.repo_id}
Storage Path
{memoryInfo.storage_path}
)}
The knowledge index contains chunked embeddings of the intelligence report, source files, architecture concepts, API endpoints, dependencies, business flows, and concepts. Embeddings are generated using Gemini text-embedding-004.
Indexed Categories
{CATEGORY_OPTIONS.filter(o => o.value).map(o => { const cs = getCatStyle(o.value); return (
{o.label}
); })}
)} {/* ── CONVERSATIONS ── */} {activeTab === 'conversations' && (
Conversation History

All AI Assistant chat sessions for this repository, stored in conversation memory.

{convsLoading && (
Loading conversations…
)} {!convsLoading && conversations.length === 0 && (
No conversations yet. Start chatting in the AI Assistant tab to see sessions here.
)}
{conversations.map((s, idx) => (
handleSelectSession(s.session_id)} id={`conv-item-${idx}`} >
#{idx + 1} {s.summary || 'Untitled Session'} {s.message_count} msgs
{s.session_id?.substring(0, 8)}… {new Date(s.last_updated * 1000).toLocaleString()}
))}
{selectedSession && (
Session Messages
{historyLoading && (
Loading message history…
)} {!historyLoading && sessionHistory.length === 0 && (
No messages in this session.
)} {!historyLoading && sessionHistory.length > 0 && (
{sessionHistory.map((msg, i) => (
{msg.role}
{msg.content}
))}
)}
)}
)} {/* ── TOOL CATALOG ── */} {activeTab === 'tools' && (
Tool Catalog (MCP-Ready)

These tools are available to the Planner Agent during orchestration. Interfaces are compatible with Model Context Protocol (MCP) and Google ADK.

{TOOLS.map(t => (
{t.icon}
{t.name}
{t.desc}
execute()
))}
Each tool implements a BaseTool interface with name, description, and execute(**kwargs). This design is forward-compatible with Google ADK, LangGraph, CrewAI, and MCP server registration.
)}
); }