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 => (
setActiveTab(t.id)}
id={`ke-tab-${t.id}`}
>
{t.icon} {t.label}
))}
{/* ── SEMANTIC SEARCH ── */}
{activeTab === 'search' && (
Semantic Knowledge Search
Search across all indexed repository knowledge using natural language.
Results are ranked by cosine similarity score.
{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} }
{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
Refresh
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
Refresh
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) => (
))}
)}
)}
)}
{/* ── 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.
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.
)}
);
}