@vezlo/assistant-server 2.9.0 → 2.11.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.
- package/README.md +10 -0
- package/database-schema.sql +44 -0
- package/dist/src/bootstrap/initializeServices.d.ts +7 -0
- package/dist/src/bootstrap/initializeServices.d.ts.map +1 -1
- package/dist/src/bootstrap/initializeServices.js +25 -4
- package/dist/src/bootstrap/initializeServices.js.map +1 -1
- package/dist/src/controllers/ChatController.d.ts +6 -0
- package/dist/src/controllers/ChatController.d.ts.map +1 -1
- package/dist/src/controllers/ChatController.js +91 -17
- package/dist/src/controllers/ChatController.js.map +1 -1
- package/dist/src/controllers/DatabaseToolConfigController.d.ts +81 -0
- package/dist/src/controllers/DatabaseToolConfigController.d.ts.map +1 -0
- package/dist/src/controllers/DatabaseToolConfigController.js +524 -0
- package/dist/src/controllers/DatabaseToolConfigController.js.map +1 -0
- package/dist/src/controllers/KnowledgeController.js +4 -4
- package/dist/src/controllers/KnowledgeController.js.map +1 -1
- package/dist/src/migrations/011_create_database_tool_configs.d.ts +4 -0
- package/dist/src/migrations/011_create_database_tool_configs.d.ts.map +1 -0
- package/dist/src/migrations/011_create_database_tool_configs.js +49 -0
- package/dist/src/migrations/011_create_database_tool_configs.js.map +1 -0
- package/dist/src/server.js +399 -0
- package/dist/src/server.js.map +1 -1
- package/dist/src/services/AIService.d.ts +6 -0
- package/dist/src/services/AIService.d.ts.map +1 -1
- package/dist/src/services/AIService.js +30 -2
- package/dist/src/services/AIService.js.map +1 -1
- package/dist/src/services/DatabaseToolConfigService.d.ts +133 -0
- package/dist/src/services/DatabaseToolConfigService.d.ts.map +1 -0
- package/dist/src/services/DatabaseToolConfigService.js +442 -0
- package/dist/src/services/DatabaseToolConfigService.js.map +1 -0
- package/dist/src/services/DatabaseToolService.d.ts +75 -0
- package/dist/src/services/DatabaseToolService.d.ts.map +1 -0
- package/dist/src/services/DatabaseToolService.js +299 -0
- package/dist/src/services/DatabaseToolService.js.map +1 -0
- package/dist/src/services/IntentService.d.ts +12 -1
- package/dist/src/services/IntentService.d.ts.map +1 -1
- package/dist/src/services/IntentService.js +36 -5
- package/dist/src/services/IntentService.js.map +1 -1
- package/dist/src/services/KnowledgeBaseService.d.ts +4 -0
- package/dist/src/services/KnowledgeBaseService.d.ts.map +1 -1
- package/dist/src/services/KnowledgeBaseService.js +5 -1
- package/dist/src/services/KnowledgeBaseService.js.map +1 -1
- package/dist/src/services/ResponseGenerationService.d.ts +11 -4
- package/dist/src/services/ResponseGenerationService.d.ts.map +1 -1
- package/dist/src/services/ResponseGenerationService.js +51 -7
- package/dist/src/services/ResponseGenerationService.js.map +1 -1
- package/dist/src/services/ResponseStreamingService.d.ts +13 -1
- package/dist/src/services/ResponseStreamingService.d.ts.map +1 -1
- package/dist/src/services/ResponseStreamingService.js +50 -9
- package/dist/src/services/ResponseStreamingService.js.map +1 -1
- package/dist/src/services/ValidationService.d.ts +16 -0
- package/dist/src/services/ValidationService.d.ts.map +1 -0
- package/dist/src/services/ValidationService.js +83 -0
- package/dist/src/services/ValidationService.js.map +1 -0
- package/env.example +9 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -32,10 +32,12 @@ See [CHANGELOG.md](./CHANGELOG.md) for complete migration guide.
|
|
|
32
32
|
## 🏗️ Architecture
|
|
33
33
|
|
|
34
34
|
- **Backend APIs** - RESTful API endpoints for AI chat and knowledge management
|
|
35
|
+
- **AI Response Validation** - LLM-as-Judge validation with developer/user modes via `@vezlo/ai-validator`
|
|
35
36
|
- **Real-time Communication** - WebSocket support for live chat with Supabase Realtime broadcasting
|
|
36
37
|
- **Human Agent Handoff** - Agent join/leave workflows with realtime status updates and message synchronization
|
|
37
38
|
- **Advanced RAG System** - Chunk-based semantic search with adjacent retrieval using OpenAI text-embedding-3-large (3072 dims) and pgvector
|
|
38
39
|
- **Conversation Management** - Persistent conversation history with agent support
|
|
40
|
+
- **Database Tools** - Connect external Supabase databases for natural language data queries ([see docs](./docs/DATABASE_TOOLS.md))
|
|
39
41
|
- **Slack Integration** - Direct query bot with full AI responses, conversation history, and reaction-based feedback ([setup guide](./docs/SLACK_INTEGRATION.md))
|
|
40
42
|
- **Feedback System** - Message rating and improvement tracking
|
|
41
43
|
- **Database Migrations** - Knex.js migration system for schema management
|
|
@@ -151,6 +153,14 @@ AI_MODEL=gpt-4o
|
|
|
151
153
|
|
|
152
154
|
# Migration Security
|
|
153
155
|
MIGRATION_SECRET_KEY=your-secure-migration-key-here
|
|
156
|
+
|
|
157
|
+
# AI Response Validation (Optional)
|
|
158
|
+
AI_VALIDATION_ENABLED=false
|
|
159
|
+
|
|
160
|
+
# Developer Mode (Optional)
|
|
161
|
+
# true = Strict code grounding for technical queries
|
|
162
|
+
# false = User-friendly generic responses
|
|
163
|
+
DEVELOPER_MODE=false
|
|
154
164
|
```
|
|
155
165
|
|
|
156
166
|
#### 3. Run Database Migrations (Recommended)
|
package/database-schema.sql
CHANGED
|
@@ -66,6 +66,10 @@ INSERT INTO knex_migrations (name, batch, migration_time)
|
|
|
66
66
|
SELECT '010_add_slack_fields.ts', 1, NOW()
|
|
67
67
|
WHERE NOT EXISTS (SELECT 1 FROM knex_migrations WHERE name = '010_add_slack_fields.ts');
|
|
68
68
|
|
|
69
|
+
INSERT INTO knex_migrations (name, batch, migration_time)
|
|
70
|
+
SELECT '011_create_database_tool_configs.ts', 1, NOW()
|
|
71
|
+
WHERE NOT EXISTS (SELECT 1 FROM knex_migrations WHERE name = '011_create_database_tool_configs.ts');
|
|
72
|
+
|
|
69
73
|
-- Set migration lock to unlocked (0 = unlocked, 1 = locked)
|
|
70
74
|
INSERT INTO knex_migrations_lock (index, is_locked)
|
|
71
75
|
VALUES (1, 0)
|
|
@@ -617,3 +621,43 @@ BEGIN
|
|
|
617
621
|
RETURN result;
|
|
618
622
|
END;
|
|
619
623
|
$$;
|
|
624
|
+
|
|
625
|
+
-- ============================================================================
|
|
626
|
+
-- DATABASE TOOLS SCHEMA (Migration 011)
|
|
627
|
+
-- ============================================================================
|
|
628
|
+
|
|
629
|
+
-- External database configurations
|
|
630
|
+
CREATE TABLE IF NOT EXISTS vezlo_database_tool_configs (
|
|
631
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
632
|
+
company_id BIGINT NOT NULL REFERENCES vezlo_companies(id) ON DELETE CASCADE,
|
|
633
|
+
db_url_encrypted TEXT NOT NULL,
|
|
634
|
+
db_key_encrypted TEXT NOT NULL,
|
|
635
|
+
enabled BOOLEAN DEFAULT true,
|
|
636
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
637
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
CREATE INDEX IF NOT EXISTS idx_database_tool_configs_company_id ON vezlo_database_tool_configs(company_id);
|
|
641
|
+
|
|
642
|
+
-- Individual tool configurations
|
|
643
|
+
CREATE TABLE IF NOT EXISTS vezlo_database_tools (
|
|
644
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
645
|
+
config_id UUID NOT NULL REFERENCES vezlo_database_tool_configs(id) ON DELETE CASCADE,
|
|
646
|
+
table_name TEXT NOT NULL,
|
|
647
|
+
tool_name TEXT NOT NULL,
|
|
648
|
+
tool_description TEXT,
|
|
649
|
+
columns JSONB NOT NULL,
|
|
650
|
+
id_column TEXT NOT NULL DEFAULT 'id',
|
|
651
|
+
id_column_type TEXT NOT NULL DEFAULT 'integer',
|
|
652
|
+
enabled BOOLEAN DEFAULT true,
|
|
653
|
+
requires_user_context BOOLEAN DEFAULT false,
|
|
654
|
+
user_filter_column TEXT,
|
|
655
|
+
user_filter_type TEXT,
|
|
656
|
+
user_context_key TEXT,
|
|
657
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
658
|
+
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
659
|
+
UNIQUE(config_id, table_name)
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
CREATE INDEX IF NOT EXISTS idx_database_tools_config_id ON vezlo_database_tools(config_id);
|
|
663
|
+
CREATE INDEX IF NOT EXISTS idx_database_tools_enabled ON vezlo_database_tools(enabled);
|
|
@@ -13,6 +13,9 @@ import { CompanyService } from '../services/CompanyService';
|
|
|
13
13
|
import { CompanyController } from '../controllers/CompanyController';
|
|
14
14
|
import { SlackService } from '../services/SlackService';
|
|
15
15
|
import { SlackController } from '../controllers/SlackController';
|
|
16
|
+
import { ValidationService } from '../services/ValidationService';
|
|
17
|
+
import { DatabaseToolConfigService } from '../services/DatabaseToolConfigService';
|
|
18
|
+
import { DatabaseToolConfigController } from '../controllers/DatabaseToolConfigController';
|
|
16
19
|
export interface ServiceInitOptions {
|
|
17
20
|
supabase: SupabaseClient;
|
|
18
21
|
tablePrefix?: string;
|
|
@@ -30,6 +33,9 @@ export interface InitializedCoreServices {
|
|
|
30
33
|
companyService: CompanyService;
|
|
31
34
|
citationService: CitationService;
|
|
32
35
|
slackService: SlackService;
|
|
36
|
+
validationService: ValidationService;
|
|
37
|
+
databaseToolService: any;
|
|
38
|
+
databaseToolConfigService: DatabaseToolConfigService;
|
|
33
39
|
};
|
|
34
40
|
controllers: {
|
|
35
41
|
chatController: ChatController;
|
|
@@ -38,6 +44,7 @@ export interface InitializedCoreServices {
|
|
|
38
44
|
apiKeyController: ApiKeyController;
|
|
39
45
|
companyController: CompanyController;
|
|
40
46
|
slackController: SlackController;
|
|
47
|
+
databaseToolConfigController: DatabaseToolConfigController;
|
|
41
48
|
};
|
|
42
49
|
config: {
|
|
43
50
|
chatHistoryLength: number;
|
|
@@ -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,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;AAGrE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;
|
|
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;AAGrE,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,EAAE,4BAA4B,EAAE,MAAM,6CAA6C,CAAC;AAE3F,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;QACjC,YAAY,EAAE,YAAY,CAAC;QAC3B,iBAAiB,EAAE,iBAAiB,CAAC;QACrC,mBAAmB,EAAE,GAAG,CAAC;QACzB,yBAAyB,EAAE,yBAAyB,CAAC;KACtD,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;QACrC,eAAe,EAAE,eAAe,CAAC;QACjC,4BAA4B,EAAE,4BAA4B,CAAC;KAC5D,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,CA8I3F"}
|
|
@@ -22,6 +22,10 @@ const IntentService_1 = require("../services/IntentService");
|
|
|
22
22
|
const RealtimePublisher_1 = require("../services/RealtimePublisher");
|
|
23
23
|
const SlackService_1 = require("../services/SlackService");
|
|
24
24
|
const SlackController_1 = require("../controllers/SlackController");
|
|
25
|
+
const ValidationService_1 = require("../services/ValidationService");
|
|
26
|
+
const DatabaseToolService_1 = require("../services/DatabaseToolService");
|
|
27
|
+
const DatabaseToolConfigService_1 = require("../services/DatabaseToolConfigService");
|
|
28
|
+
const DatabaseToolConfigController_1 = require("../controllers/DatabaseToolConfigController");
|
|
25
29
|
const DEFAULT_CHAT_HISTORY_LENGTH = 2;
|
|
26
30
|
const DEFAULT_CONVERSATION_TIMEOUT = 3600000; // 1 hour
|
|
27
31
|
function getChatHistoryLength() {
|
|
@@ -69,6 +73,12 @@ function initializeCoreServices(options) {
|
|
|
69
73
|
maxTokens: aiMaxTokens,
|
|
70
74
|
knowledgeBaseService: knowledgeBase
|
|
71
75
|
});
|
|
76
|
+
// Initialize Database Tool Config service
|
|
77
|
+
const databaseToolConfigService = new DatabaseToolConfigService_1.DatabaseToolConfigService(supabase);
|
|
78
|
+
// Initialize external database tool service (dynamic)
|
|
79
|
+
const databaseToolService = new DatabaseToolService_1.DatabaseToolService(supabase, databaseToolConfigService);
|
|
80
|
+
aiService.setDatabaseToolService(databaseToolService);
|
|
81
|
+
logger_1.default.info('✅ Dynamic database tool service initialized');
|
|
72
82
|
// Set V2 service for adjacent chunk retrieval
|
|
73
83
|
const chatManager = new ChatManager_1.ChatManager({
|
|
74
84
|
aiService,
|
|
@@ -82,7 +92,8 @@ function initializeCoreServices(options) {
|
|
|
82
92
|
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
83
93
|
model: aiModel,
|
|
84
94
|
assistantName: process.env.ASSISTANT_NAME,
|
|
85
|
-
organizationName: process.env.ORGANIZATION_NAME
|
|
95
|
+
organizationName: process.env.ORGANIZATION_NAME,
|
|
96
|
+
databaseToolsEnabled: process.env.EXTERNAL_DB_ENABLED === 'true'
|
|
86
97
|
});
|
|
87
98
|
// Initialize realtime publisher if env vars are available
|
|
88
99
|
let realtimePublisher;
|
|
@@ -93,10 +104,14 @@ function initializeCoreServices(options) {
|
|
|
93
104
|
else {
|
|
94
105
|
logger_1.default.warn('⚠️ Realtime publisher not initialized (missing SUPABASE_URL or SUPABASE_SERVICE_KEY)');
|
|
95
106
|
}
|
|
107
|
+
// Initialize Validation service
|
|
108
|
+
const validationService = new ValidationService_1.ValidationService(process.env.AI_VALIDATION_ENABLED === 'true');
|
|
96
109
|
const chatController = new ChatController_1.ChatController(chatManager, storage, supabase, {
|
|
97
110
|
historyLength: resolvedHistoryLength,
|
|
98
111
|
intentService,
|
|
99
|
-
realtimePublisher
|
|
112
|
+
realtimePublisher,
|
|
113
|
+
validationService,
|
|
114
|
+
databaseToolService
|
|
100
115
|
});
|
|
101
116
|
const knowledgeController = new KnowledgeController_1.KnowledgeController(knowledgeBase, aiService, citationService);
|
|
102
117
|
const authController = new AuthController_1.AuthController(supabase);
|
|
@@ -107,6 +122,8 @@ function initializeCoreServices(options) {
|
|
|
107
122
|
// Initialize Slack integration
|
|
108
123
|
const slackService = new SlackService_1.SlackService();
|
|
109
124
|
const slackController = new SlackController_1.SlackController(slackService, chatManager, storage, resolvedHistoryLength);
|
|
125
|
+
// Initialize Database Tool Config controller (pass both services for cache management)
|
|
126
|
+
const databaseToolConfigController = new DatabaseToolConfigController_1.DatabaseToolConfigController(databaseToolConfigService, databaseToolService);
|
|
110
127
|
return {
|
|
111
128
|
services: {
|
|
112
129
|
storage,
|
|
@@ -116,7 +133,10 @@ function initializeCoreServices(options) {
|
|
|
116
133
|
chatManager,
|
|
117
134
|
apiKeyService,
|
|
118
135
|
companyService,
|
|
119
|
-
slackService
|
|
136
|
+
slackService,
|
|
137
|
+
databaseToolService,
|
|
138
|
+
validationService,
|
|
139
|
+
databaseToolConfigService
|
|
120
140
|
},
|
|
121
141
|
controllers: {
|
|
122
142
|
chatController,
|
|
@@ -124,7 +144,8 @@ function initializeCoreServices(options) {
|
|
|
124
144
|
authController,
|
|
125
145
|
apiKeyController,
|
|
126
146
|
companyController,
|
|
127
|
-
slackController
|
|
147
|
+
slackController,
|
|
148
|
+
databaseToolConfigController
|
|
128
149
|
},
|
|
129
150
|
config: {
|
|
130
151
|
chatHistoryLength: resolvedHistoryLength
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initializeServices.js","sourceRoot":"","sources":["../../../src/bootstrap/initializeServices.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"initializeServices.js","sourceRoot":"","sources":["../../../src/bootstrap/initializeServices.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,8 @@ import { UnifiedStorage } from '../storage/UnifiedStorage';
|
|
|
5
5
|
import { AuthenticatedRequest } from '../middleware/auth';
|
|
6
6
|
import { IntentService } from '../services/IntentService';
|
|
7
7
|
import { RealtimePublisher } from '../services/RealtimePublisher';
|
|
8
|
+
import { ValidationService } from '../services/ValidationService';
|
|
9
|
+
import { DatabaseToolService } from '../services/DatabaseToolService';
|
|
8
10
|
export declare class ChatController {
|
|
9
11
|
private chatManager;
|
|
10
12
|
private storage;
|
|
@@ -14,10 +16,14 @@ export declare class ChatController {
|
|
|
14
16
|
private realtimePublisher?;
|
|
15
17
|
private responseGenerationService;
|
|
16
18
|
private responseStreamingService;
|
|
19
|
+
private validationService?;
|
|
20
|
+
private databaseToolService?;
|
|
17
21
|
constructor(chatManager: ChatManager, storage: UnifiedStorage, supabase: SupabaseClient, options?: {
|
|
18
22
|
historyLength?: number;
|
|
19
23
|
intentService?: IntentService;
|
|
20
24
|
realtimePublisher?: RealtimePublisher;
|
|
25
|
+
validationService?: ValidationService;
|
|
26
|
+
databaseToolService?: DatabaseToolService;
|
|
21
27
|
});
|
|
22
28
|
createConversation(req: AuthenticatedRequest, res: Response): Promise<void>;
|
|
23
29
|
createUserMessage(req: Request, res: Response): Promise<void>;
|
|
@@ -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,EAAE,MAAM,2BAA2B,CAAC;AAE1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatController.d.ts","sourceRoot":"","sources":["../../../src/controllers/ChatController.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,9 +16,11 @@ class ChatController {
|
|
|
16
16
|
this.chatHistoryLength = typeof historyLength === 'number' && historyLength > 0 ? historyLength : 2;
|
|
17
17
|
this.intentService = options.intentService;
|
|
18
18
|
this.realtimePublisher = options.realtimePublisher;
|
|
19
|
+
this.validationService = options.validationService;
|
|
20
|
+
this.databaseToolService = options.databaseToolService;
|
|
19
21
|
// Initialize services
|
|
20
22
|
const aiService = chatManager.aiService;
|
|
21
|
-
this.responseGenerationService = new ResponseGenerationService_1.ResponseGenerationService(this.intentService, aiService, this.chatHistoryLength);
|
|
23
|
+
this.responseGenerationService = new ResponseGenerationService_1.ResponseGenerationService(this.intentService, aiService, this.chatHistoryLength, this.databaseToolService);
|
|
22
24
|
this.responseStreamingService = new ResponseStreamingService_1.ResponseStreamingService();
|
|
23
25
|
}
|
|
24
26
|
// Create a new conversation
|
|
@@ -151,7 +153,7 @@ class ChatController {
|
|
|
151
153
|
// Create a user message in a conversation
|
|
152
154
|
async createUserMessage(req, res) {
|
|
153
155
|
try {
|
|
154
|
-
const
|
|
156
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
155
157
|
const { content } = req.body;
|
|
156
158
|
if (!content) {
|
|
157
159
|
res.status(400).json({ error: 'content is required' });
|
|
@@ -228,7 +230,7 @@ class ChatController {
|
|
|
228
230
|
}
|
|
229
231
|
// Generate AI response for a user message
|
|
230
232
|
async generateResponse(req, res) {
|
|
231
|
-
const
|
|
233
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
232
234
|
if (!uuid) {
|
|
233
235
|
res.status(400).json({ error: 'Message UUID is required' });
|
|
234
236
|
return;
|
|
@@ -244,6 +246,8 @@ class ChatController {
|
|
|
244
246
|
}
|
|
245
247
|
const conversationId = userMessage.conversationId;
|
|
246
248
|
const userMessageContent = userMessage.content;
|
|
249
|
+
// Extract user_context from request body (optional)
|
|
250
|
+
const userContext = req.body?.user_context || {};
|
|
247
251
|
// Get conversation context (recent messages)
|
|
248
252
|
// Exclude the current user message to avoid duplication (it's added separately as the query)
|
|
249
253
|
const allMessages = await this.chatManager.getRecentMessages(conversationId, this.chatHistoryLength + 1);
|
|
@@ -260,15 +264,72 @@ class ChatController {
|
|
|
260
264
|
}
|
|
261
265
|
// Set up Server-Sent Events (SSE) headers for streaming
|
|
262
266
|
this.responseStreamingService.setupSSEHeaders(res);
|
|
263
|
-
// Run intent classification to decide handling strategy
|
|
264
|
-
const
|
|
267
|
+
// Run intent classification to decide handling strategy (with dynamic tools)
|
|
268
|
+
const companyId = conversation?.organizationId ? parseInt(String(conversation.organizationId), 10) : undefined;
|
|
269
|
+
const intentResult = await this.responseGenerationService.classifyIntent(userMessageContent, messages, companyId);
|
|
265
270
|
const intentResponse = this.responseGenerationService.handleIntentResult(intentResult, userMessageContent);
|
|
266
271
|
let accumulatedContent = '';
|
|
267
272
|
let assistantMessageId;
|
|
268
273
|
let sources = [];
|
|
269
274
|
try {
|
|
270
|
-
//
|
|
271
|
-
if (
|
|
275
|
+
// Handle database_tool intent separately
|
|
276
|
+
if (intentResult.intent === 'database_tool' && intentResult.toolCall) {
|
|
277
|
+
logger_1.default.info(`🔧 Database tool call detected: ${intentResult.toolCall.toolName}`);
|
|
278
|
+
if (!this.databaseToolService || !companyId) {
|
|
279
|
+
logger_1.default.warn('⚠️ Database tool requested but service not available or no company ID');
|
|
280
|
+
await this.responseStreamingService.streamTextContent('I apologize, but I cannot access your data at the moment. Please contact support.', res);
|
|
281
|
+
accumulatedContent = 'I apologize, but I cannot access your data at the moment. Please contact support.';
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
// Execute tool with dynamic parameters and user context
|
|
285
|
+
logger_1.default.info(`🔧 Executing tool: ${intentResult.toolCall.toolName} for company: ${companyId}`);
|
|
286
|
+
const toolResult = await this.databaseToolService.executeTool(intentResult.toolCall.toolName, intentResult.toolCall.parameters, companyId, userContext // Pass user context for filtering
|
|
287
|
+
);
|
|
288
|
+
// Check if tool execution was successful
|
|
289
|
+
if (!toolResult.success) {
|
|
290
|
+
logger_1.default.error(`❌ Tool execution failed: ${toolResult.error}`);
|
|
291
|
+
await this.responseStreamingService.streamTextContent('I apologize, but I encountered an error accessing your data. Please try again or contact support.', res);
|
|
292
|
+
accumulatedContent = 'I apologize, but I encountered an error accessing your data. Please try again or contact support.';
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
// Format result with LLM - use direct OpenAI call to bypass system prompt restrictions
|
|
296
|
+
logger_1.default.info('🔧 Formatting tool result with LLM');
|
|
297
|
+
const aiService = this.responseGenerationService.getAIService();
|
|
298
|
+
if (!aiService) {
|
|
299
|
+
throw new Error('AI service not available');
|
|
300
|
+
}
|
|
301
|
+
// Direct OpenAI call bypassing the restrictive system prompt
|
|
302
|
+
const openai = aiService.openai;
|
|
303
|
+
const modelToUse = process.env.AI_MODEL || 'gpt-4o-mini';
|
|
304
|
+
const completion = await openai.chat.completions.create({
|
|
305
|
+
model: modelToUse,
|
|
306
|
+
messages: [
|
|
307
|
+
{
|
|
308
|
+
role: 'system',
|
|
309
|
+
content: 'You are a helpful assistant. Format database query results naturally and conversationally for the user.'
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
role: 'user',
|
|
313
|
+
content: `User asked: "${userMessageContent}"
|
|
314
|
+
|
|
315
|
+
Database returned:
|
|
316
|
+
${JSON.stringify(toolResult.data || toolResult, null, 2)}
|
|
317
|
+
|
|
318
|
+
Provide a natural, friendly response to the user's question using this data.`
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
temperature: 0.7,
|
|
322
|
+
max_tokens: 500
|
|
323
|
+
});
|
|
324
|
+
const formattedContent = completion.choices[0]?.message?.content || 'Here is your information.';
|
|
325
|
+
logger_1.default.info('📤 Streaming database tool response');
|
|
326
|
+
await this.responseStreamingService.streamTextContent(formattedContent, res);
|
|
327
|
+
accumulatedContent = formattedContent;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
// If intent returned a response (non-knowledge/non-tool intent), stream it
|
|
332
|
+
else if (intentResponse) {
|
|
272
333
|
logger_1.default.info(`📤 Streaming intent response for: ${intentResult.intent}`);
|
|
273
334
|
await this.responseStreamingService.streamTextContent(intentResponse, res);
|
|
274
335
|
accumulatedContent = intentResponse;
|
|
@@ -280,7 +341,7 @@ class ChatController {
|
|
|
280
341
|
const companyIdRaw = req.profile?.companyId || conversation?.organizationId;
|
|
281
342
|
const companyId = companyIdRaw ? (typeof companyIdRaw === 'string' ? parseInt(companyIdRaw, 10) : companyIdRaw) : undefined;
|
|
282
343
|
// Search knowledge base and extract sources
|
|
283
|
-
const { knowledgeResults, sources: extractedSources } = await this.responseGenerationService.searchKnowledgeBase(userMessageContent, companyId);
|
|
344
|
+
const { knowledgeResults, sources: extractedSources, chunks: knowledgeChunks } = await this.responseGenerationService.searchKnowledgeBase(userMessageContent, companyId);
|
|
284
345
|
sources = extractedSources;
|
|
285
346
|
// Build context for AI
|
|
286
347
|
const chatContext = this.responseGenerationService.buildChatContext(messages, knowledgeResults);
|
|
@@ -290,7 +351,20 @@ class ChatController {
|
|
|
290
351
|
}
|
|
291
352
|
// Stream response from OpenAI
|
|
292
353
|
const stream = aiService.generateResponseStream(userMessageContent, chatContext);
|
|
293
|
-
|
|
354
|
+
// Create validation callback if service is available
|
|
355
|
+
const validationCallback = this.validationService && knowledgeChunks.length > 0
|
|
356
|
+
? async (response, query) => {
|
|
357
|
+
try {
|
|
358
|
+
// Use individual chunks for accurate validation
|
|
359
|
+
return await this.validationService.validateResponse(query, response, knowledgeChunks);
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
logger_1.default.error('Validation error:', error);
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
: null;
|
|
367
|
+
accumulatedContent = await this.responseStreamingService.streamAIResponse(stream, res, sources, knowledgeResults, validationCallback, userMessageContent);
|
|
294
368
|
}
|
|
295
369
|
// Save the message after streaming completes
|
|
296
370
|
try {
|
|
@@ -370,7 +444,7 @@ class ChatController {
|
|
|
370
444
|
res.status(401).json({ error: 'Authentication required' });
|
|
371
445
|
return;
|
|
372
446
|
}
|
|
373
|
-
const
|
|
447
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
374
448
|
const conversation = await this.storage.getConversation(uuid);
|
|
375
449
|
if (!conversation) {
|
|
376
450
|
res.status(404).json({ error: 'Conversation not found' });
|
|
@@ -419,7 +493,7 @@ class ChatController {
|
|
|
419
493
|
res.status(401).json({ error: 'Authentication required' });
|
|
420
494
|
return;
|
|
421
495
|
}
|
|
422
|
-
const
|
|
496
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
423
497
|
const page = Math.max(1, parseInt(req.query.page || '1', 10) || 1);
|
|
424
498
|
const pageSizeRaw = parseInt(req.query.page_size || '50', 10);
|
|
425
499
|
const pageSize = Math.min(200, Math.max(1, isNaN(pageSizeRaw) ? 50 : pageSizeRaw));
|
|
@@ -473,7 +547,7 @@ class ChatController {
|
|
|
473
547
|
res.status(401).json({ error: 'Authentication required' });
|
|
474
548
|
return;
|
|
475
549
|
}
|
|
476
|
-
const
|
|
550
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
477
551
|
const conversation = await this.storage.getConversation(uuid);
|
|
478
552
|
if (!conversation) {
|
|
479
553
|
res.status(404).json({ error: 'Conversation not found' });
|
|
@@ -561,7 +635,7 @@ class ChatController {
|
|
|
561
635
|
res.status(401).json({ error: 'Authentication required' });
|
|
562
636
|
return;
|
|
563
637
|
}
|
|
564
|
-
const
|
|
638
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
565
639
|
const conversation = await this.storage.getConversation(uuid);
|
|
566
640
|
if (!conversation) {
|
|
567
641
|
res.status(404).json({ error: 'Conversation not found' });
|
|
@@ -647,7 +721,7 @@ class ChatController {
|
|
|
647
721
|
res.status(401).json({ error: 'Authentication required' });
|
|
648
722
|
return;
|
|
649
723
|
}
|
|
650
|
-
const
|
|
724
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
651
725
|
const conversation = await this.storage.getConversation(uuid);
|
|
652
726
|
if (!conversation) {
|
|
653
727
|
res.status(404).json({ error: 'Conversation not found' });
|
|
@@ -710,7 +784,7 @@ class ChatController {
|
|
|
710
784
|
res.status(401).json({ error: 'Authentication required' });
|
|
711
785
|
return;
|
|
712
786
|
}
|
|
713
|
-
const
|
|
787
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
714
788
|
const { content } = req.body;
|
|
715
789
|
if (!content) {
|
|
716
790
|
res.status(400).json({ error: 'content is required' });
|
|
@@ -849,7 +923,7 @@ class ChatController {
|
|
|
849
923
|
// Delete conversation
|
|
850
924
|
async deleteConversation(req, res) {
|
|
851
925
|
try {
|
|
852
|
-
const
|
|
926
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
853
927
|
const success = await this.storage.deleteConversation(uuid);
|
|
854
928
|
if (!success) {
|
|
855
929
|
res.status(404).json({ error: 'Conversation not found or could not be deleted' });
|
|
@@ -924,7 +998,7 @@ class ChatController {
|
|
|
924
998
|
// Delete/undo message feedback - Public API
|
|
925
999
|
async deleteFeedback(req, res) {
|
|
926
1000
|
try {
|
|
927
|
-
const
|
|
1001
|
+
const uuid = Array.isArray(req.params.uuid) ? req.params.uuid[0] : req.params.uuid;
|
|
928
1002
|
if (!uuid) {
|
|
929
1003
|
res.status(400).json({ error: 'Feedback UUID is required' });
|
|
930
1004
|
return;
|