mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-09 22:33:39 +00:00
AI cyfer query working
This commit is contained in:
parent
e98d921e32
commit
35b73f1b33
6 changed files with 245 additions and 42 deletions
|
|
@ -62,10 +62,12 @@ export class ReActAgent {
|
|||
private cypherGenerator: CypherGenerator;
|
||||
private context: ReActContext | null = null;
|
||||
private chatHistory: LocalStorageChatHistory | null = null;
|
||||
private graph?: KnowledgeGraph;
|
||||
|
||||
constructor(llmService: LLMService, cypherGenerator: CypherGenerator, _kuzuQueryEngine?: any) {
|
||||
constructor(llmService: LLMService, cypherGenerator: CypherGenerator, graph?: KnowledgeGraph) {
|
||||
this.llmService = llmService;
|
||||
this.cypherGenerator = cypherGenerator;
|
||||
this.graph = graph; // Store graph reference for KuzuDB access
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -85,7 +87,33 @@ export class ReActAgent {
|
|||
projectName: context.projectName,
|
||||
sessionId: context.sessionId
|
||||
};
|
||||
|
||||
// Update graph reference for KuzuDB access
|
||||
this.graph = context.graph;
|
||||
|
||||
this.cypherGenerator.updateSchema(context.graph);
|
||||
|
||||
// Test KuzuDB connectivity
|
||||
await this.testKuzuDBConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test KuzuDB connection and log results
|
||||
*/
|
||||
private async testKuzuDBConnection(): Promise<void> {
|
||||
try {
|
||||
console.log('🧪 Testing KuzuDB connection...');
|
||||
const testResult = await this.executeGraphQuery('MATCH (n) RETURN COUNT(n) as nodeCount LIMIT 1');
|
||||
|
||||
if (testResult.success && testResult.source === 'KuzuDB') {
|
||||
console.log('✅ KuzuDB connection test successful!');
|
||||
console.log(`📊 Total nodes in KuzuDB: ${testResult.rows[0]?.[0] || 'unknown'}`);
|
||||
} else {
|
||||
console.log('⚠️ KuzuDB connection test failed, using fallback');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('❌ KuzuDB connection test error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -352,8 +380,23 @@ CRITICAL: The action field must be exactly one of these four values: query_graph
|
|||
maxRetries: 3
|
||||
});
|
||||
|
||||
// Execute the query (placeholder - implement based on your graph engine)
|
||||
const results = await this.executeGraphQuery(cypherQuery.cypher);
|
||||
// Add LIMIT if not present to prevent JSON truncation issues
|
||||
let finalCypher = cypherQuery.cypher;
|
||||
if (!finalCypher.toLowerCase().includes('limit')) {
|
||||
finalCypher += ' LIMIT 20';
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
const results = await this.executeGraphQuery(finalCypher);
|
||||
|
||||
// Truncate large responses to prevent JSON parsing issues
|
||||
if (results.rows && results.rows.length > 20) {
|
||||
results.rows = results.rows.slice(0, 20);
|
||||
results.rowCount = 20;
|
||||
results.truncated = true;
|
||||
results.summary += ' (showing first 20 results)';
|
||||
}
|
||||
|
||||
output = JSON.stringify(results, null, 2);
|
||||
success = true;
|
||||
} catch (error) {
|
||||
|
|
@ -424,12 +467,93 @@ CRITICAL: The action field must be exactly one of these four values: query_graph
|
|||
}
|
||||
|
||||
/**
|
||||
* Execute a graph query (placeholder implementation)
|
||||
* Execute a graph query using KuzuDB if available, fallback to JSON graph
|
||||
*/
|
||||
private async executeGraphQuery(cypher: string): Promise<any> {
|
||||
// This is a placeholder - implement based on your graph engine
|
||||
console.log('Executing Cypher query:', cypher);
|
||||
return { nodes: [], relationships: [], message: 'Graph query executed (placeholder)' };
|
||||
console.log('🔍 ReActAgent executing Cypher query:', cypher);
|
||||
|
||||
// Try to get KuzuQueryEngine from DualWriteKnowledgeGraph
|
||||
if (this.graph && 'getKuzuGraph' in this.graph) {
|
||||
const kuzuGraph = (this.graph as any).getKuzuGraph();
|
||||
if (kuzuGraph && 'executeQuery' in kuzuGraph) {
|
||||
try {
|
||||
console.log('🚀 Using KuzuDB for query execution');
|
||||
const result = await kuzuGraph.executeQuery(cypher);
|
||||
|
||||
// Format result for AI consumption
|
||||
const formattedResult = {
|
||||
success: true,
|
||||
source: 'KuzuDB',
|
||||
columns: result.columns || [],
|
||||
rows: result.rows || [],
|
||||
rowCount: result.rowCount || result.rows?.length || 0,
|
||||
executionTime: result.executionTime || 0,
|
||||
// Add human-readable summary
|
||||
summary: `Found ${result.rowCount || result.rows?.length || 0} results in ${result.executionTime || 0}ms`
|
||||
};
|
||||
|
||||
console.log(`✅ KuzuDB query successful: ${formattedResult.rowCount} rows returned`);
|
||||
return formattedResult;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ KuzuDB query failed:', error);
|
||||
console.log('🔄 Falling back to JSON graph query');
|
||||
|
||||
// Return error info for AI to understand what went wrong
|
||||
return {
|
||||
success: false,
|
||||
source: 'KuzuDB',
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
fallback: 'Attempting JSON graph query...'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to JSON graph query
|
||||
console.log('📊 Using JSON graph fallback');
|
||||
return this.fallbackGraphQuery(cypher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback graph query using the JSON graph and GraphQueryEngine
|
||||
*/
|
||||
private async fallbackGraphQuery(cypher: string): Promise<any> {
|
||||
if (!this.context?.graph) {
|
||||
return {
|
||||
nodes: [],
|
||||
relationships: [],
|
||||
message: 'No graph context available',
|
||||
success: false,
|
||||
source: 'none'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// Use existing GraphQueryEngine for fallback
|
||||
const { GraphQueryEngine } = await import('../core/graph/query-engine.ts');
|
||||
const queryEngine = new GraphQueryEngine(this.context.graph);
|
||||
|
||||
console.log('📄 Using JSON graph fallback for query execution');
|
||||
const result = queryEngine.executeQuery(cypher);
|
||||
return {
|
||||
nodes: result.nodes,
|
||||
relationships: result.relationships,
|
||||
data: result.data,
|
||||
success: true,
|
||||
source: 'JSON'
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('❌ Fallback query failed:', error);
|
||||
return {
|
||||
nodes: [],
|
||||
relationships: [],
|
||||
message: 'Query execution failed',
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
source: 'error'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -286,9 +286,17 @@ class KuzuInstanceImpl implements KuzuInstance {
|
|||
const columns = result.getColumnNames();
|
||||
const rows: any[][] = [];
|
||||
|
||||
// Fetch all rows
|
||||
// Fetch all rows and handle BigInt serialization
|
||||
while (result.hasNext()) {
|
||||
rows.push(result.getNext());
|
||||
const row = result.getNext();
|
||||
// Convert BigInt values to strings to avoid serialization issues
|
||||
const processedRow = row.map(cell => {
|
||||
if (typeof cell === 'bigint') {
|
||||
return cell.toString();
|
||||
}
|
||||
return cell;
|
||||
});
|
||||
rows.push(processedRow);
|
||||
}
|
||||
|
||||
result.close();
|
||||
|
|
|
|||
|
|
@ -156,13 +156,23 @@ function createKuzuInstance(): KuzuInstance {
|
|||
|
||||
// Extract data from kuzu-wasm result
|
||||
const columns = result.getColumnNames();
|
||||
const rows = await result.getAllRows(); // Get all rows at once (async)
|
||||
const rawRows = await result.getAllRows(); // Get all rows at once (async)
|
||||
const rowCount = await result.getNumTuples(); // This might be async too
|
||||
|
||||
// Convert BigInt values to strings to avoid serialization issues
|
||||
const rows = rawRows.map(row =>
|
||||
row.map(cell => {
|
||||
if (typeof cell === 'bigint') {
|
||||
return cell.toString();
|
||||
}
|
||||
return cell;
|
||||
})
|
||||
);
|
||||
|
||||
const queryResult: QueryResult = {
|
||||
columns,
|
||||
rows,
|
||||
rowCount,
|
||||
rowCount: typeof rowCount === 'bigint' ? Number(rowCount) : rowCount,
|
||||
executionTime: 0 // TODO: Add timing if available
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -70,21 +70,11 @@ export class IngestionService {
|
|||
throw new Error(result.error || 'Processing failed');
|
||||
}
|
||||
|
||||
// Recreate the appropriate graph based on worker metadata
|
||||
const graph = await this.recreateGraphFromResult(result);
|
||||
|
||||
return {
|
||||
graph: {
|
||||
nodes: result.nodes || [],
|
||||
relationships: result.relationships || [],
|
||||
addNode: () => {},
|
||||
addRelationship: () => {},
|
||||
removeNode: () => {},
|
||||
removeRelationship: () => {},
|
||||
clear: () => {},
|
||||
getNodeById: () => null,
|
||||
getRelationshipById: () => null,
|
||||
getNodesByLabel: () => [],
|
||||
getRelationshipsByType: () => [],
|
||||
getConnectedNodes: () => ({ incoming: [], outgoing: [] })
|
||||
},
|
||||
graph,
|
||||
fileContents
|
||||
};
|
||||
} finally {
|
||||
|
|
@ -137,21 +127,11 @@ export class IngestionService {
|
|||
throw new Error(result.error || 'Processing failed');
|
||||
}
|
||||
|
||||
// Recreate the appropriate graph based on worker metadata
|
||||
const graph = await this.recreateGraphFromResult(result);
|
||||
|
||||
return {
|
||||
graph: {
|
||||
nodes: result.nodes || [],
|
||||
relationships: result.relationships || [],
|
||||
addNode: () => {},
|
||||
addRelationship: () => {},
|
||||
removeNode: () => {},
|
||||
removeRelationship: () => {},
|
||||
clear: () => {},
|
||||
getNodeById: () => null,
|
||||
getRelationshipById: () => null,
|
||||
getNodesByLabel: () => [],
|
||||
getRelationshipsByType: () => [],
|
||||
getConnectedNodes: () => ({ incoming: [], outgoing: [] })
|
||||
},
|
||||
graph,
|
||||
fileContents
|
||||
};
|
||||
} finally {
|
||||
|
|
@ -219,4 +199,64 @@ export class IngestionService {
|
|||
|
||||
return structure; // No normalization if prefix isn't common enough
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreate the appropriate graph type based on worker result metadata
|
||||
*/
|
||||
private async recreateGraphFromResult(result: any): Promise<KnowledgeGraph> {
|
||||
console.log(`📊 Recreating graph: type=${result.graphType}, kuzuEnabled=${result.kuzuEnabled}`);
|
||||
|
||||
if (result.graphType === 'DualWriteKnowledgeGraph' && result.kuzuEnabled) {
|
||||
try {
|
||||
// Recreate DualWriteKnowledgeGraph with KuzuDB
|
||||
console.log('🚀 Recreating DualWriteKnowledgeGraph with KuzuDB...');
|
||||
|
||||
// Initialize KuzuDB query engine
|
||||
const { KuzuQueryEngine } = await import('../core/graph/kuzu-query-engine.ts');
|
||||
const queryEngine = new KuzuQueryEngine({
|
||||
enableCache: true,
|
||||
cacheSize: 1000,
|
||||
cacheTTL: 5 * 60 * 1000 // 5 minutes
|
||||
});
|
||||
|
||||
await queryEngine.initialize();
|
||||
|
||||
// Create KuzuDB knowledge graph
|
||||
const { KuzuKnowledgeGraph } = await import('../core/graph/kuzu-knowledge-graph.ts');
|
||||
const kuzuGraph = new KuzuKnowledgeGraph(queryEngine, {
|
||||
enableCache: true,
|
||||
batchSize: 100,
|
||||
autoCommit: false
|
||||
});
|
||||
|
||||
// Create dual-write graph
|
||||
const { DualWriteKnowledgeGraph } = await import('../core/graph/dual-write-knowledge-graph.ts');
|
||||
const dualWriteGraph = new DualWriteKnowledgeGraph(kuzuGraph);
|
||||
|
||||
// Add the data from worker result
|
||||
(result.nodes || []).forEach((node: any) => dualWriteGraph.addNode(node));
|
||||
(result.relationships || []).forEach((rel: any) => dualWriteGraph.addRelationship(rel));
|
||||
|
||||
// Commit to KuzuDB
|
||||
await dualWriteGraph.flushKuzuDB();
|
||||
|
||||
console.log('✅ Successfully recreated DualWriteKnowledgeGraph with KuzuDB');
|
||||
return dualWriteGraph;
|
||||
|
||||
} catch (error) {
|
||||
console.warn('❌ Failed to recreate DualWriteKnowledgeGraph, falling back to SimpleKnowledgeGraph:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to SimpleKnowledgeGraph
|
||||
console.log('📄 Creating SimpleKnowledgeGraph fallback');
|
||||
const { SimpleKnowledgeGraph } = await import('../core/graph/graph.ts');
|
||||
const simpleGraph = new SimpleKnowledgeGraph();
|
||||
|
||||
// Add nodes and relationships from result
|
||||
(result.nodes || []).forEach((node: any) => simpleGraph.addNode(node));
|
||||
(result.relationships || []).forEach((rel: any) => simpleGraph.addRelationship(rel));
|
||||
|
||||
return simpleGraph;
|
||||
}
|
||||
}
|
||||
|
|
@ -107,8 +107,8 @@ const ChatInterface: React.FC<ChatInterfaceProps> = ({
|
|||
// Services
|
||||
const [llmService] = useState(new LLMService());
|
||||
const [cypherGenerator] = useState(new CypherGenerator(llmService));
|
||||
// KuzuDB query engine removed - using graph directly
|
||||
const [ragOrchestrator] = useState(new ReActAgent(llmService, cypherGenerator, graph));
|
||||
// Create ReActAgent with initial graph, will be updated in useEffect
|
||||
const [ragOrchestrator] = useState(() => new ReActAgent(llmService, cypherGenerator, graph));
|
||||
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
|
@ -119,6 +119,12 @@ const ChatInterface: React.FC<ChatInterfaceProps> = ({
|
|||
try {
|
||||
await ragOrchestrator.initialize();
|
||||
|
||||
// Update graph reference in ReActAgent
|
||||
if (graph) {
|
||||
(ragOrchestrator as any).graph = graph;
|
||||
console.log('✅ Updated graph reference in ReActAgent:', graph.constructor.name);
|
||||
}
|
||||
|
||||
// Only set context if we have valid graph data
|
||||
if (graph && graph.nodes && graph.nodes.length > 0) {
|
||||
// Get or create session
|
||||
|
|
|
|||
|
|
@ -22,11 +22,14 @@ export interface IngestionResult {
|
|||
callStats: { totalCalls: number; callTypes: Record<string, number> };
|
||||
};
|
||||
duration: number;
|
||||
graphType?: 'DualWriteKnowledgeGraph' | 'SimpleKnowledgeGraph';
|
||||
kuzuEnabled?: boolean;
|
||||
}
|
||||
|
||||
export class IngestionWorker {
|
||||
private pipeline: GraphPipeline | ParallelGraphPipeline;
|
||||
private progressCallback?: (progress: IngestionProgress) => void;
|
||||
private currentGraph: KnowledgeGraph | null = null;
|
||||
|
||||
constructor() {
|
||||
// Choose pipeline based on feature flag - same logic as main thread
|
||||
|
|
@ -99,6 +102,9 @@ export class IngestionWorker {
|
|||
fileContents: fileContentsMap
|
||||
});
|
||||
|
||||
// Store the graph for later access
|
||||
this.currentGraph = graph;
|
||||
|
||||
// Note: Keeping file contents available for UI components
|
||||
// fileContentsMap.clear(); // Commented out to preserve file contents for SourceViewer
|
||||
|
||||
|
|
@ -119,6 +125,12 @@ export class IngestionWorker {
|
|||
relationshipStats[rel.type] = (relationshipStats[rel.type] || 0) + 1;
|
||||
});
|
||||
|
||||
// Determine graph type and KuzuDB status
|
||||
const graphType = graph.constructor.name === 'DualWriteKnowledgeGraph' ? 'DualWriteKnowledgeGraph' : 'SimpleKnowledgeGraph';
|
||||
const kuzuEnabled = graphType === 'DualWriteKnowledgeGraph' && 'isKuzuDBEnabled' in graph && (graph as any).isKuzuDBEnabled();
|
||||
|
||||
console.log(`📊 Worker returning graph type: ${graphType}, KuzuDB enabled: ${kuzuEnabled}`);
|
||||
|
||||
// Return only serializable data - nodes and relationships arrays
|
||||
return {
|
||||
success: true,
|
||||
|
|
@ -129,7 +141,9 @@ export class IngestionWorker {
|
|||
relationshipStats,
|
||||
callStats: { totalCalls: 0, callTypes: {} }
|
||||
},
|
||||
duration
|
||||
duration,
|
||||
graphType: graphType as 'DualWriteKnowledgeGraph' | 'SimpleKnowledgeGraph',
|
||||
kuzuEnabled
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('IngestionWorker: Processing failed:', error);
|
||||
|
|
@ -254,6 +268,7 @@ export class IngestionWorker {
|
|||
// Cleanup resources if needed
|
||||
console.log('Ingestion worker terminated');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Expose the worker class via Comlink
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue