|
@@ -30,9 +30,9 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="message-content ai-message chat" v-if="message.aiContent || message.loading">
|
|
|
|
|
|
|
+ <div class="message-content ai-message chat" v-if="message.aiContent || message.loading || message?.jsonData?.error || !message.aiContent">
|
|
|
<div v-if="message.aiContent" v-html="message.aiContent"></div>
|
|
<div v-if="message.aiContent" v-html="message.aiContent"></div>
|
|
|
- <svg v-else xmlns="http://www.w3.org/2000/svg" width="32" height="32"
|
|
|
|
|
|
|
+ <svg v-else-if="message.loading" xmlns="http://www.w3.org/2000/svg" width="32" height="32"
|
|
|
viewBox="0 0 24 24"><!-- Icon from SVG Spinners by Utkarsh Verma - https://github.com/n3r4zzurr0/svg-spinners/blob/main/LICENSE -->
|
|
viewBox="0 0 24 24"><!-- Icon from SVG Spinners by Utkarsh Verma - https://github.com/n3r4zzurr0/svg-spinners/blob/main/LICENSE -->
|
|
|
<circle cx="4" cy="12" r="3" fill="currentColor">
|
|
<circle cx="4" cy="12" r="3" fill="currentColor">
|
|
|
<animate id="svgSpinners3DotsBounce0" attributeName="cy" begin="0;svgSpinners3DotsBounce1.end+0.25s"
|
|
<animate id="svgSpinners3DotsBounce0" attributeName="cy" begin="0;svgSpinners3DotsBounce1.end+0.25s"
|
|
@@ -47,6 +47,12 @@
|
|
|
calcMode="spline" dur="0.6s" keySplines=".33,.66,.66,1;.33,0,.66,.33" values="12;6;12" />
|
|
calcMode="spline" dur="0.6s" keySplines=".33,.66,.66,1;.33,0,.66,.33" values="12;6;12" />
|
|
|
</circle>
|
|
</circle>
|
|
|
</svg>
|
|
</svg>
|
|
|
|
|
+ <div v-else-if="message?.jsonData?.error || !message.aiContent" class="error-message">
|
|
|
|
|
+ <div class="error-text">{{ message?.jsonData?.errorMessage || lang.ssRetryMessage }}</div>
|
|
|
|
|
+ <button class="retry-btn" @click="retryMessage(index)">
|
|
|
|
|
+ <IconRefresh class="retry-icon"/>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -141,6 +147,9 @@ interface ChatMessage {
|
|
|
url?: string
|
|
url?: string
|
|
|
}>
|
|
}>
|
|
|
jsonData?: {
|
|
jsonData?: {
|
|
|
|
|
+ error?: boolean
|
|
|
|
|
+ errorMessage?: string
|
|
|
|
|
+ retryable?: boolean
|
|
|
gType?: string
|
|
gType?: string
|
|
|
isGenerate?: boolean
|
|
isGenerate?: boolean
|
|
|
headUrl?: string
|
|
headUrl?: string
|
|
@@ -328,6 +337,9 @@ const sendMessage = () => {
|
|
|
title: file.title,
|
|
title: file.title,
|
|
|
id: file.id
|
|
id: file.id
|
|
|
}))
|
|
}))
|
|
|
|
|
+ if (!messages.value.at(-1).jsonData) {
|
|
|
|
|
+ messages.value.at(-1).jsonData = {}
|
|
|
|
|
+ }
|
|
|
chatLoading.value = true
|
|
chatLoading.value = true
|
|
|
sendAction(inputText.value)
|
|
sendAction(inputText.value)
|
|
|
inputText.value = ''
|
|
inputText.value = ''
|
|
@@ -424,6 +436,14 @@ const sendQuickAction = (action: string) => {
|
|
|
sendMessage()
|
|
sendMessage()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const retryMessage = (index: number) => {
|
|
|
|
|
+ const message = messages.value[index]
|
|
|
|
|
+ if (message && message?.jsonData?.retryable) {
|
|
|
|
|
+ inputText.value = message.content || ''
|
|
|
|
|
+ sendMessage()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
|
const session_name = ref('')
|
|
const session_name = ref('')
|
|
|
const slidesStore = useSlidesStore()
|
|
const slidesStore = useSlidesStore()
|
|
@@ -490,34 +510,66 @@ const sendAction = async (action: string) => {
|
|
|
${promptText}
|
|
${promptText}
|
|
|
query:${action}
|
|
query:${action}
|
|
|
`
|
|
`
|
|
|
- chat_stream(prompt, agentid2.value, props.userid || '', lang.lang, (event) => {
|
|
|
|
|
- if (event.type === 'message') {
|
|
|
|
|
- messages.value.at(-1).aiContent = md.render(event.data)
|
|
|
|
|
-
|
|
|
|
|
- messages.value.at(-1).loading = false
|
|
|
|
|
- prevChatResult()
|
|
|
|
|
- }
|
|
|
|
|
- else if (event.type === 'messageEnd') {
|
|
|
|
|
- messages.value.at(-1).aiContent = md.render(event.data)
|
|
|
|
|
- messages.value.at(-1).chatloading = false
|
|
|
|
|
- chatLoading.value = false
|
|
|
|
|
- prevChatResult()
|
|
|
|
|
- insertChat({
|
|
|
|
|
- answer: messages.value.at(-1).aiContent,
|
|
|
|
|
- problem: messages.value.at(-1).content,
|
|
|
|
|
- type: 'chat',
|
|
|
|
|
- alltext: messages.value.at(-1).aiContent,
|
|
|
|
|
- assistant_id: props.workJson.id
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- }, session_name.value, messages.value.at(-1).sourceFiles?.map(file => file.id).filter(Boolean)).then(controller => {
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 设置超时
|
|
|
|
|
+ const timeoutPromise = new Promise<never>((_, reject) => {
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ reject(new Error('请求超时'))
|
|
|
|
|
+ }, 30000) // 30秒超时
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const controller = await Promise.race([
|
|
|
|
|
+ chat_stream(prompt, agentid2.value, props.userid || '', lang.lang, (event) => {
|
|
|
|
|
+ if (event.type === 'message') {
|
|
|
|
|
+ messages.value.at(-1).aiContent = md.render(event.data)
|
|
|
|
|
+ messages.value.at(-1).loading = false
|
|
|
|
|
+ prevChatResult()
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (event.type === 'messageEnd') {
|
|
|
|
|
+ messages.value.at(-1).aiContent = md.render(event.data)
|
|
|
|
|
+ messages.value.at(-1).chatloading = false
|
|
|
|
|
+ chatLoading.value = false
|
|
|
|
|
+ prevChatResult()
|
|
|
|
|
+ addChat()
|
|
|
|
|
+ }
|
|
|
|
|
+ else if (event.type === 'error') {
|
|
|
|
|
+ errorSet()
|
|
|
|
|
+ }
|
|
|
|
|
+ }, session_name.value, messages.value.at(-1)?.sourceFiles?.map(file => file.id).filter(Boolean)),
|
|
|
|
|
+ timeoutPromise
|
|
|
|
|
+ ])
|
|
|
|
|
+
|
|
|
streamController.value = controller
|
|
streamController.value = controller
|
|
|
- }).catch(err => {
|
|
|
|
|
- chatLoading.value = false
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ catch (err) {
|
|
|
console.log('err', err)
|
|
console.log('err', err)
|
|
|
|
|
+ errorSet()
|
|
|
stopMessage()
|
|
stopMessage()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const errorSet = () => {
|
|
|
|
|
+ chatLoading.value = false
|
|
|
|
|
+ messages.value.at(-1).chatloading = false
|
|
|
|
|
+ messages.value.at(-1).loading = false
|
|
|
|
|
+ messages.value.at(-1).jsonData.error = true
|
|
|
|
|
+ messages.value.at(-1).jsonData.errorMessage = lang.ssRetryMessage
|
|
|
|
|
+ messages.value.at(-1).jsonData.retryable = true
|
|
|
|
|
+ addChat()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const addChat = () => {
|
|
|
|
|
+ insertChat({
|
|
|
|
|
+ answer: messages.value.at(-1).aiContent,
|
|
|
|
|
+ problem: messages.value.at(-1).content,
|
|
|
|
|
+ type: 'chat',
|
|
|
|
|
+ alltext: messages.value.at(-1).aiContent,
|
|
|
|
|
+ assistant_id: props.workJson.id,
|
|
|
|
|
+ jsonData: messages.value.at(-1).jsonData
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
import useCreateElement from '@/hooks/useCreateElement'
|
|
import useCreateElement from '@/hooks/useCreateElement'
|
|
|
import useSlideHandler from '@/hooks/useSlideHandler'
|
|
import useSlideHandler from '@/hooks/useSlideHandler'
|
|
|
const { createSlide } = useSlideHandler()
|
|
const { createSlide } = useSlideHandler()
|
|
@@ -895,6 +947,8 @@ onMounted(() => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.message-content {
|
|
.message-content {
|
|
|
|
|
+ word-break: break-word;
|
|
|
|
|
+
|
|
|
&.ai-message {
|
|
&.ai-message {
|
|
|
align-self: flex-start;
|
|
align-self: flex-start;
|
|
|
background: #fff;
|
|
background: #fff;
|
|
@@ -917,6 +971,40 @@ onMounted(() => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.error-message {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ color: #ef4444;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+
|
|
|
|
|
+ .error-text {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .retry-btn {
|
|
|
|
|
+ background: none;
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ color: #ff9300;
|
|
|
|
|
+ padding: 4px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+
|
|
|
|
|
+ .retry-icon {
|
|
|
|
|
+ width: 16px;
|
|
|
|
|
+ height: 16px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &:hover {
|
|
|
|
|
+ color: #e68a00;
|
|
|
|
|
+ transform: rotate(180deg);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.initial-state {
|
|
.initial-state {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|