@quidgest/chatbot 0.5.3 → 0.5.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.
Files changed (31) hide show
  1. package/dist/components/ChatBot/ChatBot.vue.d.ts +2 -0
  2. package/dist/components/ChatBot/__tests__/ChatBot.spec.d.ts +1 -0
  3. package/dist/components/ChatBot/types.d.ts +2 -1
  4. package/dist/components/ChatBotMessage/ChatBotMessage.vue.d.ts +3 -1
  5. package/dist/components/FieldPreview/FieldPreview.vue.d.ts +2 -0
  6. package/dist/composables/useChatApi.d.ts +1 -1
  7. package/dist/composables/useChatMessages.d.ts +1 -0
  8. package/dist/composables/useTexts.d.ts +4 -0
  9. package/dist/index.js +15 -15
  10. package/dist/index.mjs +1597 -2209
  11. package/dist/style.css +1 -1
  12. package/package.json +2 -2
  13. package/src/components/ChatBot/ChatBot.vue +57 -60
  14. package/src/components/ChatBot/__tests__/ChatBot.spec.ts +52 -0
  15. package/src/components/ChatBot/__tests__/__snapshots__/ChatBot.spec.ts.snap +39 -0
  16. package/src/components/ChatBot/types.ts +2 -1
  17. package/src/components/ChatBotMessage/ChatBotMessage.vue +5 -3
  18. package/src/components/ChatBotMessage/ChatBotMessageButtons.vue +1 -0
  19. package/src/components/ChatBotMessage/__tests__/ChatBotMessage.spec.ts +7 -3
  20. package/src/components/ChatBotMessage/__tests__/ChatBotMessageButtons.spec.ts +4 -4
  21. package/src/components/ChatToolBar/ChatToolBar.vue +2 -1
  22. package/src/components/ChatToolBar/__tests__/ChatToolBar.spec.ts +38 -18
  23. package/src/components/FieldPreview/FieldPreview.vue +31 -9
  24. package/src/components/FieldPreview/__tests__/__snapshots__/FieldPreview.spec.ts.snap +4 -10
  25. package/src/components/FieldPreview/field-preview.scss +4 -0
  26. package/src/components/MarkdownRender/MarkdownRender.vue +1 -0
  27. package/src/composables/__tests__/useChatMessages.spec.ts +14 -1
  28. package/src/composables/useChatApi.ts +57 -53
  29. package/src/composables/useChatMessages.ts +6 -1
  30. package/src/composables/useTexts.ts +4 -0
  31. package/src/test/setup.ts +5 -0
@@ -5,23 +5,24 @@
5
5
  {{ texts.suggestionsForField }} <b>{{ props.name }}</b>
6
6
  </span>
7
7
  </div>
8
- <div class="q-field-preview__content">
8
+ <div :class="classes">
9
9
  <component
10
10
  :is="previewComponent"
11
11
  v-bind="previewComponentProps" />
12
12
  </div>
13
13
  <div class="q-field-preview__footer">
14
14
  <q-button-group borderless>
15
- <!-- <q-button
16
- :title="texts.regenerateResponse"
17
- :disabled="props.disabled"
18
- borderless
19
- @click="console.log('Regenerate response')">
20
- <q-icon icon="reset" />
21
- </q-button> -->
22
15
  <q-button
23
- data-testid="apply-button"
16
+ :title="texts.regenerateResponse"
17
+ :disabled="blockRegenerateButton"
18
+ :readonly="blockRegenerateButton"
19
+ borderless
20
+ @click="regenerate">
21
+ <q-icon icon="reset" />
22
+ </q-button>
23
+ <q-button
24
24
  :label="texts.apply"
25
+ data-testid="apply-button"
25
26
  :disabled="blockApplyButton"
26
27
  :readonly="blockApplyButton"
27
28
  @click="emitApply">
@@ -43,13 +44,19 @@
43
44
  const props = defineProps<FieldPreviewProps>()
44
45
  const emit = defineEmits<{
45
46
  (e: 'apply', text: unknown): void
47
+ (e: 'regenerate', name: string): void
46
48
  }>()
47
49
  const texts = useTexts()
48
50
  const blockApply = ref(props.applied)
51
+ const blockRegenerate = ref(false)
49
52
  const blockApplyButton = computed(() => {
50
53
  return props.disabled || blockApply.value
51
54
  })
52
55
 
56
+ const blockRegenerateButton = computed(() => {
57
+ return props.disabled || blockRegenerate.value
58
+ })
59
+
53
60
  const previewComponent = computed(() => {
54
61
  if (props.type === 'text' || props.type === 'multiline_text') return MarkdownRender
55
62
 
@@ -65,6 +72,14 @@
65
72
  return componentProps
66
73
  })
67
74
 
