@n8n/chat 0.6.0 → 0.7.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/.eslintrc.cjs +0 -41
- package/README.md +27 -4
- package/build.config.js +21 -0
- package/package.json +8 -4
- package/resources/images/fullscreen.png +0 -0
- package/resources/images/windowed.png +0 -0
- package/resources/workflow-manual.json +238 -0
- package/resources/workflow.json +38 -212
- package/scripts/postbuild.js +20 -0
- package/src/App.vue +2 -2
- package/src/__stories__/App.stories.ts +3 -3
- package/src/__tests__/index.spec.ts +12 -17
- package/src/__tests__/utils/create.ts +1 -1
- package/src/__tests__/utils/fetch.ts +1 -1
- package/src/__tests__/utils/selectors.ts +1 -1
- package/src/api/generic.ts +5 -6
- package/src/api/message.ts +11 -5
- package/src/components/Chat.vue +19 -15
- package/src/components/ChatWindow.vue +3 -3
- package/src/components/GetStarted.vue +2 -2
- package/src/components/GetStartedFooter.vue +2 -2
- package/src/components/Input.vue +1 -1
- package/src/components/Layout.vue +2 -2
- package/src/components/Message.vue +6 -6
- package/src/components/MessageTyping.vue +2 -2
- package/src/components/MessagesList.vue +4 -4
- package/src/composables/useChat.ts +2 -2
- package/src/composables/useI18n.ts +1 -1
- package/src/composables/useOptions.ts +2 -2
- package/src/constants/defaults.ts +5 -1
- package/src/constants/symbols.ts +1 -1
- package/src/css/_tokens.scss +36 -0
- package/src/css/index.scss +1 -0
- package/src/event-buses/chatEventBus.ts +1 -1
- package/src/index.ts +4 -4
- package/src/main.scss +1 -36
- package/src/plugins/chat.ts +8 -4
- package/src/types/chat.ts +2 -2
- package/src/types/options.ts +5 -0
- package/tsconfig.json +1 -1
- package/vite.config.ts +3 -1
package/resources/workflow.json
CHANGED
|
@@ -1,245 +1,77 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "AI
|
|
2
|
+
"name": "Hosted n8n AI Chat",
|
|
3
3
|
"nodes": [
|
|
4
|
-
{
|
|
5
|
-
"parameters": {
|
|
6
|
-
"httpMethod": "POST",
|
|
7
|
-
"path": "513107b3-6f3a-4a1e-af21-659f0ed14183",
|
|
8
|
-
"responseMode": "responseNode",
|
|
9
|
-
"options": {
|
|
10
|
-
"domainAllowlist": "*.localhost"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"id": "51ab2689-647d-4cff-9d6f-0ba4df45e904",
|
|
14
|
-
"name": "Webhook",
|
|
15
|
-
"type": "n8n-nodes-base.webhook",
|
|
16
|
-
"typeVersion": 1,
|
|
17
|
-
"position": [
|
|
18
|
-
900,
|
|
19
|
-
200
|
|
20
|
-
],
|
|
21
|
-
"webhookId": "513107b3-6f3a-4a1e-af21-659f0ed14183"
|
|
22
|
-
},
|
|
23
4
|
{
|
|
24
5
|
"parameters": {
|
|
25
6
|
"options": {}
|
|
26
7
|
},
|
|
27
|
-
"id": "
|
|
28
|
-
"name": "Chat
|
|
8
|
+
"id": "4c109d13-62a2-4e23-9979-e50201db743d",
|
|
9
|
+
"name": "OpenAI Chat Model",
|
|
29
10
|
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
|
|
30
11
|
"typeVersion": 1,
|
|
31
12
|
"position": [
|
|
32
|
-
|
|
33
|
-
|
|
13
|
+
640,
|
|
14
|
+
540
|
|
34
15
|
],
|
|
35
16
|
"credentials": {
|
|
36
17
|
"openAiApi": {
|
|
37
|
-
"id": "
|
|
38
|
-
"name": "
|
|
18
|
+
"id": "cIIkOhl7tUX1KsL6",
|
|
19
|
+
"name": "OpenAi account"
|
|
39
20
|
}
|
|
40
21
|
}
|
|
41
22
|
},
|
|
42
23
|
{
|
|
43
24
|
"parameters": {
|
|
44
|
-
"sessionKey": "={{ $json.
|
|
25
|
+
"sessionKey": "={{ $json.sessionId }}"
|
|
45
26
|
},
|
|
46
|
-
"id": "
|
|
27
|
+
"id": "b416df7b-4802-462f-8f74-f0a71dc4c0be",
|
|
47
28
|
"name": "Window Buffer Memory",
|
|
48
29
|
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
|
|
49
30
|
"typeVersion": 1,
|
|
50
31
|
"position": [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
]
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"parameters": {
|
|
57
|
-
"simplifyOutput": false
|
|
58
|
-
},
|
|
59
|
-
"id": "d6721a60-159b-4a93-ac6b-b81e16d9f16f",
|
|
60
|
-
"name": "Memory Chat Retriever",
|
|
61
|
-
"type": "@n8n/n8n-nodes-langchain.memoryChatRetriever",
|
|
62
|
-
"typeVersion": 1,
|
|
63
|
-
"position": [
|
|
64
|
-
1780,
|
|
65
|
-
-40
|
|
32
|
+
340,
|
|
33
|
+
540
|
|
66
34
|
]
|
|
67
35
|
},
|
|
68
36
|
{
|
|
69
37
|
"parameters": {
|
|
70
|
-
"
|
|
38
|
+
"text": "={{ $json.chatInput }}",
|
|
39
|
+
"options": {}
|
|
71
40
|
},
|
|
72
|
-
"id": "
|
|
73
|
-
"name": "
|
|
74
|
-
"type": "@n8n/n8n-nodes-langchain.
|
|
75
|
-
"typeVersion": 1,
|
|
41
|
+
"id": "4de25807-a2ef-4453-900e-e00e0021ecdc",
|
|
42
|
+
"name": "AI Agent",
|
|
43
|
+
"type": "@n8n/n8n-nodes-langchain.agent",
|
|
44
|
+
"typeVersion": 1.1,
|
|
76
45
|
"position": [
|
|
77
|
-
|
|
78
|
-
|
|
46
|
+
620,
|
|
47
|
+
300
|
|
79
48
|
]
|
|
80
49
|
},
|
|
81
50
|
{
|
|
82
51
|
"parameters": {
|
|
52
|
+
"public": true,
|
|
83
53
|
"options": {
|
|
84
|
-
"
|
|
85
|
-
"responseHeaders": {
|
|
86
|
-
"entries": [
|
|
87
|
-
{
|
|
88
|
-
"name": "sessionId",
|
|
89
|
-
"value": "={{ $json.body.sessionId }}"
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
"name": "Access-Control-Allow-Headers",
|
|
93
|
-
"value": "*"
|
|
94
|
-
}
|
|
95
|
-
]
|
|
96
|
-
}
|
|
54
|
+
"loadPreviousSession": "memory"
|
|
97
55
|
}
|
|
98
56
|
},
|
|
99
|
-
"id": "
|
|
100
|
-
"name": "
|
|
101
|
-
"type": "n8n-nodes-
|
|
57
|
+
"id": "5a9612ae-51c1-4be2-bd8b-8556872d1149",
|
|
58
|
+
"name": "Chat Trigger",
|
|
59
|
+
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
|
|
102
60
|
"typeVersion": 1,
|
|
103
61
|
"position": [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
]
|
|
107
|
-
|
|
108
|
-
{
|
|
109
|
-
"parameters": {
|
|
110
|
-
"dataType": "string",
|
|
111
|
-
"value1": "={{ $json.body.action }}",
|
|
112
|
-
"rules": {
|
|
113
|
-
"rules": [
|
|
114
|
-
{
|
|
115
|
-
"value2": "loadPreviousSession"
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
"value2": "sendMessage",
|
|
119
|
-
"output": 1
|
|
120
|
-
}
|
|
121
|
-
]
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
"id": "fc4ad994-5f38-4dce-b1e5-397acc512687",
|
|
125
|
-
"name": "Chatbot Action",
|
|
126
|
-
"type": "n8n-nodes-base.switch",
|
|
127
|
-
"typeVersion": 1,
|
|
128
|
-
"position": [
|
|
129
|
-
1320,
|
|
130
|
-
200
|
|
131
|
-
]
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
"parameters": {
|
|
135
|
-
"jsCode": "const response = { data: [] };\n\nfor (const item of $input.all()) {\n response.data.push(item.json);\n}\n\nreturn {\n json: response,\n pairedItem: 0\n};"
|
|
136
|
-
},
|
|
137
|
-
"id": "e1a80bdc-411a-42df-88dd-36915b1ae8f4",
|
|
138
|
-
"name": "Code",
|
|
139
|
-
"type": "n8n-nodes-base.code",
|
|
140
|
-
"typeVersion": 2,
|
|
141
|
-
"position": [
|
|
142
|
-
2160,
|
|
143
|
-
-40
|
|
144
|
-
]
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
"parameters": {
|
|
148
|
-
"text": "={{ $json.body.message }}",
|
|
149
|
-
"options": {}
|
|
150
|
-
},
|
|
151
|
-
"id": "f28f5c00-c742-41d5-8ddb-f0f59ab111a3",
|
|
152
|
-
"name": "Agent",
|
|
153
|
-
"type": "@n8n/n8n-nodes-langchain.agent",
|
|
154
|
-
"typeVersion": 1,
|
|
155
|
-
"position": [
|
|
156
|
-
1780,
|
|
157
|
-
340
|
|
158
|
-
]
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
"parameters": {
|
|
162
|
-
"jsCode": "// Loop over input items and add a new field called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.body = JSON.parse(item.json.body);\n}\n\nreturn $input.all();"
|
|
163
|
-
},
|
|
164
|
-
"id": "415c071b-18b2-4ac5-8634-e3d939bf36ac",
|
|
165
|
-
"name": "Transform request body",
|
|
166
|
-
"type": "n8n-nodes-base.code",
|
|
167
|
-
"typeVersion": 2,
|
|
168
|
-
"position": [
|
|
169
|
-
1120,
|
|
170
|
-
200
|
|
171
|
-
]
|
|
62
|
+
340,
|
|
63
|
+
300
|
|
64
|
+
],
|
|
65
|
+
"webhookId": "f406671e-c954-4691-b39a-66c90aa2f103"
|
|
172
66
|
}
|
|
173
67
|
],
|
|
174
68
|
"pinData": {},
|
|
175
69
|
"connections": {
|
|
176
|
-
"
|
|
177
|
-
"main": [
|
|
178
|
-
[
|
|
179
|
-
{
|
|
180
|
-
"node": "Transform request body",
|
|
181
|
-
"type": "main",
|
|
182
|
-
"index": 0
|
|
183
|
-
}
|
|
184
|
-
]
|
|
185
|
-
]
|
|
186
|
-
},
|
|
187
|
-
"Memory Chat Retriever": {
|
|
188
|
-
"main": [
|
|
189
|
-
[
|
|
190
|
-
{
|
|
191
|
-
"node": "Code",
|
|
192
|
-
"type": "main",
|
|
193
|
-
"index": 0
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
]
|
|
197
|
-
},
|
|
198
|
-
"Memory Chat Retriever Window Buffer Memory": {
|
|
199
|
-
"ai_memory": [
|
|
200
|
-
[
|
|
201
|
-
{
|
|
202
|
-
"node": "Memory Chat Retriever",
|
|
203
|
-
"type": "ai_memory",
|
|
204
|
-
"index": 0
|
|
205
|
-
}
|
|
206
|
-
]
|
|
207
|
-
]
|
|
208
|
-
},
|
|
209
|
-
"Chatbot Action": {
|
|
210
|
-
"main": [
|
|
211
|
-
[
|
|
212
|
-
{
|
|
213
|
-
"node": "Memory Chat Retriever",
|
|
214
|
-
"type": "main",
|
|
215
|
-
"index": 0
|
|
216
|
-
}
|
|
217
|
-
],
|
|
218
|
-
[
|
|
219
|
-
{
|
|
220
|
-
"node": "Agent",
|
|
221
|
-
"type": "main",
|
|
222
|
-
"index": 0
|
|
223
|
-
}
|
|
224
|
-
]
|
|
225
|
-
]
|
|
226
|
-
},
|
|
227
|
-
"Code": {
|
|
228
|
-
"main": [
|
|
229
|
-
[
|
|
230
|
-
{
|
|
231
|
-
"node": "Respond to Webhook",
|
|
232
|
-
"type": "main",
|
|
233
|
-
"index": 0
|
|
234
|
-
}
|
|
235
|
-
]
|
|
236
|
-
]
|
|
237
|
-
},
|
|
238
|
-
"Chat OpenAI": {
|
|
70
|
+
"OpenAI Chat Model": {
|
|
239
71
|
"ai_languageModel": [
|
|
240
72
|
[
|
|
241
73
|
{
|
|
242
|
-
"node": "Agent",
|
|
74
|
+
"node": "AI Agent",
|
|
243
75
|
"type": "ai_languageModel",
|
|
244
76
|
"index": 0
|
|
245
77
|
}
|
|
@@ -250,29 +82,23 @@
|
|
|
250
82
|
"ai_memory": [
|
|
251
83
|
[
|
|
252
84
|
{
|
|
253
|
-
"node": "Agent",
|
|
85
|
+
"node": "AI Agent",
|
|
254
86
|
"type": "ai_memory",
|
|
255
87
|
"index": 0
|
|
256
|
-
}
|
|
257
|
-
]
|
|
258
|
-
]
|
|
259
|
-
},
|
|
260
|
-
"Agent": {
|
|
261
|
-
"main": [
|
|
262
|
-
[
|
|
88
|
+
},
|
|
263
89
|
{
|
|
264
|
-
"node": "
|
|
265
|
-
"type": "
|
|
90
|
+
"node": "Chat Trigger",
|
|
91
|
+
"type": "ai_memory",
|
|
266
92
|
"index": 0
|
|
267
93
|
}
|
|
268
94
|
]
|
|
269
95
|
]
|
|
270
96
|
},
|
|
271
|
-
"
|
|
97
|
+
"Chat Trigger": {
|
|
272
98
|
"main": [
|
|
273
99
|
[
|
|
274
100
|
{
|
|
275
|
-
"node": "
|
|
101
|
+
"node": "AI Agent",
|
|
276
102
|
"type": "main",
|
|
277
103
|
"index": 0
|
|
278
104
|
}
|
|
@@ -284,8 +110,8 @@
|
|
|
284
110
|
"settings": {
|
|
285
111
|
"executionOrder": "v1"
|
|
286
112
|
},
|
|
287
|
-
"versionId": "
|
|
288
|
-
"id": "
|
|
113
|
+
"versionId": "6076136f-fdb4-48d9-b483-d1c24c95ef9e",
|
|
114
|
+
"id": "zaBHnDtj22BzEQ6K",
|
|
289
115
|
"meta": {
|
|
290
116
|
"instanceId": "374b43d8b8d6299cc777811a4ad220fc688ee2d54a308cfb0de4450a5233ca9e"
|
|
291
117
|
},
|
package/scripts/postbuild.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const shelljs = require('shelljs');
|
|
3
|
+
const glob = require('fast-glob');
|
|
3
4
|
|
|
4
5
|
const rootDirPath = path.resolve(__dirname, '..');
|
|
5
6
|
const n8nRootDirPath = path.resolve(rootDirPath, '..', '..', '..');
|
|
6
7
|
const distDirPath = path.resolve(rootDirPath, 'dist');
|
|
8
|
+
const srcDirPath = path.resolve(rootDirPath, 'src');
|
|
9
|
+
const libDirPath = path.resolve(rootDirPath, 'tmp', 'lib');
|
|
10
|
+
const cjsDirPath = path.resolve(rootDirPath, 'tmp', 'cjs');
|
|
7
11
|
|
|
8
12
|
const packageJsonFilePath = path.resolve(rootDirPath, 'package.json');
|
|
9
13
|
const readmeFilePath = path.resolve(rootDirPath, 'README.md');
|
|
@@ -14,3 +18,19 @@ shelljs.cp(readmeFilePath, distDirPath);
|
|
|
14
18
|
shelljs.cp(licenseFilePath, distDirPath);
|
|
15
19
|
|
|
16
20
|
shelljs.mv(path.resolve(distDirPath, 'src'), path.resolve(distDirPath, 'types'));
|
|
21
|
+
|
|
22
|
+
function moveFiles(files, from, to) {
|
|
23
|
+
files.forEach((file) => {
|
|
24
|
+
const toFile = file.replace(from, to);
|
|
25
|
+
shelljs.mkdir('-p', path.dirname(toFile));
|
|
26
|
+
shelljs.mv(file, toFile);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const cjsFiles = glob.sync(path.resolve(cjsDirPath, '**', '*'));
|
|
31
|
+
moveFiles(cjsFiles, 'tmp/cjs', 'dist');
|
|
32
|
+
shelljs.rm('-rf', cjsDirPath);
|
|
33
|
+
|
|
34
|
+
const libFiles = glob.sync(path.resolve(libDirPath, '**/*'));
|
|
35
|
+
moveFiles(libFiles, 'tmp/lib', 'dist');
|
|
36
|
+
shelljs.rm('-rf', libDirPath);
|
package/src/App.vue
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { Chat, ChatWindow } from '@/components';
|
|
3
2
|
import { computed, onMounted } from 'vue';
|
|
4
3
|
import hljs from 'highlight.js/lib/core';
|
|
5
4
|
import hljsXML from 'highlight.js/lib/languages/xml';
|
|
6
5
|
import hljsJavascript from 'highlight.js/lib/languages/javascript';
|
|
7
|
-
import {
|
|
6
|
+
import { Chat, ChatWindow } from '@n8n/chat/components';
|
|
7
|
+
import { useOptions } from '@n8n/chat/composables';
|
|
8
8
|
|
|
9
9
|
defineProps({});
|
|
10
10
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
2
2
|
import type { StoryObj } from '@storybook/vue3';
|
|
3
|
-
import type { ChatOptions } from '@/types';
|
|
4
|
-
import { createChat } from '@/index';
|
|
5
3
|
import { onMounted } from 'vue';
|
|
4
|
+
import type { ChatOptions } from '@n8n/chat/types';
|
|
5
|
+
import { createChat } from '@n8n/chat/index';
|
|
6
6
|
|
|
7
|
-
const webhookUrl = 'http://localhost:5678/webhook/
|
|
7
|
+
const webhookUrl = 'http://localhost:5678/webhook/f406671e-c954-4691-b39a-66c90aa2f103/chat';
|
|
8
8
|
|
|
9
9
|
const meta = {
|
|
10
10
|
title: 'Chat',
|
|
@@ -14,9 +14,8 @@ import {
|
|
|
14
14
|
getChatWrapper,
|
|
15
15
|
getGetStartedButton,
|
|
16
16
|
getMountingTarget,
|
|
17
|
-
} from '
|
|
18
|
-
import { createChat } from '
|
|
19
|
-
import { useChat } from '@/composables';
|
|
17
|
+
} from '@n8n/chat/__tests__/utils';
|
|
18
|
+
import { createChat } from '@n8n/chat/index';
|
|
20
19
|
|
|
21
20
|
describe('createChat()', () => {
|
|
22
21
|
let app: ReturnType<typeof createChat>;
|
|
@@ -77,6 +76,7 @@ describe('createChat()', () => {
|
|
|
77
76
|
|
|
78
77
|
app = createChat({
|
|
79
78
|
mode: 'fullscreen',
|
|
79
|
+
showWelcomeScreen: true,
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
const getStartedButton = getGetStartedButton();
|
|
@@ -85,7 +85,9 @@ describe('createChat()', () => {
|
|
|
85
85
|
expect(fetchSpy.mock.calls[0][1]).toEqual(
|
|
86
86
|
expect.objectContaining({
|
|
87
87
|
method: 'POST',
|
|
88
|
-
headers: {
|
|
88
|
+
headers: {
|
|
89
|
+
'Content-Type': 'application/json',
|
|
90
|
+
},
|
|
89
91
|
body: expect.stringContaining('"action":"loadPreviousSession"') as unknown,
|
|
90
92
|
mode: 'cors',
|
|
91
93
|
cache: 'no-cache',
|
|
@@ -112,9 +114,6 @@ describe('createChat()', () => {
|
|
|
112
114
|
await fireEvent.click(trigger as HTMLElement);
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
const getStartedButton = getGetStartedButton();
|
|
116
|
-
await fireEvent.click(getStartedButton as HTMLElement);
|
|
117
|
-
|
|
118
117
|
expect(getChatMessages().length).toBe(initialMessages.length);
|
|
119
118
|
expect(getChatMessageByText(initialMessages[0])).toBeInTheDocument();
|
|
120
119
|
expect(getChatMessageByText(initialMessages[1])).toBeInTheDocument();
|
|
@@ -144,12 +143,10 @@ describe('createChat()', () => {
|
|
|
144
143
|
}
|
|
145
144
|
|
|
146
145
|
expect(getChatMessageTyping()).not.toBeInTheDocument();
|
|
147
|
-
|
|
148
|
-
const getStartedButton = getGetStartedButton();
|
|
149
|
-
await fireEvent.click(getStartedButton as HTMLElement);
|
|
150
|
-
|
|
151
146
|
expect(getChatMessages().length).toBe(2);
|
|
152
147
|
|
|
148
|
+
await waitFor(() => expect(getChatInputTextarea()).toBeInTheDocument());
|
|
149
|
+
|
|
153
150
|
const textarea = getChatInputTextarea();
|
|
154
151
|
const sendButton = getChatInputSendButton();
|
|
155
152
|
await fireEvent.update(textarea as HTMLElement, input);
|
|
@@ -159,7 +156,9 @@ describe('createChat()', () => {
|
|
|
159
156
|
expect(fetchSpy.mock.calls[1][1]).toEqual(
|
|
160
157
|
expect.objectContaining({
|
|
161
158
|
method: 'POST',
|
|
162
|
-
headers: {
|
|
159
|
+
headers: {
|
|
160
|
+
'Content-Type': 'application/json',
|
|
161
|
+
},
|
|
163
162
|
body: expect.stringMatching(/"action":"sendMessage"/) as unknown,
|
|
164
163
|
mode: 'cors',
|
|
165
164
|
cache: 'no-cache',
|
|
@@ -182,9 +181,6 @@ describe('createChat()', () => {
|
|
|
182
181
|
const input = 'Teach me javascript!';
|
|
183
182
|
const output = '# Code\n```js\nconsole.log("Hello World!");\n```';
|
|
184
183
|
|
|
185
|
-
const chatStore = useChat();
|
|
186
|
-
console.log(chatStore);
|
|
187
|
-
|
|
188
184
|
const fetchSpy = vi.spyOn(window, 'fetch');
|
|
189
185
|
fetchSpy
|
|
190
186
|
.mockImplementationOnce(createFetchResponse(createGetLatestMessagesResponse))
|
|
@@ -199,8 +195,7 @@ describe('createChat()', () => {
|
|
|
199
195
|
await fireEvent.click(trigger as HTMLElement);
|
|
200
196
|
}
|
|
201
197
|
|
|
202
|
-
|
|
203
|
-
await fireEvent.click(getStartedButton as HTMLElement);
|
|
198
|
+
await waitFor(() => expect(getChatInputTextarea()).toBeInTheDocument());
|
|
204
199
|
|
|
205
200
|
const textarea = getChatInputTextarea();
|
|
206
201
|
const sendButton = getChatInputSendButton();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { screen } from '@testing-library/vue';
|
|
2
|
-
import { defaultMountingTarget } from '
|
|
2
|
+
import { defaultMountingTarget } from '@n8n/chat/constants';
|
|
3
3
|
|
|
4
4
|
export function getMountingTarget(target = defaultMountingTarget) {
|
|
5
5
|
return document.querySelector(target);
|
package/src/api/generic.ts
CHANGED
|
@@ -10,6 +10,7 @@ export async function authenticatedFetch<T>(...args: Parameters<typeof fetch>):
|
|
|
10
10
|
mode: 'cors',
|
|
11
11
|
cache: 'no-cache',
|
|
12
12
|
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
13
14
|
...(accessToken ? { authorization: `Bearer ${accessToken}` } : {}),
|
|
14
15
|
...args[1]?.headers,
|
|
15
16
|
},
|
|
@@ -18,14 +19,12 @@ export async function authenticatedFetch<T>(...args: Parameters<typeof fetch>):
|
|
|
18
19
|
return (await response.json()) as Promise<T>;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export async function get<T>(
|
|
22
|
-
url: string,
|
|
23
|
-
query: Record<string, string> = {},
|
|
24
|
-
options: RequestInit = {},
|
|
25
|
-
) {
|
|
22
|
+
export async function get<T>(url: string, query: object = {}, options: RequestInit = {}) {
|
|
26
23
|
let resolvedUrl = url;
|
|
27
24
|
if (Object.keys(query).length > 0) {
|
|
28
|
-
resolvedUrl = `${resolvedUrl}?${new URLSearchParams(
|
|
25
|
+
resolvedUrl = `${resolvedUrl}?${new URLSearchParams(
|
|
26
|
+
query as Record<string, string>,
|
|
27
|
+
).toString()}`;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
return authenticatedFetch<T>(resolvedUrl, { ...options, method: 'GET' });
|
package/src/api/message.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { get, post } from '
|
|
2
|
-
import type {
|
|
1
|
+
import { get, post } from '@n8n/chat/api/generic';
|
|
2
|
+
import type {
|
|
3
|
+
ChatOptions,
|
|
4
|
+
LoadPreviousSessionResponse,
|
|
5
|
+
SendMessageResponse,
|
|
6
|
+
} from '@n8n/chat/types';
|
|
3
7
|
|
|
4
8
|
export async function loadPreviousSession(sessionId: string, options: ChatOptions) {
|
|
5
9
|
const method = options.webhookConfig?.method === 'POST' ? post : get;
|
|
@@ -7,7 +11,8 @@ export async function loadPreviousSession(sessionId: string, options: ChatOption
|
|
|
7
11
|
`${options.webhookUrl}`,
|
|
8
12
|
{
|
|
9
13
|
action: 'loadPreviousSession',
|
|
10
|
-
sessionId,
|
|
14
|
+
[options.chatSessionKey as string]: sessionId,
|
|
15
|
+
...(options.metadata ? { metadata: options.metadata } : {}),
|
|
11
16
|
},
|
|
12
17
|
{
|
|
13
18
|
headers: options.webhookConfig?.headers,
|
|
@@ -21,8 +26,9 @@ export async function sendMessage(message: string, sessionId: string, options: C
|
|
|
21
26
|
`${options.webhookUrl}`,
|
|
22
27
|
{
|
|
23
28
|
action: 'sendMessage',
|
|
24
|
-
sessionId,
|
|
25
|
-
message,
|
|
29
|
+
[options.chatSessionKey as string]: sessionId,
|
|
30
|
+
[options.chatInputKey as string]: message,
|
|
31
|
+
...(options.metadata ? { metadata: options.metadata } : {}),
|
|
26
32
|
},
|
|
27
33
|
{
|
|
28
34
|
headers: options.webhookConfig?.headers,
|
package/src/components/Chat.vue
CHANGED
|
@@ -1,44 +1,48 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import Layout from '@/components/Layout.vue';
|
|
3
|
-
import GetStarted from '@/components/GetStarted.vue';
|
|
4
|
-
import GetStartedFooter from '@/components/GetStartedFooter.vue';
|
|
5
|
-
import MessagesList from '@/components/MessagesList.vue';
|
|
6
|
-
import Input from '@/components/Input.vue';
|
|
7
2
|
import { nextTick, onMounted } from 'vue';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
3
|
+
import Layout from '@n8n/chat/components/Layout.vue';
|
|
4
|
+
import GetStarted from '@n8n/chat/components/GetStarted.vue';
|
|
5
|
+
import GetStartedFooter from '@n8n/chat/components/GetStartedFooter.vue';
|
|
6
|
+
import MessagesList from '@n8n/chat/components/MessagesList.vue';
|
|
7
|
+
import Input from '@n8n/chat/components/Input.vue';
|
|
8
|
+
import { useI18n, useChat, useOptions } from '@n8n/chat/composables';
|
|
9
|
+
import { chatEventBus } from '@n8n/chat/event-buses';
|
|
10
10
|
|
|
11
11
|
const { t } = useI18n();
|
|
12
12
|
const chatStore = useChat();
|
|
13
13
|
|
|
14
14
|
const { messages, currentSessionId } = chatStore;
|
|
15
|
+
const { options } = useOptions();
|
|
15
16
|
|
|
16
|
-
async function
|
|
17
|
-
|
|
17
|
+
async function getStarted() {
|
|
18
|
+
void chatStore.startNewSession();
|
|
18
19
|
void nextTick(() => {
|
|
19
20
|
chatEventBus.emit('scrollToBottom');
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
async function
|
|
24
|
-
|
|
24
|
+
async function initialize() {
|
|
25
|
+
await chatStore.loadPreviousSession();
|
|
25
26
|
void nextTick(() => {
|
|
26
27
|
chatEventBus.emit('scrollToBottom');
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
onMounted(() => {
|
|
31
|
-
|
|
31
|
+
onMounted(async () => {
|
|
32
|
+
await initialize();
|
|
33
|
+
if (!options.showWelcomeScreen && !currentSessionId.value) {
|
|
34
|
+
await getStarted();
|
|
35
|
+
}
|
|
32
36
|
});
|
|
33
37
|
</script>
|
|
34
38
|
|
|
35
39
|
<template>
|
|
36
40
|
<Layout class="chat-wrapper">
|
|
37
|
-
<template #header
|
|
41
|
+
<template #header>
|
|
38
42
|
<h1>{{ t('title') }}</h1>
|
|
39
43
|
<p>{{ t('subtitle') }}</p>
|
|
40
44
|
</template>
|
|
41
|
-
<GetStarted v-if="!currentSessionId" @click:button="getStarted" />
|
|
45
|
+
<GetStarted v-if="!currentSessionId && options.showWelcomeScreen" @click:button="getStarted" />
|
|
42
46
|
<MessagesList v-else :messages="messages" />
|
|
43
47
|
<template #footer>
|
|
44
48
|
<Input v-if="currentSessionId" />
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import IconChat from 'virtual:icons/mdi/chat';
|
|
4
4
|
// eslint-disable-next-line import/no-unresolved
|
|
5
5
|
import IconChevronDown from 'virtual:icons/mdi/chevron-down';
|
|
6
|
-
import Chat from '@/components/Chat.vue';
|
|
7
6
|
import { nextTick, ref } from 'vue';
|
|
8
|
-
import
|
|
7
|
+
import Chat from '@n8n/chat/components/Chat.vue';
|
|
8
|
+
import { chatEventBus } from '@n8n/chat/event-buses';
|
|
9
9
|
|
|
10
10
|
const isOpen = ref(false);
|
|
11
11
|
|
|
@@ -23,7 +23,7 @@ function toggle() {
|
|
|
23
23
|
<template>
|
|
24
24
|
<div class="chat-window-wrapper">
|
|
25
25
|
<Transition name="chat-window-transition">
|
|
26
|
-
<div class="chat-window"
|
|
26
|
+
<div v-show="isOpen" class="chat-window">
|
|
27
27
|
<Chat />
|
|
28
28
|
</div>
|
|
29
29
|
</Transition>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { useI18n } from '
|
|
3
|
-
import PoweredBy from '
|
|
2
|
+
import { useI18n } from '@n8n/chat/composables';
|
|
3
|
+
import PoweredBy from '@n8n/chat/components/PoweredBy.vue';
|
|
4
4
|
|
|
5
5
|
const { t, te } = useI18n();
|
|
6
6
|
</script>
|
package/src/components/Input.vue
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
// eslint-disable-next-line import/no-unresolved
|
|
3
3
|
import IconSend from 'virtual:icons/mdi/send';
|
|
4
|
-
import { useI18n, useChat } from '@/composables';
|
|
5
4
|
import { computed, ref } from 'vue';
|
|
5
|
+
import { useI18n, useChat } from '@n8n/chat/composables';
|
|
6
6
|
|
|
7
7
|
const chatStore = useChat();
|
|
8
8
|
const { waitingForResponse } = chatStore;
|