n8n 1.116.2 → 1.117.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/active-executions.d.ts +3 -1
  2. package/dist/active-executions.js +7 -8
  3. package/dist/active-executions.js.map +1 -1
  4. package/dist/build.tsbuildinfo +1 -1
  5. package/dist/commands/base-command.js +1 -5
  6. package/dist/commands/base-command.js.map +1 -1
  7. package/dist/commands/execute-batch.js +2 -3
  8. package/dist/commands/execute-batch.js.map +1 -1
  9. package/dist/commands/execute.js +2 -6
  10. package/dist/commands/execute.js.map +1 -1
  11. package/dist/commands/start.js +5 -5
  12. package/dist/commands/start.js.map +1 -1
  13. package/dist/commands/webhook.js +4 -8
  14. package/dist/commands/webhook.js.map +1 -1
  15. package/dist/commands/worker.js +5 -9
  16. package/dist/commands/worker.js.map +1 -1
  17. package/dist/concurrency/concurrency-control.service.js +1 -2
  18. package/dist/concurrency/concurrency-control.service.js.map +1 -1
  19. package/dist/config/index.d.ts +0 -3
  20. package/dist/config/schema.d.ts +0 -8
  21. package/dist/config/schema.js +0 -8
  22. package/dist/config/schema.js.map +1 -1
  23. package/dist/controllers/ai.controller.d.ts +2 -1
  24. package/dist/controllers/ai.controller.js +19 -0
  25. package/dist/controllers/ai.controller.js.map +1 -1
  26. package/dist/controllers/e2e.controller.d.ts +3 -1
  27. package/dist/controllers/e2e.controller.js +7 -5
  28. package/dist/controllers/e2e.controller.js.map +1 -1
  29. package/dist/controllers/users.controller.d.ts +1 -1
  30. package/dist/controllers/users.controller.js +1 -1
  31. package/dist/controllers/users.controller.js.map +1 -1
  32. package/dist/deprecation/deprecation.service.js +1 -5
  33. package/dist/deprecation/deprecation.service.js.map +1 -1
  34. package/dist/environments.ee/source-control/source-control-export.service.ee.d.ts +1 -1
  35. package/dist/environments.ee/source-control/source-control-export.service.ee.js +2 -2
  36. package/dist/environments.ee/source-control/source-control-export.service.ee.js.map +1 -1
  37. package/dist/environments.ee/source-control/source-control-helper.ee.d.ts +2 -0
  38. package/dist/environments.ee/source-control/source-control-helper.ee.js +11 -2
  39. package/dist/environments.ee/source-control/source-control-helper.ee.js.map +1 -1
  40. package/dist/environments.ee/source-control/source-control-import.service.ee.d.ts +6 -4
  41. package/dist/environments.ee/source-control/source-control-import.service.ee.js +63 -51
  42. package/dist/environments.ee/source-control/source-control-import.service.ee.js.map +1 -1
  43. package/dist/environments.ee/source-control/source-control-status.service.ee.js +28 -3
  44. package/dist/environments.ee/source-control/source-control-status.service.ee.js.map +1 -1
  45. package/dist/environments.ee/variables/variables.service.ee.js +6 -1
  46. package/dist/environments.ee/variables/variables.service.ee.js.map +1 -1
  47. package/dist/evaluation.ee/test-runner/test-runner.service.ee.d.ts +3 -1
  48. package/dist/evaluation.ee/test-runner/test-runner.service.ee.js +7 -5
  49. package/dist/evaluation.ee/test-runner/test-runner.service.ee.js.map +1 -1
  50. package/dist/eventbus/message-event-bus/message-event-bus.js +1 -2
  51. package/dist/eventbus/message-event-bus/message-event-bus.js.map +1 -1
  52. package/dist/eventbus/message-event-bus-destination/message-event-bus-destination-syslog.ee.js +1 -0
  53. package/dist/eventbus/message-event-bus-destination/message-event-bus-destination-syslog.ee.js.map +1 -1
  54. package/dist/events/maps/relay.event-map.d.ts +18 -1
  55. package/dist/events/relays/telemetry.event-relay.d.ts +1 -0
  56. package/dist/events/relays/telemetry.event-relay.js +12 -4
  57. package/dist/events/relays/telemetry.event-relay.js.map +1 -1
  58. package/dist/executions/execution.service.js +2 -6
  59. package/dist/executions/execution.service.js.map +1 -1
  60. package/dist/license.js +1 -5
  61. package/dist/license.js.map +1 -1
  62. package/dist/metrics/prometheus-metrics.service.js +1 -2
  63. package/dist/metrics/prometheus-metrics.service.js.map +1 -1
  64. package/dist/middlewares/list-query/dtos/workflow.select.dto.js +1 -0
  65. package/dist/middlewares/list-query/dtos/workflow.select.dto.js.map +1 -1
  66. package/dist/modules/chat-hub/chat-hub-message.entity.d.ts +28 -0
  67. package/dist/modules/chat-hub/chat-hub-message.entity.js +124 -0
  68. package/dist/modules/chat-hub/chat-hub-message.entity.js.map +1 -0
  69. package/dist/modules/chat-hub/chat-hub-session.entity.d.ts +17 -0
  70. package/dist/modules/chat-hub/chat-hub-session.entity.js +72 -0
  71. package/dist/modules/chat-hub/chat-hub-session.entity.js.map +1 -0
  72. package/dist/modules/chat-hub/chat-hub.controller.d.ts +8 -5
  73. package/dist/modules/chat-hub/chat-hub.controller.js +151 -22
  74. package/dist/modules/chat-hub/chat-hub.controller.js.map +1 -1
  75. package/dist/modules/chat-hub/chat-hub.module.d.ts +1 -0
  76. package/dist/modules/chat-hub/chat-hub.module.js +6 -1
  77. package/dist/modules/chat-hub/chat-hub.module.js.map +1 -1
  78. package/dist/modules/chat-hub/chat-hub.service.d.ts +34 -13
  79. package/dist/modules/chat-hub/chat-hub.service.js +500 -315
  80. package/dist/modules/chat-hub/chat-hub.service.js.map +1 -1
  81. package/dist/modules/chat-hub/chat-hub.types.d.ts +25 -11
  82. package/dist/modules/chat-hub/chat-message.repository.d.ts +16 -0
  83. package/dist/modules/chat-hub/chat-message.repository.js +60 -0
  84. package/dist/modules/chat-hub/chat-message.repository.js.map +1 -0
  85. package/dist/modules/chat-hub/chat-session.repository.d.ts +12 -0
  86. package/dist/modules/chat-hub/chat-session.repository.js +77 -0
  87. package/dist/modules/chat-hub/chat-session.repository.js.map +1 -0
  88. package/dist/modules/chat-hub/context-limits.d.ts +3 -0
  89. package/dist/modules/chat-hub/context-limits.js +142 -0
  90. package/dist/modules/chat-hub/context-limits.js.map +1 -0
  91. package/dist/modules/community-packages/community-node-types.controller.js +2 -2
  92. package/dist/modules/community-packages/community-node-types.controller.js.map +1 -1
  93. package/dist/modules/mcp/mcp-api-key.service.d.ts +4 -1
  94. package/dist/modules/mcp/mcp-api-key.service.js +23 -8
  95. package/dist/modules/mcp/mcp-api-key.service.js.map +1 -1
  96. package/dist/modules/mcp/mcp.constants.d.ts +5 -0
  97. package/dist/modules/mcp/mcp.constants.js +9 -0
  98. package/dist/modules/mcp/mcp.constants.js.map +1 -0
  99. package/dist/modules/mcp/mcp.controller.d.ts +4 -1
  100. package/dist/modules/mcp/mcp.controller.js +56 -4
  101. package/dist/modules/mcp/mcp.controller.js.map +1 -1
  102. package/dist/modules/mcp/mcp.event-relay.d.ts +11 -0
  103. package/dist/modules/mcp/mcp.event-relay.js +61 -0
  104. package/dist/modules/mcp/mcp.event-relay.js.map +1 -0
  105. package/dist/modules/mcp/mcp.module.js +2 -0
  106. package/dist/modules/mcp/mcp.module.js.map +1 -1
  107. package/dist/modules/mcp/mcp.settings.controller.js +1 -1
  108. package/dist/modules/mcp/mcp.settings.controller.js.map +1 -1
  109. package/dist/modules/mcp/mcp.typeguards.d.ts +3 -0
  110. package/dist/modules/mcp/mcp.typeguards.js +19 -1
  111. package/dist/modules/mcp/mcp.typeguards.js.map +1 -1
  112. package/dist/modules/mcp/mcp.types.d.ts +24 -0
  113. package/dist/modules/mcp/mcp.utils.d.ts +8 -0
  114. package/dist/modules/mcp/mcp.utils.js +37 -0
  115. package/dist/modules/mcp/mcp.utils.js.map +1 -0
  116. package/dist/modules/provisioning.ee/constants.d.ts +1 -0
  117. package/dist/modules/provisioning.ee/constants.js +5 -0
  118. package/dist/modules/provisioning.ee/constants.js.map +1 -0
  119. package/dist/modules/provisioning.ee/provisioning.controller.ee.d.ts +10 -0
  120. package/dist/modules/provisioning.ee/provisioning.controller.ee.js +41 -0
  121. package/dist/modules/provisioning.ee/provisioning.controller.ee.js.map +1 -0
  122. package/dist/modules/provisioning.ee/provisioning.module.d.ts +4 -0
  123. package/dist/modules/provisioning.ee/provisioning.module.js +53 -0
  124. package/dist/modules/provisioning.ee/provisioning.module.js.map +1 -0
  125. package/dist/modules/provisioning.ee/provisioning.service.ee.d.ts +15 -0
  126. package/dist/modules/provisioning.ee/provisioning.service.ee.js +67 -0
  127. package/dist/modules/provisioning.ee/provisioning.service.ee.js.map +1 -0
  128. package/dist/posthog/index.js +0 -4
  129. package/dist/posthog/index.js.map +1 -1
  130. package/dist/public-api/v1/handlers/workflows/workflows.handler.js +12 -0
  131. package/dist/public-api/v1/handlers/workflows/workflows.handler.js.map +1 -1
  132. package/dist/scaling/pubsub/publisher.service.d.ts +3 -1
  133. package/dist/scaling/pubsub/publisher.service.js +7 -8
  134. package/dist/scaling/pubsub/publisher.service.js.map +1 -1
  135. package/dist/scaling/pubsub/subscriber.service.d.ts +3 -1
  136. package/dist/scaling/pubsub/subscriber.service.js +6 -4
  137. package/dist/scaling/pubsub/subscriber.service.js.map +1 -1
  138. package/dist/server.js +2 -2
  139. package/dist/server.js.map +1 -1
  140. package/dist/services/ai-workflow-builder.service.d.ts +3 -0
  141. package/dist/services/ai-workflow-builder.service.js +6 -0
  142. package/dist/services/ai-workflow-builder.service.js.map +1 -1
  143. package/dist/services/cache/cache.service.js +1 -5
  144. package/dist/services/cache/cache.service.js.map +1 -1
  145. package/dist/services/frontend.service.js +12 -3
  146. package/dist/services/frontend.service.js.map +1 -1
  147. package/dist/services/role.service.js +15 -3
  148. package/dist/services/role.service.js.map +1 -1
  149. package/dist/services/user.service.d.ts +3 -1
  150. package/dist/services/user.service.js +14 -5
  151. package/dist/services/user.service.js.map +1 -1
  152. package/dist/sso.ee/oidc/oidc.service.ee.js +6 -1
  153. package/dist/sso.ee/oidc/oidc.service.ee.js.map +1 -1
  154. package/dist/workflow-runner.d.ts +0 -2
  155. package/dist/workflow-runner.js +4 -9
  156. package/dist/workflow-runner.js.map +1 -1
  157. package/dist/workflows/workflow-execution.service.js +1 -5
  158. package/dist/workflows/workflow-execution.service.js.map +1 -1
  159. package/dist/workflows/workflow.service.js +24 -0
  160. package/dist/workflows/workflow.service.js.map +1 -1
  161. package/package.json +16 -16