75
+ const classes = computed(() => {
76
+ const classes = ['q-field-preview__content']
77
+
78
+ if (previewComponent.value !== MarkdownRender) classes.push('preserve-whitespace')
79
+
80
+ return classes
81
+ })
82
+
68
83
  function emitApply() {
69
84
  if (blockApply.value) return
70
85
  blockApply.value = true
@@ -73,4 +88,11 @@
73
88
 
74
89
  emit('apply', text)
75
90
  }
91
+
92
+ function regenerate() {
93
+ if (blockRegenerate.value) return
94
+ blockRegenerate.value = true
95
+
96
+ emit('regenerate', props.name)
97
+ }
76
98
  </script>
@@ -9,17 +9,11 @@ exports[`FieldPreview > renders correctly with default props 1`] = `
9
9
  </div>
10
10
  </div>
11
11
  <div class="q-field-preview__footer">
12
- <div class="q-button-group" role="group">
13
- <!-- <q-button
14
- :title="texts.regenerateResponse"
15
- :disabled="props.disabled"
16
- borderless
17
- @click="console.log('Regenerate response')">
18
- <q-icon icon="reset" />
19
- </q-button> --><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless" data-testid="apply-button">
12
+ <div class="q-button-group" role="group"><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless" title="Regenerate response">
13
+ <!--v-if--><span class="q-button__content"><span data-test="reset"></span> </span>
14
+ </button><button type="button" class="q-button q-button--outlined q-button--primary q-button--borderless" data-testid="apply-button">
20
15
  <!--v-if--><span class="q-button__content"><span data-test="apply"></span> Apply</span>
21
- </button>
22
- </div>
16
+ </button></div>
23
17
  </div>
24
18
  </div>"
25
19
  `;
@@ -20,6 +20,10 @@
20
20
  border-radius: 6px;
21
21
  padding: 0.75rem 1rem;
22
22
  font-size: 0.875rem;
23
+
24
+ &.preserve-whitespace {
25
+ white-space: pre-wrap;
26
+ }
23
27
  }
24
28
 
25
29
  &__footer {
@@ -15,6 +15,7 @@
15
15
  const md = ref(
16
16
  new MarkdownIt({
17
17
  html: true,
18
+ breaks: true,
18
19
  ...(props.options ?? {})
19
20
  })
20
21
  )
@@ -4,7 +4,8 @@ import { useChatMessages } from '../useChatMessages'
4
4
  // Utils
5
5
  import { describe, it, expect, beforeEach } from 'vitest'
6
6
 
7
- const { addChatMessage, clearMessages, getMessages, getLastMessage } = useChatMessages()
7
+ const { addChatMessage, clearMessages, getMessages, getLastMessage, deleteMessageById } =
8
+ useChatMessages()
8
9
 
9
10
  beforeEach(() => {
10
11
  // Reset chat messages before each test
@@ -48,4 +49,16 @@ describe('useChatMessages', () => {
48
49
  expect(messages).toEqual([])
49
50
  expect(messages.length).toBe(0)
50
51
  })
52
+
53
+ it('should delete a message by ID', () => {
54
+ const msg1 = addChatMessage('First message', 'bot')
55
+ const msg2 = addChatMessage('Second message', 'user')
56
+
57
+ expect(getMessages().length).toBe(2)
58
+ deleteMessageById(msg1.id)
59
+
60
+ const messages = getMessages()
61
+ expect(messages.length).toBe(1)
62
+ expect(messages[0].id).toBe(msg2.id)
63
+ })
51
64
  })
@@ -56,6 +56,62 @@ export function useChatApi(apiEndpoint: string) {
56
56
  })
57
57
  }
58
58
 
59
+ async function sendPrompt(
60
+ formData: FormData,
61
+ onChunk: (chunk: string) => void,
62
+ onError?: (error: Error) => void
63
+ ) {
64
+ isLoading.value = true
65
+ try {
66
+ return await useSSE(
67
+ {
68
+ method: 'POST',
69
+ url: `${apiEndpoint}/prompt/submit`,
70
+ data: formData
71
+ },
72
+ {
73
+ onMessage: (data) => {
74
+ onChunk(data)
75
+ },
76
+ onDone: () => (isLoading.value = false)
77
+ }
78
+ )
79
+ } catch (error) {
80
+ isLoading.value = false
81
+ onError?.(error as Error)
82
+ console.error('Error in sendPrompt:', error)
83
+ throw error
84
+ }
85
+ }
86
+
87
+ async function getJobResultData(
88
+ jobId: string,
89
+ onChunk: (chunk: string) => void,
90
+ onMetaData: (metadata: Record<string, unknown>) => void,
91
+ onRequestError?: (error: Error) => void
92
+ ) {
93
+ isLoading.value = true
94
+ try {
95
+ return await useSSE(
96
+ {
97
+ method: 'POST',
98
+ url: `${apiEndpoint}/get-job-result`,
99
+ data: {
100
+ jobId
101
+ }
102
+ },
103
+ {
104
+ onMessage: (data) => onChunk(data),
105
+ onFieldMetadata: (metadata) => onMetaData(metadata),
106
+ onDone: () => (isLoading.value = false)
107
+ }
108
+ )
109
+ } catch (error) {
110
+ lastError.value = error as Error
111
+ onRequestError?.(error as Error)
112
+ isLoading.value = false
113
+ }
114
+ }
59
115
  async function clearChatData(
60
116
  username: string,
61
117
  project: string,
@@ -74,58 +130,6 @@ export function useChatApi(apiEndpoint: string) {
74
130
  })
