"use client"; import { useState, useEffect } from 'react'; import { useAuth } from '@/components/providers/auth-provider'; import { tokenManager } from '@/lib/token-manager'; interface SearchResult { document: { id: string; content: string; metadata: Record; }; score: number; debug_info?: Record; } interface DebugInfo { query_embedding?: number[]; embedding_dimension?: number; score_stats?: { min: number; max: number; avg: number; stddev: number; }; collection_stats?: { total_documents: number; total_chunks: number; languages: string[]; }; } export default function RAGDemoPage() { const { user, loading } = useAuth(); const [query, setQuery] = useState('are sd card backups encrypted?'); const [results, setResults] = useState([]); const [debugInfo, setDebugInfo] = useState({}); const [searchTime, setSearchTime] = useState(0); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); // Configuration state const [config, setConfig] = useState({ max_results: 10, score_threshold: 0.3, collection_name: '', chunk_size: 300, chunk_overlap: 50, enable_hybrid: false, vector_weight: 0.7, bm25_weight: 0.3, use_query_prefix: true, use_passage_prefix: true, show_timing: true, show_embeddings: false, }); // Available collections const [collections, setCollections] = useState([]); const [collectionsLoading, setCollectionsLoading] = useState(false); const presets = { default: { max_results: 10, score_threshold: 0.3, chunk_size: 300, chunk_overlap: 50, enable_hybrid: false, vector_weight: 0.7, bm25_weight: 0.3, }, high_precision: { max_results: 5, score_threshold: 0.5, chunk_size: 200, chunk_overlap: 30, enable_hybrid: true, vector_weight: 0.8, bm25_weight: 0.2, }, high_recall: { max_results: 20, score_threshold: 0.1, chunk_size: 400, chunk_overlap: 100, enable_hybrid: true, vector_weight: 0.6, bm25_weight: 0.4, }, hybrid: { max_results: 10, score_threshold: 0.2, chunk_size: 300, chunk_overlap: 50, enable_hybrid: true, vector_weight: 0.5, bm25_weight: 0.5, }, }; useEffect(() => { // Check if we have tokens in localStorage but not in tokenManager const syncTokens = async () => { const rawTokens = localStorage.getItem('auth_tokens'); if (rawTokens && !tokenManager.isAuthenticated()) { try { const tokens = JSON.parse(rawTokens); // Sync tokens to tokenManager tokenManager.setTokens( tokens.access_token, tokens.refresh_token, Math.floor((tokens.access_expires_at - Date.now()) / 1000) ); console.log('RAG Demo: Tokens synced from localStorage to tokenManager'); } catch (e) { console.error('RAG Demo: Failed to sync tokens:', e); } } loadCollections(); }; syncTokens(); }, [user]); const loadCollections = async () => { setCollectionsLoading(true); try { console.log('RAG Demo: Loading collections...'); console.log('RAG Demo: User authenticated:', !!user); console.log('RAG Demo: TokenManager authenticated:', tokenManager.isAuthenticated()); const token = await tokenManager.getAccessToken(); console.log('RAG Demo: Token retrieved:', token ? 'Yes' : 'No'); console.log('RAG Demo: Token expiry:', tokenManager.getTokenExpiry()); const headers: Record = { 'Content-Type': 'application/json', }; if (token) { headers['Authorization'] = `Bearer ${token}`; console.log('RAG Demo: Authorization header set'); } else { console.warn('RAG Demo: No token available'); } const response = await fetch('/api/rag/debug/collections', { headers }); console.log('RAG Demo: Collections response status:', response.status); if (response.ok) { const data = await response.json(); console.log('RAG Demo: Collections loaded:', data.collections); setCollections(data.collections || []); // Auto-select first collection if none selected if (data.collections && data.collections.length > 0 && !config.collection_name) { setConfig(prev => ({ ...prev, collection_name: data.collections[0] })); } } else { const errorText = await response.text(); console.error('RAG Demo: Collections failed:', response.status, errorText); } } catch (err) { console.error('RAG Demo: Failed to load collections:', err); } finally { setCollectionsLoading(false); } }; const loadPreset = (presetName: keyof typeof presets) => { setConfig(prev => ({ ...prev, ...presets[presetName], })); }; const performSearch = async () => { if (!query.trim()) return; if (!config.collection_name) { setError('Please select a collection'); return; } setIsLoading(true); setError(''); setResults([]); try { const token = await tokenManager.getAccessToken(); const headers: Record = { 'Content-Type': 'application/json', }; if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch('/api/rag/debug/search', { method: 'POST', headers, body: JSON.stringify({ query, max_results: config.max_results, score_threshold: config.score_threshold, collection_name: config.collection_name, config, }), }); if (!response.ok) { throw new Error(`Search failed: ${response.statusText}`); } const data = await response.json(); setResults(data.results || []); setDebugInfo(data.debug_info || {}); setSearchTime(data.search_time_ms || 0); } catch (err) { setError(err instanceof Error ? err.message : 'Unknown error'); } finally { setIsLoading(false); } }; const updateConfig = (key: string, value: any) => { setConfig(prev => ({ ...prev, [key]: value })); }; if (loading) { return (
Loading...
); } if (!user) { return (

RAG Demo

Please log in to access the RAG demo interface.

); } return (

🔍 RAG Search Demo

Test and tune your RAG system with real-time search and debugging

{/* Search Results - Main Content */}
{/* Preset Buttons */}
{Object.entries(presets).map(([name, _]) => ( ))}
{/* Search Box */}
setQuery(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && performSearch()} placeholder="Enter your search query..." className="flex-1 px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" />
{error && (
Error: {error}
)} {/* Results Summary */} {results.length > 0 && (

Found {results.length} results in {searchTime.toFixed(0)}ms {config.enable_hybrid && ( • Hybrid Search Enabled )}

)}
{/* Search Results */}
{results.map((result, index) => (

Result {index + 1}

= 0.5 ? 'bg-green-100 text-green-800' : result.score >= 0.3 ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800' }`}> Score: {result.score.toFixed(4)}
{result.document.content}
{/* Metadata */}
{result.document.metadata.content_type && ( Type: {result.document.metadata.content_type} )} {result.document.metadata.language && ( Language: {result.document.metadata.language} )} {result.document.metadata.filename && ( File: {result.document.metadata.filename} )} {result.document.metadata.chunk_index !== undefined && ( Chunk: {result.document.metadata.chunk_index + 1}/{result.document.metadata.chunk_count || '?'} )}
{/* Debug Details */} {config.show_timing && result.debug_info && (

Debug Information:

{result.debug_info.vector_score !== undefined && (

Vector Score: {result.debug_info.vector_score.toFixed(4)}

)} {result.debug_info.bm25_score !== undefined && (

BM25 Score: {result.debug_info.bm25_score.toFixed(4)}

)} {result.document.metadata.question && (

Question: {result.document.metadata.question}

)}
)}
))}
{/* Debug Section */} {debugInfo && Object.keys(debugInfo).length > 0 && (

Debug Information

{debugInfo.score_stats && (

Score Statistics:

Min: {debugInfo.score_stats.min?.toFixed(4)}
Max: {debugInfo.score_stats.max?.toFixed(4)}
Avg: {debugInfo.score_stats.avg?.toFixed(4)}
StdDev: {debugInfo.score_stats.stddev?.toFixed(4)}
)} {debugInfo.collection_stats && (

Collection Stats:

Total Documents: {debugInfo.collection_stats.total_documents}

Total Chunks: {debugInfo.collection_stats.total_chunks}

Languages: {debugInfo.collection_stats.languages?.join(', ')}

)} {debugInfo.query_embedding && config.show_embeddings && (

Query Embedding (first 10 dims):

[{debugInfo.query_embedding.slice(0, 10).map(x => x.toFixed(6)).join(', ')}...]

)}
)}
{/* Configuration Panel */}

⚙️ Configuration

{/* Search Settings */}

Search Settings

updateConfig('max_results', parseInt(e.target.value))} className="w-full" />
updateConfig('score_threshold', parseFloat(e.target.value))} className="w-full" />
{collectionsLoading ? ( ) : ( )}
{/* Chunking Settings */}

Chunking Settings

updateConfig('chunk_size', parseInt(e.target.value))} className="w-full" />
updateConfig('chunk_overlap', parseInt(e.target.value))} className="w-full" />
{/* Hybrid Search */}

Hybrid Search

{config.enable_hybrid && ( <>
updateConfig('vector_weight', parseFloat(e.target.value))} className="w-full" />
updateConfig('bm25_weight', parseFloat(e.target.value))} className="w-full" />
)}
{/* Debug Options */}

Debug Options

); }