Browse Source

integrated socket to publish agent palns and exeuction results

Dr-Swopt 1 week ago
parent
commit
e45a50d12b

+ 3 - 3
AgentQueryPlan.json

@@ -1,6 +1,6 @@
 {
   "description": "MongoDB Query Planner for FFB Production",
-  "instructions": "You are an intelligent MongoDB query planner for FFBProduction.\n\nYour job is to:\n1. Understand the user's question and extract intent (AGGREGATE or SEARCH).\n2. Generate a minimal preFilter ($match or $vectorSearch.filter) for efficiency.\n3. Decide whether a vector search or pure aggregation is needed and output vectorQuery accordingly.\n4. Build a postPipeline array (aggregation stages after the search or match) to compute summaries, projections, or other transformations.\n5. Parse natural language dates into ISO format (YYYY-MM-DD).\n6. Use only allowed fields: [\"site\",\"phase\",\"block\",\"productionDate\",\"weight\",\"quantity\"].\n7. Use only allowed operators: [\"$eq\",\"$in\",\"$gte\",\"$lte\"].\n8. Output valid JSON only, no extra text.",
+  "instructions": "You are an intelligent MongoDB query planner for FFBProduction.\n\nYour job is to:\n1. Understand the user's question and extract intent (AGGREGATE or SEARCH).\n2. Generate a minimal preFilter ($match or $vectorSearch.filter) for efficiency.\n3. Decide whether a vector search or pure aggregation is needed and output vectorQuery accordingly.\n4. Build a postPipeline array (aggregation stages after the search or match) to compute summaries, projections, or other transformations.\n5. Parse natural language dates into ISO format (YYYY-MM-DD).\n6. Use only allowed fields: [\"site\",\"phase\",\"block\",\"productionDate\",\"weight\",\"quantity\"].\n7. Use only allowed operators: [\"$eq\",\"$in\",\"$gte\",\"$lte\"].\n8. Output valid JSON only, no extra text. Try to set the limit higher so that you can factor in as many data as possible",
   "examples": [
     {
       "question": "Total output of FFB production in Site A during November and December",
@@ -14,7 +14,7 @@
           }
         },
         "vectorQuery": null,
-        "vectorOptions": { "limit": 5, "numCandidates": 50 },
+        "vectorOptions": { "limit": 50, "numCandidates": 50 },
         "postPipeline": [
           { "$group": { "_id": "$site", "totalWeight": { "$sum": "$weight" } } },
           { "$project": { "site": "$_id", "totalWeight": 1, "_id": 0 } }
@@ -28,7 +28,7 @@
         "intent": "SEARCH",
         "preFilter": { "site": "Site B" },
         "vectorQuery": "highest producing block in Site B",
-        "vectorOptions": { "limit": 5, "numCandidates": 50 },
+        "vectorOptions": { "limit": 50, "numCandidates": 50 },
         "postPipeline": [
           { "$project": { "site": 1, "phase": 1, "block": 1, "weight": 1, "quantity": 1, "_id": 0 } }
         ],

+ 1 - 1
AgentResultCompiler.json

@@ -1,6 +1,6 @@
 {
   "description": "Result Compiler for FFB Production Queries",
-  "instructions": "You are a result compiler for FFBProduction data.\n\nYour job is to:\n1. Take the raw results from MongoDB aggregation or vector search (JSON array).\n2. Take the original AgentQueryPlan for context.\n3. Perform any arithmetic/aggregation needed (if not already done in postPipeline).\n4. Produce a clear, concise natural language answer for the user.\n5. Do not invent data; only use the provided results.\n6. Include units (e.g., kg for weight) when summarizing production.\n7. If results are empty, politely indicate that no data matches the query.\n8. Return ONLY the natural language answer (no JSON).",
+  "instructions": "You are a result compiler for FFBProduction data.\n\nYour job is to:\n1. Take the raw results from MongoDB aggregation or vector search (JSON array).\n2. Take the original AgentQueryPlan for context.\n3. You do NOT need to perform any arithmetic or aggregation; assume all computations are already done in the results.\n4. Produce a clear, concise natural language answer for the user based on the results.\n5. Do not invent data; only use the provided results.\n6. Include units (e.g., kg for weight) when summarizing production.\n7. If results are empty, politely indicate that no data matches the query.\n8. Return ONLY the natural language answer (no JSON).",
   "examples": [
     {
       "plan": {

+ 0 - 3
src/FFB/ffb-production.controller.ts

@@ -18,13 +18,10 @@ export class FFBProductionController {
   async query(@Body('message') message: string) {
     // 1. Planner generates AgentQueryPlan
     const plan = await this.planner.plan(message);
-    console.log(plan)
     // 2. Executor runs query against MongoDB / vector store
     const rawResults = await this.executor.execute(plan);
-    console.log(rawResults)
     // 3. Compiler formats results into natural language
     const answer = await this.compiler.compile(plan, rawResults);
-    console.log(answer)
 
     return { answer }; // Return final human-readable response
   }

+ 4 - 2
src/FFB/ffb-production.module.ts

@@ -6,12 +6,14 @@ import { FFBVectorService } from './ffb-vector.service';
 import { FFBQueryExecutorService } from './ffb-query-executor.service';
 import { FFBQueryPlannerService } from './ffb-query-planner.service';
 import { FFBResultCompilerService } from './ffb-result-compiler.service';
+import { FFBGateway } from './ffb.gateway';
 
 @Module({
   imports: [
     MongoModule
   ],
   controllers: [FFBProductionController],
-  providers: [FFBProductionService, FFBVectorService, FFBQueryExecutorService, FFBQueryPlannerService, FFBResultCompilerService],
+  providers: [FFBProductionService, FFBVectorService, FFBQueryExecutorService, FFBQueryPlannerService, FFBResultCompilerService, FFBGateway],
+  exports: [FFBGateway],
 })
-export class FFBProductionModule {}
+export class FFBProductionModule { }

+ 10 - 1
src/FFB/ffb-query-executor.service.ts

@@ -3,6 +3,7 @@ import { AgentQueryPlan } from './ffb-agent.types';
 import { FFBVectorService } from './ffb-vector.service';
 import { MongoCoreService } from 'src/mongo/mongo-core.service';
 import { FFBProductionRepository } from 'src/mongo/mongo-ffb-production.repository';
+import { FFBGateway } from './ffb.gateway';
 
 @Injectable()
 export class FFBQueryExecutorService {
@@ -11,6 +12,7 @@ export class FFBQueryExecutorService {
     constructor(
         private readonly mongoCore: MongoCoreService,
         private readonly vectorService: FFBVectorService,
+        private readonly gateway: FFBGateway,
     ) { }
 
     private async getRepo() {
@@ -74,11 +76,18 @@ export class FFBQueryExecutorService {
             ];
         }
 
-        console.log('--- Aggregation pipeline ---\n', JSON.stringify(pipeline, null, 2));
+        // console.log('--- Aggregation pipeline ---\n', JSON.stringify(pipeline, null, 2));
 
         const results = await repo.aggregate(pipeline);
         console.log('--- Raw results ---\n', results);
 
+        this.gateway.emitExecutorResult({
+            receivedPlan: plan,
+            pipeline: pipeline,
+            count: results.length,
+            results,
+        });
+
         return results;
     }
 }

+ 23 - 1
src/FFB/ffb-query-planner.service.ts

@@ -3,11 +3,16 @@ import * as fs from 'fs';
 import * as path from 'path';
 import axios from 'axios';
 import { AgentQueryPlan } from './ffb-agent.types';
+import { FFBGateway } from './ffb.gateway';
 
 @Injectable()
 export class FFBQueryPlannerService implements OnModuleInit {
   private systemPrompt: any;
 
+  constructor(private readonly gateway: FFBGateway,) {
+    // Logic here 
+  }
+
   async onModuleInit() {
     const filePath = path.join(process.cwd(), 'AgentQueryPlan.json'); // updated file
     const data = fs.readFileSync(filePath, 'utf-8');
@@ -40,10 +45,27 @@ Q: "${userMessage}"
     const responseText = await this.callGemini(promptText);
     const sanitized = this.sanitizeLLMOutput(responseText);
 
+    this.gateway.emitPlannerOutput({
+      stage: 'raw_llm_output',
+      content: responseText,
+    });
+
     try {
-      return JSON.parse(sanitized);
+      const plan = JSON.parse(sanitized);
+
+      this.gateway.emitPlannerOutput({
+        stage: 'parsed_plan',
+        plan,
+      });
+
+      return plan;
     } catch (err) {
       console.error('Failed to parse Gemini output:', sanitized);
+      this.gateway.emitError({
+        source: 'planner',
+        error: 'Invalid JSON returned by LLM',
+        raw: sanitized,
+      });
       throw new Error('LLM returned invalid JSON');
     }
   }

+ 28 - 0
src/FFB/ffb.gateway.ts

@@ -0,0 +1,28 @@
+import {
+  WebSocketGateway,
+  WebSocketServer,
+} from '@nestjs/websockets';
+import { Server } from 'socket.io';
+
+@WebSocketGateway({
+  namespace: '/ffb',
+  cors: {
+    origin: '*',
+  },
+})
+export class FFBGateway {
+  @WebSocketServer()
+  server: Server;
+
+  emitPlannerOutput(payload: any) {
+    this.server.emit('planner.output', payload);
+  }
+
+  emitExecutorResult(payload: any) {
+    this.server.emit('executor.result', payload);
+  }
+
+  emitError(payload: any) {
+    this.server.emit('error', payload);
+  }
+}

+ 1 - 0
src/mongo/mongo-ffb-production.repository.ts

@@ -80,6 +80,7 @@ export class FFBProductionRepository {
 
     // Execute aggregation
     const results = await this.collection.aggregate(pipeline).toArray();
+    // console.log('Aggregation results:', results);
 
     // Optional: strip out any internal vector fields if accidentally included
     return results.map(r => {