@hed-hog/core 0.0.350 → 0.0.353
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/dist/access-log/access-log.controller.d.ts +16 -0
- package/dist/access-log/access-log.controller.d.ts.map +1 -0
- package/dist/access-log/access-log.controller.js +44 -0
- package/dist/access-log/access-log.controller.js.map +1 -0
- package/dist/access-log/access-log.interceptor.d.ts +9 -0
- package/dist/access-log/access-log.interceptor.d.ts.map +1 -0
- package/dist/access-log/access-log.interceptor.js +46 -0
- package/dist/access-log/access-log.interceptor.js.map +1 -0
- package/dist/access-log/access-log.module.d.ts +3 -0
- package/dist/access-log/access-log.module.d.ts.map +1 -0
- package/dist/access-log/access-log.module.js +29 -0
- package/dist/access-log/access-log.module.js.map +1 -0
- package/dist/access-log/access-log.service.d.ts +47 -0
- package/dist/access-log/access-log.service.d.ts.map +1 -0
- package/dist/access-log/access-log.service.js +113 -0
- package/dist/access-log/access-log.service.js.map +1 -0
- package/dist/access-log/dto/query-access-log.dto.d.ts +8 -0
- package/dist/access-log/dto/query-access-log.dto.d.ts.map +1 -0
- package/dist/access-log/dto/query-access-log.dto.js +42 -0
- package/dist/access-log/dto/query-access-log.dto.js.map +1 -0
- package/dist/auth/auth.controller.d.ts +9 -9
- package/dist/auth/auth.service.d.ts +19 -19
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +0 -4
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts +2 -2
- package/dist/challenge/challenge.service.d.ts.map +1 -1
- package/dist/challenge/challenge.service.js +0 -1
- package/dist/challenge/challenge.service.js.map +1 -1
- package/dist/core/system.module.d.ts.map +1 -1
- package/dist/core/system.module.js +1 -0
- package/dist/core/system.module.js.map +1 -1
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +12 -0
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard-user/dashboard-user.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-user/dashboard-user.service.d.ts +3 -3
- package/dist/dashboard/dashboard.module.d.ts.map +1 -1
- package/dist/dashboard/dashboard.module.js +4 -1
- package/dist/dashboard/dashboard.module.js.map +1 -1
- package/dist/file/file.controller.js +0 -1
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.service.d.ts +65 -0
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +151 -3
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/provider/abstract.provider.d.ts +8 -0
- package/dist/file/provider/abstract.provider.d.ts.map +1 -1
- package/dist/file/provider/abstract.provider.js.map +1 -1
- package/dist/file/provider/azure.provider.d.ts +4 -0
- package/dist/file/provider/azure.provider.d.ts.map +1 -1
- package/dist/file/provider/azure.provider.js +14 -0
- package/dist/file/provider/azure.provider.js.map +1 -1
- package/dist/file/provider/gcs.provider.d.ts +4 -0
- package/dist/file/provider/gcs.provider.d.ts.map +1 -1
- package/dist/file/provider/gcs.provider.js +14 -0
- package/dist/file/provider/gcs.provider.js.map +1 -1
- package/dist/file/provider/local.provider.d.ts +4 -0
- package/dist/file/provider/local.provider.d.ts.map +1 -1
- package/dist/file/provider/local.provider.js +19 -0
- package/dist/file/provider/local.provider.js.map +1 -1
- package/dist/file/provider/provider.factory.d.ts.map +1 -1
- package/dist/file/provider/provider.factory.js +3 -0
- package/dist/file/provider/provider.factory.js.map +1 -1
- package/dist/file/provider/s3.provider.d.ts +4 -0
- package/dist/file/provider/s3.provider.d.ts.map +1 -1
- package/dist/file/provider/s3.provider.js +17 -0
- package/dist/file/provider/s3.provider.js.map +1 -1
- package/dist/mcp/mcp-audit.service.d.ts +3 -1
- package/dist/mcp/mcp-audit.service.d.ts.map +1 -1
- package/dist/mcp/mcp-audit.service.js +14 -2
- package/dist/mcp/mcp-audit.service.js.map +1 -1
- package/dist/mcp/mcp.controller.d.ts +3 -1
- package/dist/mcp/mcp.controller.d.ts.map +1 -1
- package/dist/mcp/mcp.controller.js +10 -2
- package/dist/mcp/mcp.controller.js.map +1 -1
- package/dist/mcp/mcp.module.d.ts.map +1 -1
- package/dist/mcp/mcp.module.js +72 -0
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp/tools/core-ai.mcp-tools.d.ts +45 -0
- package/dist/mcp/tools/core-ai.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-ai.mcp-tools.js +214 -0
- package/dist/mcp/tools/core-ai.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts +21 -0
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js +89 -0
- package/dist/mcp/tools/core-dashboard-items.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts +29 -0
- package/dist/mcp/tools/core-dashboard.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-dashboard.mcp-tools.js +149 -0
- package/dist/mcp/tools/core-dashboard.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts +30 -0
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js +185 -0
- package/dist/mcp/tools/core-event-webhooks.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-files.mcp-tools.d.ts +26 -0
- package/dist/mcp/tools/core-files.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-files.mcp-tools.js +140 -0
- package/dist/mcp/tools/core-files.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts +26 -0
- package/dist/mcp/tools/core-integration-events.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-integration-events.mcp-tools.js +168 -0
- package/dist/mcp/tools/core-integration-events.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +32 -0
- package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-mail-profile.mcp-tools.js +178 -0
- package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts +14 -0
- package/dist/mcp/tools/core-mail-sent.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js +106 -0
- package/dist/mcp/tools/core-mail-sent.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-mail.mcp-tools.d.ts +36 -0
- package/dist/mcp/tools/core-mail.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-mail.mcp-tools.js +184 -0
- package/dist/mcp/tools/core-mail.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts +21 -0
- package/dist/mcp/tools/core-mcp-chat.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-mcp-chat.mcp-tools.js +118 -0
- package/dist/mcp/tools/core-mcp-chat.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-menus.mcp-tools.d.ts +40 -0
- package/dist/mcp/tools/core-menus.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-menus.mcp-tools.js +268 -0
- package/dist/mcp/tools/core-menus.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-notifications.mcp-tools.d.ts +31 -0
- package/dist/mcp/tools/core-notifications.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-notifications.mcp-tools.js +178 -0
- package/dist/mcp/tools/core-notifications.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-profile.mcp-tools.d.ts +20 -0
- package/dist/mcp/tools/core-profile.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-profile.mcp-tools.js +111 -0
- package/dist/mcp/tools/core-profile.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-roles.mcp-tools.d.ts +70 -0
- package/dist/mcp/tools/core-roles.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-roles.mcp-tools.js +355 -0
- package/dist/mcp/tools/core-roles.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-routes.mcp-tools.d.ts +52 -0
- package/dist/mcp/tools/core-routes.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-routes.mcp-tools.js +254 -0
- package/dist/mcp/tools/core-routes.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-screens.mcp-tools.d.ts +47 -0
- package/dist/mcp/tools/core-screens.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-screens.mcp-tools.js +250 -0
- package/dist/mcp/tools/core-screens.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-sessions.mcp-tools.d.ts +20 -0
- package/dist/mcp/tools/core-sessions.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-sessions.mcp-tools.js +128 -0
- package/dist/mcp/tools/core-sessions.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-settings.mcp-tools.d.ts +25 -0
- package/dist/mcp/tools/core-settings.mcp-tools.d.ts.map +1 -1
- package/dist/mcp/tools/core-settings.mcp-tools.js +138 -0
- package/dist/mcp/tools/core-settings.mcp-tools.js.map +1 -1
- package/dist/mcp/tools/core-system.mcp-tools.d.ts +8 -0
- package/dist/mcp/tools/core-system.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-system.mcp-tools.js +40 -0
- package/dist/mcp/tools/core-system.mcp-tools.js.map +1 -0
- package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts +46 -0
- package/dist/mcp/tools/core-webhooks.mcp-tools.d.ts.map +1 -0
- package/dist/mcp/tools/core-webhooks.mcp-tools.js +266 -0
- package/dist/mcp/tools/core-webhooks.mcp-tools.js.map +1 -0
- package/dist/mcp-chat/mcp-chat.controller.d.ts +1 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts +25 -2
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +666 -36
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/notification/notification.controller.d.ts +8 -4
- package/dist/notification/notification.controller.d.ts.map +1 -1
- package/dist/notification/notification.controller.js +12 -0
- package/dist/notification/notification.controller.js.map +1 -1
- package/dist/notification/notification.service.d.ts +19 -5
- package/dist/notification/notification.service.d.ts.map +1 -1
- package/dist/notification/notification.service.js +155 -9
- package/dist/notification/notification.service.js.map +1 -1
- package/dist/pdf/pdf.service.js +1 -1
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +3 -3
- package/dist/profile/profile.controller.js +0 -1
- package/dist/profile/profile.controller.js.map +1 -1
- package/dist/profile/profile.module.d.ts.map +1 -1
- package/dist/profile/profile.module.js +1 -0
- package/dist/profile/profile.module.js.map +1 -1
- package/dist/profile/profile.service.d.ts +3 -3
- package/dist/session/session.controller.d.ts +2 -2
- package/dist/session/session.service.d.ts +6 -6
- package/dist/setting/setting.controller.d.ts +1 -1
- package/dist/setting/setting.service.d.ts +1 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +1 -4
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/task/task.service.d.ts +1 -0
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +25 -0
- package/dist/task/task.service.js.map +1 -1
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +0 -1
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +9 -9
- package/dist/user/user.service.d.ts +21 -21
- package/dist/webhook-integration/dto/webhook-action.dto.d.ts +22 -0
- package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -0
- package/dist/webhook-integration/dto/webhook-action.dto.js +120 -0
- package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -0
- package/dist/webhook-integration/webhook-integration.controller.d.ts +7 -5
- package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.controller.js +18 -5
- package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.d.ts +14 -5
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +174 -41
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/hedhog/data/menu.yaml +15 -0
- package/hedhog/data/route.yaml +1304 -0
- package/hedhog/data/setting_group.yaml +67 -0
- package/hedhog/data/setting_subgroup.yaml +16 -0
- package/hedhog/frontend/app/access-log/page.tsx.ejs +772 -0
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +10 -3
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +33 -13
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +12 -5
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +12 -5
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +1817 -356
- package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +43 -5
- package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +35 -7
- package/hedhog/frontend/app/menu/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/roles/page.tsx.ejs +18 -6
- package/hedhog/frontend/app/routes/page.tsx.ejs +9 -3
- package/hedhog/frontend/app/setting-list/page.tsx.ejs +344 -171
- package/hedhog/frontend/app/users/user-access-sheet.tsx.ejs +310 -304
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +25 -7
- package/hedhog/frontend/messages/en.json +104 -3
- package/hedhog/frontend/messages/pt.json +104 -3
- package/hedhog/table/access_log.yaml +31 -0
- package/package.json +4 -3
- package/src/access-log/access-log.controller.ts +29 -0
- package/src/access-log/access-log.interceptor.ts +38 -0
- package/src/access-log/access-log.module.ts +16 -0
- package/src/access-log/access-log.service.ts +129 -0
- package/src/access-log/dto/query-access-log.dto.ts +23 -0
- package/src/auth/auth.service.ts +0 -7
- package/src/challenge/challenge.service.ts +0 -2
- package/src/core/system.module.ts +1 -0
- package/src/core.module.ts +12 -0
- package/src/dashboard/dashboard.module.ts +4 -1
- package/src/file/file.controller.ts +14 -14
- package/src/file/file.service.ts +198 -4
- package/src/file/provider/abstract.provider.ts +10 -0
- package/src/file/provider/azure.provider.ts +24 -0
- package/src/file/provider/gcs.provider.ts +20 -0
- package/src/file/provider/local.provider.ts +30 -2
- package/src/file/provider/provider.factory.ts +3 -0
- package/src/file/provider/s3.provider.ts +22 -0
- package/src/mcp/mcp-audit.service.ts +47 -36
- package/src/mcp/mcp.controller.ts +40 -30
- package/src/mcp/mcp.module.ts +72 -0
- package/src/mcp/tools/core-ai.mcp-tools.ts +156 -0
- package/src/mcp/tools/core-dashboard-items.mcp-tools.ts +63 -0
- package/src/mcp/tools/core-dashboard.mcp-tools.ts +103 -0
- package/src/mcp/tools/core-event-webhooks.mcp-tools.ts +134 -0
- package/src/mcp/tools/core-files.mcp-tools.ts +105 -0
- package/src/mcp/tools/core-integration-events.mcp-tools.ts +118 -0
- package/src/mcp/tools/core-mail-profile.mcp-tools.ts +128 -0
- package/src/mcp/tools/core-mail-sent.mcp-tools.ts +76 -0
- package/src/mcp/tools/core-mail.mcp-tools.ts +130 -0
- package/src/mcp/tools/core-mcp-chat.mcp-tools.ts +81 -0
- package/src/mcp/tools/core-menus.mcp-tools.ts +197 -0
- package/src/mcp/tools/core-notifications.mcp-tools.ts +128 -0
- package/src/mcp/tools/core-profile.mcp-tools.ts +74 -0
- package/src/mcp/tools/core-roles.mcp-tools.ts +288 -0
- package/src/mcp/tools/core-routes.mcp-tools.ts +196 -0
- package/src/mcp/tools/core-screens.mcp-tools.ts +186 -0
- package/src/mcp/tools/core-sessions.mcp-tools.ts +97 -0
- package/src/mcp/tools/core-settings.mcp-tools.ts +171 -53
- package/src/mcp/tools/core-system.mcp-tools.ts +19 -0
- package/src/mcp/tools/core-webhooks.mcp-tools.ts +199 -0
- package/src/mcp-chat/mcp-chat.service.ts +887 -38
- package/src/notification/notification.controller.ts +20 -8
- package/src/notification/notification.service.ts +209 -9
- package/src/pdf/pdf.service.ts +1 -1
- package/src/profile/profile.controller.ts +1 -1
- package/src/profile/profile.module.ts +1 -0
- package/src/setting/setting.service.ts +8 -11
- package/src/task/task.service.ts +31 -0
- package/src/token/token.service.ts +0 -2
- package/src/webhook-integration/dto/webhook-action.dto.ts +96 -0
- package/src/webhook-integration/webhook-integration.controller.ts +124 -103
- package/src/webhook-integration/webhook-integration.service.ts +267 -64
|
@@ -23,11 +23,152 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
23
23
|
const mcp_registry_service_1 = require("../mcp/mcp-registry.service");
|
|
24
24
|
const setting_service_1 = require("../setting/setting.service");
|
|
25
25
|
const MAX_OPENAI_TOOLS = 128;
|
|
26
|
+
const TARGET_SELECTED_TOOLS = 112;
|
|
27
|
+
const MAX_ITERATIONS = 20;
|
|
28
|
+
const FORCE_FINISH_THRESHOLD = 2;
|
|
29
|
+
const MIN_DOMAIN_CONFIDENCE = 2;
|
|
30
|
+
const AMBIGUOUS_DOMAIN_DELTA = 1;
|
|
26
31
|
const SYSTEM_PROMPT = 'You are a helpful assistant with access to tools from the HedHog system. ' +
|
|
27
32
|
'Use the available tools to answer the user\'s questions accurately. ' +
|
|
28
33
|
'Always respond in the same language the user writes in.';
|
|
29
34
|
const sanitize = (n) => n.replace(/\./g, '__');
|
|
30
35
|
const restore = (n) => n.replace(/__/g, '.');
|
|
36
|
+
const DOMAIN_KEYWORDS = {
|
|
37
|
+
finance: [
|
|
38
|
+
'finance',
|
|
39
|
+
'financeiro',
|
|
40
|
+
'financeira',
|
|
41
|
+
'financas',
|
|
42
|
+
'finanças',
|
|
43
|
+
'banco',
|
|
44
|
+
'bancos',
|
|
45
|
+
'bank',
|
|
46
|
+
'banks',
|
|
47
|
+
'conta',
|
|
48
|
+
'contas',
|
|
49
|
+
'account',
|
|
50
|
+
'accounts',
|
|
51
|
+
'conta bancaria',
|
|
52
|
+
'contas bancarias',
|
|
53
|
+
'conta bancária',
|
|
54
|
+
'contas bancárias',
|
|
55
|
+
'extrato',
|
|
56
|
+
'extratos',
|
|
57
|
+
'statement',
|
|
58
|
+
'statements',
|
|
59
|
+
'pagamento',
|
|
60
|
+
'pagamentos',
|
|
61
|
+
'payment',
|
|
62
|
+
'payments',
|
|
63
|
+
'recebimento',
|
|
64
|
+
'recebimentos',
|
|
65
|
+
'receivable',
|
|
66
|
+
'payable',
|
|
67
|
+
'currency',
|
|
68
|
+
'moeda',
|
|
69
|
+
'transferencia',
|
|
70
|
+
'transferência',
|
|
71
|
+
],
|
|
72
|
+
operations: [
|
|
73
|
+
'operations',
|
|
74
|
+
'operacoes',
|
|
75
|
+
'operações',
|
|
76
|
+
'tarefa',
|
|
77
|
+
'tarefas',
|
|
78
|
+
'task',
|
|
79
|
+
'tasks',
|
|
80
|
+
'projeto',
|
|
81
|
+
'projetos',
|
|
82
|
+
'project',
|
|
83
|
+
'projects',
|
|
84
|
+
'timesheet',
|
|
85
|
+
'apontamento',
|
|
86
|
+
'aprovacao',
|
|
87
|
+
'aprovação',
|
|
88
|
+
'approval',
|
|
89
|
+
'colaborador',
|
|
90
|
+
'collaborator',
|
|
91
|
+
],
|
|
92
|
+
lms: [
|
|
93
|
+
'lms',
|
|
94
|
+
'curso',
|
|
95
|
+
'cursos',
|
|
96
|
+
'course',
|
|
97
|
+
'courses',
|
|
98
|
+
'aula',
|
|
99
|
+
'aulas',
|
|
100
|
+
'lesson',
|
|
101
|
+
'lessons',
|
|
102
|
+
'treinamento',
|
|
103
|
+
'training',
|
|
104
|
+
'turma',
|
|
105
|
+
'class',
|
|
106
|
+
'aluno',
|
|
107
|
+
'student',
|
|
108
|
+
'prova',
|
|
109
|
+
'exam',
|
|
110
|
+
],
|
|
111
|
+
core: [
|
|
112
|
+
'core',
|
|
113
|
+
'usuario',
|
|
114
|
+
'usuário',
|
|
115
|
+
'usuarios',
|
|
116
|
+
'usuários',
|
|
117
|
+
'user',
|
|
118
|
+
'users',
|
|
119
|
+
'perfil',
|
|
120
|
+
'profile',
|
|
121
|
+
'role',
|
|
122
|
+
'roles',
|
|
123
|
+
'menu',
|
|
124
|
+
'menus',
|
|
125
|
+
'rota',
|
|
126
|
+
'rotas',
|
|
127
|
+
'route',
|
|
128
|
+
'routes',
|
|
129
|
+
'configuracao',
|
|
130
|
+
'configuração',
|
|
131
|
+
'setting',
|
|
132
|
+
'settings',
|
|
133
|
+
],
|
|
134
|
+
contact: [
|
|
135
|
+
'contato',
|
|
136
|
+
'contatos',
|
|
137
|
+
'contact',
|
|
138
|
+
'contacts',
|
|
139
|
+
'pessoa',
|
|
140
|
+
'pessoas',
|
|
141
|
+
'person',
|
|
142
|
+
'proposal',
|
|
143
|
+
'proposta',
|
|
144
|
+
],
|
|
145
|
+
};
|
|
146
|
+
const ACTION_KEYWORDS = [
|
|
147
|
+
'listar',
|
|
148
|
+
'lista',
|
|
149
|
+
'buscar',
|
|
150
|
+
'procurar',
|
|
151
|
+
'consultar',
|
|
152
|
+
'mostrar',
|
|
153
|
+
'ver',
|
|
154
|
+
'criar',
|
|
155
|
+
'adicionar',
|
|
156
|
+
'atualizar',
|
|
157
|
+
'alterar',
|
|
158
|
+
'remover',
|
|
159
|
+
'deletar',
|
|
160
|
+
'excluir',
|
|
161
|
+
'list',
|
|
162
|
+
'search',
|
|
163
|
+
'find',
|
|
164
|
+
'show',
|
|
165
|
+
'get',
|
|
166
|
+
'create',
|
|
167
|
+
'add',
|
|
168
|
+
'update',
|
|
169
|
+
'delete',
|
|
170
|
+
'remove',
|
|
171
|
+
];
|
|
31
172
|
let McpChatService = McpChatService_1 = class McpChatService {
|
|
32
173
|
constructor(prisma, setting, registry) {
|
|
33
174
|
this.prisma = prisma;
|
|
@@ -65,9 +206,12 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
65
206
|
});
|
|
66
207
|
}
|
|
67
208
|
async sendMessage(conversationId, userId, locale, message) {
|
|
68
|
-
|
|
209
|
+
const cfg = await this.setting.getSettingValues(['mcp-enabled']);
|
|
210
|
+
if (cfg['mcp-enabled'] === false) {
|
|
211
|
+
throw new common_1.BadRequestException('MCP server is disabled.');
|
|
212
|
+
}
|
|
69
213
|
await this.assertOwnership(conversationId, userId);
|
|
70
|
-
const apiKey = await this.
|
|
214
|
+
const { provider, apiKey } = await this.getProviderKey();
|
|
71
215
|
// Save user message
|
|
72
216
|
await this.prisma.mcp_conversation_message.create({
|
|
73
217
|
data: {
|
|
@@ -86,7 +230,14 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
86
230
|
data: { title: message.slice(0, 60) },
|
|
87
231
|
});
|
|
88
232
|
}
|
|
89
|
-
|
|
233
|
+
if (provider === 'gemini') {
|
|
234
|
+
return this.runGeminiLoop(conversationId, userId, locale, message, apiKey);
|
|
235
|
+
}
|
|
236
|
+
return this.runOpenAiLoop(conversationId, userId, locale, message, apiKey);
|
|
237
|
+
}
|
|
238
|
+
// ── OpenAI loop ──────────────────────────────────────────────────────────
|
|
239
|
+
async runOpenAiLoop(conversationId, userId, locale, message, apiKey) {
|
|
240
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
90
241
|
const history = await this.prisma.mcp_conversation_message.findMany({
|
|
91
242
|
where: { conversation_id: conversationId },
|
|
92
243
|
orderBy: { created_at: 'asc' },
|
|
@@ -95,18 +246,22 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
95
246
|
{ role: 'system', content: SYSTEM_PROMPT },
|
|
96
247
|
...this.toOpenAiMessages(history),
|
|
97
248
|
];
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
249
|
+
const toolSelection = await this.buildOpenAiTools(message, conversationId, userId, history);
|
|
250
|
+
if (toolSelection.clarification) {
|
|
251
|
+
return this.recordAssistantAndReturnHistory(conversationId, toolSelection.clarification);
|
|
252
|
+
}
|
|
253
|
+
const tools = toolSelection.tools;
|
|
254
|
+
const callSignatures = new Set();
|
|
255
|
+
let maxIterations = MAX_ITERATIONS;
|
|
102
256
|
while (maxIterations-- > 0) {
|
|
257
|
+
const forceFinish = maxIterations < FORCE_FINISH_THRESHOLD;
|
|
103
258
|
let response;
|
|
104
259
|
try {
|
|
105
260
|
response = await axios_1.default.post('https://api.openai.com/v1/chat/completions', {
|
|
106
261
|
model: 'gpt-4o-mini',
|
|
107
262
|
messages: openAiMessages,
|
|
108
|
-
tools: tools.length > 0 ? tools : undefined,
|
|
109
|
-
tool_choice: tools.length > 0 ? 'auto' : undefined,
|
|
263
|
+
tools: !forceFinish && tools.length > 0 ? tools : undefined,
|
|
264
|
+
tool_choice: !forceFinish && tools.length > 0 ? 'auto' : undefined,
|
|
110
265
|
}, {
|
|
111
266
|
headers: {
|
|
112
267
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -127,14 +282,12 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
127
282
|
}
|
|
128
283
|
const choice = (_g = (_f = response.data) === null || _f === void 0 ? void 0 : _f.choices) === null || _g === void 0 ? void 0 : _g[0];
|
|
129
284
|
const assistantMsg = choice === null || choice === void 0 ? void 0 : choice.message;
|
|
130
|
-
if ((_h = assistantMsg === null || assistantMsg === void 0 ? void 0 : assistantMsg.tool_calls) === null || _h === void 0 ? void 0 : _h.length) {
|
|
131
|
-
// Add assistant tool-call message to context
|
|
285
|
+
if (((_h = assistantMsg === null || assistantMsg === void 0 ? void 0 : assistantMsg.tool_calls) === null || _h === void 0 ? void 0 : _h.length) && !forceFinish) {
|
|
132
286
|
openAiMessages.push({
|
|
133
287
|
role: 'assistant',
|
|
134
288
|
content: null,
|
|
135
289
|
tool_calls: assistantMsg.tool_calls,
|
|
136
290
|
});
|
|
137
|
-
// Execute each tool call
|
|
138
291
|
for (const toolCall of assistantMsg.tool_calls) {
|
|
139
292
|
const originalName = restore(toolCall.function.name);
|
|
140
293
|
const argsJson = toolCall.function.arguments;
|
|
@@ -145,7 +298,6 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
145
298
|
catch (_j) {
|
|
146
299
|
args = {};
|
|
147
300
|
}
|
|
148
|
-
// Persist tool_call message
|
|
149
301
|
await this.prisma.mcp_conversation_message.create({
|
|
150
302
|
data: {
|
|
151
303
|
conversation_id: conversationId,
|
|
@@ -155,7 +307,28 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
155
307
|
tool_call_id: toolCall.id,
|
|
156
308
|
},
|
|
157
309
|
});
|
|
158
|
-
|
|
310
|
+
const sig = `${toolCall.function.name}:${argsJson}`;
|
|
311
|
+
if (callSignatures.has(sig)) {
|
|
312
|
+
this.logger.warn(`MCP chat duplicate tool call detected — aborting re-execution. ` +
|
|
313
|
+
`conversationId=${conversationId} tool=${originalName}`);
|
|
314
|
+
const dupResult = JSON.stringify({ error: 'Duplicate tool call detected, aborting loop.' });
|
|
315
|
+
await this.prisma.mcp_conversation_message.create({
|
|
316
|
+
data: {
|
|
317
|
+
conversation_id: conversationId,
|
|
318
|
+
role: 'tool_result',
|
|
319
|
+
content: dupResult,
|
|
320
|
+
tool_name: originalName,
|
|
321
|
+
tool_call_id: toolCall.id,
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
openAiMessages.push({
|
|
325
|
+
role: 'tool',
|
|
326
|
+
tool_call_id: toolCall.id,
|
|
327
|
+
content: dupResult,
|
|
328
|
+
});
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
callSignatures.add(sig);
|
|
159
332
|
let resultContent;
|
|
160
333
|
const entry = this.registry.get(originalName);
|
|
161
334
|
if (entry) {
|
|
@@ -176,7 +349,6 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
176
349
|
else {
|
|
177
350
|
resultContent = JSON.stringify({ error: `Tool '${originalName}' not found` });
|
|
178
351
|
}
|
|
179
|
-
// Persist tool_result message
|
|
180
352
|
await this.prisma.mcp_conversation_message.create({
|
|
181
353
|
data: {
|
|
182
354
|
conversation_id: conversationId,
|
|
@@ -186,16 +358,14 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
186
358
|
tool_call_id: toolCall.id,
|
|
187
359
|
},
|
|
188
360
|
});
|
|
189
|
-
// Add tool result to context for next iteration
|
|
190
361
|
openAiMessages.push({
|
|
191
362
|
role: 'tool',
|
|
192
363
|
tool_call_id: toolCall.id,
|
|
193
364
|
content: resultContent,
|
|
194
365
|
});
|
|
195
366
|
}
|
|
196
|
-
continue;
|
|
367
|
+
continue;
|
|
197
368
|
}
|
|
198
|
-
// Final assistant response
|
|
199
369
|
const finalContent = (assistantMsg === null || assistantMsg === void 0 ? void 0 : assistantMsg.content) || '';
|
|
200
370
|
await this.prisma.mcp_conversation_message.create({
|
|
201
371
|
data: {
|
|
@@ -211,6 +381,123 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
211
381
|
}
|
|
212
382
|
throw new common_1.BadRequestException('Maximum tool call iterations exceeded.');
|
|
213
383
|
}
|
|
384
|
+
// ── Gemini loop ──────────────────────────────────────────────────────────
|
|
385
|
+
async runGeminiLoop(conversationId, userId, locale, message, apiKey) {
|
|
386
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
387
|
+
const history = await this.prisma.mcp_conversation_message.findMany({
|
|
388
|
+
where: { conversation_id: conversationId },
|
|
389
|
+
orderBy: { created_at: 'asc' },
|
|
390
|
+
});
|
|
391
|
+
const geminiContents = this.toGeminiContents(history);
|
|
392
|
+
const toolSelection = await this.buildGeminiTools(message, conversationId, userId, history);
|
|
393
|
+
if (toolSelection.clarification) {
|
|
394
|
+
return this.recordAssistantAndReturnHistory(conversationId, toolSelection.clarification);
|
|
395
|
+
}
|
|
396
|
+
const functionDeclarations = toolSelection.tools;
|
|
397
|
+
let maxIterations = MAX_ITERATIONS;
|
|
398
|
+
let callIndex = 0;
|
|
399
|
+
while (maxIterations-- > 0) {
|
|
400
|
+
const forceFinish = maxIterations < FORCE_FINISH_THRESHOLD;
|
|
401
|
+
let response;
|
|
402
|
+
try {
|
|
403
|
+
response = await axios_1.default.post(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`, {
|
|
404
|
+
systemInstruction: { parts: [{ text: SYSTEM_PROMPT }] },
|
|
405
|
+
contents: geminiContents,
|
|
406
|
+
tools: !forceFinish && functionDeclarations.length > 0
|
|
407
|
+
? [{ functionDeclarations }]
|
|
408
|
+
: undefined,
|
|
409
|
+
}, { headers: { 'Content-Type': 'application/json' } });
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
const status = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
413
|
+
const errData = (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.data;
|
|
414
|
+
if (status === 429) {
|
|
415
|
+
throw new common_1.HttpException((_d = (_c = errData === null || errData === void 0 ? void 0 : errData.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : 'Gemini rate limit exceeded. Please try again later.', common_1.HttpStatus.TOO_MANY_REQUESTS);
|
|
416
|
+
}
|
|
417
|
+
if (status === 400) {
|
|
418
|
+
throw new common_1.BadRequestException((_f = (_e = errData === null || errData === void 0 ? void 0 : errData.error) === null || _e === void 0 ? void 0 : _e.message) !== null && _f !== void 0 ? _f : 'Invalid Gemini API request.');
|
|
419
|
+
}
|
|
420
|
+
if (status === 401 || status === 403) {
|
|
421
|
+
throw new common_1.BadRequestException('Invalid Gemini API key.');
|
|
422
|
+
}
|
|
423
|
+
throw new common_1.HttpException((_h = (_g = errData === null || errData === void 0 ? void 0 : errData.error) === null || _g === void 0 ? void 0 : _g.message) !== null && _h !== void 0 ? _h : 'Gemini API error.', status !== null && status !== void 0 ? status : common_1.HttpStatus.BAD_GATEWAY);
|
|
424
|
+
}
|
|
425
|
+
const candidate = (_k = (_j = response.data) === null || _j === void 0 ? void 0 : _j.candidates) === null || _k === void 0 ? void 0 : _k[0];
|
|
426
|
+
const parts = (_m = (_l = candidate === null || candidate === void 0 ? void 0 : candidate.content) === null || _l === void 0 ? void 0 : _l.parts) !== null && _m !== void 0 ? _m : [];
|
|
427
|
+
const functionCallParts = parts.filter((p) => p.functionCall);
|
|
428
|
+
const textParts = parts.filter((p) => p.text);
|
|
429
|
+
if (functionCallParts.length > 0 && !forceFinish) {
|
|
430
|
+
geminiContents.push({ role: 'model', parts: functionCallParts });
|
|
431
|
+
const functionResponseParts = [];
|
|
432
|
+
for (const part of functionCallParts) {
|
|
433
|
+
const { name, args } = part.functionCall;
|
|
434
|
+
const originalName = restore(name);
|
|
435
|
+
const argsJson = JSON.stringify(args !== null && args !== void 0 ? args : {});
|
|
436
|
+
const toolCallId = `gemini-${callIndex++}`;
|
|
437
|
+
await this.prisma.mcp_conversation_message.create({
|
|
438
|
+
data: {
|
|
439
|
+
conversation_id: conversationId,
|
|
440
|
+
role: 'tool_call',
|
|
441
|
+
content: argsJson,
|
|
442
|
+
tool_name: originalName,
|
|
443
|
+
tool_call_id: toolCallId,
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
let resultContent;
|
|
447
|
+
const entry = this.registry.get(originalName);
|
|
448
|
+
if (entry) {
|
|
449
|
+
const context = {
|
|
450
|
+
userId,
|
|
451
|
+
sessionId: '',
|
|
452
|
+
locale,
|
|
453
|
+
toolName: originalName,
|
|
454
|
+
};
|
|
455
|
+
try {
|
|
456
|
+
const result = await entry.handler(args !== null && args !== void 0 ? args : {}, context);
|
|
457
|
+
resultContent = JSON.stringify(result);
|
|
458
|
+
}
|
|
459
|
+
catch (e) {
|
|
460
|
+
resultContent = JSON.stringify({ error: e.message });
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
resultContent = JSON.stringify({ error: `Tool '${originalName}' not found` });
|
|
465
|
+
}
|
|
466
|
+
await this.prisma.mcp_conversation_message.create({
|
|
467
|
+
data: {
|
|
468
|
+
conversation_id: conversationId,
|
|
469
|
+
role: 'tool_result',
|
|
470
|
+
content: resultContent,
|
|
471
|
+
tool_name: originalName,
|
|
472
|
+
tool_call_id: toolCallId,
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
functionResponseParts.push({
|
|
476
|
+
functionResponse: {
|
|
477
|
+
name,
|
|
478
|
+
response: { output: resultContent },
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
geminiContents.push({ role: 'user', parts: functionResponseParts });
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
const finalContent = textParts.map((p) => p.text).join('');
|
|
486
|
+
await this.prisma.mcp_conversation_message.create({
|
|
487
|
+
data: {
|
|
488
|
+
conversation_id: conversationId,
|
|
489
|
+
role: 'assistant',
|
|
490
|
+
content: finalContent,
|
|
491
|
+
},
|
|
492
|
+
});
|
|
493
|
+
return this.prisma.mcp_conversation_message.findMany({
|
|
494
|
+
where: { conversation_id: conversationId },
|
|
495
|
+
orderBy: { created_at: 'asc' },
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
throw new common_1.BadRequestException('Maximum tool call iterations exceeded.');
|
|
499
|
+
}
|
|
500
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
214
501
|
toOpenAiMessages(history) {
|
|
215
502
|
const messages = [];
|
|
216
503
|
const toolCallMap = new Map(); // tool_call_id → sanitized name
|
|
@@ -246,6 +533,58 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
246
533
|
}
|
|
247
534
|
return messages;
|
|
248
535
|
}
|
|
536
|
+
toGeminiContents(history) {
|
|
537
|
+
var _a, _b;
|
|
538
|
+
const contents = [];
|
|
539
|
+
let i = 0;
|
|
540
|
+
while (i < history.length) {
|
|
541
|
+
const msg = history[i];
|
|
542
|
+
if (msg.role === 'user') {
|
|
543
|
+
contents.push({ role: 'user', parts: [{ text: msg.content }] });
|
|
544
|
+
i++;
|
|
545
|
+
}
|
|
546
|
+
else if (msg.role === 'assistant') {
|
|
547
|
+
contents.push({ role: 'model', parts: [{ text: msg.content }] });
|
|
548
|
+
i++;
|
|
549
|
+
}
|
|
550
|
+
else if (msg.role === 'tool_call') {
|
|
551
|
+
const functionCallParts = [];
|
|
552
|
+
while (i < history.length && history[i].role === 'tool_call') {
|
|
553
|
+
const m = history[i];
|
|
554
|
+
let args = {};
|
|
555
|
+
try {
|
|
556
|
+
args = JSON.parse(m.content);
|
|
557
|
+
}
|
|
558
|
+
catch (_c) {
|
|
559
|
+
args = {};
|
|
560
|
+
}
|
|
561
|
+
functionCallParts.push({
|
|
562
|
+
functionCall: { name: sanitize((_a = m.tool_name) !== null && _a !== void 0 ? _a : ''), args },
|
|
563
|
+
});
|
|
564
|
+
i++;
|
|
565
|
+
}
|
|
566
|
+
contents.push({ role: 'model', parts: functionCallParts });
|
|
567
|
+
}
|
|
568
|
+
else if (msg.role === 'tool_result') {
|
|
569
|
+
const functionResponseParts = [];
|
|
570
|
+
while (i < history.length && history[i].role === 'tool_result') {
|
|
571
|
+
const m = history[i];
|
|
572
|
+
functionResponseParts.push({
|
|
573
|
+
functionResponse: {
|
|
574
|
+
name: sanitize((_b = m.tool_name) !== null && _b !== void 0 ? _b : ''),
|
|
575
|
+
response: { output: m.content },
|
|
576
|
+
},
|
|
577
|
+
});
|
|
578
|
+
i++;
|
|
579
|
+
}
|
|
580
|
+
contents.push({ role: 'user', parts: functionResponseParts });
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
i++;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return contents;
|
|
587
|
+
}
|
|
249
588
|
async assertOwnership(conversationId, userId) {
|
|
250
589
|
const conv = await this.prisma.mcp_conversation.findUnique({
|
|
251
590
|
where: { id: conversationId },
|
|
@@ -258,33 +597,75 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
258
597
|
throw new common_1.ForbiddenException('Access denied to this conversation.');
|
|
259
598
|
}
|
|
260
599
|
}
|
|
261
|
-
async
|
|
262
|
-
const settings = await this.setting.getSettingValues([
|
|
600
|
+
async getProviderKey() {
|
|
601
|
+
const settings = await this.setting.getSettingValues([
|
|
602
|
+
'mcp-ai-provider',
|
|
603
|
+
'ai-openai-api-key',
|
|
604
|
+
'ai-gemini-api-key',
|
|
605
|
+
]);
|
|
606
|
+
const provider = (settings['mcp-ai-provider'] || 'openai');
|
|
607
|
+
if (provider === 'gemini') {
|
|
608
|
+
const key = settings['ai-gemini-api-key'];
|
|
609
|
+
if (!key) {
|
|
610
|
+
throw new common_1.BadRequestException('Gemini API key is not configured. Please set it in Settings → AI → Gemini API Key.');
|
|
611
|
+
}
|
|
612
|
+
return { provider: 'gemini', apiKey: key };
|
|
613
|
+
}
|
|
263
614
|
const key = settings['ai-openai-api-key'];
|
|
264
615
|
if (!key) {
|
|
265
|
-
throw new common_1.BadRequestException('OpenAI API key is not configured
|
|
616
|
+
throw new common_1.BadRequestException('OpenAI API key is not configured. Please set it in Settings → AI → OpenAI API Key.');
|
|
266
617
|
}
|
|
267
|
-
return key;
|
|
618
|
+
return { provider: 'openai', apiKey: key };
|
|
268
619
|
}
|
|
269
|
-
buildOpenAiTools(message, conversationId, userId) {
|
|
620
|
+
async buildOpenAiTools(message, conversationId, userId, history) {
|
|
621
|
+
return this.selectOpenAiTools(message, conversationId, userId, history);
|
|
622
|
+
}
|
|
623
|
+
async buildGeminiTools(message, conversationId, userId, history) {
|
|
624
|
+
const selection = await this.selectOpenAiTools(message, conversationId, userId, history);
|
|
625
|
+
return {
|
|
626
|
+
clarification: selection.clarification,
|
|
627
|
+
tools: selection.tools.map((t) => t.function),
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
async selectOpenAiTools(message, conversationId, userId, history) {
|
|
270
631
|
var _a;
|
|
271
632
|
const allEntries = this.registry.getAll();
|
|
633
|
+
const allowedToolNames = await this.getAllowedMcpToolNames(userId);
|
|
634
|
+
const allowedEntries = allEntries.filter((entry) => allowedToolNames.has(entry.definition.name));
|
|
272
635
|
const dedupedBySanitized = new Map();
|
|
273
636
|
const excludedDuplicates = [];
|
|
274
|
-
const
|
|
275
|
-
|
|
637
|
+
const routerText = this.buildToolRouterText(message, history);
|
|
638
|
+
const normalizedMessage = this.normalizeText(routerText);
|
|
639
|
+
const messageTerms = this.getMessageKeywordSet(normalizedMessage);
|
|
640
|
+
const domainScores = this.scoreToolDomains(normalizedMessage, allowedEntries);
|
|
641
|
+
const candidateDomains = this.getCandidateDomains(domainScores);
|
|
642
|
+
const clarification = this.getToolSelectionClarification(message, normalizedMessage, domainScores, candidateDomains, allowedEntries);
|
|
643
|
+
if (clarification) {
|
|
644
|
+
this.logger.log(`MCP tools need clarification: conversationId=${conversationId} userId=${userId} ` +
|
|
645
|
+
`registered=${allEntries.length} authorized=${allowedEntries.length} ` +
|
|
646
|
+
`domains=${this.formatDomainScores(domainScores)} selected=0`);
|
|
647
|
+
return { tools: [], clarification };
|
|
648
|
+
}
|
|
649
|
+
const entriesForRanking = candidateDomains.length > 0
|
|
650
|
+
? allowedEntries.filter((entry) => candidateDomains.includes(this.getToolDomain(entry.definition.name)))
|
|
651
|
+
: allowedEntries;
|
|
652
|
+
for (const [index, entry] of entriesForRanking.entries()) {
|
|
276
653
|
const originalName = entry.definition.name;
|
|
277
654
|
const sanitizedName = sanitize(originalName);
|
|
278
655
|
const readOnly = Boolean(entry.definition.readOnly);
|
|
656
|
+
const domain = this.getToolDomain(originalName);
|
|
657
|
+
const matchScore = this.scoreToolMatch(normalizedMessage, messageTerms, entry, candidateDomains);
|
|
279
658
|
if (dedupedBySanitized.has(sanitizedName)) {
|
|
280
659
|
excludedDuplicates.push(originalName);
|
|
281
660
|
continue;
|
|
282
661
|
}
|
|
283
662
|
const scored = {
|
|
284
663
|
name: originalName,
|
|
664
|
+
domain,
|
|
285
665
|
readOnly,
|
|
286
|
-
priorityBucket: this.getDomainPriorityBucket(originalName),
|
|
666
|
+
priorityBucket: this.getDomainPriorityBucket(originalName, candidateDomains),
|
|
287
667
|
keywordBoost: this.getKeywordBoost(normalizedMessage, originalName),
|
|
668
|
+
matchScore,
|
|
288
669
|
index,
|
|
289
670
|
tool: {
|
|
290
671
|
type: 'function',
|
|
@@ -302,6 +683,9 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
302
683
|
}
|
|
303
684
|
const scoredTools = Array.from(dedupedBySanitized.values());
|
|
304
685
|
scoredTools.sort((a, b) => {
|
|
686
|
+
if (a.matchScore !== b.matchScore) {
|
|
687
|
+
return b.matchScore - a.matchScore;
|
|
688
|
+
}
|
|
305
689
|
if (a.priorityBucket !== b.priorityBucket) {
|
|
306
690
|
return a.priorityBucket - b.priorityBucket;
|
|
307
691
|
}
|
|
@@ -313,32 +697,62 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
313
697
|
}
|
|
314
698
|
return a.index - b.index;
|
|
315
699
|
});
|
|
316
|
-
const selected = scoredTools.slice(0,
|
|
700
|
+
const selected = scoredTools.slice(0, TARGET_SELECTED_TOOLS);
|
|
317
701
|
const excludedByLimit = scoredTools
|
|
318
|
-
.slice(
|
|
702
|
+
.slice(TARGET_SELECTED_TOOLS)
|
|
319
703
|
.map((tool) => tool.name);
|
|
320
704
|
if (excludedDuplicates.length > 0) {
|
|
321
705
|
this.logger.warn(`MCP tools duplicate sanitize names ignored: ${excludedDuplicates.length}. ` +
|
|
322
706
|
`Examples: ${excludedDuplicates.slice(0, 10).join(', ')}`);
|
|
323
707
|
}
|
|
324
708
|
if (excludedByLimit.length > 0) {
|
|
325
|
-
this.logger.warn(`MCP tools truncated for
|
|
709
|
+
this.logger.warn(`MCP tools truncated for provider budget (${TARGET_SELECTED_TOOLS}/${MAX_OPENAI_TOOLS}). ` +
|
|
326
710
|
`conversationId=${conversationId} userId=${userId} ` +
|
|
327
711
|
`available=${scoredTools.length} selected=${selected.length} excluded=${excludedByLimit.length}. ` +
|
|
328
712
|
`Top excluded: ${excludedByLimit.slice(0, 12).join(', ')}`);
|
|
329
713
|
}
|
|
330
714
|
this.logger.log(`MCP tools prepared: conversationId=${conversationId} userId=${userId} ` +
|
|
331
|
-
`
|
|
332
|
-
|
|
715
|
+
`registered=${allEntries.length} authorized=${allowedEntries.length} ` +
|
|
716
|
+
`domains=${this.formatDomainScores(domainScores)} ranked=${scoredTools.length} ` +
|
|
717
|
+
`selected=${selected.length}`);
|
|
718
|
+
return { tools: selected.map((entry) => entry.tool) };
|
|
333
719
|
}
|
|
334
|
-
|
|
720
|
+
async getAllowedMcpToolNames(userId) {
|
|
721
|
+
const routes = await this.prisma.route.findMany({
|
|
722
|
+
where: {
|
|
723
|
+
type: 'MCP',
|
|
724
|
+
tool_name: { not: null },
|
|
725
|
+
role_route: {
|
|
726
|
+
some: {
|
|
727
|
+
role: {
|
|
728
|
+
role_user: {
|
|
729
|
+
some: { user_id: userId },
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
},
|
|
733
|
+
},
|
|
734
|
+
},
|
|
735
|
+
select: { tool_name: true },
|
|
736
|
+
});
|
|
737
|
+
return new Set(routes
|
|
738
|
+
.map((route) => route.tool_name)
|
|
739
|
+
.filter((toolName) => typeof toolName === 'string'));
|
|
740
|
+
}
|
|
741
|
+
getDomainPriorityBucket(toolName, candidateDomains) {
|
|
742
|
+
const domain = this.getToolDomain(toolName);
|
|
743
|
+
const candidateIndex = candidateDomains.indexOf(domain);
|
|
744
|
+
if (candidateIndex >= 0) {
|
|
745
|
+
return candidateIndex;
|
|
746
|
+
}
|
|
335
747
|
if (toolName.startsWith('operations.'))
|
|
336
|
-
return
|
|
748
|
+
return 10;
|
|
749
|
+
if (toolName.startsWith('finance.'))
|
|
750
|
+
return 11;
|
|
337
751
|
if (toolName.startsWith('lms.'))
|
|
338
|
-
return
|
|
752
|
+
return 12;
|
|
339
753
|
if (toolName.startsWith('core.'))
|
|
340
|
-
return
|
|
341
|
-
return
|
|
754
|
+
return 13;
|
|
755
|
+
return 99;
|
|
342
756
|
}
|
|
343
757
|
getKeywordBoost(message, toolName) {
|
|
344
758
|
const terms = this.getMessageKeywordSet(message);
|
|
@@ -368,6 +782,216 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
368
782
|
}
|
|
369
783
|
return boost;
|
|
370
784
|
}
|
|
785
|
+
buildToolRouterText(message, history) {
|
|
786
|
+
const recentContext = history
|
|
787
|
+
.slice(-8)
|
|
788
|
+
.map((item) => [item.role === 'user' ? item.content : undefined, item.tool_name]
|
|
789
|
+
.filter(Boolean)
|
|
790
|
+
.join(' '))
|
|
791
|
+
.join(' ');
|
|
792
|
+
return `${recentContext} ${message}`;
|
|
793
|
+
}
|
|
794
|
+
normalizeText(value) {
|
|
795
|
+
return value
|
|
796
|
+
.toLowerCase()
|
|
797
|
+
.normalize('NFD')
|
|
798
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
799
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
800
|
+
.replace(/\s+/g, ' ')
|
|
801
|
+
.trim();
|
|
802
|
+
}
|
|
803
|
+
getToolDomain(toolName) {
|
|
804
|
+
return toolName.split('.')[0] || 'unknown';
|
|
805
|
+
}
|
|
806
|
+
scoreToolDomains(normalizedMessage, entries) {
|
|
807
|
+
var _a, _b;
|
|
808
|
+
const scores = new Map();
|
|
809
|
+
for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORDS)) {
|
|
810
|
+
let score = 0;
|
|
811
|
+
if (this.hasTerm(normalizedMessage, domain)) {
|
|
812
|
+
score += 3;
|
|
813
|
+
}
|
|
814
|
+
for (const keyword of keywords) {
|
|
815
|
+
const normalizedKeyword = this.normalizeText(keyword);
|
|
816
|
+
if (normalizedKeyword && normalizedMessage.includes(normalizedKeyword)) {
|
|
817
|
+
score += normalizedKeyword.includes(' ') ? 3 : 2;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (score > 0 && entries.some((entry) => this.getToolDomain(entry.definition.name) === domain)) {
|
|
821
|
+
scores.set(domain, score);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
for (const entry of entries) {
|
|
825
|
+
const domain = this.getToolDomain(entry.definition.name);
|
|
826
|
+
const toolText = this.normalizeText(`${entry.definition.name} ${(_a = entry.definition.description) !== null && _a !== void 0 ? _a : ''}`);
|
|
827
|
+
let score = (_b = scores.get(domain)) !== null && _b !== void 0 ? _b : 0;
|
|
828
|
+
for (const term of normalizedMessage.split(' ')) {
|
|
829
|
+
if (term.length >= 4 && toolText.includes(term)) {
|
|
830
|
+
score += 1;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
if (score > 0) {
|
|
834
|
+
scores.set(domain, score);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return scores;
|
|
838
|
+
}
|
|
839
|
+
getCandidateDomains(domainScores) {
|
|
840
|
+
const ranked = Array.from(domainScores.entries())
|
|
841
|
+
.filter(([, score]) => score >= MIN_DOMAIN_CONFIDENCE)
|
|
842
|
+
.sort((a, b) => b[1] - a[1]);
|
|
843
|
+
if (ranked.length === 0) {
|
|
844
|
+
return [];
|
|
845
|
+
}
|
|
846
|
+
const topScore = ranked[0][1];
|
|
847
|
+
return ranked
|
|
848
|
+
.filter(([, score]) => topScore - score <= AMBIGUOUS_DOMAIN_DELTA)
|
|
849
|
+
.map(([domain]) => domain);
|
|
850
|
+
}
|
|
851
|
+
getToolSelectionClarification(originalMessage, normalizedMessage, domainScores, candidateDomains, entries) {
|
|
852
|
+
if (!this.messageLooksLikeToolRequest(normalizedMessage)) {
|
|
853
|
+
return undefined;
|
|
854
|
+
}
|
|
855
|
+
const rankedDomains = Array.from(domainScores.entries()).sort((a, b) => b[1] - a[1]);
|
|
856
|
+
if (rankedDomains.length === 0 || rankedDomains[0][1] < MIN_DOMAIN_CONFIDENCE) {
|
|
857
|
+
return this.isLikelyPortuguese(originalMessage)
|
|
858
|
+
? 'Você quer consultar qual área do sistema? Por exemplo: financeiro, operações, LMS ou configurações.'
|
|
859
|
+
: 'Which system area do you want to query? For example: finance, operations, LMS, or settings.';
|
|
860
|
+
}
|
|
861
|
+
if (candidateDomains.length > 1) {
|
|
862
|
+
const options = candidateDomains
|
|
863
|
+
.slice(0, 3)
|
|
864
|
+
.map((domain) => this.getDomainLabel(domain, originalMessage))
|
|
865
|
+
.join(', ');
|
|
866
|
+
return this.isLikelyPortuguese(originalMessage)
|
|
867
|
+
? `Você quer consultar qual área: ${options}?`
|
|
868
|
+
: `Which area do you want to query: ${options}?`;
|
|
869
|
+
}
|
|
870
|
+
if (candidateDomains[0] === 'finance' &&
|
|
871
|
+
this.isAmbiguousFinanceAccountsRequest(normalizedMessage, entries)) {
|
|
872
|
+
return this.isLikelyPortuguese(originalMessage)
|
|
873
|
+
? 'Você quer consultar contas bancárias, contas a pagar ou contas a receber?'
|
|
874
|
+
: 'Do you want bank accounts, accounts payable, or accounts receivable?';
|
|
875
|
+
}
|
|
876
|
+
return undefined;
|
|
877
|
+
}
|
|
878
|
+
scoreToolMatch(normalizedMessage, terms, entry, candidateDomains) {
|
|
879
|
+
var _a;
|
|
880
|
+
const toolName = entry.definition.name;
|
|
881
|
+
const toolText = this.normalizeText(`${toolName} ${(_a = entry.definition.description) !== null && _a !== void 0 ? _a : ''} ${this.getSchemaText(entry.definition.inputSchema)}`);
|
|
882
|
+
const domain = this.getToolDomain(toolName);
|
|
883
|
+
let score = candidateDomains.includes(domain) ? 8 : 0;
|
|
884
|
+
for (const term of normalizedMessage.split(' ')) {
|
|
885
|
+
if (term.length < 3)
|
|
886
|
+
continue;
|
|
887
|
+
if (toolText.includes(term)) {
|
|
888
|
+
score += term.length >= 5 ? 2 : 1;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
if (terms.has('list') && entry.definition.readOnly)
|
|
892
|
+
score += 4;
|
|
893
|
+
if (terms.has('write') && !entry.definition.readOnly)
|
|
894
|
+
score += 4;
|
|
895
|
+
score += this.getFinanceSpecificBoost(normalizedMessage, toolName);
|
|
896
|
+
score += this.getKeywordBoost(normalizedMessage, toolName);
|
|
897
|
+
return score;
|
|
898
|
+
}
|
|
899
|
+
getSchemaText(schema) {
|
|
900
|
+
if (!schema || typeof schema !== 'object') {
|
|
901
|
+
return '';
|
|
902
|
+
}
|
|
903
|
+
const properties = schema.properties;
|
|
904
|
+
if (!properties || typeof properties !== 'object') {
|
|
905
|
+
return '';
|
|
906
|
+
}
|
|
907
|
+
return Object.keys(properties).join(' ');
|
|
908
|
+
}
|
|
909
|
+
getFinanceSpecificBoost(message, toolName) {
|
|
910
|
+
if (!toolName.startsWith('finance.')) {
|
|
911
|
+
return 0;
|
|
912
|
+
}
|
|
913
|
+
let boost = 0;
|
|
914
|
+
if (/banco|bancaria|bancarias|bank/.test(message) &&
|
|
915
|
+
toolName.includes('bank-accounts')) {
|
|
916
|
+
boost += 10;
|
|
917
|
+
}
|
|
918
|
+
if (/extrato|statement/.test(message) && toolName.includes('statements')) {
|
|
919
|
+
boost += 10;
|
|
920
|
+
}
|
|
921
|
+
if (/pagar|payable/.test(message) && toolName.includes('accounts-payable')) {
|
|
922
|
+
boost += 10;
|
|
923
|
+
}
|
|
924
|
+
if (/receber|receivable/.test(message) && toolName.includes('accounts-receivable')) {
|
|
925
|
+
boost += 10;
|
|
926
|
+
}
|
|
927
|
+
if (/moeda|currency/.test(message) && toolName.includes('currencies')) {
|
|
928
|
+
boost += 8;
|
|
929
|
+
}
|
|
930
|
+
if (/transferencia|transfer|transferencias/.test(message) && toolName.includes('transfers')) {
|
|
931
|
+
boost += 8;
|
|
932
|
+
}
|
|
933
|
+
return boost;
|
|
934
|
+
}
|
|
935
|
+
messageLooksLikeToolRequest(normalizedMessage) {
|
|
936
|
+
return ACTION_KEYWORDS.some((keyword) => this.hasTerm(normalizedMessage, this.normalizeText(keyword)));
|
|
937
|
+
}
|
|
938
|
+
isAmbiguousFinanceAccountsRequest(normalizedMessage, entries) {
|
|
939
|
+
const mentionsGenericAccounts = /\b(conta|contas|account|accounts)\b/.test(normalizedMessage);
|
|
940
|
+
const hasQualifier = /banco|bancaria|bancarias|bank|extrato|statement|pagar|payable|receber|receivable/.test(normalizedMessage);
|
|
941
|
+
if (!mentionsGenericAccounts || hasQualifier) {
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
const financeToolNames = entries
|
|
945
|
+
.map((entry) => entry.definition.name)
|
|
946
|
+
.filter((name) => name.startsWith('finance.'));
|
|
947
|
+
return (financeToolNames.some((name) => name.includes('bank-accounts')) &&
|
|
948
|
+
(financeToolNames.some((name) => name.includes('accounts-payable')) ||
|
|
949
|
+
financeToolNames.some((name) => name.includes('accounts-receivable'))));
|
|
950
|
+
}
|
|
951
|
+
hasTerm(text, term) {
|
|
952
|
+
return new RegExp(`(^|\\s)${this.escapeRegex(term)}($|\\s)`).test(text);
|
|
953
|
+
}
|
|
954
|
+
escapeRegex(value) {
|
|
955
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
956
|
+
}
|
|
957
|
+
getDomainLabel(domain, message) {
|
|
958
|
+
const labels = {
|
|
959
|
+
finance: { pt: 'financeiro', en: 'finance' },
|
|
960
|
+
operations: { pt: 'operações', en: 'operations' },
|
|
961
|
+
lms: { pt: 'LMS/cursos', en: 'LMS/courses' },
|
|
962
|
+
core: { pt: 'configurações/core', en: 'settings/core' },
|
|
963
|
+
contact: { pt: 'contatos', en: 'contacts' },
|
|
964
|
+
};
|
|
965
|
+
const label = labels[domain];
|
|
966
|
+
if (!label) {
|
|
967
|
+
return domain;
|
|
968
|
+
}
|
|
969
|
+
return this.isLikelyPortuguese(message) ? label.pt : label.en;
|
|
970
|
+
}
|
|
971
|
+
isLikelyPortuguese(message) {
|
|
972
|
+
return /(voce|você|qual|consultar|listar|conta|contas|bancaria|bancária|extrato|tarefas|curso|cursos)/i.test(message);
|
|
973
|
+
}
|
|
974
|
+
formatDomainScores(domainScores) {
|
|
975
|
+
const formatted = Array.from(domainScores.entries())
|
|
976
|
+
.sort((a, b) => b[1] - a[1])
|
|
977
|
+
.slice(0, 5)
|
|
978
|
+
.map(([domain, score]) => `${domain}:${score}`)
|
|
979
|
+
.join(',');
|
|
980
|
+
return formatted || 'none';
|
|
981
|
+
}
|
|
982
|
+
async recordAssistantAndReturnHistory(conversationId, content) {
|
|
983
|
+
await this.prisma.mcp_conversation_message.create({
|
|
984
|
+
data: {
|
|
985
|
+
conversation_id: conversationId,
|
|
986
|
+
role: 'assistant',
|
|
987
|
+
content,
|
|
988
|
+
},
|
|
989
|
+
});
|
|
990
|
+
return this.prisma.mcp_conversation_message.findMany({
|
|
991
|
+
where: { conversation_id: conversationId },
|
|
992
|
+
orderBy: { created_at: 'asc' },
|
|
993
|
+
});
|
|
994
|
+
}
|
|
371
995
|
getMessageKeywordSet(message) {
|
|
372
996
|
const keywords = new Set();
|
|
373
997
|
if (/(tarefa|tarefas|task|tasks)/i.test(message)) {
|
|
@@ -382,6 +1006,12 @@ let McpChatService = McpChatService_1 = class McpChatService {
|
|
|
382
1006
|
if (/(pendente|pendentes|pending|open|aberta|abertas)/i.test(message)) {
|
|
383
1007
|
keywords.add('pending');
|
|
384
1008
|
}
|
|
1009
|
+
if (/(listar|lista|buscar|procurar|consultar|mostrar|ver|list|search|find|show|get)/i.test(message)) {
|
|
1010
|
+
keywords.add('list');
|
|
1011
|
+
}
|
|
1012
|
+
if (/(criar|adicionar|atualizar|alterar|remover|deletar|excluir|create|add|update|delete|remove)/i.test(message)) {
|
|
1013
|
+
keywords.add('write');
|
|
1014
|
+
}
|
|
385
1015
|
return keywords;
|
|
386
1016
|
}
|
|
387
1017
|
};
|