@@ -16,24 +16,53 @@ const db_1 = require("@n8n/db");
16
16
  const di_1 = require("@n8n/di");
17
17
  const n8n_workflow_1 = require("n8n-workflow");
18
18
  const uuid_1 = require("uuid");
19
- const credentials_helper_1 = require("../../credentials-helper");
19
+ const active_executions_1 = require("../../active-executions");
20
+ const credentials_service_1 = require("../../credentials/credentials.service");
21
+ const bad_request_error_1 = require("../../errors/response-errors/bad-request.error");
20
22
  const not_found_error_1 = require("../../errors/response-errors/not-found.error");
23
+ const execution_service_1 = require("../../executions/execution.service");
24
+ const dynamic_node_parameters_service_1 = require("../../services/dynamic-node-parameters.service");
21
25
  const workflow_execute_additional_data_1 = require("../../workflow-execute-additional-data");
22
26
  const workflow_execution_service_1 = require("../../workflows/workflow-execution.service");
23
- const credentials_service_1 = require("../../credentials/credentials.service");
24
- const active_executions_1 = require("../../active-executions");
27
+ const chat_message_repository_1 = require("./chat-message.repository");
28
+ const chat_session_repository_1 = require("./chat-session.repository");
29
+ const context_limits_1 = require("./context-limits");
30
+ const providerNodeTypeMapping = {
31
+ openai: {
32
+ name: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
33
+ version: 1.2,
34
+ },
35
+ anthropic: {
36
+ name: '@n8n/n8n-nodes-langchain.lmChatAnthropic',
37
+ version: 1.3,
38
+ },
39
+ google: {
40
+ name: '@n8n/n8n-nodes-langchain.lmChatGoogleGemini',
41
+ version: 1.2,
42
+ },
43
+ };
44
+ const NODE_NAMES = {
45
+ CHAT_TRIGGER: 'When chat message received',
46
+ AI_AGENT: 'AI Agent',
47
+ CHAT_MODEL: 'Chat Model',
48
+ MEMORY: 'Memory',
49
+ RESTORE_CHAT_MEMORY: 'Restore Chat Memory',
50
+ CLEAR_CHAT_MEMORY: 'Clear Chat Memory',
51
+ };
25
52
  let ChatHubService = class ChatHubService {
26
- constructor(logger, credentialsService, credentialsHelper, executionRepository, workflowExecutionService, workflowRepository, projectRepository, sharedWorkflowRepository, activeExecutions) {
53
+ constructor(logger, credentialsService, executionService, nodeParametersService, executionRepository, workflowExecutionService, workflowRepository, projectRepository, sharedWorkflowRepository, activeExecutions, sessionRepository, messageRepository) {
27
54
  this.logger = logger;
28
55
  this.credentialsService = credentialsService;
29
- this.credentialsHelper = credentialsHelper;
56
+ this.executionService = executionService;
57
+ this.nodeParametersService = nodeParametersService;
30
58
  this.executionRepository = executionRepository;
31
59
  this.workflowExecutionService = workflowExecutionService;
32
60
  this.workflowRepository = workflowRepository;
33
61
  this.projectRepository = projectRepository;
34
62
  this.sharedWorkflowRepository = sharedWorkflowRepository;
35
63
  this.activeExecutions = activeExecutions;
36
- this.sesssions = new Map();
64
+ this.sessionRepository = sessionRepository;
65
+ this.messageRepository = messageRepository;
37
66
  }
38
67
  async getModels(user, credentialIds) {
39
68
  const additionalData = await (0, workflow_execute_additional_data_1.getBase)({ userId: user.id });
@@ -43,16 +72,14 @@ let ChatHubService = class ChatHubService {
43
72
  return [provider, { models: [] }];
44
73
  }
45
74
  await this.credentialsService.getOne(user, credentialId, false);
46
- const credentials = await this.credentialsHelper.getDecrypted(additionalData, {
47
- id: credentialId,
48
- name: api_types_1.PROVIDER_CREDENTIAL_TYPE_MAP[provider],
49
- }, api_types_1.PROVIDER_CREDENTIAL_TYPE_MAP[provider], 'internal', undefined, true);
50
- const apiKey = this.extractApiKey(provider, credentials);
51
- if (!apiKey) {
52
- return [provider, { models: [] }];
53
- }
54
75
  try {
55
- return [provider, await this.fetchModelsForProvider(provider, apiKey)];
76
+ const credentials = {
77
+ [api_types_1.PROVIDER_CREDENTIAL_TYPE_MAP[provider]]: { name: '', id: credentialId },
78
+ };
79
+ return [
80
+ provider,
81
+ await this.fetchModelsForProvider(provider, credentials, additionalData),
82
+ ];
56
83
  }
57
84
  catch {
58
85
  return [
@@ -70,93 +97,74 @@ let ChatHubService = class ChatHubService {
70
97
  google: { models: [] },
71
98
  });
72
99
  }
73
- async fetchModelsForProvider(provider, apiKey) {
100
+ async fetchModelsForProvider(provider, credentials, additionalData) {
74
101
  switch (provider) {
75
102
  case 'openai':
76
- return await this.fetchOpenAiModels(apiKey);
103
+ return await this.fetchOpenAiModels(credentials, additionalData);
77
104
  case 'anthropic':
78
- return await this.fetchAnthropicModels(apiKey);
105
+ return await this.fetchAnthropicModels(credentials, additionalData);
79
106
  case 'google':
80
- return await this.fetchGoogleModels(apiKey);
107
+ return await this.fetchGoogleModels(credentials, additionalData);
81
108
  }
82
109
  }
83
- async fetchOpenAiModels(apiKey) {
84
- const response = await fetch('https://api.openai.com/v1/models', {
85
- method: 'GET',
86
- headers: {
87
- Authorization: `Bearer ${apiKey}`,
88
- },
89
- });
90
- if (!response.ok) {
91
- throw new Error(`Failed to fetch OpenAI models: ${response.statusText}`);
92
- }
93
- const data = await response.json();
110
+ async fetchOpenAiModels(credentials, additionalData) {
111
+ const resourceLocatorResults = await this.nodeParametersService.getResourceLocatorResults('searchModels', 'parameters.model', additionalData, providerNodeTypeMapping.openai, {}, credentials);
94
112
  return {
95
- models: data.data
96
- .filter((model) => model.id.includes('gpt') &&
97
- !model.id.includes('instruct') &&
98
- !model.id.includes('audio'))
99
- .map((model) => ({ name: model.id })),
113
+ models: resourceLocatorResults.results.map((result) => ({ name: String(result.value) })),
100
114
  };
101
115
  }
102
- async fetchAnthropicModels(apiKey) {
103
- const response = await fetch('https://api.anthropic.com/v1/models', {
104
- method: 'GET',
105
- headers: {
106
- 'x-api-key': apiKey,
107
- 'anthropic-version': '2023-06-01',
108
- },
109
- });
110
- if (!response.ok) {
111
- throw new Error(`Failed to fetch Anthropic models: ${response.statusText}`);
112
- }
113
- const data = (await response.json());
116
+ async fetchAnthropicModels(credentials, additionalData) {
117
+ const resourceLocatorResults = await this.nodeParametersService.getResourceLocatorResults('searchModels', 'parameters.model', additionalData, providerNodeTypeMapping.anthropic, {}, credentials);
114
118
  return {
115
- models: (data.data || [])
116
- .sort((a, b) => {
117
- const dateA = new Date(a.created_at);
118
- const dateB = new Date(b.created_at);
119
- return dateB.getTime() - dateA.getTime();
120
- })
121
- .map((model) => ({ name: model.id })),
119
+ models: resourceLocatorResults.results.map((result) => ({ name: String(result.value) })),
122
120
  };
123
121
  }
124
- async fetchGoogleModels(apiKey) {
125
- const response = await fetch(`https://generativelanguage.googleapis.com/v1/models?key=${apiKey}`, {
126
- method: 'GET',
127
- });
128
- if (!response.ok) {
129
- throw new Error(`Failed to fetch Google models: ${response.statusText}`);
130
- }
131
- const data = await response.json();
122
+ async fetchGoogleModels(credentials, additionalData) {
123
+ const results = await this.nodeParametersService.getOptionsViaLoadOptions({
124
+ routing: {
125
+ request: {
126
+ method: 'GET',
127
+ url: '/v1beta/models',
128
+ },
129
+ output: {
130
+ postReceive: [
131
+ {
132
+ type: 'rootProperty',
133
+ properties: {
134
+ property: 'models',
135
+ },
136
+ },
137
+ {
138
+ type: 'filter',
139
+ properties: {
140
+ pass: "={{ !$responseItem.name.includes('embedding') }}",
141
+ },
142
+ },
143
+ {
144
+ type: 'setKeyValue',
145
+ properties: {
146
+ name: '={{$responseItem.name}}',
147
+ value: '={{$responseItem.name}}',
148
+ description: '={{$responseItem.description}}',
149
+ },
150
+ },
151
+ {
152
+ type: 'sort',
153
+ properties: {
154
+ key: 'name',
155
+ },
156
+ },
157
+ ],
158
+ },
159
+ },
160
+ }, additionalData, providerNodeTypeMapping.google, {}, credentials);
132
161
  return {
133
- models: data.models
134
- ?.filter((model) => model.name.includes('gemini') &&
135
- model.supportedGenerationMethods?.includes('generateContent'))
136
- .map((model) => {
137
- const modelId = model.name.split('/').pop();
138
- return { name: modelId };
139
- }),
162
+ models: results.map((result) => ({ name: String(result.value) })),
140
163
  };
141
164
  }
142
- extractApiKey(provider, credentials) {
143
- if (typeof credentials !== 'object' || credentials === null) {
144
- return undefined;
145
- }
146
- const creds = credentials;
147
- switch (provider) {
148
- case 'openai':
149
- case 'anthropic':
150
- case 'google':
151
- return typeof creds.apiKey === 'string' ? creds.apiKey : undefined;
152
- }
153
- }
154
- async createChatWorkflow(user, sessionId, nodes, connections) {
165
+ async createChatWorkflow(user, sessionId, history, humanMessage, credentials, model) {
166
+ const { nodes, connections, startNodes, triggerToStartFrom } = this.prepareChatWorkflow(sessionId, history, humanMessage, credentials, model);
155
167
  const { manager } = this.projectRepository;
156
- const existing = await this.workflowRepository.findOneBy({ id: sessionId });
157
- if (existing) {
158
- return existing;
159
- }
160
168
  return await manager.transaction(async (trx) => {
161
169
  const project = await this.projectRepository.getPersonalProjectForUser(user.id, trx);
162
170
  if (!project) {
@@ -164,7 +172,6 @@ let ChatHubService = class ChatHubService {
164
172
  }
165
173
  const newWorkflow = new db_1.WorkflowEntity();
166
174
  newWorkflow.versionId = (0, uuid_1.v4)();
167
- newWorkflow.id = sessionId;
168
175
  newWorkflow.name = `Chat ${sessionId}`;
169
176
  newWorkflow.active = false;
170
177
  newWorkflow.nodes = nodes;
@@ -175,40 +182,218 @@ let ChatHubService = class ChatHubService {
175
182
  projectId: project.id,
176
183
  workflow,
177
184
  }));
178
- return workflow;
185
+ return {
186
+ workflowData: {
187
+ ...workflow,
188
+ nodes,
189
+ connections,
190
+ versionId: (0, uuid_1.v4)(),
191
+ },
192
+ startNodes,
193
+ triggerToStartFrom,
194
+ };
179
195
  });
180
196
  }
181
- getMessage(execution) {
182
- const lastNodeExecuted = execution.data.resultData.lastNodeExecuted;
183
- if (typeof lastNodeExecuted !== 'string')
197
+ async deleteChatWorkflow(workflowId) {
198
+ await this.workflowRepository.delete(workflowId);
199
+ }
200
+ getErrorMessage(execution) {
201
+ if (execution.data.resultData.error) {
202
+ return execution.data.resultData.error.description ?? execution.data.resultData.error.message;
203
+ }
204
+ return undefined;
205
+ }
206
+ getAIOutput(execution) {
207
+ const agent = execution.data.resultData.runData[NODE_NAMES.AI_AGENT];
208
+ if (!agent || !Array.isArray(agent) || agent.length === 0)
184
209
  return undefined;
185
- const runIndex = execution.data.resultData.runData[lastNodeExecuted].length - 1;
186
- const mainOutputs = execution.data.resultData.runData[lastNodeExecuted][runIndex]?.data?.main;
210
+ const runIndex = agent.length - 1;
211
+ const mainOutputs = agent[runIndex].data?.main;
187
212
  if (mainOutputs && Array.isArray(mainOutputs)) {
188
213
  for (const branch of mainOutputs) {
189
214
  if (branch && Array.isArray(branch) && branch.length > 0 && branch[0].json?.output) {
190
- return branch[0].json.output;
215
+ if (typeof branch[0].json.output === 'string') {
216
+ return branch[0].json.output;
217
+ }
191
218
  }
192
219
  }
193
220
  }
194
221
  return undefined;
195
222
  }
196
- async askN8n(res, user, payload) {
197
- let session = this.sesssions.get(payload.sessionId);
198
- if (!session) {
199
- session = [];
200
- this.sesssions.set(payload.sessionId, session);
223
+ getCredentialId(provider, credentials) {
224
+ return credentials[api_types_1.PROVIDER_CREDENTIAL_TYPE_MAP[provider]]?.id ?? null;
225
+ }
226
+ async sendHumanMessage(res, user, payload) {
227
+ const { sessionId, messageId, replyId, message } = payload;
228
+ const selectedModel = {
229
+ ...payload.model,
230
+ credentialId: this.getCredentialId(payload.model.provider, payload.credentials),
231
+ };
232
+ const session = await this.getChatSession(user, sessionId, selectedModel, true, message);
233
+ if (payload.previousMessageId) {
234
+ const previousMessage = await this.messageRepository.getOneById(payload.previousMessageId, sessionId);
235
+ if (!previousMessage) {
236
+ throw new bad_request_error_1.BadRequestError('The previous message does not exist in the session');
237
+ }
201
238
  }
202
- const chatHistory = session.map((msg) => ({
203
- type: msg.type,
204
- message: msg.message,
205
- }));
206
- session.push({
207
- id: payload.messageId,
208
- message: payload.message,
209
- type: 'user',
210
- createdAt: new Date(),
239
+ const messages = Object.fromEntries((session.messages ?? []).map((m) => [m.id, m]));
240
+ const history = this.buildMessageHistory(messages, payload.previousMessageId);
241
+ await this.saveHumanMessage(payload, user, payload.previousMessageId, selectedModel);
242
+ const workflow = await this.createChatWorkflow(user, session.id, history, message, payload.credentials, payload.model);
243
+ try {
244
+ await this.executeChatWorkflow(res, user, workflow, replyId, sessionId, messageId, selectedModel);
245
+ }
246
+ finally {
247
+ await this.deleteChatWorkflow(workflow.workflowData.id);
248
+ }
249
+ }
250
+ async editHumanMessage(res, user, payload) {
251
+ const { sessionId, editId, messageId, message, replyId } = payload;
252
+ const selectedModel = {
253
+ ...payload.model,
254
+ credentialId: this.getCredentialId(payload.model.provider, payload.credentials),
255
+ };
256
+ const session = await this.getChatSession(user, sessionId);
257
+ const messageToEdit = await this.getChatMessage(session.id, editId);
258
+ if (messageToEdit.type !== 'human') {
259
+ throw new bad_request_error_1.BadRequestError('Can only edit human messages');
260
+ }
261
+ const messages = Object.fromEntries((session.messages ?? []).map((m) => [m.id, m]));
262
+ const history = this.buildMessageHistory(messages, messageToEdit.previousMessageId);
263
+ const revisionOfMessageId = messageToEdit.revisionOfMessageId ?? messageToEdit.id;
264
+ await this.saveHumanMessage(payload, user, messageToEdit.previousMessageId, selectedModel, revisionOfMessageId);
265
+ const workflow = await this.createChatWorkflow(user, session.id, history, message, payload.credentials, payload.model);
266
+ try {
267
+ await this.executeChatWorkflow(res, user, workflow, replyId, sessionId, messageId, selectedModel);
268
+ }
269
+ finally {
270
+ await this.deleteChatWorkflow(workflow.workflowData.id);
271
+ }
272
+ }
273
+ async regenerateAIMessage(res, user, payload) {
274
+ const { sessionId, retryId, replyId } = payload;
275
+ const selectedModel = {
276
+ ...payload.model,
277
+ credentialId: this.getCredentialId(payload.model.provider, payload.credentials),
278
+ };
279
+ const session = await this.getChatSession(user, sessionId);
280
+ const messageToRetry = await this.getChatMessage(session.id, retryId);
281
+ if (messageToRetry.type !== 'ai') {
282
+ throw new bad_request_error_1.BadRequestError('Can only retry AI messages');
283
+ }
284
+ const messages = Object.fromEntries((session.messages ?? []).map((m) => [m.id, m]));
285
+ const history = this.buildMessageHistory(messages, messageToRetry.previousMessageId);
286
+ const lastHumanMessage = history.filter((m) => m.type === 'human').pop();
287
+ if (!lastHumanMessage) {
288
+ throw new bad_request_error_1.BadRequestError('No human message found to base the retry on');
289
+ }
290
+ const lastHumanMessageIndex = history.indexOf(lastHumanMessage);
291
+ if (lastHumanMessageIndex !== -1) {
292
+ history.splice(lastHumanMessageIndex + 1);
293
+ }
294
+ const retryOfMessageId = messageToRetry.retryOfMessageId ?? messageToRetry.id;
295
+ const workflow = await this.createChatWorkflow(user, session.id, history, lastHumanMessage ? lastHumanMessage.content : '', payload.credentials, payload.model);
296
+ try {
297
+ await this.executeChatWorkflow(res, user, workflow, replyId, sessionId, lastHumanMessage.id, selectedModel, retryOfMessageId);
298
+ }
299
+ finally {
300
+ await this.deleteChatWorkflow(workflow.workflowData.id);
301
+ }
302
+ }
303
+ async stopGeneration(user, sessionId, messageId) {
304
+ const session = await this.getChatSession(user, sessionId);
305
+ const message = await this.getChatMessage(session.id, messageId, [
306
+ 'execution',
307
+ 'execution.workflow',
308
+ ]);
309
+ if (message.type !== 'ai') {
310
+ throw new bad_request_error_1.BadRequestError('Can only stop AI messages');
311
+ }
312
+ if (!message.executionId || !message.execution) {
313
+ throw new bad_request_error_1.BadRequestError('Message is not associated with a workflow execution');
314
+ }
315
+ if (message.status !== 'running') {
316
+ throw new bad_request_error_1.BadRequestError('Can only stop messages that are currently running');
317
+ }
318
+ await this.executionService.stop(message.execution.id, [message.execution.workflowId]);
319
+ await this.messageRepository.updateChatMessage(messageId, { status: 'cancelled' });
320
+ }
321
+ async executeChatWorkflow(res, user, workflow, replyId, sessionId, previousMessageId, selectedModel, retryOfMessageId) {
322
+ const { workflowData, startNodes, triggerToStartFrom } = workflow;
323
+ this.logger.debug(`Starting execution of workflow "${workflowData.name}" with ID ${workflowData.id}`);
324
+ const { executionId } = await this.workflowExecutionService.executeManually({
325
+ workflowData,
326
+ startNodes,
327
+ triggerToStartFrom,
328
+ }, user, undefined, true, res);
329
+ if (!executionId) {
330
+ throw new n8n_workflow_1.OperationalError('There was a problem starting the chat execution.');
331
+ }
332
+ await this.saveAIMessage({
333
+ id: replyId,
334
+ sessionId,
335
+ executionId,
336
+ previousMessageId,
337
+ message: '',
338
+ selectedModel,
339
+ retryOfMessageId,
340
+ status: 'running',
211
341
  });
342
+ try {
343
+ let result;
344
+ try {
345
+ result = await this.activeExecutions.getPostExecutePromise(executionId);
346
+ if (!result) {
347
+ throw new n8n_workflow_1.OperationalError('There was a problem executing the chat workflow.');
348
+ }
349
+ }
350
+ catch (error) {
351
+ if (error instanceof n8n_workflow_1.ManualExecutionCancelledError) {
352
+ const execution = await this.executionRepository.findWithUnflattenedData(executionId, [
353
+ workflowData.id,
354
+ ]);
355
+ if (!execution) {
356
+ throw new n8n_workflow_1.OperationalError(`Could not find execution with ID ${executionId}`);
357
+ }
358
+ if (execution.status === 'canceled') {
359
+ const message = 'Generation cancelled.';
360
+ await this.messageRepository.updateChatMessage(replyId, {
361
+ content: message,
362
+ status: 'cancelled',
363
+ });
364
+ return;
365
+ }
366
+ }
367
+ throw error;
368
+ }
369
+ const execution = await this.executionRepository.findWithUnflattenedData(executionId, [
370
+ workflowData.id,
371
+ ]);
372
+ if (!execution) {
373
+ throw new n8n_workflow_1.OperationalError(`Could not find execution with ID ${executionId}`);
374
+ }
375
+ if (!execution.status || execution.status !== 'success') {
376
+ const message = this.getErrorMessage(execution) ?? 'Failed to generate a response';
377
+ throw new n8n_workflow_1.OperationalError(message);
378
+ }
379
+ const message = this.getAIOutput(execution);
380
+ if (!message) {
381
+ throw new n8n_workflow_1.OperationalError('No response generated');
382
+ }
383
+ await this.messageRepository.updateChatMessage(replyId, {
384
+ content: message,
385
+ status: 'success',
386
+ });
387
+ }
388
+ catch (error) {
389
+ const message = error instanceof Error ? error.message : 'Unknown error';
390
+ await this.messageRepository.updateChatMessage(replyId, {
391
+ content: `Error: ${message}`,
392
+ status: 'error',
393
+ });
394
+ }
395
+ }
396
+ prepareChatWorkflow(sessionId, history, humanMessage, credentials, model) {
212
397
  const nodes = [
213
398
  {
214
399
  parameters: {
@@ -220,7 +405,7 @@ let ChatHubService = class ChatHubService {
220
405
  typeVersion: 1.3,
221
406
  position: [0, 0],
222
407
  id: (0, uuid_1.v4)(),
223
- name: 'When chat message received',
408
+ name: NODE_NAMES.CHAT_TRIGGER,
224
409
  webhookId: (0, uuid_1.v4)(),
225
410
  },
226
411
  {
@@ -229,69 +414,103 @@ let ChatHubService = class ChatHubService {
229
414
  text: "={{ $('When chat message received').item.json.chatInput }}",
230
415
  options: {
231
416
  enableStreaming: true,
417
+ maxTokensFromMemory: (0, context_limits_1.getMaxContextWindowTokens)(model.provider, model.model),
232
418
  },
233
419
  },
234
420
  type: n8n_workflow_1.AGENT_LANGCHAIN_NODE_TYPE,
235
421
  typeVersion: 3,
236
422
  position: [600, 0],
237
423
  id: (0, uuid_1.v4)(),
238
- name: 'AI Agent',
424
+ name: NODE_NAMES.AI_AGENT,
239
425
  },
240
- this.createModelNode(payload),
426
+ this.createModelNode(credentials, model),
241
427
  {
242
428
  parameters: {
243
429
  sessionIdType: 'customKey',
244
- sessionKey: "={{ $('When chat message received').item.json.sessionId }}",
430
+ sessionKey: `={{ $('${NODE_NAMES.CHAT_TRIGGER}').item.json.sessionId }}`,
431
+ contextWindowLength: 20,
245
432
  },
246
433
  type: '@n8n/n8n-nodes-langchain.memoryBufferWindow',
247
434
  typeVersion: 1.3,
248
- position: [500, 200],
435
+ position: [480, 208],
249
436
  id: (0, uuid_1.v4)(),
250
- name: 'Memory',
437
+ name: NODE_NAMES.MEMORY,
251
438
  },
252
439
  {
253
440
  parameters: {
254
441
  mode: 'insert',
442
+ insertMode: 'override',
255
443
  messages: {
256
- messageValues: chatHistory,
444
+ messageValues: history.map((message) => {
445
+ const typeMap = {
446
+ human: 'user',
447
+ ai: 'ai',
448
+ system: 'system',
449
+ };
450
+ return {
451
+ type: typeMap[message.type] || 'system',
452
+ message: message.content,
453
+ hideFromUI: false,
454
+ };
455
+ }),
257
456
  },
258
457
  },
259
458
  type: '@n8n/n8n-nodes-langchain.memoryManager',
260
459
  typeVersion: 1.1,
261
- position: [200, 0],
460
+ position: [224, 0],
262
461
  id: (0, uuid_1.v4)(),
263
- name: 'Restore Chat Memory',
462
+ name: NODE_NAMES.RESTORE_CHAT_MEMORY,
463
+ },
464
+ {
465
+ parameters: {
466
+ mode: 'delete',
467
+ deleteMode: 'all',
468
+ },
469
+ type: '@n8n/n8n-nodes-langchain.memoryManager',
470
+ typeVersion: 1.1,
471
+ position: [976, 0],
472
+ id: (0, uuid_1.v4)(),
473
+ name: NODE_NAMES.CLEAR_CHAT_MEMORY,
264
474
  },
265
475
  ];
266
476
  const connections = {
267
- 'When chat message received': {
268
- main: [[{ node: 'Restore Chat Memory', type: 'main', index: 0 }]],
477
+ [NODE_NAMES.CHAT_TRIGGER]: {
478
+ main: [
479
+ [{ node: NODE_NAMES.RESTORE_CHAT_MEMORY, type: n8n_workflow_1.NodeConnectionTypes.Main, index: 0 }],
480
+ ],
269
481
  },
270
- 'Restore Chat Memory': {
271
- main: [[{ node: 'AI Agent', type: 'main', index: 0 }]],
482
+ [NODE_NAMES.RESTORE_CHAT_MEMORY]: {
483
+ main: [[{ node: NODE_NAMES.AI_AGENT, type: n8n_workflow_1.NodeConnectionTypes.Main, index: 0 }]],
272
484
  },
273
- 'Chat Model': {
274
- ai_languageModel: [[{ node: 'AI Agent', type: 'ai_languageModel', index: 0 }]],
485
+ [NODE_NAMES.CHAT_MODEL]: {
486
+ ai_languageModel: [
487
+ [{ node: NODE_NAMES.AI_AGENT, type: n8n_workflow_1.NodeConnectionTypes.AiLanguageModel, index: 0 }],
488
+ ],
275
489
  },
276
- Memory: {
490
+ [NODE_NAMES.MEMORY]: {
277
491
  ai_memory: [
278
492
  [
279
- { node: 'AI Agent', type: 'ai_memory', index: 0 },
280
- { node: 'Restore Chat Memory', type: 'ai_memory', index: 0 },
493
+ { node: NODE_NAMES.AI_AGENT, type: n8n_workflow_1.NodeConnectionTypes.AiMemory, index: 0 },
494
+ { node: NODE_NAMES.RESTORE_CHAT_MEMORY, type: n8n_workflow_1.NodeConnectionTypes.AiMemory, index: 0 },
495
+ { node: NODE_NAMES.CLEAR_CHAT_MEMORY, type: n8n_workflow_1.NodeConnectionTypes.AiMemory, index: 0 },
496
+ ],
497
+ ],
498
+ },
499
+ [NODE_NAMES.AI_AGENT]: {
500
+ main: [
501
+ [
502
+ {
503
+ node: NODE_NAMES.CLEAR_CHAT_MEMORY,
504
+ type: n8n_workflow_1.NodeConnectionTypes.Main,
505
+ index: 0,
506
+ },
281
507
  ],
282
508
  ],
283
509
  },
284
- };
285
- const workflow = await this.createChatWorkflow(user, payload.sessionId, nodes, connections);
286
- const workflowData = {
287
- ...workflow,
288
- nodes,
289
- connections,
290
- versionId: (0, uuid_1.v4)(),
291
510
  };
292
511
  const startNodes = [{ name: 'Restore Chat Memory', sourceData: null }];
293
512
  const triggerToStartFrom = {
294
- name: 'When chat message received',
513
+ name: NODE_NAMES.CHAT_TRIGGER,
295
514
  data: {
296
515
  startTime: Date.now(),
297
516
  executionTime: 0,
@@ -302,9 +521,9 @@ let ChatHubService = class ChatHubService {
302
521
  [
303
522
  {
304
523
  json: {
305
- sessionId: `${payload.sessionId}-${payload.messageId}`,
524
+ sessionId,
306
525
  action: 'sendMessage',
307
- chatInput: payload.message,
526
+ chatInput: humanMessage,
308
527
  },
309
528
  },
310
529
  ],
@@ -313,53 +532,74 @@ let ChatHubService = class ChatHubService {
313
532
  source: [null],
314
533
  },
315
534
  };
316
- this.logger.debug(`Starting execution of workflow "${workflow.name}" with ID ${workflow.id}`);
317
- const { executionId } = await this.workflowExecutionService.executeManually({
318
- workflowData,
319
- startNodes,
320
- triggerToStartFrom,
321
- }, user, undefined, true, res);
322
- if (!executionId) {
323
- throw new n8n_workflow_1.OperationalError('There was a problem starting the chat execution.');
535
+ return { nodes, connections, startNodes, triggerToStartFrom };
536
+ }
537
+ async saveHumanMessage(payload, user, previousMessageId, selectedModel, revisionOfMessageId) {
538
+ await this.messageRepository.createChatMessage({
539
+ id: payload.messageId,
540
+ sessionId: payload.sessionId,
541
+ type: 'human',
542
+ name: user.firstName || 'User',
543
+ status: 'success',
544
+ content: payload.message,
545
+ previousMessageId,
546
+ revisionOfMessageId,
547
+ ...selectedModel,
548
+ });
549
+ }
550
+ async saveAIMessage({ id, sessionId, executionId, previousMessageId, message, selectedModel, retryOfMessageId, status, }) {
551
+ await this.messageRepository.createChatMessage({
552
+ id,
553
+ sessionId,
554
+ previousMessageId,
555
+ executionId: parseInt(executionId, 10),
556
+ type: 'ai',
557
+ name: 'AI',
558
+ status,
559
+ content: message,
560
+ retryOfMessageId,
561
+ ...selectedModel,
562
+ });
563
+ }
564
+ async getChatSession(user, sessionId, selectedModel, initialize = false, title = null) {
565
+ const existing = await this.sessionRepository.getOneById(sessionId, user.id);
566
+ if (existing) {
567
+ return existing;
324
568
  }
325
- const result = await this.activeExecutions.getPostExecutePromise(executionId);
326
- if (!result) {
327
- throw new n8n_workflow_1.OperationalError('There was a problem executing the chat workflow.');
569
+ else if (!initialize) {
570
+ throw new not_found_error_1.NotFoundError('Chat session not found');
328
571
  }
329
- const execution = await this.executionRepository.findWithUnflattenedData(executionId, [
330
- workflow.id,
331
- ]);
332
- if (!execution) {
333
- throw new not_found_error_1.NotFoundError(`Could not find execution with ID ${executionId}`);
334
- }
335
- const message = this.getMessage(execution);
336
- if (message) {
337
- this.logger.debug(`Assistant: ${message} (${payload.replyId})`);
338
- session.push({
339
- id: payload.replyId,
340
- message,
341
- type: 'ai',
342
- createdAt: new Date(),
343
- });
572
+ return await this.sessionRepository.createChatSession({
573
+ id: sessionId,
574
+ ownerId: user.id,
575
+ title: title ?? 'New Chat',
576
+ ...selectedModel,
577
+ });
578
+ }
579
+ async getChatMessage(sessionId, messageId, relations = []) {
580
+ const message = await this.messageRepository.getOneById(messageId, sessionId, relations);
581
+ if (!message) {
582
+ throw new not_found_error_1.NotFoundError('Chat message not found');
344
583
  }
584
+ return message;
345
585
  }
346
- createModelNode(payload) {
586
+ createModelNode(credentials, { provider, model }) {
347
587
  const common = {
348
588
  position: [600, 200],
349
589
  id: (0, uuid_1.v4)(),
350
590
  name: 'Chat Model',
351
- credentials: payload.credentials,
591
+ credentials,
592
+ type: providerNodeTypeMapping[provider].name,
593
+ typeVersion: providerNodeTypeMapping[provider].version,
352
594
  };
353
- switch (payload.model.provider) {
595
+ switch (provider) {
354
596
  case 'openai':
355
597
  return {
356
598
  ...common,
357
599
  parameters: {
358
- model: { __rl: true, mode: 'list', value: payload.model.model },
600
+ model: { __rl: true, mode: 'list', value: model },
359
601
  options: {},
360
602
  },
361
- type: '@n8n/n8n-nodes-langchain.lmChatOpenAi',
362
- typeVersion: 1.2,
363
603
  };
364
604
  case 'anthropic':
365
605
  return {
@@ -368,169 +608,111 @@ let ChatHubService = class ChatHubService {
368
608
  model: {
369
609
  __rl: true,
370
610
  mode: 'list',
371
- value: payload.model.model,
372
- cachedResultName: payload.model.model,
611
+ value: model,
612
+ cachedResultName: model,
373
613
  },
374
614
  options: {},
375
615
  },
376
- type: '@n8n/n8n-nodes-langchain.lmChatAnthropic',
377
- typeVersion: 1.3,
378
616
  };
379
617
  case 'google':
380
618
  return {
381
619
  ...common,
382
620
  parameters: {
383
- model: { __rl: true, mode: 'list', value: payload.model.model },
621
+ model: { __rl: true, mode: 'list', value: model },
384
622
  options: {},
385
623
  },
386
- type: '@n8n/n8n-nodes-langchain.lmChatGoogleGemini',
387
- typeVersion: 1.2,
388
624
  };
389
625
  }
390
626
  }
391
- async getConversations() {
392
- const now = new Date();
393
- const today = new Date(now);
394
- const yesterday = new Date(now);
395
- yesterday.setDate(yesterday.getDate() - 1);
396
- const threeDaysAgo = new Date(now);
397
- threeDaysAgo.setDate(threeDaysAgo.getDate() - 3);
398
- const twoWeeksAgo = new Date(now);
399
- twoWeeksAgo.setDate(twoWeeksAgo.getDate() - 14);
400
- const twoMonthsAgo = new Date(now);
401
- twoMonthsAgo.setMonth(twoMonthsAgo.getMonth() - 2);
402
- return [
403
- {
404
- id: '7f3e2a91-8c4d-4b5a-9e1f-2d6c8a4b5e7f',
405
- title: 'Getting Started with n8n',
406
- createdAt: today.toISOString(),
407
- updatedAt: today.toISOString(),
408
- },
409
- {
410
- id: '3a8f5c2d-1e9b-4d7a-8c3e-6f2a9b4d8e1c',
411
- title: 'Workflow Automation Ideas',
412
- createdAt: yesterday.toISOString(),
413
- updatedAt: yesterday.toISOString(),
414
- },
415
- {
416
- id: '9b2e4f6a-7d1c-4a8b-9e3f-5c7d2a8b4e6f',
417
- title: 'API Integration Help',
418
- createdAt: threeDaysAgo.toISOString(),
419
- updatedAt: threeDaysAgo.toISOString(),
420
- },
421
- {
422
- id: '5c8a1d3e-4b9f-4e2a-8d6c-7f3a9b2e4c8d',
423
- title: 'Database Schema Design',
424
- createdAt: twoWeeksAgo.toISOString(),
425
- updatedAt: twoWeeksAgo.toISOString(),
627
+ async getConversations(userId) {
628
+ const sessions = await this.sessionRepository.getManyByUserId(userId);
629
+ return sessions.map((session) => ({
630
+ id: session.id,
631
+ title: session.title,
632
+ ownerId: session.ownerId,
633
+ lastMessageAt: session.lastMessageAt?.toISOString() ?? null,
634
+ credentialId: session.credentialId,
635
+ provider: session.provider,
636
+ model: session.model,
637
+ workflowId: session.workflowId,
638
+ createdAt: session.createdAt.toISOString(),
639
+ updatedAt: session.updatedAt.toISOString(),
640
+ }));
641
+ }
642
+ async getConversation(userId, sessionId) {
643
+ const session = await this.sessionRepository.getOneById(sessionId, userId);
644
+ if (!session) {
645
+ throw new not_found_error_1.NotFoundError('Chat session not found');
646
+ }
647
+ const messages = await this.messageRepository.getManyBySessionId(sessionId);
648
+ return {
649
+ session: {
650
+ id: session.id,
651
+ title: session.title,
652
+ ownerId: session.ownerId,
653
+ lastMessageAt: session.lastMessageAt?.toISOString() ?? null,
654
+ credentialId: session.credentialId,
655
+ provider: session.provider,
656
+ model: session.model,
657
+ workflowId: session.workflowId,
658
+ createdAt: session.createdAt.toISOString(),
659
+ updatedAt: session.updatedAt.toISOString(),
426
660
  },
427
- {
428
- id: '2f6d9a4c-8e1b-4d7a-9c3e-6a8f2b5d4e9c',
429
- title: 'Docker Deployment Questions',
430
- createdAt: twoMonthsAgo.toISOString(),
431
- updatedAt: twoMonthsAgo.toISOString(),
661
+ conversation: {
662
+ messages: Object.fromEntries(messages.map((m) => [m.id, this.convertMessageToDto(m)])),
432
663
  },
433
- ];
664
+ };
434
665
  }
435
- async getConversationMessages(conversationId) {
436
- this.logger.debug(`Fetching messages for conversation ${conversationId}`);
437
- const mockConversations = {
438
- '7f3e2a91-8c4d-4b5a-9e1f-2d6c8a4b5e7f': [
439
- {
440
- id: '650e8400-e29b-41d4-a716-446655440001',
441
- conversationId,
442
- role: 'user',
443
- content: 'How do I create my first workflow in n8n?',
444
- createdAt: new Date('2025-01-08T10:00:00Z').toISOString(),
445
- },
446
- {
447
- id: '650e8400-e29b-41d4-a716-446655440002',
448
- conversationId,
449
- role: 'assistant',
450
- content: "To create your first workflow in n8n:\n\n1. Click the '+' button in the top left\n2. Select 'Create New Workflow'\n3. Add nodes by clicking the '+' on the canvas\n4. Configure each node\n5. Connect nodes by dragging from one to another\n6. Test and activate your workflow\n\nWould you like help with a specific type of workflow?",
451
- createdAt: new Date('2025-01-08T10:00:30Z').toISOString(),
452
- },
453
- {
454
- id: '650e8400-e29b-41d4-a716-446655440003',
455
- conversationId,
456
- role: 'user',
457
- content: 'Yes, I want to automate sending emails when a new row is added to a Google Sheet.',
458
- createdAt: new Date('2025-01-08T10:05:00Z').toISOString(),
459
- },
460
- {
461
- id: '650e8400-e29b-41d4-a716-446655440004',
462
- conversationId,
463
- role: 'assistant',
464
- content: "Perfect! Here's how to set that up:\n\n1. Add a **Google Sheets Trigger** node\n - Select 'On Row Added'\n - Connect your Google account\n - Choose your spreadsheet\n\n2. Add a **Gmail** node\n - Connect your Gmail account\n - Set the recipient email\n - Use expressions to include data from the sheet\n\n3. Activate the workflow\n\nWould you like more details on any of these steps?",
465
- createdAt: new Date('2025-01-08T10:05:45Z').toISOString(),
466
- },
467
- ],
468
- '3a8f5c2d-1e9b-4d7a-8c3e-6f2a9b4d8e1c': [
469
- {
470
- id: '650e8400-e29b-41d4-a716-446655440011',
471
- conversationId,
472
- role: 'user',
473
- content: 'What are some creative workflow automation ideas?',
474
- createdAt: new Date('2025-01-07T14:30:00Z').toISOString(),
475
- },
476
- {
477
- id: '650e8400-e29b-41d4-a716-446655440012',
478
- conversationId,
479
- role: 'assistant',
480
- content: 'Here are some creative workflow automation ideas:\n\n**Social Media Automation:**\n- Auto-post blog content to multiple platforms\n- Monitor mentions and send notifications\n- Generate reports on engagement metrics\n\n**Business Operations:**\n- Sync data between CRM and accounting software\n- Auto-generate invoices from project completion\n- Send weekly team reports\n\n**Personal Productivity:**\n- Save email attachments to cloud storage\n- Create calendar events from emails\n- Track expenses from receipts\n\nWhich area interests you most?',
481
- createdAt: new Date('2025-01-07T14:32:00Z').toISOString(),
482
- },
483
- {
484
- id: '650e8400-e29b-41d4-a716-446655440013',
485
- conversationId,
486
- role: 'user',
487
- content: 'The social media automation sounds great! How complex is it to set up?',
488
- createdAt: new Date('2025-01-07T14:45:00Z').toISOString(),
489
- },
490
- {
491
- id: '650e8400-e29b-41d4-a716-446655440014',
492
- conversationId,
493
- role: 'assistant',
494
- content: "It's actually quite straightforward! For auto-posting to multiple platforms:\n\n**Difficulty: Beginner-friendly**\n\n1. Use an **RSS** trigger to monitor your blog\n2. Add **Twitter**, **LinkedIn**, and **Facebook** nodes\n3. Format your message with expressions\n4. Add conditions to customize per platform\n\nMost of the work is just connecting your accounts. The actual workflow can be set up in under 30 minutes!\n\nWant me to walk you through the specific nodes you'll need?",
495
- createdAt: new Date('2025-01-07T14:47:00Z').toISOString(),
496
- },
497
- ],
498
- '9b2e4f6a-7d1c-4a8b-9e3f-5c7d2a8b4e6f': [
499
- {
500
- id: '650e8400-e29b-41d4-a716-446655440021',
501
- conversationId,
502
- role: 'user',
503
- content: "I'm having trouble integrating with the Stripe API. Any tips?",
504
- createdAt: new Date('2025-01-06T09:00:00Z').toISOString(),
505
- },
506
- {
507
- id: '650e8400-e29b-41d4-a716-446655440022',
508
- conversationId,
509
- role: 'assistant',
510
- content: "I'd be happy to help with Stripe integration! What specific issue are you encountering?\n\n**Common Stripe integration patterns in n8n:**\n\n1. **Webhook-based** (Recommended)\n - Stripe sends events to n8n\n - Great for payment notifications\n - Real-time updates\n\n2. **Polling-based**\n - Check for new data periodically\n - Good for reports and syncing\n\n3. **Manual trigger**\n - Run on-demand operations\n - Create customers, charges, etc.\n\nWhat's your use case?",
511
- createdAt: new Date('2025-01-06T09:02:00Z').toISOString(),
512
- },
513
- {
514
- id: '650e8400-e29b-41d4-a716-446655440023',
515
- conversationId,
516
- role: 'user',
517
- content: 'I want to receive notifications when a payment succeeds and create an invoice in my accounting software.',
518
- createdAt: new Date('2025-01-06T09:15:00Z').toISOString(),
519
- },
520
- {
521
- id: '650e8400-e29b-41d4-a716-446655440024',
522
- conversationId,
523
- role: 'assistant',
524
- content: "Perfect use case for webhooks! Here's the setup:\n\n**Step 1: n8n Webhook**\n- Add a Webhook node\n- Set method to POST\n- Copy the webhook URL\n\n**Step 2: Stripe Dashboard**\n- Go to Developers → Webhooks\n- Add endpoint with your n8n URL\n- Select `payment_intent.succeeded` event\n\n**Step 3: Process Payment Data**\n- Add a **Function** node to extract payment details\n- Parse customer, amount, currency\n\n**Step 4: Create Invoice**\n- Add your accounting software node (QuickBooks, Xero, etc.)\n- Map payment data to invoice fields\n\n**Step 5: Send Notification**\n- Add Email/Slack node for confirmation\n\nWant the specific code for the Function node?",
525
- createdAt: new Date('2025-01-06T09:18:00Z').toISOString(),
526
- },
527
- ],
666
+ convertMessageToDto(message) {
667
+ return {
668
+ id: message.id,
669
+ sessionId: message.sessionId,
670
+ type: message.type,
671
+ name: message.name,
672
+ content: message.content,
673
+ provider: message.provider,
674
+ model: message.model,
675
+ workflowId: message.workflowId,
676
+ executionId: message.executionId,
677
+ status: message.status,
678
+ createdAt: message.createdAt.toISOString(),
679
+ updatedAt: message.updatedAt.toISOString(),
680
+ previousMessageId: message.previousMessageId,
681
+ retryOfMessageId: message.retryOfMessageId,
682
+ revisionOfMessageId: message.revisionOfMessageId,
528
683
  };
529
- const messages = mockConversations[conversationId];
530
- if (messages) {
531
- return messages;
684
+ }
685
+ buildMessageHistory(messages, lastMessageId) {
686
+ if (!lastMessageId)
687
+ return [];
688
+ const visited = new Set();
689
+ const historyIds = [];
690
+ let current = lastMessageId;
691
+ while (current && !visited.has(current)) {
692
+ historyIds.unshift(current);
693
+ visited.add(current);
694
+ current = messages[current]?.previousMessageId ?? null;
695
+ }
696
+ const history = historyIds.flatMap((id) => messages[id] ?? []);
697
+ return history;
698
+ }
699
+ async deleteAllSessions() {
700
+ const result = await this.sessionRepository.deleteAll();
701
+ return result;
702
+ }
703
+ async updateSessionTitle(userId, sessionId, title) {
704
+ const session = await this.sessionRepository.getOneById(sessionId, userId);
705
+ if (!session) {
706
+ throw new not_found_error_1.NotFoundError('Session not found');
707
+ }
708
+ return await this.sessionRepository.updateChatTitle(sessionId, title);
709
+ }
710
+ async deleteSession(userId, sessionId) {
711
+ const session = await this.sessionRepository.getOneById(sessionId, userId);
712
+ if (!session) {
713
+ throw new not_found_error_1.NotFoundError('Session not found');
532
714
  }
533
- throw new not_found_error_1.NotFoundError(`Conversation not found. ID: ${conversationId}`);
715
+ await this.sessionRepository.deleteChatHubSession(sessionId);
534
716
  }
535
717
  };
536
718
  exports.ChatHubService = ChatHubService;
@@ -538,12 +720,15 @@ exports.ChatHubService = ChatHubService = __decorate([
538
720
  (0, di_1.Service)(),
539
721
  __metadata("design:paramtypes", [backend_common_1.Logger,
540
722
  credentials_service_1.CredentialsService,
541
- credentials_helper_1.CredentialsHelper,
723
+ execution_service_1.ExecutionService,
724
+ dynamic_node_parameters_service_1.DynamicNodeParametersService,
542
725
  db_1.ExecutionRepository,
543
726
  workflow_execution_service_1.WorkflowExecutionService,
544
727
  db_1.WorkflowRepository,
545
728
  db_1.ProjectRepository,
546
729
  db_1.SharedWorkflowRepository,
547
- active_executions_1.ActiveExecutions])
730
+ active_executions_1.ActiveExecutions,
731
+ chat_session_repository_1.ChatHubSessionRepository,
732
+ chat_message_repository_1.ChatHubMessageRepository])
548
733
  ], ChatHubService);
549
734
  //# sourceMappingURL=chat-hub.service.js.map