@vezlo/assistant-server 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +1 -1
  2. package/dist/src/bootstrap/initializeServices.d.ts +2 -0
  3. package/dist/src/bootstrap/initializeServices.d.ts.map +1 -1
  4. package/dist/src/bootstrap/initializeServices.js +4 -1
  5. package/dist/src/bootstrap/initializeServices.js.map +1 -1
  6. package/dist/src/controllers/ChatController.d.ts +2 -12
  7. package/dist/src/controllers/ChatController.d.ts.map +1 -1
  8. package/dist/src/controllers/ChatController.js +38 -192
  9. package/dist/src/controllers/ChatController.js.map +1 -1
  10. package/dist/src/controllers/KnowledgeController.d.ts +8 -1
  11. package/dist/src/controllers/KnowledgeController.d.ts.map +1 -1
  12. package/dist/src/controllers/KnowledgeController.js +38 -1
  13. package/dist/src/controllers/KnowledgeController.js.map +1 -1
  14. package/dist/src/server.js +44 -1
  15. package/dist/src/server.js.map +1 -1
  16. package/dist/src/services/CitationService.d.ts +14 -0
  17. package/dist/src/services/CitationService.d.ts.map +1 -0
  18. package/dist/src/services/CitationService.js +76 -0
  19. package/dist/src/services/CitationService.js.map +1 -0
  20. package/dist/src/services/ResponseGenerationService.d.ts +65 -0
  21. package/dist/src/services/ResponseGenerationService.d.ts.map +1 -0
  22. package/dist/src/services/ResponseGenerationService.js +165 -0
  23. package/dist/src/services/ResponseGenerationService.js.map +1 -0
  24. package/dist/src/services/ResponseStreamingService.d.ts +33 -0
  25. package/dist/src/services/ResponseStreamingService.d.ts.map +1 -0
  26. package/dist/src/services/ResponseStreamingService.js +114 -0
  27. package/dist/src/services/ResponseStreamingService.js.map +1 -0
  28. package/package.json +1 -1
package/README.md CHANGED
@@ -727,4 +727,4 @@ This project is dual-licensed:
727
727
 
728
728
  ---
729
729
 
730
- **Status**: ✅ Production Ready | **Version**: 2.7.0 | **Node.js**: 20+ | **TypeScript**: 5+
730
+ **Status**: ✅ Production Ready | **Version**: 2.8.0 | **Node.js**: 20+ | **TypeScript**: 5+
@@ -1,6 +1,7 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { UnifiedStorage } from '../storage/UnifiedStorage';
3
3
  import { KnowledgeBaseService } from '../services/KnowledgeBaseService';
4
+ import { CitationService } from '../services/CitationService';
4
5
  import { AIService } from '../services/AIService';
5
6
  import { ChatManager } from '../services/ChatManager';
6
7
  import { ChatController } from '../controllers/ChatController';
@@ -25,6 +26,7 @@ export interface InitializedCoreServices {
25
26
  chatManager: ChatManager;
26
27
  apiKeyService: ApiKeyService;
27
28
  companyService: CompanyService;
29
+ citationService: CitationService;
28
30
  };