75
131
  }
76
132
 
77
- async function sendPrompt(
78
- formData: FormData,
79
- onChunk: (chunk: string) => void,
80
- onError?: (error: Error) => void
81
- ) {
82
- isLoading.value = true
83
- return await useSSE(
84
- {
85
- method: 'POST',
86
- url: `${apiEndpoint}/prompt/submit`,
87
- data: formData
88
- },
89
- {
90
- onMessage: (data) => {
91
- onChunk(data)
92
- },
93
- onError: (error) => {
94
- lastError.value = error
95
- onError?.(error)
96
- console.error('Error in sendPrompt:', error)
97
- },
98
- onDone: () => (isLoading.value = false)
99
- }
100
- )
101
- }
102
-
103
- async function getFieldSuggestionData(
104
- jobId: string,
105
- onChunk: (chunk: string) => void,
106
- onMetaData: (metadata: Record<string, unknown>) => void
107
- ) {
108
- isLoading.value = true
109
- return await useSSE(
110
- {
111
- method: 'POST',
112
- url: `${apiEndpoint}/get-job-result`,
113
- data: {
114
- jobId
115
- }
116
- },
117
- {
118
- onMessage: (data) => onChunk(data),
119
- onFieldMetadata: (metadata) => onMetaData(metadata),
120
- onError: (error) => {
121
- lastError.value = error
122
- console.error('Error in getFieldSuggestionData:', error)
123
- },
124
- onDone: () => (isLoading.value = false)
125
- }
126
- )
127
- }
128
-
129
133
  async function handleFeedback(feedback: number, comment: string, sessionID: string) {
130
134
  return await baseRequest<{ success: boolean }>({
131
135
  method: 'POST',
@@ -143,7 +147,7 @@ export function useChatApi(apiEndpoint: string) {
143
147
  lastError,
144
148
  getChatData,
145
149
  clearChatData,
146
- getFieldSuggestionData,
150
+ getJobResultData,
147
151
  sendPrompt,
148
152
  handleFeedback
149
153
  }
@@ -41,6 +41,10 @@ export function useChatMessages() {
41
41
  nextMessageId.value = 1
42
42
  }
43
43
 
44
+ function deleteMessageById(messageId: number) {
45
+ messages.value = messages.value.filter((m) => m.id !== messageId)
46
+ }
47
+
44
48
  function getMessages() {
45
49
  return messages.value
46
50
  }
@@ -51,6 +55,7 @@ export function useChatMessages() {
51
55
  addChatMessage,
52
56
  getLastMessage,
53
57
  clearMessages,
54
- getMessages
58
+ getMessages,
59
+ deleteMessageById
55
60
  }
56
61
  }
@@ -1,3 +1,6 @@
1
+ /**
2
+ * TODO: Implement localization
3
+ */
1
4
  export function useTexts() {
2
5
  return {
3
6
  copy: 'Copy',
@@ -24,6 +27,7 @@ export function useTexts() {
24
27
  cancelButton: 'Cancel',
25
28
  senderImage: 'Sender Image',
26
29
  imagePreview: 'Image preview',
30
+ regenerateResponsePrompt: 'Regenerate a new response for field {0}',
27
31
  regenerateResponse: 'Regenerate response',
28
32
  generatingResponse: 'Generating',
29
33
  suggestionsForField: 'Suggestions for field:',
package/src/test/setup.ts CHANGED
@@ -2,6 +2,7 @@ import { vi } from 'vitest'
2
2
  import { config } from '@vue/test-utils'
3
3
 
4
4
  import { createFramework } from '@quidgest/ui/framework'
5
+ import { QSelect } from '@quidgest/ui/components'
5
6
 
6
7
  vi.mock('@quidgest/ui/components', async (importOriginal) => {
7
8
  const original = await importOriginal<typeof import('@quidgest/ui/components')>()
@@ -34,3 +35,7 @@ config.global.plugins = [
34
35
  }
35
36
  }
36
37
  ]
38
+
39
+ config.global.components = {
40
+ QSelect
41
+ }