AI cyfer query working

This commit is contained in:
abhigyantrumio 2025-09-23 04:20:02 +05:30
parent e98d921e32
commit 35b73f1b33
6 changed files with 245 additions and 42 deletions

View file

@ -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'
};
}
}
/**

View file

@ -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();

View file

@ -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
};

View file

@ -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;
}
}

View file

@ -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

View file

@ -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