29
31
  controllers: {
30
32
  chatController: ChatController;
@@ -1 +1 @@
1
- {"version":3,"file":"initializeServices.d.ts","sourceRoot":"","sources":["../../../src/bootstrap/initializeServices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAIrE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,cAAc,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE;QACR,OAAO,EAAE,cAAc,CAAC;QACxB,aAAa,EAAE,oBAAoB,CAAC;QACpC,SAAS,EAAE,SAAS,CAAC;QACrB,WAAW,EAAE,WAAW,CAAC;QACzB,aAAa,EAAE,aAAa,CAAC;QAC7B,cAAc,EAAE,cAAc,CAAC;KAChC,CAAC;IACF,WAAW,EAAE;QACX,cAAc,EAAE,cAAc,CAAC;QAC/B,mBAAmB,EAAE,mBAAmB,CAAC;QACzC,cAAc,EAAE,cAAc,CAAC;QAC/B,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,iBAAiB,EAAE,iBAAiB,CAAC;KACtC,CAAC;IACF,MAAM,EAAE;QACN,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH;AAKD,wBAAgB,oBAAoB,IAAI,MAAM,CAa7C;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,kBAAkB,GAAG,uBAAuB,CA8G3F"}
1
+ {"version":3,"file":"initializeServices.d.ts","sourceRoot":"","sources":["../../../src/bootstrap/initializeServices.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAIrE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,cAAc,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE;QACR,OAAO,EAAE,cAAc,CAAC;QACxB,aAAa,EAAE,oBAAoB,CAAC;QACpC,SAAS,EAAE,SAAS,CAAC;QACrB,WAAW,EAAE,WAAW,CAAC;QACzB,aAAa,EAAE,aAAa,CAAC;QAC7B,cAAc,EAAE,cAAc,CAAC;QAC/B,eAAe,EAAE,eAAe,CAAC;KAClC,CAAC;IACF,WAAW,EAAE;QACX,cAAc,EAAE,cAAc,CAAC;QAC/B,mBAAmB,EAAE,mBAAmB,CAAC;QACzC,cAAc,EAAE,cAAc,CAAC;QAC/B,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,iBAAiB,EAAE,iBAAiB,CAAC;KACtC,CAAC;IACF,MAAM,EAAE;QACN,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH;AAKD,wBAAgB,oBAAoB,IAAI,MAAM,CAa7C;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,kBAAkB,GAAG,uBAAuB,CAiH3F"}
@@ -8,6 +8,7 @@ exports.initializeCoreServices = initializeCoreServices;
8
8
  const logger_1 = __importDefault(require("../config/logger"));
9
9
  const UnifiedStorage_1 = require("../storage/UnifiedStorage");
10
10
  const KnowledgeBaseService_1 = require("../services/KnowledgeBaseService");
11
+ const CitationService_1 = require("../services/CitationService");
11
12
  const AIService_1 = require("../services/AIService");
12
13
  const ChatManager_1 = require("../services/ChatManager");
13
14
  const ChatController_1 = require("../controllers/ChatController");
@@ -44,6 +45,7 @@ function initializeCoreServices(options) {
44
45
  supabase,
45
46
  tableName: knowledgeTableName
46
47
  });
48
+ const citationService = new CitationService_1.CitationService(supabase, tablePrefix);
47
49
  // Initialize V2 service for adjacent chunk retrieval
48
50
  // Read AI configuration from environment
49
51
  // Use AI_MODEL for all OpenAI calls (intent classification + response generation)
@@ -94,7 +96,7 @@ function initializeCoreServices(options) {
94
96
  intentService,
95
97
  realtimePublisher
96
98
  });
97
- const knowledgeController = new KnowledgeController_1.KnowledgeController(knowledgeBase, aiService);
99
+ const knowledgeController = new KnowledgeController_1.KnowledgeController(knowledgeBase, aiService, citationService);
98
100
  const authController = new AuthController_1.AuthController(supabase);
99
101
  const apiKeyService = new ApiKeyService_1.ApiKeyService(supabase);
100
102
  const apiKeyController = new ApiKeyController_1.ApiKeyController(apiKeyService);
@@ -104,6 +106,7 @@ function initializeCoreServices(options) {
104
106
  services: {
105
107
  storage,
106
108
  knowledgeBase,
109
+ citationService,
107
110
  aiService,
108
111
  chatManager,
109
112
  apiKeyService,
@@ -1 +1 @@
1
- {"version":3,"file":"initializeServices.js","sourceRoot":"","sources":["../../../src/bootstrap/initializeServices.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"initializeServices.js","sourceRoot":"","sources":["../../../src/bootstrap/initializeServices.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,8 @@ export declare class ChatController {
12
12
  private chatHistoryLength;
13
13
  private intentService?;
14
14
  private realtimePublisher?;
15
+ private responseGenerationService;
16
+ private responseStreamingService;
15
17
  constructor(chatManager: ChatManager, storage: UnifiedStorage, supabase: SupabaseClient, options?: {
16
18
  historyLength?: number;
17
19
  intentService?: IntentService;
@@ -30,18 +32,6 @@ export declare class ChatController {
30
32
  deleteConversation(req: Request, res: Response): Promise<void>;
31
33
  submitFeedback(req: Request | AuthenticatedRequest, res: Response): Promise<void>;
32
34
  deleteFeedback(req: Request | AuthenticatedRequest, res: Response): Promise<void>;
33
- private classifyIntent;
34
- /**
35
- * Handle intent classification result
36
- * Returns response content if non-knowledge intent, null if knowledge intent
37
- */
38
- private handleIntentResult;
39
- /**
40
- * Stream text content word by word to simulate streaming
41
- * This ensures consistent SSE format for all responses
42
- */
43
- private streamTextContent;
44
- private getFallbackResponse;
45
35
  private respondWithAssistantMessage;
46
36
  private saveAssistantMessage;
47
37
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ChatController.d.ts","sourceRoot":"","sources":["../../../src/controllers/ChatController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,aAAa,EAA8B,MAAM,2BAA2B,CAAC;AAEtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAElE,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,iBAAiB,CAAC,CAAoB;gBAG5C,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,aAAa,CAAC;QAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;KAAO;IAY1G,kBAAkB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA4I3E,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAyF7D,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAsPzE,eAAe,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAuDxE,uBAAuB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA4DhF,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAuGzE,iBAAiB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoG1E,mBAAmB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA6E5E,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAmGzE,oBAAoB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoE7E,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB9D,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgEjF,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;YAkCzE,cAAc;IAmB5B;;;OAGG;YACW,kBAAkB;IAwBhC;;;OAGG;YACW,iBAAiB;IA2B/B,OAAO,CAAC,mBAAmB;YAeb,2BAA2B;YA4B3B,oBAAoB;CA4EnC"}
1
+ {"version":3,"file":"ChatController.d.ts","sourceRoot":"","sources":["../../../src/controllers/ChatController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAIlE,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,iBAAiB,CAAC,CAAoB;IAC9C,OAAO,CAAC,yBAAyB,CAA4B;IAC7D,OAAO,CAAC,wBAAwB,CAA2B;gBAGzD,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,aAAa,CAAC;QAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;KAAO;IAqB1G,kBAAkB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA4I3E,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAyF7D,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgLzE,eAAe,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAuDxE,uBAAuB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA4DhF,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAuGzE,iBAAiB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoG1E,mBAAmB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IA6E5E,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAmGzE,oBAAoB,CAAC,GAAG,EAAE,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAoE7E,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB9D,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgEjF,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,oBAAoB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;YAmCzE,2BAA2B;YA4B3B,oBAAoB;CA4EnC"}
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ChatController = void 0;
7
7
  const logger_1 = __importDefault(require("../config/logger"));
8
+ const ResponseGenerationService_1 = require("../services/ResponseGenerationService");
9
+ const ResponseStreamingService_1 = require("../services/ResponseStreamingService");
8
10
  class ChatController {
9
11
  constructor(chatManager, storage, supabase, options = {}) {
10
12
  this.chatManager = chatManager;
@@ -14,6 +16,10 @@ class ChatController {
14
16
  this.chatHistoryLength = typeof historyLength === 'number' && historyLength > 0 ? historyLength : 2;
15
17
  this.intentService = options.intentService;
16
18
  this.realtimePublisher = options.realtimePublisher;
19
+ // Initialize services
20
+ const aiService = chatManager.aiService;
21
+ this.responseGenerationService = new ResponseGenerationService_1.ResponseGenerationService(this.intentService, aiService, this.chatHistoryLength);
22
+ this.responseStreamingService = new ResponseStreamingService_1.ResponseStreamingService();
17
23
  }
18
24
  // Create a new conversation
19
25
  async createConversation(req, res) {
@@ -222,10 +228,16 @@ class ChatController {
222
228
  }
223
229
  // Generate AI response for a user message
224
230
  async generateResponse(req, res) {
231
+ const { uuid } = req.params;
232
+ if (!uuid) {
233
+ res.status(400).json({ error: 'Message UUID is required' });
234
+ return;
235
+ }
236
+ // After validation, uuid is guaranteed to be a string - use type assertion
237
+ const messageUuid = uuid;
225
238
  try {
226
- const { uuid } = req.params;
227
239
  // Get the user message by ID using the repository
228
- const userMessage = await this.storage.getMessageById(uuid);
240
+ const userMessage = await this.storage.getMessageById(messageUuid);
229
241
  if (!userMessage) {
230
242
  res.status(404).json({ error: 'Message not found' });
231
243
  return;
@@ -235,7 +247,7 @@ class ChatController {
235
247
  // Get conversation context (recent messages)
236
248
  // Exclude the current user message to avoid duplication (it's added separately as the query)
237
249
  const allMessages = await this.chatManager.getRecentMessages(conversationId, this.chatHistoryLength + 1);
238
- const messages = allMessages.filter(msg => msg.id !== uuid).slice(-this.chatHistoryLength);
250
+ const messages = allMessages.filter(msg => msg.id !== messageUuid).slice(-this.chatHistoryLength);
239
251
  logger_1.default.info(`📜 Retrieved ${messages.length} message(s) from conversation history (limit: ${this.chatHistoryLength})`);
240
252
  const conversation = await this.storage.getConversation(conversationId);
241
253
  // Check if conversation has been joined by an agent
@@ -246,140 +258,57 @@ class ChatController {
246
258
  });
247
259
  return;
248
260
  }
249
- // Set up Server-Sent Events (SSE) headers for streaming (always stream, consistent format)
250
- res.setHeader('Content-Type', 'text/event-stream');
251
- res.setHeader('Cache-Control', 'no-cache');
252
- res.setHeader('Connection', 'keep-alive');
253
- res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
261
+ // Set up Server-Sent Events (SSE) headers for streaming
262
+ this.responseStreamingService.setupSSEHeaders(res);
254
263
  // Run intent classification to decide handling strategy
255
- const intentResult = await this.classifyIntent(userMessageContent, messages);
256
- const intentResponse = await this.handleIntentResult(intentResult, userMessage, conversationId, conversation);
264
+ const intentResult = await this.responseGenerationService.classifyIntent(userMessageContent, messages);
265
+ const intentResponse = this.responseGenerationService.handleIntentResult(intentResult, userMessageContent);
257
266
  let accumulatedContent = '';
258
267
  let assistantMessageId;
268
+ let sources = [];
259
269
  try {
260
270
  // If intent returned a response (non-knowledge intent), stream it
261
271
  if (intentResponse) {
262
272
  logger_1.default.info(`📤 Streaming intent response for: ${intentResult.intent}`);
263
- await this.streamTextContent(intentResponse, res);
273
+ await this.responseStreamingService.streamTextContent(intentResponse, res);
264
274
  accumulatedContent = intentResponse;
265
275
  }
266
276
  else {
267
277
  // Knowledge intent - proceed with RAG flow and stream AI response
268
278
  logger_1.default.info('📚 Streaming knowledge-based response');
269
- // Get knowledge base search results if available
270
- const aiService = this.chatManager.aiService;
271
- let knowledgeResults = null;
272
279
  // Get conversation to extract company_id for knowledge base search
273
280
  const companyIdRaw = req.profile?.companyId || conversation?.organizationId;
274
281
  const companyId = companyIdRaw ? (typeof companyIdRaw === 'string' ? parseInt(companyIdRaw, 10) : companyIdRaw) : undefined;
275
- if (aiService && aiService.knowledgeBaseService) {
276
- try {
277
- logger_1.default.info(`🔍 Searching KB: query="${userMessageContent.substring(0, 50)}...", companyId=${companyId}`);
278
- const searchResults = await aiService.knowledgeBaseService.search(userMessageContent, {
279
- limit: 5,
280
- company_id: companyId
281
- });
282
- logger_1.default.info(`📊 Found knowledge base results: ${searchResults.length}`);
283
- if (searchResults.length > 0) {
284
- knowledgeResults = '\n\nRelevant information from knowledge base:\n';
285
- searchResults.forEach((result) => {
286
- const title = result.title || 'Untitled';
287
- const content = result.content || '';
288
- if (content.trim()) {
289
- knowledgeResults += `- ${title}: ${content}\n`;
290
- }
291
- });
292
- // Verify we actually have meaningful content (not just the header)
293
- const headerLength = '\n\nRelevant information from knowledge base:\n'.length;
294
- if (knowledgeResults.length > headerLength + 10) {
295
- logger_1.default.info(`✅ Knowledge context prepared (${knowledgeResults.length} chars, ${searchResults.length} results)`);
296
- // Log first 200 chars for debugging
297
- logger_1.default.info(`📝 Knowledge preview: ${knowledgeResults.substring(0, 200)}...`);
298
- }
299
- else {
300
- logger_1.default.warn(`⚠️ Knowledge results found but content is empty or too short (${knowledgeResults.length} chars), treating as no results`);
301
- knowledgeResults = '';
302
- }
303
- }
304
- else {
305
- knowledgeResults = '';
306
- logger_1.default.info('⚠️ No knowledge base results found; will return appropriate fallback response');
307
- }
308
- }
309
- catch (error) {
310
- console.error('❌ Failed to search knowledge base:', error);
311
- logger_1.default.error('Failed to search knowledge base:', error);
312
- knowledgeResults = null;
313
- }
314
- }
315
- else {
316
- logger_1.default.warn('⚠️ AI service or knowledge base service not available');
317
- }
282
+ // Search knowledge base and extract sources
283
+ const { knowledgeResults, sources: extractedSources } = await this.responseGenerationService.searchKnowledgeBase(userMessageContent, companyId);
284
+ sources = extractedSources;
318
285
  // Build context for AI
319
- const chatContext = {
320
- conversationHistory: messages.map(msg => ({
321
- role: msg.role,
322
- content: msg.content
323
- })),
324
- knowledgeResults: knowledgeResults ?? undefined
325
- };
286
+ const chatContext = this.responseGenerationService.buildChatContext(messages, knowledgeResults);
287
+ const aiService = this.responseGenerationService.getAIService();
288
+ if (!aiService) {
289
+ throw new Error('AI service not available');
290
+ }
326
291
  // Stream response from OpenAI
327
- logger_1.default.info('🔄 Starting OpenAI stream...');
328
292
  const stream = aiService.generateResponseStream(userMessageContent, chatContext);
329
- let chunkCount = 0;
330
- for await (const { chunk, done, fullContent } of stream) {
331
- chunkCount++;
332
- // Always send the chunk (even if empty with done flag)
333
- const chunkData = JSON.stringify({
334
- type: 'chunk',
335
- content: chunk,
336
- done: done || false // Include done flag
337
- });
338
- res.write(`data: ${chunkData}\n\n`);
339
- if (res.flush)
340
- res.flush();
341
- // Update accumulated content
342
- if (chunk) {
343
- accumulatedContent += chunk;
344
- }
345
- // Log first and last chunks
346
- if (chunkCount === 1) {
347
- logger_1.default.info(`📤 First chunk sent: "${chunk.substring(0, 30)}..."`);
348
- }
349
- if (done && fullContent) {
350
- accumulatedContent = fullContent;
351
- logger_1.default.info(`🏁 Stream complete: ${chunkCount} chunks sent, ${fullContent.length} total chars`);
352
- }
353
- }
293
+ accumulatedContent = await this.responseStreamingService.streamAIResponse(stream, res, sources, knowledgeResults);
354
294
  }
355
295
  // Save the message after streaming completes
356
296
  try {
357
297
  const assistantMessage = await this.saveAssistantMessage({
358
298
  conversation,
359
299
  conversationId,
360
- parentMessageId: uuid,
300
+ parentMessageId: messageUuid,
361
301
  content: accumulatedContent,
362
302
  toolResults: []
363
303
  });
364
304
  assistantMessageId = assistantMessage.id;
365
- // Send completion event with final message metadata (no content - already streamed)
366
- const completionData = JSON.stringify({
367
- type: 'completion',
368
- uuid: assistantMessage.id,
369
- parent_message_uuid: uuid,
370
- status: 'completed',
371
- created_at: assistantMessage.createdAt.toISOString()
372
- });
373
- res.write(`data: ${completionData}\n\n`);
305
+ // Send completion event with final message metadata
306
+ // Use non-null assertion since both are guaranteed to exist at this point
307
+ this.responseStreamingService.sendCompletionEvent(res, assistantMessage.id, messageUuid, assistantMessage.createdAt);
374
308
  }
375
309
  catch (saveError) {
376
310
  logger_1.default.error('Failed to save assistant message:', saveError);
377
- const errorData = JSON.stringify({
378
- type: 'error',
379
- error: 'Failed to save message',
380
- message: saveError instanceof Error ? saveError.message : 'Unknown error'
381
- });
382
- res.write(`data: ${errorData}\n\n`);
311
+ this.responseStreamingService.sendErrorEvent(res, 'Failed to save message', saveError instanceof Error ? saveError.message : 'Unknown error');
383
312
  }
384
313
  // Close the stream
385
314
  res.end();
@@ -388,12 +317,7 @@ class ChatController {
388
317
  logger_1.default.error('Streaming error:', streamError);
389
318
  // Try to send error to client if connection is still open
390
319
  try {
391
- const errorData = JSON.stringify({
392
- type: 'error',
393
- error: 'Failed to generate response',
394
- message: streamError instanceof Error ? streamError.message : 'Unknown error'
395
- });
396
- res.write(`data: ${errorData}\n\n`);
320
+ this.responseStreamingService.sendErrorEvent(res, 'Failed to generate response', streamError instanceof Error ? streamError.message : 'Unknown error');
397
321
  res.end();
398
322
  }
399
323
  catch (writeError) {
@@ -406,7 +330,7 @@ class ChatController {
406
330
  await this.saveAssistantMessage({
407
331
  conversation,
408
332
  conversationId,
409
- parentMessageId: uuid,
333
+ parentMessageId: messageUuid,
410
334
  content: accumulatedContent,
411
335
  toolResults: []
412
336
  });
@@ -429,12 +353,7 @@ class ChatController {
429
353
  else {
430
354
  // Headers already sent, try to send SSE error
431
355
  try {
432
- const errorData = JSON.stringify({
433
- type: 'error',
434
- error: 'Failed to generate response',
435
- message: error instanceof Error ? error.message : 'Unknown error'
436
- });
437
- res.write(`data: ${errorData}\n\n`);
356
+ this.responseStreamingService.sendErrorEvent(res, 'Failed to generate response', error instanceof Error ? error.message : 'Unknown error');
438
357
  res.end();
439
358
  }
440
359
  catch (writeError) {
@@ -1032,79 +951,6 @@ class ChatController {
1032
951
  });
1033
952
  }
1034
953
  }
1035
- async classifyIntent(message, history) {
1036
- if (!this.intentService) {
1037
- return {
1038
- intent: 'knowledge',
1039
- needsGuardrail: false,
1040
- contactEmail: null
1041
- };
1042
- }
1043
- const resolvedHistory = Array.isArray(history) ? history : [];
1044
- logger_1.default.info('🧭 Classifying user intent...');
1045
- return this.intentService.classify({
1046
- message,
1047
- conversationHistory: resolvedHistory
1048
- });
1049
- }
1050
- /**
1051
- * Handle intent classification result
1052
- * Returns response content if non-knowledge intent, null if knowledge intent
1053
- */
1054
- async handleIntentResult(result, userMessage, conversationId, conversation) {
1055
- if (result.needsGuardrail && result.intent !== 'guardrail') {
1056
- logger_1.default.info('🛡️ Guardrail triggered');
1057
- return `I can help with documentation or implementation guidance, but I can't share credentials or confidential configuration. Please contact your system administrator or support for access.`;
1058
- }
1059
- logger_1.default.info(`🧾 Intent result: ${result.intent}${result.needsGuardrail ? ' (guardrail triggered)' : ''}`);
1060
- // For non-knowledge intents, return the response content to be streamed
1061
- if (result.intent !== 'knowledge') {
1062
- const responseContent = result.response || this.getFallbackResponse(result.intent);
1063
- return responseContent;
1064
- }
1065
- // Knowledge intent - proceed to RAG flow (return null to indicate streaming will happen later)
1066
- logger_1.default.info('📚 Intent requires knowledge lookup; proceeding with RAG flow.');
1067
- return null;
1068
- }
1069
- /**
1070
- * Stream text content word by word to simulate streaming
1071
- * This ensures consistent SSE format for all responses
1072
- */
1073
- async streamTextContent(content, res) {
1074
- const words = content.split(' ');
1075
- const chunkSize = 2; // Stream 2 words at a time for smoother experience
1076
- const totalChunks = Math.ceil(words.length / chunkSize);
1077
- for (let i = 0; i < words.length; i += chunkSize) {
1078
- const chunk = words.slice(i, i + chunkSize).join(' ') + (i + chunkSize < words.length ? ' ' : '');
1079
- const chunkIndex = Math.floor(i / chunkSize) + 1;
1080
- const isLastChunk = chunkIndex === totalChunks;
1081
- const chunkData = JSON.stringify({
1082
- type: 'chunk',
1083
- content: chunk,
1084
- done: isLastChunk // Mark last chunk with done: true
1085
- });
1086
- res.write(`data: ${chunkData}\n\n`);
1087
- // Flush the response to ensure chunks are sent immediately
1088
- if (res.flush) {
1089
- res.flush();
1090
- }
1091
- // Delay for smooth streaming effect (30ms for better visibility)
1092
- await new Promise(resolve => setTimeout(resolve, 30));
1093
- }
1094
- }
1095
- getFallbackResponse(intent) {
1096
- // Fallback responses in case LLM doesn't generate one (shouldn't happen, but safety net)
1097
- const fallbacks = {
1098
- greeting: 'Hello! How can I help you today?',
1099
- acknowledgment: "You're welcome! Let me know if you need anything else.",
1100
- personality: `I'm ${process.env.ASSISTANT_NAME || 'AI Assistant'}, your AI assistant for ${process.env.ORGANIZATION_NAME || 'Your Organization'}.`,
1101
- clarification: "I'm not sure I understood. Could you clarify what you need help with?",
1102
- guardrail: "I can help with documentation or implementation guidance, but I can't share credentials or confidential configuration.",
1103
- human_support_request: "I'd be happy to connect you with our support team. Could you please provide your email address?",
1104
- human_support_email: "Thank you! Our support team will reach out to you shortly."
1105
- };
1106
- return fallbacks[intent] || "I'm here to help. What would you like to know?";
1107
- }
1108
954
  async respondWithAssistantMessage(payload, res) {
1109
955
  const assistantMessage = await this.saveAssistantMessage({
1110
956
  conversation: payload.conversation,