@quidgest/chatbot 0.0.2 → 0.0.4

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.
@@ -16,25 +16,59 @@
16
16
  flex: 1;
17
17
  }
18
18
 
19
+ .clear-btn {
20
+ color: red;
21
+ }
22
+
19
23
  &__content {
20
24
  background-color: white;
21
25
  border: 1px solid #eaebec;
22
26
  height: 100%;
23
27
  width: 100%;
24
- padding: 2rem 1rem;
25
28
  display: flex;
26
29
  flex-direction: column;
27
30
  overflow: auto;
28
31
  gap: 1.5rem;
29
32
  }
30
33
 
31
- &__message-wrapper {
34
+ &__messages-container {
35
+ display: flex;
36
+ flex-direction: column;
37
+ padding: 0 1rem 2rem 1rem;
38
+ gap: 1.5rem;
39
+ }
40
+
41
+ &__messages-wrapper {
32
42
  display: flex;
33
43
  max-width: 100%;
34
44
  gap: 0.2rem;
45
+
46
+ &_right {
47
+ justify-content: flex-end;
48
+ }
49
+ }
50
+
51
+ &__tools {
52
+ display: flex;
53
+ flex-direction: row;
54
+ justify-content: end;
55
+ max-width: 100%;
56
+ }
57
+
58
+ &__message-wrapper {
59
+ display: flex;
60
+ flex-direction: column;
61
+ gap: 0.2rem;
62
+ }
63
+
64
+ &__message-container {
65
+ display: flex;
66
+ flex-direction: row;
67
+ align-items: center;
68
+ gap: 0.5rem;
35
69
  }
36
70
 
37
- &__message-wrapper_right {
71
+ &__messages-wrapper_right {
38
72
  justify-content: flex-end;
39
73
  }
40
74
 
@@ -45,63 +79,23 @@
45
79
  }
46
80
 
47
81
  &__message {
48
- position: relative;
49
- margin: 0rem 0.5rem;
50
- max-width: 75%;
82
+ min-width: 4rem;
51
83
  display: flex;
52
84
  align-items: center;
53
85
  padding: 0.3rem 0.5rem;
54
86
  background-color: #eaebec;
55
87
  white-space: normal;
88
+ min-height: 2rem;
56
89
  }
57
90
 
58
- &__message-wrapper_right .q-chatbot__message {
91
+ &__messages-wrapper_right .q-chatbot__message {
59
92
  background-color: rgba(#018bd2, 20%);
60
93
  }
61
94
 
62
95
  &__sender {
63
96
  white-space: nowrap;
64
- position: absolute;
65
97
  color: #7c858d;
66
98
  font-size: 0.7rem;
67
- left: 0;
68
- top: -1rem;
69
- }
70
-
71
- &__timestamp {
72
- white-space: nowrap;
73
- position: absolute;
74
- color: #7c858d;
75
- font-size: 0.7rem;
76
- right: 0;
77
- top: -1rem;
78
- }
79
-
80
- &__message-loading {
81
- display: flex;
82
- justify-content: center;
83
- align-items: center;
84
- gap: 0.3rem;
85
-
86
- div {
87
- background-color: rgba(#018bd2, 20%);
88
- border-radius: 50%;
89
- width: 0.5rem;
90
- height: 0.5rem;
91
- animation: dotPulse 1.5s infinite;
92
-
93
- &:nth-child(1) {
94
- animation-delay: 0s;
95
- }
96
-
97
- &:nth-child(2) {
98
- animation-delay: 0.5s;
99
- }
100
-
101
- &:nth-child(3) {
102
- animation-delay: 1s;
103
- }
104
- }
105
99
  }
106
100
  }
107
101
 
@@ -0,0 +1,81 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import ChatBotIcon from '@/assets/chatbot.png'
4
+ import { QLineLoader } from '@quidgest/ui'
5
+
6
+ export interface CBMessageProps {
7
+ /*
8
+ * Sender of the message
9
+ */
10
+ sender?: 'bot' | 'user'
11
+
12
+ /*
13
+ * Message to be displayed
14
+ */
15
+ message?: string
16
+
17
+ /*
18
+ * Date of when the message was sent
19
+ */
20
+ date?: Date
21
+
22
+ /*
23
+ * If the message is loading
24
+ */
25
+ loading?: boolean
26
+ }
27
+
28
+ const props = withDefaults(defineProps<CBMessageProps>(), {
29
+ sender: 'user',
30
+ date: () => new Date()
31
+ })
32
+
33
+ const senderName = computed(() => {
34
+ return props.sender === 'bot' ? 'GenioBot' : 'You'
35
+ })
36
+
37
+ const getLocaleDate = computed(() => {
38
+ return props.date.toLocaleString()
39
+ })
40
+
41
+ const messageHeader = computed(() => {
42
+ return `${senderName.value} ${getLocaleDate.value}`
43
+ })
44
+ </script>
45
+
46
+ <template>
47
+ <div class="q-chatbot__message-container">
48
+
49
+ <!-- Chatbot Image -->
50
+ <img
51
+ v-if="props.sender === 'bot'"
52
+ :src="ChatBotIcon"
53
+ alt="Chatbot"
54
+ class="q-chatbot__profile" />
55
+
56
+ <div class="q-chatbot__message-wrapper">
57
+ <!-- Message header -->
58
+ <div
59
+ v-if="!loading"
60
+ class="q-chatbot__sender">
61
+ {{ messageHeader }}
62
+ </div>
63
+
64
+ <!-- Message body -->
65
+ <div class="q-chatbot__message">
66
+ <QLineLoader v-if="loading" />
67
+ <template v-else>
68
+ <div
69
+ class="q-chatbot__text"
70
+ v-if="props.sender == 'bot'"
71
+ v-html="props.message"></div>
72
+ <div
73
+ class="q-chatbot__text"
74
+ v-else>
75
+ {{ props.message }}
76
+ </div>
77
+ </template>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </template>
@@ -3,6 +3,8 @@
3
3
  import type { Ref } from 'vue'
4
4
  import Axios from 'axios'
5
5
  import type { AxiosResponse } from 'axios'
6
+ import { CBMessage } from '@/components'
7
+
6
8
  import {
7
9
  QButton,
8
10
  QTextField,
@@ -11,13 +13,13 @@
11
13
  } from '@quidgest/ui'
12
14
 
13
15
  import type { ResourceStrings } from '@/types/texts.type'
14
- import type { ChatBotMessage } from '@/types/message.type'
15
- import ChatBotIcon from '@/assets/chatbot.png'
16
+ import type { ChatBotMessage, ChatBotMessageContent } from '@/types/message.type'
17
+
16
18
 
17
19
  let messages: Ref<ChatBotMessage[]> = ref([])
18
20
  let msgHistoryStack: string[] = []
19
21
  let nextMessageId: number = 1
20
- let userPrompt: Ref<string> = ref('');
22
+ let userPrompt: Ref<string> = ref('')
21
23
  let isChatDisabled: boolean = false
22
24
  let isLoading: boolean = false
23
25
 
@@ -40,6 +42,11 @@
40
42
  * Genio username
41
43
  */
42
44
  username: string
45
+
46
+ /**
47
+ * Project aplication path
48
+ */
49
+ projectPath: string
43
50
  }
44
51
 
45
52
  const props = withDefaults(defineProps<ChatBotProps>(), {
@@ -62,66 +69,74 @@
62
69
  nextTick(scrollChatToBottom)
63
70
  })
64
71
 
65
- const getParsedEndpoint = computed(() => {
66
- try {
67
- let url = new URL(props.apiEndpoint).href
68
-
69
- if (url.charAt(url.length - 1) == '/') {
70
- url = url.slice(0, -1)
71
- }
72
- return url
73
- } catch (ex) {
74
- addChatMessage(props.texts.botIsSick)
75
-
76
- isChatDisabled = true
77
- throw Error('Could not parse Endpoint URL:\n' + ex)
78
- }
79
- })
80
-
81
- const sortedMessages = computed(() => {
82
- return messages.value.toSorted((a: ChatBotMessage, b: ChatBotMessage) => {
83
- const diff = new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
84
- return diff !== 0 ? diff : a.sender === 'user' ? -1 : 1
85
- })
86
- })
87
-
88
72
  const userMessages = computed(() => {
89
73
  return messages.value.filter((m: ChatBotMessage) => m.sender === 'user')
90
74
  })
75
+
76
+ function setDisabledState(state: boolean) {
77
+ isChatDisabled = state
78
+ }
91
79
 
92
80
  function initChat() {
93
- Axios.post(getParsedEndpoint.value + '/auth/login', {
81
+ Axios.post(props.apiEndpoint + '/auth/login', {
94
82
  username: props.username,
95
83
  password: 'test'
96
84
  })
97
85
  .then((response: AxiosResponse) => {
98
86
  if (response.status != 200 || !response.data.success) {
99
- isChatDisabled = true
87
+ setDisabledState(true)
100
88
  addChatMessage(props.texts.loginError)
101
89
  return console.log(
102
90
  `Unsuccessful login, endpoint gave status ${response.status}`
103
91
  )
104
92
  }
105
93
 
106
- sendInitialMessage()
94
+ loadChatData()
107
95
  })
108
96
  .catch((error: Error) => {
109
- if (error) {
110
- isChatDisabled = true
97
+ setDisabledState(true)
98
+ addChatMessage(props.texts.loginError)
99
+ console.log(
100
+ 'The following error ocurred while trying to login: \n' +
101
+ error
102
+ )
103
+ })
104
+ }
105
+
106
+ function loadChatData() {
107
+ Axios.post(props.apiEndpoint + '/prompt/load', {
108
+ username: props.username,
109
+ project: props.projectPath
110
+ })
111
+ .then((response: AxiosResponse) => {
112
+ if (response.status != 200 || !response.data.success) {
113
+ setDisabledState(true)
111
114
  addChatMessage(props.texts.loginError)
112
- console.log(
113
- 'The following error ocurred while trying to login: \n' +
114
- error
115
+ return console.log(
116
+ `Unsuccessful load, endpoint gave status ${response.status}`
115
117
  )
116
118
  }
119
+
120
+ sendInitialMessage()
121
+ response.data.history.forEach((message: ChatBotMessageContent) => {
122
+ addChatMessage(message.content, message.type === "ai" ? "bot" : "user")
123
+ })
124
+ })
125
+ .catch((error: Error) => {
126
+ setDisabledState(true)
127
+ addChatMessage(props.texts.loginError)
128
+ console.log(
129
+ 'The following error ocurred while trying to login: \n' +
130
+ error
131
+ )
117
132
  })
118
133
  }
119
134
 
120
135
  function addChatMessage(message: string, sender: 'bot' | 'user' = 'bot') {
121
136
  messages.value.push({
122
137
  id: nextMessageId++,
123
- text: message,
124
- timestamp: new Date(),
138
+ message,
139
+ date: new Date(),
125
140
  sender: sender
126
141
  })
127
142
  }
@@ -141,9 +156,7 @@
141
156
  msgHistoryStack = []
142
157
  userPrompt.value = ''
143
158
  isLoading = false
144
- isChatDisabled = false
145
-
146
- sendInitialMessage()
159
+ setDisabledState(false)
147
160
  }
148
161
 
149
162
  function scrollChatToBottom() {
@@ -177,13 +190,13 @@
177
190
 
178
191
  //Save current prompt (even if modified) & update input
179
192
  msgHistoryStack.push(userPrompt.value)
180
- userPrompt.value = lastMsgObj.text
193
+ userPrompt.value = lastMsgObj.message
181
194
 
182
195
  //Set the cursor to the end of text
183
196
  nextTick(() =>
184
197
  setCursorPosition(
185
198
  promptInput.value as HTMLInputElement,
186
- lastMsgObj.text.length
199
+ lastMsgObj.message.length
187
200
  )
188
201
  )
189
202
  } else if (event.key == 'ArrowDown') {
@@ -222,13 +235,13 @@
222
235
  //Send message request
223
236
  let params = {
224
237
  message: prompt,
225
- project: 'GenIO',
238
+ project: props.projectPath,
226
239
  user: props.username
227
240
  }
228
241
 
229
242
  isLoading = true
230
243
  Axios({
231
- url: getParsedEndpoint.value + '/prompt/message',
244
+ url: props.apiEndpoint + '/prompt/message',
232
245
  method: 'POST',
233
246
  data: params,
234
247
  onDownloadProgress: (progressEvent) => {
@@ -238,16 +251,16 @@
238
251
 
239
252
  if (status != 200) return
240
253
 
241
- if (msg) msg.text = chunk
254
+ if (msg) msg.message = chunk
242
255
  }
243
256
  })
244
257
  .then(({ data }) => {
245
- if (msg) msg.text = data
258
+ if (msg) msg.message = data
246
259
  })
247
260
  .catch((error) => {
248
261
  addChatMessage(props.texts.botIsSick)
249
262
 
250
- isChatDisabled = true
263
+ setDisabledState(true)
251
264
  console.log(error)
252
265
  })
253
266
  .finally(() => {
@@ -255,21 +268,50 @@
255
268
  })
256
269
  }
257
270
 
258
- function getSenderName(sender: 'bot' | 'user') {
259
- return sender === 'bot' ? 'GenioBot' : 'You'
271
+ function setCursorPosition(elem: HTMLInputElement, pos: number) {
272
+ elem.focus()
273
+ elem.setSelectionRange(pos, pos)
260
274
  }
261
275
 
262
- function getConvertedTime(date: Date) {
263
- return date.toLocaleString()
276
+ function clearChat() {
277
+ Axios.post(props.apiEndpoint + '/prompt/clear', {
278
+ username: props.username,
279
+ project: props.projectPath
280
+ })
281
+ .then((response: AxiosResponse) => {
282
+ if (response.status != 200 || !response.data.success) {
283
+ setDisabledState(true)
284
+ addChatMessage(props.texts.loginError)
285
+ return console.log(
286
+ `Unsuccessful login, endpoint gave status ${response.status}`
287
+ )
288
+ }
289
+
290
+ resetChat()
291
+ sendInitialMessage()
292
+ })
293
+ .catch((error: Error) => {
294
+ setDisabledState(true)
295
+ addChatMessage(props.texts.loginError)
296
+ console.log(
297
+ 'The following error ocurred while trying to communicate with the endpoint: \n' +
298
+ error
299
+ )
300
+ })
264
301
  }
265
302
 
266
- function setCursorPosition(elem: HTMLInputElement, pos: number) {
267
- elem.focus()
268
- elem.setSelectionRange(pos, pos)
303
+ function getMessageClasses(sender: 'user' | 'bot') {
304
+ const classes: string[] = ['q-chatbot__messages-wrapper']
305
+
306
+ if(sender == 'user')
307
+ classes.push('q-chatbot__messages-wrapper_right')
308
+
309
+ return classes
269
310
  }
270
311
 
271
312
  watch(() => props.apiEndpoint, () => {
272
313
  resetChat()
314
+ initChat()
273
315
  })
274
316
 
275
317
  defineOptions({ name: 'ChatBot' })
@@ -277,63 +319,29 @@
277
319
 
278
320
  <template>
279
321
  <div class="q-chatbot">
280
- <div class="c-sidebar__subtitle">
281
- <span>{{ props.texts.chatbotTitle }}</span>
282
- </div>
283
-
284
322
  <div
285
- class="q-chatbot__content"
286
- ref="messagesContainer">
287
- <div
288
- v-for="message in sortedMessages"
289
- :key="message.id"
290
- :class="[
291
- 'q-chatbot__message-wrapper',
292
- {
293
- 'q-chatbot__message-wrapper_right':
294
- message.sender == 'user'
295
- }
296
- ]">
297
- <img
298
- v-if="message.sender == 'bot'"
299
- :src="ChatBotIcon"
300
- alt=""
301
- class="q-chatbot__profile" />
302
-
303
- <div class="q-chatbot__message">
304
- <div
305
- v-if="isLoading && !message.text"
306
- class="q-chatbot__message-loading">
307
- <div></div>
308
- <div></div>
309
- <div></div>
310
- </div>
311
-
312
- <template v-else>
313
- <div
314
- class="q-chatbot__sender"
315
- v-if="message.text && message.sender == 'bot'">
316
- {{
317
- getSenderName(message.sender) +
318
- ' ' +
319
- getConvertedTime(message.timestamp)
320
- }}
321
- </div>
322
- <div
323
- class="q-chatbot__timestamp"
324
- v-if="message.text && message.sender == 'user'">
325
- {{ getConvertedTime(message.timestamp) }}
326
- </div>
327
- <div
328
- class="q-chatbot__text"
329
- v-if="message.sender == 'bot'"
330
- v-html="message.text"></div>
331
- <div
332
- class="q-chatbot__text"
333
- v-else>
334
- {{ message.text }}
335
- </div>
336
- </template>
323
+ ref="messagesContainer"
324
+ class="q-chatbot__content">
325
+
326
+ <!-- Chat tools -->
327
+ <div class="q-chatbot__tools">
328
+ <q-button
329
+ :title="props.texts.qButtonTitle"
330
+ b-style="plain"
331
+ class="clear-btn"
332
+ :disabled="isChatDisabled"
333
+ borderless
334
+ @click="clearChat">
335
+ <q-icon icon="bin" />
336
+ </q-button>
337
+ </div>
338
+
339
+ <div class="q-chatbot__messages-container">
340
+ <div
341
+ v-for="message in messages"
342
+ :key="message.id"
343
+ :class="getMessageClasses">
344
+ <CBMessage v-bind="message" :loading="isLoading && !message.message" />
337
345
  </div>
338
346
  </div>
339
347
  </div>
@@ -0,0 +1,7 @@
1
+ import CBMessage from "./CBMessage.vue"
2
+ import type { CBMessageProps } from "./CBMessage.vue"
3
+
4
+ export {
5
+ CBMessage,
6
+ CBMessageProps
7
+ }
@@ -1,6 +1,11 @@
1
1
  export type ChatBotMessage = {
2
2
  id: number,
3
- text: string,
4
- timestamp: Date,
3
+ message: string,
4
+ date: Date,
5
5
  sender: 'bot' | 'user'
6
+ }
7
+
8
+ export type ChatBotMessageContent = {
9
+ content: string,
10
+ type: string
6
11
  }