@quidgest/chatbot 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ChatBot/ChatBot.vue.d.ts +53 -0
- package/dist/components/ChatBot/types.d.ts +48 -0
- package/dist/components/ChatBotInput/ChatBotInput.vue.d.ts +17 -0
- package/dist/components/ChatBotInput/index.d.ts +5 -0
- package/dist/components/ChatBotInput/types.d.ts +28 -0
- package/dist/components/ChatBotMessage/ChatBotMessage.vue.d.ts +41 -0
- package/dist/components/ChatBotMessage/ChatBotMessageButtons.vue.d.ts +21 -0
- package/dist/components/ChatBotMessage/index.d.ts +6 -0
- package/dist/components/ChatBotMessage/types.d.ts +46 -0
- package/dist/components/ChatToolBar/ChatToolBar.vue.d.ts +39 -0
- package/dist/components/ChatToolBar/index.d.ts +5 -0
- package/dist/components/ChatToolBar/types.d.ts +16 -0
- package/dist/components/FieldPreview/FieldPreview.vue.d.ts +17 -0
- package/dist/components/FieldPreview/index.d.ts +5 -0
- package/dist/components/FieldPreview/types.d.ts +7 -0
- package/dist/components/MarkdownRender/MarkdownRender.vue.d.ts +13 -0
- package/dist/components/MarkdownRender/index.d.ts +5 -0
- package/dist/components/MarkdownRender/types.d.ts +7 -0
- package/dist/composables/useChatApi.d.ts +23 -0
- package/dist/composables/useChatMessages.d.ts +11 -0
- package/dist/composables/useSSE.d.ts +10 -0
- package/dist/composables/useTexts.d.ts +26 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +27 -47
- package/dist/index.mjs +2651 -8663
- package/dist/style.css +1 -1
- package/dist/utils/helper.d.ts +1 -0
- package/package.json +5 -5
- package/src/assets/chatbot_profile.svg +1 -0
- package/src/assets/styles/styles.scss +10 -42
- package/src/components/ChatBot/ChatBot.vue +375 -0
- package/src/components/ChatBot/types.ts +55 -0
- package/src/components/ChatBotInput/ChatBotInput.vue +195 -0
- package/src/components/ChatBotInput/index.ts +5 -0
- package/src/components/ChatBotInput/types.ts +33 -0
- package/src/components/ChatBotMessage/ChatBotMessage.vue +139 -0
- package/src/components/ChatBotMessage/ChatBotMessageButtons.vue +169 -0
- package/src/components/ChatBotMessage/index.ts +8 -0
- package/src/components/ChatBotMessage/types.ts +70 -0
- package/src/components/ChatToolBar/ChatToolBar.vue +82 -0
- package/src/components/ChatToolBar/index.ts +5 -0
- package/src/components/ChatToolBar/types.ts +18 -0
- package/src/components/FieldPreview/FieldPreview.vue +78 -0
- package/src/components/FieldPreview/field-preview.scss +34 -0
- package/src/components/FieldPreview/index.ts +5 -0
- package/src/components/FieldPreview/types.ts +7 -0
- package/src/components/MarkdownRender/MarkdownRender.vue +25 -0
- package/src/components/MarkdownRender/index.ts +5 -0
- package/src/components/MarkdownRender/markdown-render.scss +24 -0
- package/src/components/MarkdownRender/types.ts +7 -0
- package/src/components/PulseDots/PulseDots.vue +24 -0
- package/src/components/PulseDots/pulse-dots.scss +37 -0
- package/src/composables/useChatApi.ts +156 -0
- package/src/composables/useChatMessages.ts +58 -0
- package/src/composables/useSSE.ts +90 -0
- package/src/composables/useTexts.ts +32 -0
- package/src/index.ts +1 -1
- package/src/utils/helper.ts +12 -0
- package/dist/components/CBMessage.vue.d.ts +0 -95
- package/dist/components/ChatBot.vue.d.ts +0 -65
- package/dist/components/index.d.ts +0 -4
- package/dist/types/chatbot.type.d.ts +0 -14
- package/dist/types/message.type.d.ts +0 -34
- package/dist/types/texts.type.d.ts +0 -3
- package/src/assets/chatbot.png +0 -0
- package/src/components/CBMessage.vue +0 -276
- package/src/components/ChatBot.vue +0 -496
- package/src/components/PulseDots.vue +0 -15
- package/src/components/index.ts +0 -4
- package/src/types/chatbot.type.ts +0 -15
- package/src/types/message.type.ts +0 -55
- package/src/types/texts.type.ts +0 -3
- /package/dist/components/{PulseDots.vue.d.ts → PulseDots/PulseDots.vue.d.ts} +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AvailableAgents } from '../ChatBot/types'
|
|
2
|
+
|
|
3
|
+
export type ChatToolBarProps = {
|
|
4
|
+
/**
|
|
5
|
+
* If true, the toolbar will be disabled and not clickable.
|
|
6
|
+
*/
|
|
7
|
+
disabled?: boolean
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Availabe Agents to select from.
|
|
11
|
+
*/
|
|
12
|
+
availableAgents?: AvailableAgents[]
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The currently selected agent.
|
|
16
|
+
*/
|
|
17
|
+
selectedAgentKey?: string
|
|
18
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="q-field-preview">
|
|
3
|
+
<div class="q-field-preview__toolbar">
|
|
4
|
+
<span>
|
|
5
|
+
Suggestions for field: <b>{{ props.name }}</b>
|
|
6
|
+
</span>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="q-field-preview__content">
|
|
9
|
+
<component
|
|
10
|
+
:is="previewComponent"
|
|
11
|
+
v-bind="previewComponentProps" />
|
|
12
|
+
</div>
|
|
13
|
+
<div class="q-field-preview__footer">
|
|
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
|
+
<q-button
|
|
23
|
+
:label="texts.apply"
|
|
24
|
+
:disabled="blockApplyButton"
|
|
25
|
+
:readonly="blockApplyButton"
|
|
26
|
+
@click="emitApply">
|
|
27
|
+
<q-icon icon="apply" />
|
|
28
|
+
</q-button>
|
|
29
|
+
</q-button-group>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup lang="ts">
|
|
35
|
+
import { QButton, QIcon, QButtonGroup } from '@quidgest/ui/components'
|
|
36
|
+
import MarkdownRender from '../MarkdownRender/MarkdownRender.vue'
|
|
37
|
+
import { FieldPreviewProps } from './types'
|
|
38
|
+
import { useTexts } from '@/composables/useTexts'
|
|
39
|
+
import { computed, ref } from 'vue'
|
|
40
|
+
import { parseFieldValue } from '@/utils/helper'
|
|
41
|
+
|
|
42
|
+
const texts = useTexts()
|
|
43
|
+
const props = defineProps<FieldPreviewProps>()
|
|
44
|
+
const blockApply = ref(props.applied)
|
|
45
|
+
const blockApplyButton = computed(() => {
|
|
46
|
+
return props.disabled || blockApply.value
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
const previewComponent = computed(() => {
|
|
50
|
+
if (props.type === 'text' || props.type === 'multiline_text')
|
|
51
|
+
return MarkdownRender
|
|
52
|
+
|
|
53
|
+
return 'div'
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const previewComponentProps = computed(() => {
|
|
57
|
+
const componentProps: Record<string, unknown> = {}
|
|
58
|
+
|
|
59
|
+
if (previewComponent.value === MarkdownRender)
|
|
60
|
+
componentProps.source = props.text
|
|
61
|
+
else componentProps.innerHTML = props.text
|
|
62
|
+
|
|
63
|
+
return componentProps
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const emits = defineEmits<{
|
|
67
|
+
(e: 'apply', text: unknown): void
|
|
68
|
+
}>()
|
|
69
|
+
|
|
70
|
+
function emitApply() {
|
|
71
|
+
if (blockApply.value) return
|
|
72
|
+
blockApply.value = true
|
|
73
|
+
|
|
74
|
+
const text = parseFieldValue(props.type, props.text)
|
|
75
|
+
|
|
76
|
+
emits('apply', text)
|
|
77
|
+
}
|
|
78
|
+
</script>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
.q-field-preview {
|
|
2
|
+
position: relative;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
margin: 1rem 0.25rem;
|
|
6
|
+
|
|
7
|
+
&__toolbar {
|
|
8
|
+
z-index: 1;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: row;
|
|
11
|
+
align-items: center;
|
|
12
|
+
justify-content: space-between;
|
|
13
|
+
padding: 0.1rem 0.2rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&__content {
|
|
17
|
+
position: relative;
|
|
18
|
+
background-color: #f5f5f5;
|
|
19
|
+
border: 1px solid #e0e0e0;
|
|
20
|
+
border-radius: 6px;
|
|
21
|
+
padding: 0.75rem 1rem;
|
|
22
|
+
font-size: 0.875rem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&__footer {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: row;
|
|
28
|
+
margin-top: 0.25rem;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.q-field-preview:first-child {
|
|
33
|
+
margin: 0 1rem 0.25rem 0.25rem;
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="markdown-renderer"
|
|
4
|
+
v-html="renderedContent"></div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { ref, computed } from 'vue'
|
|
9
|
+
import type { MarkdownRenderProps } from './types'
|
|
10
|
+
|
|
11
|
+
import MarkdownIt from 'markdown-it'
|
|
12
|
+
|
|
13
|
+
const props = defineProps<MarkdownRenderProps>()
|
|
14
|
+
|
|
15
|
+
const md = ref(
|
|
16
|
+
new MarkdownIt({
|
|
17
|
+
html: true,
|
|
18
|
+
...(props.options ?? {})
|
|
19
|
+
})
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if (props.plugins) props.plugins.forEach((plugin) => md.value.use(plugin))
|
|
23
|
+
|
|
24
|
+
const renderedContent = computed(() => md.value.render(props.source))
|
|
25
|
+
</script>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.markdown-renderer {
|
|
2
|
+
pre,
|
|
3
|
+
code {
|
|
4
|
+
white-space: pre-wrap;
|
|
5
|
+
word-break: break-word;
|
|
6
|
+
overflow-wrap: break-word;
|
|
7
|
+
overflow-x: auto;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
pre {
|
|
11
|
+
position: relative;
|
|
12
|
+
background-color: #f5f5f5;
|
|
13
|
+
border: 1px solid #e0e0e0;
|
|
14
|
+
border-radius: 6px;
|
|
15
|
+
padding: 0.75rem 1rem;
|
|
16
|
+
font-size: 0.875rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
code {
|
|
20
|
+
padding: 0.2rem 0.4rem;
|
|
21
|
+
border-radius: 4px;
|
|
22
|
+
font-size: 0.875rem;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="pulsing-dots">
|
|
3
|
+
<span class="generating-text">
|
|
4
|
+
{{ texts.generatingResponse }}
|
|
5
|
+
</span>
|
|
6
|
+
<div class="dots-container">
|
|
7
|
+
<span
|
|
8
|
+
v-for="(_, index) in dots"
|
|
9
|
+
:key="index"
|
|
10
|
+
class="dot"
|
|
11
|
+
:style="{ animationDelay: index * 0.2 + 's' }">
|
|
12
|
+
•
|
|
13
|
+
</span>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup lang="ts">
|
|
19
|
+
import { useTexts } from '@/composables/useTexts'
|
|
20
|
+
|
|
21
|
+
const dots = [1, 2, 3]
|
|
22
|
+
|
|
23
|
+
const texts = useTexts()
|
|
24
|
+
</script>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
.pulsing-dots {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
justify-content: center;
|
|
5
|
+
flex-direction: row;
|
|
6
|
+
gap: 0.25rem;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.generating-text {
|
|
10
|
+
font-size: 0.9rem;
|
|
11
|
+
color: var(--q-theme-primary);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.dots-container {
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: 0.1rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.dot {
|
|
21
|
+
font-size: 16px;
|
|
22
|
+
line-height: 1;
|
|
23
|
+
animation: pulse 1s infinite;
|
|
24
|
+
color: var(--q-theme-primary);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@keyframes pulse {
|
|
28
|
+
0%,
|
|
29
|
+
100% {
|
|
30
|
+
transform: scale(0.8);
|
|
31
|
+
opacity: 0.6;
|
|
32
|
+
}
|
|
33
|
+
50% {
|
|
34
|
+
transform: scale(1);
|
|
35
|
+
opacity: 1;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Utils
|
|
2
|
+
import axios from 'axios'
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
// Types
|
|
6
|
+
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
|
|
7
|
+
import { ChatBotMessageContent } from '@/components/ChatBot/types'
|
|
8
|
+
import { useSSE } from './useSSE'
|
|
9
|
+
|
|
10
|
+
type RequestResponse<T> = {
|
|
11
|
+
data: T | null
|
|
12
|
+
error: Error | null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useChatApi(apiEndpoint: string) {
|
|
16
|
+
const isLoading = ref(false)
|
|
17
|
+
const lastError = ref<Error | null>(null)
|
|
18
|
+
|
|
19
|
+
async function baseRequest<T>(
|
|
20
|
+
config: AxiosRequestConfig
|
|
21
|
+
): Promise<RequestResponse<T>> {
|
|
22
|
+
isLoading.value = true
|
|
23
|
+
lastError.value = null
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const response: AxiosResponse<T> = await axios({
|
|
27
|
+
...config,
|
|
28
|
+
baseURL: apiEndpoint
|
|
29
|
+
})
|
|
30
|
+
return { data: response.data, error: null }
|
|
31
|
+
} catch (error: any) {
|
|
32
|
+
lastError.value = error
|
|
33
|
+
console.error('Error in API request:', error)
|
|
34
|
+
return { data: null, error }
|
|
35
|
+
} finally {
|
|
36
|
+
isLoading.value = false
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function getChatData(
|
|
41
|
+
username: string,
|
|
42
|
+
project: string,
|
|
43
|
+
agentID: string,
|
|
44
|
+
formId: string
|
|
45
|
+
) {
|
|
46
|
+
return await baseRequest<{
|
|
47
|
+
success: boolean
|
|
48
|
+
history: ChatBotMessageContent[]
|
|
49
|
+
}>({
|
|
50
|
+
method: 'POST',
|
|
51
|
+
url: `/prompt/load`,
|
|
52
|
+
data: {
|
|
53
|
+
username,
|
|
54
|
+
project,
|
|
55
|
+
agentID,
|
|
56
|
+
formId
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function clearChatData(
|
|
62
|
+
username: string,
|
|
63
|
+
project: string,
|
|
64
|
+
agentID: string,
|
|
65
|
+
formId: string
|
|
66
|
+
) {
|
|
67
|
+
return await baseRequest<{ success: boolean }>({
|
|
68
|
+
method: 'POST',
|
|
69
|
+
url: `/prompt/clear`,
|
|
70
|
+
data: {
|
|
71
|
+
username,
|
|
72
|
+
project,
|
|
73
|
+
agentID,
|
|
74
|
+
formId
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function sendPrompt(
|
|
80
|
+
formData: FormData,
|
|
81
|
+
onChunk: (chunk: string) => void,
|
|
82
|
+
onError?: (error: Error) => void
|
|
83
|
+
) {
|
|
84
|
+
isLoading.value = true
|
|
85
|
+
return await useSSE(
|
|
86
|
+
{
|
|
87
|
+
method: 'POST',
|
|
88
|
+
url: `${apiEndpoint}/prompt/submit`,
|
|
89
|
+
data: formData
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
onMessage: (data) => {
|
|
93
|
+
onChunk(data)
|
|
94
|
+
},
|
|
95
|
+
onError: (error) => {
|
|
96
|
+
lastError.value = error
|
|
97
|
+
onError?.(error)
|
|
98
|
+
console.error('Error in sendPrompt:', error)
|
|
99
|
+
},
|
|
100
|
+
onDone: () => (isLoading.value = false)
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function getFieldSuggestionData(
|
|
106
|
+
jobId: string,
|
|
107
|
+
onChunk: (chunk: string) => void,
|
|
108
|
+
onMetaData: (metadata: Record<string, unknown>) => void
|
|
109
|
+
) {
|
|
110
|
+
isLoading.value = true
|
|
111
|
+
return await useSSE(
|
|
112
|
+
{
|
|
113
|
+
method: 'POST',
|
|
114
|
+
url: `${apiEndpoint}/get-job-result`,
|
|
115
|
+
data: {
|
|
116
|
+
jobId
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
onMessage: (data) => onChunk(data),
|
|
121
|
+
onFieldMetadata: (metadata) => onMetaData(metadata),
|
|
122
|
+
onError: (error) => {
|
|
123
|
+
lastError.value = error
|
|
124
|
+
console.error('Error in getFieldSuggestionData:', error)
|
|
125
|
+
},
|
|
126
|
+
onDone: () => (isLoading.value = false)
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function handleFeedback(
|
|
132
|
+
feedback: number,
|
|
133
|
+
comment: string,
|
|
134
|
+
sessionID: string
|
|
135
|
+
) {
|
|
136
|
+
return await baseRequest<{ success: boolean }>({
|
|
137
|
+
method: 'POST',
|
|
138
|
+
url: `/prompt/feedback`,
|
|
139
|
+
data: {
|
|
140
|
+
messageSessionID: sessionID,
|
|
141
|
+
feedbackValue: feedback,
|
|
142
|
+
feedbackComment: comment
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
isLoading,
|
|
149
|
+
lastError,
|
|
150
|
+
getChatData,
|
|
151
|
+
clearChatData,
|
|
152
|
+
getFieldSuggestionData,
|
|
153
|
+
sendPrompt,
|
|
154
|
+
handleFeedback
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
3
|
+
|
|
4
|
+
import type { Ref } from 'vue'
|
|
5
|
+
import type {
|
|
6
|
+
ChatMessage,
|
|
7
|
+
ChatBotMessageSender
|
|
8
|
+
} from '@/components/ChatBot/types'
|
|
9
|
+
|
|
10
|
+
const messages: Ref<ChatMessage[]> = ref([])
|
|
11
|
+
const nextMessageId = ref(1)
|
|
12
|
+
|
|
13
|
+
export function useChatMessages() {
|
|
14
|
+
function addChatMessage(
|
|
15
|
+
message: string,
|
|
16
|
+
sender?: ChatBotMessageSender,
|
|
17
|
+
imagePreviewUrl?: string | null,
|
|
18
|
+
sessionID?: string,
|
|
19
|
+
isWelcomeMessage?: boolean
|
|
20
|
+
) {
|
|
21
|
+
const newMessage: ChatMessage = {
|
|
22
|
+
id: nextMessageId.value++,
|
|
23
|
+
message,
|
|
24
|
+
date: new Date(),
|
|
25
|
+
sender: sender || 'bot',
|
|
26
|
+
sessionID: sessionID || uuidv4(),
|
|
27
|
+
imagePreviewUrl: imagePreviewUrl || undefined,
|
|
28
|
+
isWelcomeMessage,
|
|
29
|
+
fields: []
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
messages.value.push(newMessage)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getLastMessage() {
|
|
36
|
+
return messages.value.find(
|
|
37
|
+
(m: ChatMessage) => m.id === nextMessageId.value - 1
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function clearMessages() {
|
|
42
|
+
messages.value = []
|
|
43
|
+
nextMessageId.value = 1
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getMessages() {
|
|
47
|
+
return messages.value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
messages,
|
|
52
|
+
nextMessageId,
|
|
53
|
+
addChatMessage,
|
|
54
|
+
getLastMessage,
|
|
55
|
+
clearMessages,
|
|
56
|
+
getMessages
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import axios, { AxiosRequestConfig } from 'axios'
|
|
2
|
+
|
|
3
|
+
type SSEvents = {
|
|
4
|
+
onMessage?: (message: string) => void
|
|
5
|
+
onError?: (error: Error) => void
|
|
6
|
+
onFieldMetadata?: (metadata: Record<string, unknown>) => void
|
|
7
|
+
onDone?: () => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function useSSE(config: AxiosRequestConfig, handlers: SSEvents) {
|
|
11
|
+
const controller = new AbortController()
|
|
12
|
+
const signal = controller.signal
|
|
13
|
+
|
|
14
|
+
const response = await axios({
|
|
15
|
+
...config,
|
|
16
|
+
headers: {
|
|
17
|
+
Accept: 'text/event-stream'
|
|
18
|
+
},
|
|
19
|
+
responseType: 'stream',
|
|
20
|
+
signal,
|
|
21
|
+
adapter: 'fetch'
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const stream = response.data
|
|
25
|
+
|
|
26
|
+
if (!stream || !(stream instanceof ReadableStream)) {
|
|
27
|
+
throw new Error('Invalid stream response')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const reader = stream.getReader()
|
|
31
|
+
const decoder = new TextDecoder()
|
|
32
|
+
|
|
33
|
+
while (true) {
|
|
34
|
+
const { done, value } = await reader.read()
|
|
35
|
+
if (done) {
|
|
36
|
+
handlers.onDone?.()
|
|
37
|
+
break
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const chunk = decoder.decode(value, { stream: true })
|
|
41
|
+
const events = chunk.split(/\n\n+/)
|
|
42
|
+
|
|
43
|
+
for (const eventBlock of events) {
|
|
44
|
+
const lines = eventBlock.trim().split('\n')
|
|
45
|
+
let name = ''
|
|
46
|
+
let data = ''
|
|
47
|
+
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
if (line.startsWith('event:')) {
|
|
50
|
+
name = line.replace('event:', '').trim()
|
|
51
|
+
} else if (line.startsWith('data:')) {
|
|
52
|
+
data = line.replace('data:', '').trim()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!name || !data) continue
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const parsedData = JSON.parse(data)
|
|
60
|
+
|
|
61
|
+
switch (name) {
|
|
62
|
+
case 'message':
|
|
63
|
+
handlers.onMessage?.(parsedData.value)
|
|
64
|
+
break
|
|
65
|
+
case 'error':
|
|
66
|
+
handlers.onError?.(new Error(parsedData.value))
|
|
67
|
+
break
|
|
68
|
+
case 'field_metadata':
|
|
69
|
+
handlers.onFieldMetadata?.(parsedData)
|
|
70
|
+
break
|
|
71
|
+
case 'done':
|
|
72
|
+
handlers.onDone?.()
|
|
73
|
+
break
|
|
74
|
+
default:
|
|
75
|
+
console.warn(`Unknown event type: ${name}`)
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
handlers.onError?.(error as Error)
|
|
79
|
+
console.error('Error processing event:', error)
|
|
80
|
+
continue
|
|
81
|
+
} finally {
|
|
82
|
+
// Ensure we always clean up the reader
|
|
83
|
+
if (done) {
|
|
84
|
+
reader.releaseLock()
|
|
85
|
+
controller.abort()
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function useTexts() {
|
|
2
|
+
return {
|
|
3
|
+
copy: 'Copy',
|
|
4
|
+
apply: 'Apply',
|
|
5
|
+
applyAll: 'Apply all',
|
|
6
|
+
chatbotTitle: 'ChatBot',
|
|
7
|
+
sendMessage: 'Send message',
|
|
8
|
+
clearChat: 'Clear chat',
|
|
9
|
+
inputLabel: 'What can I help with?',
|
|
10
|
+
imageUpload: 'Upload Image',
|
|
11
|
+
imageUploadQButton: 'Upload Image',
|
|
12
|
+
goodResponse: 'Good response',
|
|
13
|
+
badResponse: 'Bad response',
|
|
14
|
+
initialMessage:
|
|
15
|
+
"Howdy! I am GenioBot 👋, Quidgest's personal AI assistant! How can I help you?",
|
|
16
|
+
initialAgentMessage:
|
|
17
|
+
'Just a temporary message while we are working on the agent mode',
|
|
18
|
+
loginError:
|
|
19
|
+
'Uh oh, I could not authenticate with the Quidgest API endpoint 😓',
|
|
20
|
+
botIsSick:
|
|
21
|
+
'*cough cough* GenioBot is not feeling alright 🥴️🤒, looks like something failed!',
|
|
22
|
+
commentDialogTitle: 'Would you like to add a comment?',
|
|
23
|
+
commentPlaceholder: 'Type your comment here (optional)...',
|
|
24
|
+
copyResponse: 'Copy response',
|
|
25
|
+
submitButton: 'Submit',
|
|
26
|
+
cancelButton: 'Cancel',
|
|
27
|
+
senderImage: 'Sender Image',
|
|
28
|
+
imagePreview: 'Image preview',
|
|
29
|
+
regenerateResponse: 'Regenerate response',
|
|
30
|
+
generatingResponse: 'Generating'
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function parseFieldValue(type: string, text: string) {
|
|
2
|
+
switch (type) {
|
|
3
|
+
case 'date':
|
|
4
|
+
return new Date(text).toLocaleDateString()
|
|
5
|
+
case 'number':
|
|
6
|
+
return parseFloat(text)
|
|
7
|
+
case 'boolean':
|
|
8
|
+
return text.toLowerCase() === 'true'
|
|
9
|
+
default:
|
|
10
|
+
return text.toString()
|
|
11
|
+
}
|
|
12
|
+
}
|