n8n 1.118.1 → 1.119.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/dist/build.tsbuildinfo +1 -1
- package/dist/controllers/oauth/oauth2-credential.controller.d.ts +2 -0
- package/dist/controllers/oauth/oauth2-credential.controller.js +85 -1
- package/dist/controllers/oauth/oauth2-credential.controller.js.map +1 -1
- package/dist/controllers/users.controller.d.ts +1 -1
- package/dist/credentials-helper.js +8 -0
- package/dist/credentials-helper.js.map +1 -1
- package/dist/environments.ee/source-control/source-control-status.service.ee.d.ts +2 -2
- package/dist/environments.ee/source-control/source-control.controller.ee.d.ts +4 -4
- package/dist/environments.ee/source-control/source-control.service.ee.d.ts +2 -2
- package/dist/errors/response-errors/abstract/response.error.d.ts +1 -0
- package/dist/errors/response-errors/abstract/response.error.js.map +1 -1
- package/dist/errors/response-errors/license-eula-required.error.d.ts +9 -0
- package/dist/errors/response-errors/license-eula-required.error.js +13 -0
- package/dist/errors/response-errors/license-eula-required.error.js.map +1 -0
- package/dist/events/relays/telemetry.event-relay.js +3 -0
- package/dist/events/relays/telemetry.event-relay.js.map +1 -1
- package/dist/license/license.controller.js +2 -2
- package/dist/license/license.controller.js.map +1 -1
- package/dist/license/license.service.d.ts +3 -1
- package/dist/license/license.service.js +30 -5
- package/dist/license/license.service.js.map +1 -1
- package/dist/license.d.ts +1 -1
- package/dist/license.js +2 -2
- package/dist/license.js.map +1 -1
- package/dist/modules/breaking-changes/breaking-changes.controller.d.ts +12 -0
- package/dist/modules/breaking-changes/breaking-changes.controller.js +50 -0
- package/dist/modules/breaking-changes/breaking-changes.controller.js.map +1 -0
- package/dist/modules/breaking-changes/breaking-changes.module.d.ts +4 -0
- package/dist/modules/breaking-changes/breaking-changes.module.js +53 -0
- package/dist/modules/breaking-changes/breaking-changes.module.js.map +1 -0
- package/dist/modules/breaking-changes/breaking-changes.rule-registry.service.d.ts +11 -0
- package/dist/modules/breaking-changes/breaking-changes.rule-registry.service.js +47 -0
- package/dist/modules/breaking-changes/breaking-changes.rule-registry.service.js.map +1 -0
- package/dist/modules/breaking-changes/breaking-changes.service.d.ts +24 -0
- package/dist/modules/breaking-changes/breaking-changes.service.js +189 -0
- package/dist/modules/breaking-changes/breaking-changes.service.js.map +1 -0
- package/dist/modules/breaking-changes/rules/index.d.ts +4 -0
- package/dist/modules/breaking-changes/rules/index.js +7 -0
- package/dist/modules/breaking-changes/rules/index.js.map +1 -0
- package/dist/modules/breaking-changes/rules/v2/file-access.rule.d.ts +10 -0
- package/dist/modules/breaking-changes/rules/v2/file-access.rule.js +52 -0
- package/dist/modules/breaking-changes/rules/v2/file-access.rule.js.map +1 -0
- package/dist/modules/breaking-changes/rules/v2/index.d.ts +3 -0
- package/dist/modules/breaking-changes/rules/v2/index.js +9 -0
- package/dist/modules/breaking-changes/rules/v2/index.js.map +1 -0
- package/dist/modules/breaking-changes/rules/v2/process-env-access.rule.d.ts +9 -0
- package/dist/modules/breaking-changes/rules/v2/process-env-access.rule.js +71 -0
- package/dist/modules/breaking-changes/rules/v2/process-env-access.rule.js.map +1 -0
- package/dist/modules/breaking-changes/rules/v2/removed-nodes.rule.d.ts +10 -0
- package/dist/modules/breaking-changes/rules/v2/removed-nodes.rule.js +56 -0
- package/dist/modules/breaking-changes/rules/v2/removed-nodes.rule.js.map +1 -0
- package/dist/modules/breaking-changes/types/detection.types.d.ts +11 -0
- package/dist/modules/breaking-changes/types/detection.types.js +3 -0
- package/dist/modules/breaking-changes/types/detection.types.js.map +1 -0
- package/dist/modules/breaking-changes/types/index.d.ts +2 -0
- package/dist/modules/breaking-changes/types/index.js +18 -0
- package/dist/modules/breaking-changes/types/index.js.map +1 -0
- package/dist/modules/breaking-changes/types/rule.types.d.ts +71 -0
- package/dist/modules/breaking-changes/types/rule.types.js +25 -0
- package/dist/modules/breaking-changes/types/rule.types.js.map +1 -0
- package/dist/modules/chat-hub/chat-hub-agent.entity.d.ts +14 -0
- package/dist/modules/chat-hub/chat-hub-agent.entity.js +63 -0
- package/dist/modules/chat-hub/chat-hub-agent.entity.js.map +1 -0
- package/dist/modules/chat-hub/chat-hub-agent.repository.d.ts +10 -0
- package/dist/modules/chat-hub/chat-hub-agent.repository.js +61 -0
- package/dist/modules/chat-hub/chat-hub-agent.repository.js.map +1 -0
- package/dist/modules/chat-hub/chat-hub-agent.service.d.ts +32 -0
- package/dist/modules/chat-hub/chat-hub-agent.service.js +107 -0
- package/dist/modules/chat-hub/chat-hub-agent.service.js.map +1 -0
- package/dist/modules/chat-hub/chat-hub-credentials.service.d.ts +15 -0
- package/dist/modules/chat-hub/chat-hub-credentials.service.js +54 -0
- package/dist/modules/chat-hub/chat-hub-credentials.service.js.map +1 -0
- package/dist/modules/chat-hub/chat-hub-message.entity.d.ts +1 -0
- package/dist/modules/chat-hub/chat-hub-message.entity.js +4 -0
- package/dist/modules/chat-hub/chat-hub-message.entity.js.map +1 -1
- package/dist/modules/chat-hub/chat-hub-session.entity.d.ts +7 -4
- package/dist/modules/chat-hub/chat-hub-session.entity.js +9 -1
- package/dist/modules/chat-hub/chat-hub-session.entity.js.map +1 -1
- package/dist/modules/chat-hub/chat-hub-workflow.service.d.ts +29 -0
- package/dist/modules/chat-hub/chat-hub-workflow.service.js +382 -0
- package/dist/modules/chat-hub/chat-hub-workflow.service.js.map +1 -0
- package/dist/modules/chat-hub/chat-hub.constants.d.ts +18 -0
- package/dist/modules/chat-hub/chat-hub.constants.js +30 -1
- package/dist/modules/chat-hub/chat-hub.constants.js.map +1 -1
- package/dist/modules/chat-hub/chat-hub.controller.d.ts +10 -3
- package/dist/modules/chat-hub/chat-hub.controller.js +68 -7
- package/dist/modules/chat-hub/chat-hub.controller.js.map +1 -1
- package/dist/modules/chat-hub/chat-hub.module.d.ts +1 -1
- package/dist/modules/chat-hub/chat-hub.module.js +2 -1
- package/dist/modules/chat-hub/chat-hub.module.js.map +1 -1
- package/dist/modules/chat-hub/chat-hub.service.d.ts +31 -11
- package/dist/modules/chat-hub/chat-hub.service.js +519 -498
- package/dist/modules/chat-hub/chat-hub.service.js.map +1 -1
- package/dist/modules/chat-hub/chat-hub.types.d.ts +2 -1
- package/dist/modules/chat-hub/chat-message.repository.js +3 -3
- package/dist/modules/chat-hub/chat-message.repository.js.map +1 -1
- package/dist/modules/chat-hub/chat-session.repository.d.ts +1 -0
- package/dist/modules/chat-hub/chat-session.repository.js +9 -0
- package/dist/modules/chat-hub/chat-session.repository.js.map +1 -1
- package/dist/modules/chat-hub/dto/chat-models-request.dto.d.ts +1 -1
- package/dist/modules/chat-hub/stream-capturer.d.ts +17 -2
- package/dist/modules/chat-hub/stream-capturer.js +90 -19
- package/dist/modules/chat-hub/stream-capturer.js.map +1 -1
- package/dist/modules/data-table/data-table-aggregate.service.js +4 -0
- package/dist/modules/data-table/data-table-aggregate.service.js.map +1 -1
- package/dist/modules/data-table/data-table.controller.d.ts +7 -1
- package/dist/modules/data-table/data-table.controller.js +35 -17
- package/dist/modules/data-table/data-table.controller.js.map +1 -1
- package/dist/modules/insights/database/repositories/insights-by-period-query.helper.js +1 -1
- package/dist/modules/insights/database/repositories/insights-by-period-query.helper.js.map +1 -1
- package/dist/modules/insights/database/repositories/insights-by-period.repository.js +1 -1
- package/dist/modules/insights/database/repositories/insights-by-period.repository.js.map +1 -1
- package/dist/modules/insights/insights-collection.service.js +1 -0
- package/dist/modules/insights/insights-collection.service.js.map +1 -1
- package/dist/modules/insights/insights.controller.js +2 -15
- package/dist/modules/insights/insights.controller.js.map +1 -1
- package/dist/modules/insights/insights.service.d.ts +1 -1
- package/dist/modules/insights/insights.service.js.map +1 -1
- package/dist/modules/workflow-index/workflow-index.service.d.ts +22 -0
- package/dist/modules/workflow-index/workflow-index.service.js +166 -0
- package/dist/modules/workflow-index/workflow-index.service.js.map +1 -0
- package/dist/public-api/v1/handlers/workflows/workflows.handler.js +1 -1
- package/dist/public-api/v1/handlers/workflows/workflows.handler.js.map +1 -1
- package/dist/public-api/v1/handlers/workflows/workflows.service.d.ts +1 -1
- package/dist/public-api/v1/handlers/workflows/workflows.service.js +8 -2
- package/dist/public-api/v1/handlers/workflows/workflows.service.js.map +1 -1
- package/dist/public-api/v1/openapi.yml +47 -0
- package/dist/requests.d.ts +1 -0
- package/dist/response-helper.js +3 -0
- package/dist/response-helper.js.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.js +11 -0
- package/dist/server.js.map +1 -1
- package/dist/services/frontend.service.d.ts +1 -1
- package/dist/services/frontend.service.js +7 -2
- package/dist/services/frontend.service.js.map +1 -1
- package/dist/services/import.service.d.ts +5 -1
- package/dist/services/import.service.js +15 -2
- package/dist/services/import.service.js.map +1 -1
- package/dist/services/workflow-statistics.service.js +1 -0
- package/dist/services/workflow-statistics.service.js.map +1 -1
- package/dist/sso.ee/oidc/oidc.service.ee.d.ts +2 -1
- package/dist/sso.ee/oidc/oidc.service.ee.js +28 -6
- package/dist/sso.ee/oidc/oidc.service.ee.js.map +1 -1
- package/dist/sso.ee/saml/routes/saml.controller.ee.d.ts +1 -0
- package/dist/sso.ee/saml/routes/saml.controller.ee.js +19 -4
- package/dist/sso.ee/saml/routes/saml.controller.ee.js.map +1 -1
- package/dist/sso.ee/saml/saml.service.ee.d.ts +1 -1
- package/dist/sso.ee/saml/saml.service.ee.js +9 -6
- package/dist/sso.ee/saml/saml.service.ee.js.map +1 -1
- package/dist/workflow-runner.js +2 -1
- package/dist/workflow-runner.js.map +1 -1
- package/dist/workflows/workflow-execution.service.d.ts +4 -1
- package/dist/workflows/workflow-execution.service.js +14 -0
- package/dist/workflows/workflow-execution.service.js.map +1 -1
- package/dist/workflows/workflow.service.d.ts +4 -11
- package/dist/workflows/workflow.service.js +10 -8
- package/dist/workflows/workflow.service.js.map +1 -1
- package/package.json +22 -19
|
@@ -15,11 +15,9 @@ const backend_common_1 = require("@n8n/backend-common");
|
|
|
15
15
|
const db_1 = require("@n8n/db");
|
|
16
16
|
const di_1 = require("@n8n/di");
|
|
17
17
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
18
|
-
const uuid_1 = require("uuid");
|
|
19
18
|
const active_executions_1 = require("../../active-executions");
|
|
20
19
|
const credentials_finder_service_1 = require("../../credentials/credentials-finder.service");
|
|
21
20
|
const bad_request_error_1 = require("../../errors/response-errors/bad-request.error");
|
|
22
|
-
const forbidden_error_1 = require("../../errors/response-errors/forbidden.error");
|
|
23
21
|
const not_found_error_1 = require("../../errors/response-errors/not-found.error");
|
|
24
22
|
const execution_service_1 = require("../../executions/execution.service");
|
|
25
23
|
const dynamic_node_parameters_service_1 = require("../../services/dynamic-node-parameters.service");
|
|
@@ -27,42 +25,15 @@ const workflow_execute_additional_data_1 = require("../../workflow-execute-addit
|
|
|
27
25
|
const workflow_execution_service_1 = require("../../workflows/workflow-execution.service");
|
|
28
26
|
const workflow_finder_service_1 = require("../../workflows/workflow-finder.service");
|
|
29
27
|
const workflow_service_1 = require("../../workflows/workflow.service");
|
|
28
|
+
const chat_hub_agent_service_1 = require("./chat-hub-agent.service");
|
|
29
|
+
const chat_hub_credentials_service_1 = require("./chat-hub-credentials.service");
|
|
30
|
+
const chat_hub_workflow_service_1 = require("./chat-hub-workflow.service");
|
|
30
31
|
const chat_hub_constants_1 = require("./chat-hub.constants");
|
|
31
32
|
const chat_message_repository_1 = require("./chat-message.repository");
|
|
32
33
|
const chat_session_repository_1 = require("./chat-session.repository");
|
|
33
|
-
const context_limits_1 = require("./context-limits");
|
|
34
34
|
const stream_capturer_1 = require("./stream-capturer");
|
|
35
|
-
const providerNodeTypeMapping = {
|
|
36
|
-
openai: {
|
|
37
|
-
name: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
|
|
38
|
-
version: 1.2,
|
|
39
|
-
},
|
|
40
|
-
anthropic: {
|
|
41
|
-
name: '@n8n/n8n-nodes-langchain.lmChatAnthropic',
|
|
42
|
-
version: 1.3,
|
|
43
|
-
},
|
|
44
|
-
google: {
|
|
45
|
-
name: '@n8n/n8n-nodes-langchain.lmChatGoogleGemini',
|
|
46
|
-
version: 1.2,
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
const NODE_NAMES = {
|
|
50
|
-
CHAT_TRIGGER: 'When chat message received',
|
|
51
|
-
REPLY_AGENT: 'AI Agent',
|
|
52
|
-
TITLE_GENERATOR_AGENT: 'Title Generator Agent',
|
|
53
|
-
CHAT_MODEL: 'Chat Model',
|
|
54
|
-
MEMORY: 'Memory',
|
|
55
|
-
RESTORE_CHAT_MEMORY: 'Restore Chat Memory',
|
|
56
|
-
CLEAR_CHAT_MEMORY: 'Clear Chat Memory',
|
|
57
|
-
};
|
|
58
|
-
const JSONL_STREAM_HEADERS = {
|
|
59
|
-
'Content-Type': 'application/json-lines; charset=utf-8',
|
|
60
|
-
'Transfer-Encoding': 'chunked',
|
|
61
|
-
'Cache-Control': 'no-cache',
|
|
62
|
-
Connection: 'keep-alive',
|
|
63
|
-
};
|
|
64
35
|
let ChatHubService = class ChatHubService {
|
|
65
|
-
constructor(logger, executionService, nodeParametersService, executionRepository, workflowExecutionService, workflowService, workflowFinderService, workflowRepository,
|
|
36
|
+
constructor(logger, executionService, nodeParametersService, executionRepository, workflowExecutionService, workflowService, workflowFinderService, workflowRepository, activeExecutions, sessionRepository, messageRepository, credentialsFinderService, chatHubAgentService, chatHubCredentialsService, chatHubWorkflowService) {
|
|
66
37
|
this.logger = logger;
|
|
67
38
|
this.executionService = executionService;
|
|
68
39
|
this.nodeParametersService = nodeParametersService;
|
|
@@ -71,21 +42,23 @@ let ChatHubService = class ChatHubService {
|
|
|
71
42
|
this.workflowService = workflowService;
|
|
72
43
|
this.workflowFinderService = workflowFinderService;
|
|
73
44
|
this.workflowRepository = workflowRepository;
|
|
74
|
-
this.sharedWorkflowRepository = sharedWorkflowRepository;
|
|
75
45
|
this.activeExecutions = activeExecutions;
|
|
76
46
|
this.sessionRepository = sessionRepository;
|
|
77
47
|
this.messageRepository = messageRepository;
|
|
78
48
|
this.credentialsFinderService = credentialsFinderService;
|
|
49
|
+
this.chatHubAgentService = chatHubAgentService;
|
|
50
|
+
this.chatHubCredentialsService = chatHubCredentialsService;
|
|
51
|
+
this.chatHubWorkflowService = chatHubWorkflowService;
|
|
79
52
|
}
|
|
80
53
|
async getModels(user, credentialIds) {
|
|
81
54
|
const additionalData = await (0, workflow_execute_additional_data_1.getBase)({ userId: user.id });
|
|
82
|
-
const providers =
|
|
55
|
+
const providers = api_types_1.chatHubProviderSchema.options;
|
|
83
56
|
const allCredentials = await this.credentialsFinderService.findCredentialsForUser(user, [
|
|
84
57
|
'credential:read',
|
|
85
58
|
]);
|
|
86
59
|
const responses = await Promise.all(providers.map(async (provider) => {
|
|
87
60
|
const credentials = {};
|
|
88
|
-
if (provider !== 'n8n') {
|
|
61
|
+
if (provider !== 'n8n' && provider !== 'custom-agent') {
|
|
89
62
|
const credentialId = credentialIds[provider];
|
|
90
63
|
if (!credentialId) {
|
|
91
64
|
return [provider, { models: [] }];
|
|
@@ -114,12 +87,7 @@ let ChatHubService = class ChatHubService {
|
|
|
114
87
|
return responses.reduce((acc, [provider, res]) => {
|
|
115
88
|
acc[provider] = res;
|
|
116
89
|
return acc;
|
|
117
|
-
}, {
|
|
118
|
-
openai: { models: [] },
|
|
119
|
-
anthropic: { models: [] },
|
|
120
|
-
google: { models: [] },
|
|
121
|
-
n8n: { models: [] },
|
|
122
|
-
});
|
|
90
|
+
}, { ...api_types_1.emptyChatModelsResponse });
|
|
123
91
|
}
|
|
124
92
|
async fetchModelsForProvider(user, provider, credentials, additionalData) {
|
|
125
93
|
switch (provider) {
|
|
@@ -130,26 +98,38 @@ let ChatHubService = class ChatHubService {
|
|
|
130
98
|
case 'google':
|
|
131
99
|
return await this.fetchGoogleModels(credentials, additionalData);
|
|
132
100
|
case 'n8n':
|
|
133
|
-
return await this.
|
|
101
|
+
return await this.fetchAgentWorkflowsAsModels(user);
|
|
102
|
+
case 'custom-agent':
|
|
103
|
+
return await this.chatHubAgentService.getAgentsByUserIdAsModels(user.id);
|
|
134
104
|
}
|
|
135
105
|
}
|
|
136
106
|
async fetchOpenAiModels(credentials, additionalData) {
|
|
137
|
-
const resourceLocatorResults = await this.nodeParametersService.getResourceLocatorResults('searchModels', 'parameters.model', additionalData,
|
|
107
|
+
const resourceLocatorResults = await this.nodeParametersService.getResourceLocatorResults('searchModels', 'parameters.model', additionalData, chat_hub_constants_1.PROVIDER_NODE_TYPE_MAP.openai, {}, credentials);
|
|
138
108
|
return {
|
|
139
109
|
models: resourceLocatorResults.results.map((result) => ({
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
model:
|
|
110
|
+
name: result.name,
|
|
111
|
+
description: result.description ?? null,
|
|
112
|
+
model: {
|
|
113
|
+
provider: 'openai',
|
|
114
|
+
model: String(result.value),
|
|
115
|
+
},
|
|
116
|
+
createdAt: null,
|
|
117
|
+
updatedAt: null,
|
|
143
118
|
})),
|
|
144
119
|
};
|
|
145
120
|
}
|
|
146
121
|
async fetchAnthropicModels(credentials, additionalData) {
|
|
147
|
-
const resourceLocatorResults = await this.nodeParametersService.getResourceLocatorResults('searchModels', 'parameters.model', additionalData,
|
|
122
|
+
const resourceLocatorResults = await this.nodeParametersService.getResourceLocatorResults('searchModels', 'parameters.model', additionalData, chat_hub_constants_1.PROVIDER_NODE_TYPE_MAP.anthropic, {}, credentials);
|
|
148
123
|
return {
|
|
149
124
|
models: resourceLocatorResults.results.map((result) => ({
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
model:
|
|
125
|
+
name: result.name,
|
|
126
|
+
description: result.description ?? null,
|
|
127
|
+
model: {
|
|
128
|
+
provider: 'anthropic',
|
|
129
|
+
model: String(result.value),
|
|
130
|
+
},
|
|
131
|
+
createdAt: null,
|
|
132
|
+
updatedAt: null,
|
|
153
133
|
})),
|
|
154
134
|
};
|
|
155
135
|
}
|
|
@@ -191,73 +171,57 @@ let ChatHubService = class ChatHubService {
|
|
|
191
171
|
],
|
|
192
172
|
},
|
|
193
173
|
},
|
|
194
|
-
}, additionalData,
|
|
174
|
+
}, additionalData, chat_hub_constants_1.PROVIDER_NODE_TYPE_MAP.google, {}, credentials);
|
|
195
175
|
return {
|
|
196
176
|
models: results.map((result) => ({
|
|
197
|
-
provider: 'google',
|
|
198
177
|
name: String(result.value),
|
|
199
|
-
|
|
178
|
+
description: result.description ?? null,
|
|
179
|
+
model: {
|
|
180
|
+
provider: 'google',
|
|
181
|
+
model: String(result.value),
|
|
182
|
+
},
|
|
183
|
+
createdAt: null,
|
|
184
|
+
updatedAt: null,
|
|
200
185
|
})),
|
|
201
186
|
};
|
|
202
187
|
}
|
|
203
|
-
async
|
|
188
|
+
async fetchAgentWorkflowsAsModels(user) {
|
|
204
189
|
const nodeTypes = [n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE];
|
|
205
|
-
const workflows = await this.workflowService.getWorkflowsWithNodesIncluded(user, nodeTypes);
|
|
190
|
+
const workflows = await this.workflowService.getWorkflowsWithNodesIncluded(user, nodeTypes, true);
|
|
206
191
|
return {
|
|
207
192
|
models: workflows
|
|
193
|
+
.filter((workflow) => workflow.scopes.includes('workflow:read'))
|
|
208
194
|
.filter((workflow) => workflow.active)
|
|
209
|
-
.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
195
|
+
.flatMap((workflow) => {
|
|
196
|
+
const chatTrigger = workflow.nodes?.find((node) => node.type === n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE);
|
|
197
|
+
if (!chatTrigger) {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
if (chatTrigger.parameters.availableInChat !== true) {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
const name = typeof chatTrigger.parameters.agentName === 'string' &&
|
|
204
|
+
chatTrigger.parameters.agentName.length > 0
|
|
205
|
+
? chatTrigger.parameters.agentName
|
|
206
|
+
: workflow.name;
|
|
207
|
+
return [
|
|
208
|
+
{
|
|
209
|
+
name: name ?? 'Unknown Agent',
|
|
210
|
+
description: typeof chatTrigger.parameters.agentDescription === 'string' &&
|
|
211
|
+
chatTrigger.parameters.agentDescription.length > 0
|
|
212
|
+
? chatTrigger.parameters.agentDescription
|
|
213
|
+
: null,
|
|
214
|
+
model: {
|
|
215
|
+
provider: 'n8n',
|
|
216
|
+
workflowId: workflow.id,
|
|
217
|
+
},
|
|
218
|
+
createdAt: workflow.createdAt ? workflow.createdAt.toISOString() : null,
|
|
219
|
+
updatedAt: workflow.updatedAt ? workflow.updatedAt.toISOString() : null,
|
|
220
|
+
},
|
|
221
|
+
];
|
|
222
|
+
}),
|
|
214
223
|
};
|
|
215
224
|
}
|
|
216
|
-
async createChatWorkflow(sessionId, projectId, history, humanMessage, credentials, model, generateConversationTitle, trx) {
|
|
217
|
-
return await (0, db_1.withTransaction)(this.workflowRepository.manager, trx, async (em) => {
|
|
218
|
-
const { nodes, connections, triggerToStartFrom } = this.prepareChatWorkflow({
|
|
219
|
-
sessionId,
|
|
220
|
-
history,
|
|
221
|
-
humanMessage,
|
|
222
|
-
credentials,
|
|
223
|
-
model,
|
|
224
|
-
generateConversationTitle,
|
|
225
|
-
});
|
|
226
|
-
const newWorkflow = new db_1.WorkflowEntity();
|
|
227
|
-
newWorkflow.versionId = (0, uuid_1.v4)();
|
|
228
|
-
newWorkflow.name = `Chat ${sessionId}`;
|
|
229
|
-
newWorkflow.active = false;
|
|
230
|
-
newWorkflow.nodes = nodes;
|
|
231
|
-
newWorkflow.connections = connections;
|
|
232
|
-
const workflow = await em.save(newWorkflow);
|
|
233
|
-
await em.save(this.sharedWorkflowRepository.create({
|
|
234
|
-
role: 'workflow:owner',
|
|
235
|
-
projectId,
|
|
236
|
-
workflow,
|
|
237
|
-
}));
|
|
238
|
-
return {
|
|
239
|
-
workflowData: {
|
|
240
|
-
...workflow,
|
|
241
|
-
nodes,
|
|
242
|
-
connections,
|
|
243
|
-
versionId: (0, uuid_1.v4)(),
|
|
244
|
-
},
|
|
245
|
-
triggerToStartFrom,
|
|
246
|
-
};
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
async ensureCredentials(user, model, credentials, trx) {
|
|
250
|
-
const allCredentials = await this.credentialsFinderService.findAllCredentialsForUser(user, ['credential:read'], trx);
|
|
251
|
-
const credentialId = this.pickCredentialId(model.provider, credentials);
|
|
252
|
-
if (!credentialId) {
|
|
253
|
-
throw new bad_request_error_1.BadRequestError('No credentials provided for the selected model provider');
|
|
254
|
-
}
|
|
255
|
-
const credential = allCredentials.find((c) => c.id === credentialId);
|
|
256
|
-
if (!credential) {
|
|
257
|
-
throw new forbidden_error_1.ForbiddenError("You don't have access to the provided credentials");
|
|
258
|
-
}
|
|
259
|
-
return credential;
|
|
260
|
-
}
|
|
261
225
|
async deleteChatWorkflow(workflowId) {
|
|
262
226
|
await this.workflowRepository.delete(workflowId);
|
|
263
227
|
}
|
|
@@ -285,109 +249,42 @@ let ChatHubService = class ChatHubService {
|
|
|
285
249
|
return undefined;
|
|
286
250
|
}
|
|
287
251
|
pickCredentialId(provider, credentials) {
|
|
288
|
-
if (provider === 'n8n') {
|
|
252
|
+
if (provider === 'n8n' || provider === 'custom-agent') {
|
|
289
253
|
return null;
|
|
290
254
|
}
|
|
291
255
|
return credentials[api_types_1.PROVIDER_CREDENTIAL_TYPE_MAP[provider]]?.id ?? null;
|
|
292
256
|
}
|
|
293
257
|
async sendHumanMessage(res, user, payload) {
|
|
294
|
-
const { sessionId, messageId,
|
|
295
|
-
const provider =
|
|
296
|
-
const selectedModel =
|
|
297
|
-
|
|
298
|
-
credentialId: provider !== 'n8n' ? this.pickCredentialId(provider, payload.credentials) : null,
|
|
299
|
-
};
|
|
300
|
-
const workflow = await this.messageRepository.manager.transaction(async (trx) => {
|
|
258
|
+
const { sessionId, messageId, message, model, credentials, previousMessageId } = payload;
|
|
259
|
+
const { provider } = model;
|
|
260
|
+
const selectedModel = this.getModelWithCredentials(model, credentials);
|
|
261
|
+
const { executionData, workflowData } = await this.messageRepository.manager.transaction(async (trx) => {
|
|
301
262
|
const session = await this.getChatSession(user, sessionId, selectedModel, true, trx);
|
|
302
|
-
await this.ensurePreviousMessage(
|
|
263
|
+
await this.ensurePreviousMessage(previousMessageId, sessionId, trx);
|
|
303
264
|
const messages = Object.fromEntries((session.messages ?? []).map((m) => [m.id, m]));
|
|
304
|
-
const history = this.buildMessageHistory(messages,
|
|
305
|
-
await this.saveHumanMessage(payload, user,
|
|
306
|
-
if (provider
|
|
307
|
-
return await this.
|
|
265
|
+
const history = this.buildMessageHistory(messages, previousMessageId);
|
|
266
|
+
await this.saveHumanMessage(payload, user, previousMessageId, selectedModel, undefined, trx);
|
|
267
|
+
if (provider === 'n8n') {
|
|
268
|
+
return await this.prepareCustomAgentWorkflow(user, sessionId, model.workflowId, message);
|
|
308
269
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
try {
|
|
312
|
-
await this.executeChatWorkflow(res, user, workflow, replyId, sessionId, messageId, selectedModel);
|
|
313
|
-
}
|
|
314
|
-
finally {
|
|
315
|
-
if (provider !== 'n8n') {
|
|
316
|
-
await this.deleteChatWorkflow(workflow.workflowData.id);
|
|
270
|
+
if (provider === 'custom-agent') {
|
|
271
|
+
return await this.prepareChatAgentWorkflow(model.agentId, user, sessionId, history, message, trx);
|
|
317
272
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const workflowEntity = await this.workflowFinderService.findWorkflowForUser(workflowId, user, ['workflow:read'], { includeTags: false, includeParentFolder: false });
|
|
326
|
-
if (!workflowEntity) {
|
|
327
|
-
throw new bad_request_error_1.BadRequestError('Workflow not found');
|
|
328
|
-
}
|
|
329
|
-
const chatTriggers = workflowEntity.nodes.filter((node) => node.type === n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE);
|
|
330
|
-
if (chatTriggers.length !== 1) {
|
|
331
|
-
throw new bad_request_error_1.BadRequestError('Workflow must have exactly one chat trigger');
|
|
332
|
-
}
|
|
333
|
-
const chatResponseNodes = workflowEntity.nodes.filter((node) => node.type === n8n_workflow_1.RESPOND_TO_CHAT_NODE_TYPE);
|
|
334
|
-
if (chatResponseNodes.length > 0) {
|
|
335
|
-
throw new bad_request_error_1.BadRequestError('Respond to Chat nodes are not supported in custom agent workflows');
|
|
336
|
-
}
|
|
337
|
-
const agents = workflowEntity.nodes.filter((node) => node.type === n8n_workflow_1.AGENT_LANGCHAIN_NODE_TYPE);
|
|
338
|
-
if (agents.length !== 1) {
|
|
339
|
-
throw new bad_request_error_1.BadRequestError('Workflow must have exactly one AI Agent node');
|
|
340
|
-
}
|
|
341
|
-
return {
|
|
342
|
-
workflowData: {
|
|
343
|
-
...workflowEntity,
|
|
344
|
-
pinData: {},
|
|
345
|
-
},
|
|
346
|
-
triggerToStartFrom: {
|
|
347
|
-
name: chatTriggers[0].name,
|
|
348
|
-
data: {
|
|
349
|
-
startTime: Date.now(),
|
|
350
|
-
executionTime: 0,
|
|
351
|
-
executionIndex: 0,
|
|
352
|
-
executionStatus: 'success',
|
|
353
|
-
data: {
|
|
354
|
-
main: [
|
|
355
|
-
[
|
|
356
|
-
{
|
|
357
|
-
json: {
|
|
358
|
-
sessionId,
|
|
359
|
-
action: 'sendMessage',
|
|
360
|
-
chatInput: message,
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
],
|
|
364
|
-
],
|
|
365
|
-
},
|
|
366
|
-
source: [null],
|
|
367
|
-
},
|
|
368
|
-
},
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
async ensurePreviousMessage(previousMessageId, sessionId, trx) {
|
|
372
|
-
if (!previousMessageId) {
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
const previousMessage = await this.messageRepository.getOneById(previousMessageId, sessionId, [], trx);
|
|
376
|
-
if (!previousMessage) {
|
|
377
|
-
throw new bad_request_error_1.BadRequestError('The previous message does not exist in the session');
|
|
273
|
+
return await this.prepareBaseChatWorkflow(user, sessionId, credentials, model, history, message, undefined, trx);
|
|
274
|
+
});
|
|
275
|
+
await this.executeChatWorkflowWithCleanup(res, user, workflowData, executionData, sessionId, messageId, selectedModel, provider);
|
|
276
|
+
if (previousMessageId === null) {
|
|
277
|
+
await this.generateSessionTitle(user, sessionId, message, credentials, model).catch((error) => {
|
|
278
|
+
this.logger.error(`Title generation failed: ${error}`);
|
|
279
|
+
});
|
|
378
280
|
}
|
|
379
281
|
}
|
|
380
282
|
async editMessage(res, user, payload) {
|
|
381
|
-
const { sessionId, editId, messageId,
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
credentialId: payload.model.provider !== 'n8n'
|
|
385
|
-
? this.pickCredentialId(payload.model.provider, payload.credentials)
|
|
386
|
-
: null,
|
|
387
|
-
};
|
|
283
|
+
const { sessionId, editId, messageId, message, model, credentials } = payload;
|
|
284
|
+
const { provider } = model;
|
|
285
|
+
const selectedModel = this.getModelWithCredentials(model, credentials);
|
|
388
286
|
const workflow = await this.messageRepository.manager.transaction(async (trx) => {
|
|
389
|
-
const
|
|
390
|
-
const session = await this.getChatSession(user, sessionId, undefined, false, trx);
|
|
287
|
+
const session = await this.getChatSession(user, sessionId, selectedModel, true, trx);
|
|
391
288
|
const messageToEdit = await this.getChatMessage(session.id, editId, [], trx);
|
|
392
289
|
if (!['ai', 'human'].includes(messageToEdit.type)) {
|
|
393
290
|
throw new bad_request_error_1.BadRequestError('Only human and AI messages can be edited');
|
|
@@ -397,35 +294,31 @@ let ChatHubService = class ChatHubService {
|
|
|
397
294
|
return null;
|
|
398
295
|
}
|
|
399
296
|
if (messageToEdit.type === 'human') {
|
|
400
|
-
const { message } = payload;
|
|
401
297
|
const messages = Object.fromEntries((session.messages ?? []).map((m) => [m.id, m]));
|
|
402
298
|
const history = this.buildMessageHistory(messages, messageToEdit.previousMessageId);
|
|
403
299
|
const revisionOfMessageId = messageToEdit.revisionOfMessageId ?? messageToEdit.id;
|
|
404
300
|
await this.saveHumanMessage(payload, user, messageToEdit.previousMessageId, selectedModel, revisionOfMessageId, trx);
|
|
405
|
-
|
|
301
|
+
if (provider === 'n8n') {
|
|
302
|
+
return await this.prepareCustomAgentWorkflow(user, sessionId, model.workflowId, message);
|
|
303
|
+
}
|
|
304
|
+
if (provider === 'custom-agent') {
|
|
305
|
+
return await this.prepareChatAgentWorkflow(model.agentId, user, sessionId, history, message, trx);
|
|
306
|
+
}
|
|
307
|
+
return await this.prepareBaseChatWorkflow(user, sessionId, credentials, model, history, message, undefined, trx);
|
|
406
308
|
}
|
|
407
309
|
return null;
|
|
408
310
|
});
|
|
409
311
|
if (!workflow) {
|
|
410
312
|
return;
|
|
411
313
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
finally {
|
|
416
|
-
await this.deleteChatWorkflow(workflow.workflowData.id);
|
|
417
|
-
}
|
|
314
|
+
const { workflowData, executionData } = workflow;
|
|
315
|
+
await this.executeChatWorkflowWithCleanup(res, user, workflowData, executionData, sessionId, messageId, selectedModel, provider);
|
|
418
316
|
}
|
|
419
317
|
async regenerateAIMessage(res, user, payload) {
|
|
420
|
-
const { sessionId, retryId,
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
? this.pickCredentialId(payload.model.provider, payload.credentials)
|
|
425
|
-
: null,
|
|
426
|
-
};
|
|
427
|
-
const { workflow, retryOfMessageId, previousMessageId } = await this.messageRepository.manager.transaction(async (trx) => {
|
|
428
|
-
const credential = await this.ensureCredentials(user, payload.model, payload.credentials, trx);
|
|
318
|
+
const { sessionId, retryId, model, credentials } = payload;
|
|
319
|
+
const { provider } = model;
|
|
320
|
+
const selectedModel = this.getModelWithCredentials(model, credentials);
|
|
321
|
+
const { workflow: { workflowData, executionData }, retryOfMessageId, previousMessageId, } = await this.messageRepository.manager.transaction(async (trx) => {
|
|
429
322
|
const session = await this.getChatSession(user, sessionId, undefined, false, trx);
|
|
430
323
|
const messageToRetry = await this.getChatMessage(session.id, retryId, [], trx);
|
|
431
324
|
if (messageToRetry.type !== 'ai') {
|
|
@@ -442,18 +335,120 @@ let ChatHubService = class ChatHubService {
|
|
|
442
335
|
history.splice(lastHumanMessageIndex + 1);
|
|
443
336
|
}
|
|
444
337
|
const retryOfMessageId = messageToRetry.retryOfMessageId ?? messageToRetry.id;
|
|
445
|
-
const
|
|
338
|
+
const message = lastHumanMessage ? lastHumanMessage.content : '';
|
|
339
|
+
let workflow;
|
|
340
|
+
if (provider === 'n8n') {
|
|
341
|
+
workflow = await this.prepareCustomAgentWorkflow(user, sessionId, model.workflowId, message);
|
|
342
|
+
}
|
|
343
|
+
else if (provider === 'custom-agent') {
|
|
344
|
+
workflow = await this.prepareChatAgentWorkflow(model.agentId, user, sessionId, history, message, trx);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
workflow = await this.prepareBaseChatWorkflow(user, sessionId, credentials, model, history, message, undefined, trx);
|
|
348
|
+
}
|
|
446
349
|
return {
|
|
447
350
|
workflow,
|
|
448
351
|
previousMessageId: lastHumanMessage.id,
|
|
449
352
|
retryOfMessageId,
|
|
450
353
|
};
|
|
451
354
|
});
|
|
452
|
-
|
|
453
|
-
|
|
355
|
+
await this.executeChatWorkflowWithCleanup(res, user, workflowData, executionData, sessionId, previousMessageId, selectedModel, provider, retryOfMessageId);
|
|
356
|
+
}
|
|
357
|
+
async prepareBaseChatWorkflow(user, sessionId, credentials, model, history, message, systemMessage, trx) {
|
|
358
|
+
const credential = await this.chatHubCredentialsService.ensureCredentials(user, model.provider, credentials, trx);
|
|
359
|
+
return await this.chatHubWorkflowService.createChatWorkflow(user.id, sessionId, credential.projectId, history, message, credentials, model, systemMessage, trx);
|
|
360
|
+
}
|
|
361
|
+
async prepareChatAgentWorkflow(agentId, user, sessionId, history, message, trx) {
|
|
362
|
+
const agent = await this.chatHubAgentService.getAgentById(agentId, user.id);
|
|
363
|
+
if (!agent) {
|
|
364
|
+
throw new bad_request_error_1.BadRequestError('Agent not found');
|
|
454
365
|
}
|
|
455
|
-
|
|
456
|
-
|
|
366
|
+
if (!agent.provider || !agent.model) {
|
|
367
|
+
throw new bad_request_error_1.BadRequestError('Provider or model not set for agent');
|
|
368
|
+
}
|
|
369
|
+
if (agent.provider === 'n8n' || agent.provider === 'custom-agent') {
|
|
370
|
+
throw new bad_request_error_1.BadRequestError('Invalid provider');
|
|
371
|
+
}
|
|
372
|
+
const credentialId = agent.credentialId;
|
|
373
|
+
if (!credentialId) {
|
|
374
|
+
throw new bad_request_error_1.BadRequestError('Credentials not set for agent');
|
|
375
|
+
}
|
|
376
|
+
const systemMessage = agent.systemPrompt;
|
|
377
|
+
const model = {
|
|
378
|
+
provider: agent.provider,
|
|
379
|
+
model: agent.model,
|
|
380
|
+
};
|
|
381
|
+
const credentials = {
|
|
382
|
+
[api_types_1.PROVIDER_CREDENTIAL_TYPE_MAP[agent.provider]]: {
|
|
383
|
+
id: credentialId,
|
|
384
|
+
name: '',
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
return await this.prepareBaseChatWorkflow(user, sessionId, credentials, model, history, message, systemMessage, trx);
|
|
388
|
+
}
|
|
389
|
+
async prepareCustomAgentWorkflow(user, sessionId, workflowId, message) {
|
|
390
|
+
const workflowEntity = await this.workflowFinderService.findWorkflowForUser(workflowId, user, ['workflow:read'], { includeTags: false, includeParentFolder: false });
|
|
391
|
+
if (!workflowEntity) {
|
|
392
|
+
throw new bad_request_error_1.BadRequestError('Workflow not found');
|
|
393
|
+
}
|
|
394
|
+
const chatTriggers = workflowEntity.nodes.filter((node) => node.type === n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE);
|
|
395
|
+
if (chatTriggers.length !== 1) {
|
|
396
|
+
throw new bad_request_error_1.BadRequestError('Workflow must have exactly one chat trigger');
|
|
397
|
+
}
|
|
398
|
+
const chatTriggerNode = chatTriggers[0];
|
|
399
|
+
const chatResponseNodes = workflowEntity.nodes.filter((node) => node.type === n8n_workflow_1.RESPOND_TO_CHAT_NODE_TYPE);
|
|
400
|
+
if (chatResponseNodes.length > 0) {
|
|
401
|
+
throw new bad_request_error_1.BadRequestError('Respond to Chat nodes are not supported in custom agent workflows');
|
|
402
|
+
}
|
|
403
|
+
const nodeExecutionStack = [
|
|
404
|
+
{
|
|
405
|
+
node: chatTriggerNode,
|
|
406
|
+
data: {
|
|
407
|
+
main: [
|
|
408
|
+
[
|
|
409
|
+
{
|
|
410
|
+
json: {
|
|
411
|
+
sessionId,
|
|
412
|
+
action: 'sendMessage',
|
|
413
|
+
chatInput: message,
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
],
|
|
418
|
+
},
|
|
419
|
+
source: null,
|
|
420
|
+
},
|
|
421
|
+
];
|
|
422
|
+
const executionData = {
|
|
423
|
+
startData: {},
|
|
424
|
+
resultData: {
|
|
425
|
+
runData: {},
|
|
426
|
+
},
|
|
427
|
+
executionData: {
|
|
428
|
+
contextData: {},
|
|
429
|
+
metadata: {},
|
|
430
|
+
nodeExecutionStack,
|
|
431
|
+
waitingExecution: {},
|
|
432
|
+
waitingExecutionSource: {},
|
|
433
|
+
},
|
|
434
|
+
manualData: {
|
|
435
|
+
userId: user.id,
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
return {
|
|
439
|
+
workflowData: {
|
|
440
|
+
...workflowEntity,
|
|
441
|
+
},
|
|
442
|
+
executionData,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
async ensurePreviousMessage(previousMessageId, sessionId, trx) {
|
|
446
|
+
if (!previousMessageId) {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const previousMessage = await this.messageRepository.getOneById(previousMessageId, sessionId, [], trx);
|
|
450
|
+
if (!previousMessage) {
|
|
451
|
+
throw new bad_request_error_1.BadRequestError('The previous message does not exist in the session');
|
|
457
452
|
}
|
|
458
453
|
}
|
|
459
454
|
async stopGeneration(user, sessionId, messageId) {
|
|
@@ -474,286 +469,281 @@ let ChatHubService = class ChatHubService {
|
|
|
474
469
|
await this.executionService.stop(message.execution.id, [message.execution.workflowId]);
|
|
475
470
|
await this.messageRepository.updateChatMessage(messageId, { status: 'cancelled' });
|
|
476
471
|
}
|
|
477
|
-
async executeChatWorkflow(res, user,
|
|
478
|
-
const { workflowData, triggerToStartFrom } = workflow;
|
|
472
|
+
async executeChatWorkflow(res, user, workflowData, executionData, sessionId, previousMessageId, selectedModel, retryOfMessageId = null) {
|
|
479
473
|
this.logger.debug(`Starting execution of workflow "${workflowData.name}" with ID ${workflowData.id}`);
|
|
480
|
-
let
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
474
|
+
let executionId = undefined;
|
|
475
|
+
const aggregator = (0, stream_capturer_1.createStructuredChunkAggregator)(previousMessageId, retryOfMessageId, {
|
|
476
|
+
onBegin: async (message) => {
|
|
477
|
+
await this.saveAIMessage({
|
|
478
|
+
...message,
|
|
479
|
+
sessionId,
|
|
480
|
+
executionId,
|
|
481
|
+
selectedModel,
|
|
482
|
+
retryOfMessageId,
|
|
483
|
+
});
|
|
484
|
+
},
|
|
485
|
+
onItem: (_message, _chunk) => {
|
|
486
|
+
},
|
|
487
|
+
onEnd: async (message) => {
|
|
488
|
+
await this.messageRepository.updateChatMessage(message.id, {
|
|
489
|
+
content: message.content,
|
|
490
|
+
status: message.status,
|
|
491
|
+
});
|
|
492
|
+
},
|
|
493
|
+
onError: async (message, _errorText) => {
|
|
494
|
+
await this.messageRepository.manager.transaction(async (trx) => {
|
|
495
|
+
await this.messageRepository.updateChatMessage(message.id, {
|
|
496
|
+
content: message.content,
|
|
497
|
+
}, trx);
|
|
498
|
+
const savedMessage = await this.messageRepository.getOneById(message.id, sessionId, [], trx);
|
|
499
|
+
if (savedMessage?.status === 'cancelled') {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
await this.messageRepository.updateChatMessage(message.id, {
|
|
503
|
+
status: 'error',
|
|
504
|
+
}, trx);
|
|
505
|
+
});
|
|
506
|
+
},
|
|
507
|
+
});
|
|
508
|
+
const transform = (text) => {
|
|
509
|
+
const trimmed = text.trim();
|
|
510
|
+
if (!trimmed)
|
|
511
|
+
return text;
|
|
512
|
+
let chunk = null;
|
|
513
|
+
try {
|
|
514
|
+
chunk = (0, n8n_workflow_1.jsonParse)(trimmed);
|
|
515
|
+
}
|
|
516
|
+
catch {
|
|
517
|
+
return text;
|
|
485
518
|
}
|
|
519
|
+
const message = aggregator.ingest(chunk);
|
|
520
|
+
const enriched = {
|
|
521
|
+
...chunk,
|
|
522
|
+
metadata: {
|
|
523
|
+
...chunk.metadata,
|
|
524
|
+
messageId: message.id,
|
|
525
|
+
previousMessageId: message.previousMessageId,
|
|
526
|
+
retryOfMessageId: message.retryOfMessageId,
|
|
527
|
+
executionId: executionId ? +executionId : null,
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
return JSON.stringify(enriched) + '\n';
|
|
486
531
|
};
|
|
487
|
-
const stream = (0, stream_capturer_1.
|
|
488
|
-
stream.
|
|
532
|
+
const stream = (0, stream_capturer_1.interceptResponseWrites)(res, transform);
|
|
533
|
+
stream.on('finish', aggregator.finalizeAll);
|
|
534
|
+
stream.on('close', aggregator.finalizeAll);
|
|
535
|
+
stream.writeHead(200, chat_hub_constants_1.JSONL_STREAM_HEADERS);
|
|
489
536
|
stream.flushHeaders();
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
triggerToStartFrom,
|
|
493
|
-
}, user, undefined, true, stream);
|
|
537
|
+
const execution = await this.workflowExecutionService.executeChatWorkflow(workflowData, executionData, user, stream, true);
|
|
538
|
+
executionId = execution.executionId;
|
|
494
539
|
if (!executionId) {
|
|
495
540
|
throw new n8n_workflow_1.OperationalError('There was a problem starting the chat execution.');
|
|
496
541
|
}
|
|
497
|
-
await this.saveAIMessage({
|
|
498
|
-
id: replyId,
|
|
499
|
-
sessionId,
|
|
500
|
-
executionId,
|
|
501
|
-
previousMessageId,
|
|
502
|
-
message: partialMessage,
|
|
503
|
-
selectedModel,
|
|
504
|
-
retryOfMessageId,
|
|
505
|
-
status: 'running',
|
|
506
|
-
});
|
|
507
542
|
try {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
if (!result) {
|
|
512
|
-
throw new n8n_workflow_1.OperationalError('There was a problem executing the chat workflow.');
|
|
513
|
-
}
|
|
543
|
+
const result = await this.activeExecutions.getPostExecutePromise(executionId);
|
|
544
|
+
if (!result) {
|
|
545
|
+
throw new n8n_workflow_1.OperationalError('There was a problem executing the chat workflow.');
|
|
514
546
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
]);
|
|
520
|
-
if (!execution) {
|
|
521
|
-
throw new n8n_workflow_1.OperationalError(`Could not find execution with ID ${executionId}`);
|
|
522
|
-
}
|
|
523
|
-
if (execution.status === 'canceled') {
|
|
524
|
-
await this.messageRepository.updateChatMessage(replyId, {
|
|
525
|
-
content: partialMessage || 'Generation cancelled.',
|
|
526
|
-
status: 'cancelled',
|
|
527
|
-
});
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
throw error;
|
|
547
|
+
}
|
|
548
|
+
catch (error) {
|
|
549
|
+
if (error instanceof n8n_workflow_1.ManualExecutionCancelledError) {
|
|
550
|
+
return;
|
|
532
551
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
]);
|
|
536
|
-
if (!execution) {
|
|
537
|
-
throw new n8n_workflow_1.OperationalError(`Could not find execution with ID ${executionId}`);
|
|
552
|
+
if (error instanceof Error) {
|
|
553
|
+
this.logger.error(`Error during chat workflow execution: ${error}`);
|
|
538
554
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
555
|
+
throw error;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async executeChatWorkflowWithCleanup(res, user, workflowData, executionData, sessionId, previousMessageId, selectedModel, provider, retryOfMessageId = null) {
|
|
559
|
+
try {
|
|
560
|
+
await this.executeChatWorkflow(res, user, workflowData, executionData, sessionId, previousMessageId, selectedModel, retryOfMessageId);
|
|
561
|
+
}
|
|
562
|
+
finally {
|
|
563
|
+
if (provider !== 'n8n') {
|
|
564
|
+
await this.deleteChatWorkflow(workflowData.id);
|
|
542
565
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async generateSessionTitle(user, sessionId, humanMessage, credentials, model) {
|
|
569
|
+
const { executionData, workflowData } = await this.prepareTitleGenerationWorkflow(user, sessionId, humanMessage, credentials, model);
|
|
570
|
+
try {
|
|
571
|
+
const title = await this.runTitleWorkflowAndGetTitle(user, workflowData, executionData);
|
|
548
572
|
if (title) {
|
|
549
573
|
await this.sessionRepository.updateChatTitle(sessionId, title);
|
|
550
574
|
}
|
|
551
575
|
}
|
|
552
576
|
catch (error) {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
577
|
+
if (error instanceof Error) {
|
|
578
|
+
this.logger.error(`Error during session title generation workflow execution: ${error}`);
|
|
579
|
+
}
|
|
580
|
+
throw error;
|
|
581
|
+
}
|
|
582
|
+
finally {
|
|
583
|
+
await this.deleteChatWorkflow(workflowData.id);
|
|
558
584
|
}
|
|
559
585
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
{
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
};
|
|
616
|
-
return {
|
|
617
|
-
type: typeMap[message.type] || 'system',
|
|
618
|
-
message: message.content,
|
|
619
|
-
hideFromUI: false,
|
|
620
|
-
};
|
|
621
|
-
}),
|
|
622
|
-
},
|
|
623
|
-
},
|
|
624
|
-
type: '@n8n/n8n-nodes-langchain.memoryManager',
|
|
625
|
-
typeVersion: 1.1,
|
|
626
|
-
position: [224, 0],
|
|
627
|
-
id: (0, uuid_1.v4)(),
|
|
628
|
-
name: NODE_NAMES.RESTORE_CHAT_MEMORY,
|
|
629
|
-
},
|
|
630
|
-
{
|
|
631
|
-
parameters: {
|
|
632
|
-
mode: 'delete',
|
|
633
|
-
deleteMode: 'all',
|
|
634
|
-
},
|
|
635
|
-
type: '@n8n/n8n-nodes-langchain.memoryManager',
|
|
636
|
-
typeVersion: 1.1,
|
|
637
|
-
position: [976, 0],
|
|
638
|
-
id: (0, uuid_1.v4)(),
|
|
639
|
-
name: NODE_NAMES.CLEAR_CHAT_MEMORY,
|
|
640
|
-
},
|
|
641
|
-
{
|
|
642
|
-
disabled: !generateConversationTitle,
|
|
643
|
-
parameters: {
|
|
644
|
-
promptType: 'define',
|
|
645
|
-
text: "={{ $('When chat message received').item.json.chatInput }}",
|
|
646
|
-
options: {
|
|
647
|
-
enableStreaming: false,
|
|
648
|
-
systemMessage: chat_hub_constants_1.CONVERSATION_TITLE_GENERATION_PROMPT,
|
|
649
|
-
},
|
|
650
|
-
},
|
|
651
|
-
type: n8n_workflow_1.AGENT_LANGCHAIN_NODE_TYPE,
|
|
652
|
-
typeVersion: 3,
|
|
653
|
-
position: [224, 360],
|
|
654
|
-
id: (0, uuid_1.v4)(),
|
|
655
|
-
name: NODE_NAMES.TITLE_GENERATOR_AGENT,
|
|
656
|
-
},
|
|
657
|
-
];
|
|
658
|
-
const connections = {
|
|
659
|
-
[NODE_NAMES.CHAT_TRIGGER]: {
|
|
660
|
-
main: [
|
|
661
|
-
[{ node: NODE_NAMES.RESTORE_CHAT_MEMORY, type: n8n_workflow_1.NodeConnectionTypes.Main, index: 0 }],
|
|
662
|
-
],
|
|
663
|
-
},
|
|
664
|
-
[NODE_NAMES.RESTORE_CHAT_MEMORY]: {
|
|
665
|
-
main: [
|
|
666
|
-
[
|
|
667
|
-
{ node: NODE_NAMES.REPLY_AGENT, type: n8n_workflow_1.NodeConnectionTypes.Main, index: 0 },
|
|
668
|
-
{ node: NODE_NAMES.TITLE_GENERATOR_AGENT, type: n8n_workflow_1.NodeConnectionTypes.Main, index: 0 },
|
|
669
|
-
],
|
|
670
|
-
],
|
|
671
|
-
},
|
|
672
|
-
[NODE_NAMES.CHAT_MODEL]: {
|
|
673
|
-
ai_languageModel: [
|
|
674
|
-
[
|
|
675
|
-
{ node: NODE_NAMES.REPLY_AGENT, type: n8n_workflow_1.NodeConnectionTypes.AiLanguageModel, index: 0 },
|
|
676
|
-
{
|
|
677
|
-
node: NODE_NAMES.TITLE_GENERATOR_AGENT,
|
|
678
|
-
type: n8n_workflow_1.NodeConnectionTypes.AiLanguageModel,
|
|
679
|
-
index: 0,
|
|
680
|
-
},
|
|
681
|
-
],
|
|
682
|
-
],
|
|
683
|
-
},
|
|
684
|
-
[NODE_NAMES.MEMORY]: {
|
|
685
|
-
ai_memory: [
|
|
686
|
-
[
|
|
687
|
-
{ node: NODE_NAMES.REPLY_AGENT, type: n8n_workflow_1.NodeConnectionTypes.AiMemory, index: 0 },
|
|
688
|
-
{ node: NODE_NAMES.RESTORE_CHAT_MEMORY, type: n8n_workflow_1.NodeConnectionTypes.AiMemory, index: 0 },
|
|
689
|
-
{ node: NODE_NAMES.CLEAR_CHAT_MEMORY, type: n8n_workflow_1.NodeConnectionTypes.AiMemory, index: 0 },
|
|
690
|
-
],
|
|
691
|
-
],
|
|
692
|
-
},
|
|
693
|
-
[NODE_NAMES.REPLY_AGENT]: {
|
|
694
|
-
main: [
|
|
695
|
-
[
|
|
696
|
-
{
|
|
697
|
-
node: NODE_NAMES.CLEAR_CHAT_MEMORY,
|
|
698
|
-
type: n8n_workflow_1.NodeConnectionTypes.Main,
|
|
699
|
-
index: 0,
|
|
700
|
-
},
|
|
701
|
-
],
|
|
702
|
-
],
|
|
586
|
+
async prepareTitleGenerationWorkflow(user, sessionId, humanMessage, incomingCredentials, incomingModel) {
|
|
587
|
+
return await this.messageRepository.manager.transaction(async (trx) => {
|
|
588
|
+
const { resolvedCredentials, resolvedModel, credential } = await this.resolveCredentialsAndModelForTitle(user, incomingModel, incomingCredentials, trx);
|
|
589
|
+
if (!credential) {
|
|
590
|
+
throw new bad_request_error_1.BadRequestError('Could not determine credentials for title generation');
|
|
591
|
+
}
|
|
592
|
+
this.logger.debug(`Using credential ID ${credential.id} for title generation in project ${credential.projectId}, model ${JSON.stringify(resolvedModel)}`);
|
|
593
|
+
return await this.chatHubWorkflowService.createTitleGenerationWorkflow(user.id, sessionId, credential.projectId, humanMessage, resolvedCredentials, resolvedModel, trx);
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
async resolveCredentialsAndModelForTitle(user, model, credentials, trx) {
|
|
597
|
+
if (model.provider === 'n8n') {
|
|
598
|
+
return await this.resolveFromN8nWorkflow(user, model, trx);
|
|
599
|
+
}
|
|
600
|
+
if (model.provider === 'custom-agent') {
|
|
601
|
+
return await this.resolveFromCustomAgent(user, model, trx);
|
|
602
|
+
}
|
|
603
|
+
const credential = await this.chatHubCredentialsService.ensureCredentials(user, model.provider, credentials, trx);
|
|
604
|
+
return {
|
|
605
|
+
resolvedCredentials: credentials,
|
|
606
|
+
resolvedModel: model,
|
|
607
|
+
credential,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
async resolveFromN8nWorkflow(user, model, trx) {
|
|
611
|
+
const workflowEntity = await this.workflowFinderService.findWorkflowForUser(model.workflowId, user, ['workflow:read'], { includeTags: false, includeParentFolder: false });
|
|
612
|
+
if (!workflowEntity) {
|
|
613
|
+
throw new bad_request_error_1.BadRequestError('Workflow not found for title generation');
|
|
614
|
+
}
|
|
615
|
+
const modelNodes = this.findSupportedLLMNodes(workflowEntity);
|
|
616
|
+
this.logger.debug(`Found ${modelNodes.length} LLM nodes in workflow ${workflowEntity.id} for title generation`);
|
|
617
|
+
if (modelNodes.length === 0) {
|
|
618
|
+
throw new bad_request_error_1.BadRequestError('No supported Model nodes found in workflow for title generation');
|
|
619
|
+
}
|
|
620
|
+
const modelNode = modelNodes[0];
|
|
621
|
+
const llmModel = modelNode.node.parameters?.model?.value;
|
|
622
|
+
if (!llmModel) {
|
|
623
|
+
throw new bad_request_error_1.BadRequestError(`No model set on Model node "${modelNode.node.name}" for title generation`);
|
|
624
|
+
}
|
|
625
|
+
if (typeof llmModel !== 'string' || llmModel.length === 0 || llmModel.startsWith('=')) {
|
|
626
|
+
throw new bad_request_error_1.BadRequestError(`Invalid model set on Model node "${modelNode.node.name}" for title generation`);
|
|
627
|
+
}
|
|
628
|
+
const llmCredentials = modelNode.node.credentials;
|
|
629
|
+
if (!llmCredentials) {
|
|
630
|
+
throw new bad_request_error_1.BadRequestError(`No credentials found on Model node "${modelNode.node.name}" for title generation`);
|
|
631
|
+
}
|
|
632
|
+
const credential = await this.chatHubCredentialsService.ensureCredentials(user, modelNode.provider, llmCredentials, trx);
|
|
633
|
+
const resolvedModel = {
|
|
634
|
+
provider: modelNode.provider,
|
|
635
|
+
model: llmModel,
|
|
636
|
+
};
|
|
637
|
+
const resolvedCredentials = {
|
|
638
|
+
[api_types_1.PROVIDER_CREDENTIAL_TYPE_MAP[modelNode.provider]]: {
|
|
639
|
+
id: credential.id,
|
|
640
|
+
name: '',
|
|
703
641
|
},
|
|
704
642
|
};
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
643
|
+
return { resolvedCredentials, resolvedModel, credential };
|
|
644
|
+
}
|
|
645
|
+
findSupportedLLMNodes(workflowEntity) {
|
|
646
|
+
return workflowEntity.nodes.reduce((acc, node) => {
|
|
647
|
+
const supportedProvider = Object.entries(chat_hub_constants_1.PROVIDER_NODE_TYPE_MAP).find(([_provider, { name }]) => node.type === name);
|
|
648
|
+
if (supportedProvider) {
|
|
649
|
+
const [provider] = supportedProvider;
|
|
650
|
+
acc.push({ node, provider: provider });
|
|
651
|
+
}
|
|
652
|
+
return acc;
|
|
653
|
+
}, []);
|
|
654
|
+
}
|
|
655
|
+
async resolveFromCustomAgent(user, model, trx) {
|
|
656
|
+
const agent = await this.chatHubAgentService.getAgentById(model.agentId, user.id);
|
|
657
|
+
if (!agent) {
|
|
658
|
+
throw new bad_request_error_1.BadRequestError('Agent not found for title generation');
|
|
659
|
+
}
|
|
660
|
+
if (agent.provider === 'n8n' || agent.provider === 'custom-agent') {
|
|
661
|
+
throw new bad_request_error_1.BadRequestError('Invalid provider for title generation');
|
|
662
|
+
}
|
|
663
|
+
const credentialId = agent.credentialId;
|
|
664
|
+
if (!credentialId) {
|
|
665
|
+
throw new bad_request_error_1.BadRequestError('Credentials not set for agent');
|
|
666
|
+
}
|
|
667
|
+
const resolvedModel = {
|
|
668
|
+
provider: agent.provider,
|
|
669
|
+
model: agent.model,
|
|
670
|
+
};
|
|
671
|
+
const resolvedCredentials = {
|
|
672
|
+
[api_types_1.PROVIDER_CREDENTIAL_TYPE_MAP[agent.provider]]: {
|
|
673
|
+
id: credentialId,
|
|
674
|
+
name: '',
|
|
726
675
|
},
|
|
727
676
|
};
|
|
728
|
-
|
|
677
|
+
const credential = await this.chatHubCredentialsService.ensureCredentials(user, agent.provider, resolvedCredentials, trx);
|
|
678
|
+
return { resolvedCredentials, resolvedModel, credential };
|
|
679
|
+
}
|
|
680
|
+
async runTitleWorkflowAndGetTitle(user, workflowData, executionData) {
|
|
681
|
+
const started = await this.workflowExecutionService.executeChatWorkflow(workflowData, executionData, user);
|
|
682
|
+
const executionId = started.executionId;
|
|
683
|
+
if (!executionId) {
|
|
684
|
+
throw new n8n_workflow_1.OperationalError('There was a problem starting the chat execution.');
|
|
685
|
+
}
|
|
686
|
+
let run;
|
|
687
|
+
try {
|
|
688
|
+
run = await this.activeExecutions.getPostExecutePromise(executionId);
|
|
689
|
+
if (!run) {
|
|
690
|
+
throw new n8n_workflow_1.OperationalError('There was a problem executing the chat workflow.');
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
catch (error) {
|
|
694
|
+
if (error instanceof n8n_workflow_1.ManualExecutionCancelledError) {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
throw error;
|
|
698
|
+
}
|
|
699
|
+
const execution = await this.executionRepository.findWithUnflattenedData(executionId, [
|
|
700
|
+
workflowData.id,
|
|
701
|
+
]);
|
|
702
|
+
if (!execution) {
|
|
703
|
+
throw new n8n_workflow_1.OperationalError(`Could not find execution with ID ${executionId}`);
|
|
704
|
+
}
|
|
705
|
+
if (!execution.status || execution.status !== 'success') {
|
|
706
|
+
const message = this.getErrorMessage(execution) ?? 'Failed to generate a response';
|
|
707
|
+
throw new n8n_workflow_1.OperationalError(message);
|
|
708
|
+
}
|
|
709
|
+
const title = this.getAIOutput(execution, chat_hub_constants_1.NODE_NAMES.TITLE_GENERATOR_AGENT);
|
|
710
|
+
return title ?? null;
|
|
729
711
|
}
|
|
730
712
|
async saveHumanMessage(payload, user, previousMessageId, selectedModel, revisionOfMessageId, trx) {
|
|
731
713
|
await this.messageRepository.createChatMessage({
|
|
732
714
|
id: payload.messageId,
|
|
733
715
|
sessionId: payload.sessionId,
|
|
734
716
|
type: 'human',
|
|
735
|
-
name: user.firstName || 'User',
|
|
736
717
|
status: 'success',
|
|
737
718
|
content: payload.message,
|
|
738
719
|
previousMessageId,
|
|
739
720
|
revisionOfMessageId,
|
|
740
721
|
...selectedModel,
|
|
722
|
+
name: user.firstName || 'User',
|
|
741
723
|
}, trx);
|
|
742
724
|
}
|
|
743
|
-
async saveAIMessage({ id, sessionId, executionId, previousMessageId,
|
|
725
|
+
async saveAIMessage({ id, sessionId, executionId, previousMessageId, content, selectedModel, retryOfMessageId, status, }) {
|
|
744
726
|
await this.messageRepository.createChatMessage({
|
|
745
727
|
id,
|
|
746
728
|
sessionId,
|
|
747
729
|
previousMessageId,
|
|
748
|
-
executionId: parseInt(executionId, 10),
|
|
730
|
+
executionId: executionId ? parseInt(executionId, 10) : null,
|
|
749
731
|
type: 'ai',
|
|
750
732
|
name: 'AI',
|
|
751
733
|
status,
|
|
752
|
-
content
|
|
734
|
+
content,
|
|
753
735
|
retryOfMessageId,
|
|
754
736
|
...selectedModel,
|
|
755
737
|
});
|
|
756
738
|
}
|
|
739
|
+
getModelWithCredentials(selectedModel, credentials) {
|
|
740
|
+
const provider = selectedModel.provider;
|
|
741
|
+
const modelWithCredentials = {
|
|
742
|
+
...selectedModel,
|
|
743
|
+
credentialId: provider !== 'n8n' ? this.pickCredentialId(provider, credentials) : null,
|
|
744
|
+
};
|
|
745
|
+
return modelWithCredentials;
|
|
746
|
+
}
|
|
757
747
|
async getChatSession(user, sessionId, selectedModel, initialize = false, trx) {
|
|
758
748
|
const existing = await this.sessionRepository.getOneById(sessionId, user.id, trx);
|
|
759
749
|
if (existing) {
|
|
@@ -762,10 +752,34 @@ let ChatHubService = class ChatHubService {
|
|
|
762
752
|
else if (!initialize) {
|
|
763
753
|
throw new not_found_error_1.NotFoundError('Chat session not found');
|
|
764
754
|
}
|
|
755
|
+
let agentName = undefined;
|
|
756
|
+
if (selectedModel?.provider === 'custom-agent') {
|
|
757
|
+
const agent = await this.chatHubAgentService.getAgentById(selectedModel.agentId, user.id);
|
|
758
|
+
if (!agent) {
|
|
759
|
+
throw new bad_request_error_1.BadRequestError('Agent not found for chat session initialization');
|
|
760
|
+
}
|
|
761
|
+
agentName = agent.name;
|
|
762
|
+
}
|
|
763
|
+
if (selectedModel?.provider === 'n8n') {
|
|
764
|
+
const workflow = await this.workflowFinderService.findWorkflowForUser(selectedModel.workflowId, user, ['workflow:read'], { includeTags: false, includeParentFolder: false });
|
|
765
|
+
if (!workflow) {
|
|
766
|
+
throw new bad_request_error_1.BadRequestError('Workflow not found for chat session initialization');
|
|
767
|
+
}
|
|
768
|
+
const chatTrigger = workflow.nodes?.find((node) => node.type === n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE);
|
|
769
|
+
if (!chatTrigger) {
|
|
770
|
+
throw new bad_request_error_1.BadRequestError('Chat trigger not found in workflow for chat session initialization');
|
|
771
|
+
}
|
|
772
|
+
agentName =
|
|
773
|
+
typeof chatTrigger.parameters.agentName === 'string' &&
|
|
774
|
+
chatTrigger.parameters.agentName.length > 0
|
|
775
|
+
? chatTrigger.parameters.agentName
|
|
776
|
+
: workflow.name;
|
|
777
|
+
}
|
|
765
778
|
return await this.sessionRepository.createChatSession({
|
|
766
779
|
id: sessionId,
|
|
767
780
|
ownerId: user.id,
|
|
768
781
|
title: 'New Chat',
|
|
782
|
+
agentName,
|
|
769
783
|
...selectedModel,
|
|
770
784
|
}, trx);
|
|
771
785
|
}
|
|
@@ -776,51 +790,6 @@ let ChatHubService = class ChatHubService {
|
|
|
776
790
|
}
|
|
777
791
|
return message;
|
|
778
792
|
}
|
|
779
|
-
createModelNode(credentials, conversationModel) {
|
|
780
|
-
if (conversationModel.provider === 'n8n') {
|
|
781
|
-
throw new n8n_workflow_1.OperationalError('Custom agent workflows do not require a model node');
|
|
782
|
-
}
|
|
783
|
-
const { provider, model } = conversationModel;
|
|
784
|
-
const common = {
|
|
785
|
-
position: [600, 500],
|
|
786
|
-
id: (0, uuid_1.v4)(),
|
|
787
|
-
name: 'Chat Model',
|
|
788
|
-
credentials,
|
|
789
|
-
type: providerNodeTypeMapping[provider].name,
|
|
790
|
-
typeVersion: providerNodeTypeMapping[provider].version,
|
|
791
|
-
};
|
|
792
|
-
switch (provider) {
|
|
793
|
-
case 'openai':
|
|
794
|
-
return {
|
|
795
|
-
...common,
|
|
796
|
-
parameters: {
|
|
797
|
-
model: { __rl: true, mode: 'list', value: model },
|
|
798
|
-
options: {},
|
|
799
|
-
},
|
|
800
|
-
};
|
|
801
|
-
case 'anthropic':
|
|
802
|
-
return {
|
|
803
|
-
...common,
|
|
804
|
-
parameters: {
|
|
805
|
-
model: {
|
|
806
|
-
__rl: true,
|
|
807
|
-
mode: 'list',
|
|
808
|
-
value: model,
|
|
809
|
-
cachedResultName: model,
|
|
810
|
-
},
|
|
811
|
-
options: {},
|
|
812
|
-
},
|
|
813
|
-
};
|
|
814
|
-
case 'google':
|
|
815
|
-
return {
|
|
816
|
-
...common,
|
|
817
|
-
parameters: {
|
|
818
|
-
model: { __rl: true, mode: 'list', value: model },
|
|
819
|
-
options: {},
|
|
820
|
-
},
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
793
|
async getConversations(userId) {
|
|
825
794
|
const sessions = await this.sessionRepository.getManyByUserId(userId);
|
|
826
795
|
return sessions.map((session) => ({
|
|
@@ -832,6 +801,8 @@ let ChatHubService = class ChatHubService {
|
|
|
832
801
|
provider: session.provider,
|
|
833
802
|
model: session.model,
|
|
834
803
|
workflowId: session.workflowId,
|
|
804
|
+
agentId: session.agentId,
|
|
805
|
+
agentName: session.agentName,
|
|
835
806
|
createdAt: session.createdAt.toISOString(),
|
|
836
807
|
updatedAt: session.updatedAt.toISOString(),
|
|
837
808
|
}));
|
|
@@ -852,6 +823,8 @@ let ChatHubService = class ChatHubService {
|
|
|
852
823
|
provider: session.provider,
|
|
853
824
|
model: session.model,
|
|
854
825
|
workflowId: session.workflowId,
|
|
826
|
+
agentId: session.agentId,
|
|
827
|
+
agentName: session.agentName,
|
|
855
828
|
createdAt: session.createdAt.toISOString(),
|
|
856
829
|
updatedAt: session.updatedAt.toISOString(),
|
|
857
830
|
},
|
|
@@ -870,6 +843,7 @@ let ChatHubService = class ChatHubService {
|
|
|
870
843
|
provider: message.provider,
|
|
871
844
|
model: message.model,
|
|
872
845
|
workflowId: message.workflowId,
|
|
846
|
+
agentId: message.agentId,
|
|
873
847
|
executionId: message.executionId,
|
|
874
848
|
status: message.status,
|
|
875
849
|
createdAt: message.createdAt.toISOString(),
|
|
@@ -904,6 +878,51 @@ let ChatHubService = class ChatHubService {
|
|
|
904
878
|
}
|
|
905
879
|
return await this.sessionRepository.updateChatTitle(sessionId, title);
|
|
906
880
|
}
|
|
881
|
+
async updateSession(user, sessionId, updates) {
|
|
882
|
+
const session = await this.sessionRepository.getOneById(sessionId, user.id);
|
|
883
|
+
if (!session) {
|
|
884
|
+
throw new not_found_error_1.NotFoundError('Session not found');
|
|
885
|
+
}
|
|
886
|
+
if (updates.workflowId) {
|
|
887
|
+
const workflow = await this.workflowFinderService.findWorkflowForUser(updates.workflowId, user, ['workflow:read'], { includeTags: false, includeParentFolder: false });
|
|
888
|
+
if (!workflow) {
|
|
889
|
+
throw new bad_request_error_1.BadRequestError('Workflow not found');
|
|
890
|
+
}
|
|
891
|
+
const chatTriggers = workflow.nodes.filter((node) => node.type === n8n_workflow_1.CHAT_TRIGGER_NODE_TYPE);
|
|
892
|
+
if (chatTriggers.length !== 1) {
|
|
893
|
+
throw new bad_request_error_1.BadRequestError('Workflow must have exactly one chat trigger');
|
|
894
|
+
}
|
|
895
|
+
const chatTrigger = chatTriggers[0];
|
|
896
|
+
updates.agentName =
|
|
897
|
+
typeof chatTrigger.parameters.agentName === 'string' &&
|
|
898
|
+
chatTrigger.parameters.agentName.length > 0
|
|
899
|
+
? chatTrigger.parameters.agentName
|
|
900
|
+
: workflow.name;
|
|
901
|
+
}
|
|
902
|
+
if (updates.agentId) {
|
|
903
|
+
const agent = await this.chatHubAgentService.getAgentById(updates.agentId, user.id);
|
|
904
|
+
if (!agent) {
|
|
905
|
+
throw new bad_request_error_1.BadRequestError('Agent not found');
|
|
906
|
+
}
|
|
907
|
+
updates.agentName = agent.name;
|
|
908
|
+
}
|
|
909
|
+
if (updates.provider === 'n8n') {
|
|
910
|
+
updates.model = null;
|
|
911
|
+
updates.credentialId = null;
|
|
912
|
+
updates.agentId = null;
|
|
913
|
+
}
|
|
914
|
+
else if (updates.provider === 'custom-agent') {
|
|
915
|
+
updates.model = null;
|
|
916
|
+
updates.credentialId = null;
|
|
917
|
+
updates.workflowId = null;
|
|
918
|
+
}
|
|
919
|
+
else if (updates.provider) {
|
|
920
|
+
updates.workflowId = null;
|
|
921
|
+
updates.agentId = null;
|
|
922
|
+
updates.agentName = null;
|
|
923
|
+
}
|
|
924
|
+
return await this.sessionRepository.updateChatSession(sessionId, updates);
|
|
925
|
+
}
|
|
907
926
|
async deleteSession(userId, sessionId) {
|
|
908
927
|
const session = await this.sessionRepository.getOneById(sessionId, userId);
|
|
909
928
|
if (!session) {
|
|
@@ -923,10 +942,12 @@ exports.ChatHubService = ChatHubService = __decorate([
|
|
|
923
942
|
workflow_service_1.WorkflowService,
|
|
924
943
|
workflow_finder_service_1.WorkflowFinderService,
|
|
925
944
|
db_1.WorkflowRepository,
|
|
926
|
-
db_1.SharedWorkflowRepository,
|
|
927
945
|
active_executions_1.ActiveExecutions,
|
|
928
946
|
chat_session_repository_1.ChatHubSessionRepository,
|
|
929
947
|
chat_message_repository_1.ChatHubMessageRepository,
|
|
930
|
-
credentials_finder_service_1.CredentialsFinderService
|
|
948
|
+
credentials_finder_service_1.CredentialsFinderService,
|
|
949
|
+
chat_hub_agent_service_1.ChatHubAgentService,
|
|
950
|
+
chat_hub_credentials_service_1.ChatHubCredentialsService,
|
|
951
|
+
chat_hub_workflow_service_1.ChatHubWorkflowService])
|
|
931
952
|
], ChatHubService);
|
|
932
953
|
//# sourceMappingURL=chat-hub.service.js.map
|