// // SPDX-FileCopyrightText: Copyright (c) 1993-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // "use client"; import { useState, useEffect } from "react"; import { RagQuery, RagParams } from "@/components/rag-query"; import type { Triple } from "@/types/graph"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { DatabaseConnection } from "@/components/database-connection"; import { NvidiaIcon } from "@/components/nvidia-icon"; import { ArrowLeft, BarChart2, Search as SearchIcon } from "lucide-react"; export default function RagPage() { const router = useRouter(); const [results, setResults] = useState(null); const [llmAnswer, setLlmAnswer] = useState(null); const [isLoading, setIsLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(null); const [vectorEnabled, setVectorEnabled] = useState(false); const [metrics, setMetrics] = useState<{ avgQueryTime: number; avgRelevance: number; precision: number; recall: number; queryTimesByMode?: Record; } | null>(null); const [currentParams, setCurrentParams] = useState({ kNeighbors: 4096, fanout: 400, numHops: 2, topK: 5, useVectorSearch: false, usePureRag: false, queryMode: 'traditional' }); // Initialize backend when the page loads useEffect(() => { // Initialize the backend services const initializeBackend = async () => { try { // Check graph database connection (ArangoDB by default) const graphResponse = await fetch('/api/graph-db', { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); if (!graphResponse.ok) { const errorData = await graphResponse.json(); console.warn('Graph database connection warning:', errorData.error); } // Check if vector search is available const vectorResponse = await fetch('/api/pinecone-diag/stats'); if (vectorResponse.ok) { const data = await vectorResponse.json(); setVectorEnabled(data.totalVectorCount > 0); } // Fetch basic metrics const metricsResponse = await fetch('/api/metrics'); if (metricsResponse.ok) { const data = await metricsResponse.json(); setMetrics({ avgQueryTime: data.avgQueryTime, avgRelevance: data.avgRelevance, precision: data.precision, recall: data.recall, queryTimesByMode: data.queryTimesByMode }); } } catch (error) { console.warn('Error initializing backends:', error); } }; initializeBackend(); }, []); const handleQuerySubmit = async (query: string, params: RagParams) => { setIsLoading(true); setErrorMessage(null); setCurrentParams(params); // Store current params for UI rendering const startTime = Date.now(); let queryMode: 'pure-rag' | 'vector-search' | 'traditional' = 'traditional'; let resultCount = 0; let relevanceScore = 0; // Debug logging console.log('🔍 Query params:', { usePureRag: params.usePureRag, useVectorSearch: params.useVectorSearch, vectorEnabled, queryMode: params.queryMode }); try { // If using pure RAG (Pinecone + LangChain) without graph search if (params.usePureRag) { queryMode = 'pure-rag'; try { console.log('Using pure RAG with Qdrant and NVIDIA LLM for query:', query); const ragResponse = await fetch('/api/rag-query', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, topK: params.topK }) }); if (ragResponse.ok) { const data = await ragResponse.json(); console.log('📥 RAG Response data:', { hasAnswer: !!data.answer, answerLength: data.answer?.length, documentCount: data.documentCount }); // Handle the answer - we might need to display differently than triples if (data.answer) { console.log('✅ Setting answer in results:', data.answer.substring(0, 100) + '...'); // Set the LLM answer for display (same as traditional mode) setLlmAnswer(data.answer); // Set empty results array since Pure RAG doesn't return triples setResults([]); resultCount = data.documentCount || 0; relevanceScore = data.relevanceScore || 0; // Log the query with performance metrics logQuery(query, queryMode, { executionTimeMs: Date.now() - startTime, relevanceScore, resultCount }); console.log(`✅ Pure RAG query completed. Retrieved ${resultCount} document chunks`); setIsLoading(false); return; } } else { // If the RAG query fails, log but continue to try other methods const errorData = await ragResponse.json(); throw new Error(errorData.error || 'Failed to execute pure RAG query'); } } catch (ragError) { console.warn('Pure RAG query error (falling back to other methods):', ragError); // Continue to other query methods as fallback } } // If we have vector embeddings AND explicitly selected vector search, use enhanced query with metadata if (vectorEnabled && params.useVectorSearch && !params.usePureRag) { queryMode = 'vector-search'; try { console.log('Using enhanced RAG with LangChain for query:', query); const enhancedResponse = await fetch('/api/enhanced-query', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, kNeighbors: params.kNeighbors, fanout: params.fanout, numHops: params.numHops, topK: params.topK }) }); if (enhancedResponse.ok) { const data = await enhancedResponse.json(); // Update the results setResults(data.relevantTriples || []); resultCount = data.count || 0; relevanceScore = data.relevanceScore || 0; // Log the query with performance metrics logQuery(query, queryMode, { executionTimeMs: Date.now() - startTime, relevanceScore, resultCount, precision: data.precision || 0, recall: data.recall || 0, }); // Log to console instead of showing alert let message = `Enhanced query completed. Found ${resultCount} relevant triples`; if (data.metadata?.entityMatches) { message += ` from ${data.metadata.entityMatches} matched entities`; } console.log(message); setIsLoading(false); return; } } catch (enhancedError) { console.warn('Enhanced query error (falling back to traditional query):', enhancedError); // Continue to traditional query as fallback } } // Call the LLM-enhanced graph query API console.log('✅ Using Graph Search + LLM approach'); queryMode = 'traditional'; // Get selected LLM model from localStorage let llmModel = undefined; let llmProvider = undefined; try { const savedModel = localStorage.getItem("selectedModelForRAG"); if (savedModel) { const modelData = JSON.parse(savedModel); llmModel = modelData.model; llmProvider = modelData.provider; console.log(`Using LLM: ${llmModel} (${llmProvider})`); } } catch (e) { console.warn("Could not load selected LLM model, using default"); } const response = await fetch(`/api/graph-query-llm`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query, topK: params.topK || 5, useTraditional: true, llmModel, llmProvider }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || 'Failed to query with LLM'); } const data = await response.json(); // Log sample of retrieved triples for debugging console.log('📊 Retrieved Triples (sample):', data.triples.slice(0, 3)); console.log('🤖 LLM-Generated Answer (preview):', data.answer?.substring(0, 200) + '...'); console.log('📈 Triple Count:', data.count); // DEBUG: Check if depth/pathLength are present in received data if (data.triples && data.triples.length > 0) { console.log('🔍 First triple structure:', JSON.stringify(data.triples[0], null, 2)); console.log('🔍 Has depth?', 'depth' in data.triples[0]); console.log('🔍 Has pathLength?', 'pathLength' in data.triples[0]); } // Update the results with the triples (for display) setResults(data.triples || []); resultCount = data.count || 0; relevanceScore = 0; // No relevance score for traditional search // Store the LLM answer for display if (data.answer) { console.log('✅ Setting llmAnswer state (length:', data.answer.length, 'chars)'); setLlmAnswer(data.answer); } else { console.log('⚠️ No answer in response'); setLlmAnswer(null); } // Log the query with performance metrics logQuery(query, queryMode, { executionTimeMs: Date.now() - startTime, relevanceScore, resultCount, precision: data.precision || 0, recall: data.recall || 0, }); // Log to console instead of showing alert let message = `Query completed. Found ${resultCount} relevant triples`; if (vectorEnabled && params.useVectorSearch) { message += ` (using standard vector search)`; } console.log(message); } catch (error) { console.error("RAG query error:", error); setErrorMessage(error instanceof Error ? error.message : "An unknown error occurred"); setResults([]); // Log failed query logQuery(query, queryMode, { executionTimeMs: Date.now() - startTime, resultCount: 0, error: error instanceof Error ? error.message : "Unknown error" }); } finally { setIsLoading(false); } }; // Helper function to log queries const logQuery = async ( query: string, queryMode: 'pure-rag' | 'vector-search' | 'traditional', metrics: { executionTimeMs: number; relevanceScore?: number; precision?: number; recall?: number; resultCount: number; error?: string; } ) => { try { await fetch('/api/query-log', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, queryMode, metrics }) }); console.log('Query logged successfully'); } catch (error) { // Non-blocking error, just log it console.warn('Failed to log query:', error); } }; const clearResults = () => { setResults(null); setLlmAnswer(null); setErrorMessage(null); }; return (
{/* Main Content */}
{/* Header Section */}
Back to Documents
{/* Two Column Layout */}
{/* Left Column - Database Connections */}
{/* Performance Metrics Card */} {metrics && (

Performance Metrics

View All
{/* Query times by mode */} {metrics.queryTimesByMode && Object.keys(metrics.queryTimesByMode).length > 0 ? ( <> {metrics.queryTimesByMode['pure-rag'] !== undefined && (
Pure RAG: {(metrics.queryTimesByMode['pure-rag'] / 1000).toFixed(2)}s
)} {metrics.queryTimesByMode['traditional'] !== undefined && (
Graph Search: {(metrics.queryTimesByMode['traditional'] / 1000).toFixed(2)}s
)} {metrics.queryTimesByMode['vector-search'] !== undefined && (
GraphRAG: {(metrics.queryTimesByMode['vector-search'] / 1000).toFixed(2)}s
)} ) : (
Avg. Query Time: {metrics.avgQueryTime > 0 ? `${metrics.avgQueryTime.toFixed(2)}ms` : "No data"}
)}
)}
{/* Right Column - RAG Query Interface */}
{/* LLM Answer Section */} {llmAnswer && (

Answer

{currentParams.queryMode && ( {currentParams.queryMode === 'pure-rag' ? 'Pure RAG' : currentParams.queryMode === 'vector-search' ? 'GraphRAG' : 'Graph Search'} )}
{(() => { // Parse tags const thinkMatch = llmAnswer.match(/([\s\S]*?)<\/think>/); const thinkContent = thinkMatch ? thinkMatch[1].trim() : null; const mainAnswer = thinkContent ? llmAnswer.replace(/[\s\S]*?<\/think>/, '').trim() : llmAnswer; return ( <> {thinkContent && (
Reasoning Process
{thinkContent}
)}
$1') .replace(/\*(.*?)\*/g, '$1') }} />
); })()}
)} {/* Results Section */} {results && results.length > 0 && !currentParams.usePureRag && (

{llmAnswer ? `Retrieved Knowledge (${results.length})` : `Results (${results.length})`}

{results.some((r: any) => r.pathLength && r.pathLength > 1) && ( Multi-hop enabled )}
{results.map((triple, index) => (
{currentParams.usePureRag ? ( // Pure RAG display format (no subject/predicate/object columns)
{triple.usedFallback && (
Using general knowledge (no documents found)
)}

{triple.object}

) : ( // Standard triple display for other modes

Subject

{triple.subject}

Predicate

{triple.predicate}

Object

{triple.object}

)} {triple.confidence && !currentParams.usePureRag && (
Confidence: {(triple.confidence * 100).toFixed(1)}%
{triple.depth !== undefined && (
Hop: {triple.depth + 1}
)} {triple.pathLength !== undefined && triple.pathLength > 1 && (
Multi-hop path (length: {triple.pathLength})
)}
)}
))}
)} {results && results.length === 0 && !isLoading && !currentParams.usePureRag && (

No results found for your query

Try adjusting your query or parameters

)}
); }