Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 54 additions & 50 deletions ui/src/components/ai-chat/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -423,73 +423,77 @@ function getChartOpenId(chat?: any, problem?: string, re_chat?: boolean, other_p
*/
const getWrite = (chat: any, reader: any, stream: boolean) => {
let tempResult = ''
/**
*
* @param done 是否结束
* @param value 值
*/
const write_stream = ({ done, value }: { done: boolean; value: any }) => {

const write_stream = async () => {
try {
if (done) {
ChatManagement.close(chat.id)
return
}
const decoder = new TextDecoder('utf-8')
let str = decoder.decode(value, { stream: true })
// 这里解释一下 start 因为数据流返回流并不是按照后端chunk返回 我们希望得到的chunk是data:{xxx}\n\n 但是它获取到的可能是 data:{ -> xxx}\n\n 总而言之就是 fetch不能保证每个chunk都说以data:开始 \n\n结束
tempResult += str
const split = tempResult.match(/data:.*}\n\n/g)
if (split) {
str = split.join('')
tempResult = tempResult.replace(str, '')
} else {
return reader.read().then(write_stream)
}
// 这里解释一下 end
if (str && str.startsWith('data:')) {
while (true) {
const { done, value } = await reader.read()

if (done) {
ChatManagement.close(chat.id)
return
}

const decoder = new TextDecoder('utf-8')
let str = decoder.decode(value, { stream: true })

tempResult += str
const split = tempResult.match(/data:.*?}\n\n/g)

if (split) {
for (const index in split) {
const chunk = JSON?.parse(split[index].replace('data:', ''))
str = split.join('')
tempResult = tempResult.replace(str, '')

// 批量处理所有 chunk
for (const item of split) {
const chunk = JSON.parse(item.replace('data:', ''))
chat.chat_id = chunk.chat_id
chat.record_id = chunk.chat_record_id

if (!chunk.is_end) {
ChatManagement.appendChunk(chat.id, chunk)
}

if (chunk.is_end) {
// 流处理成功 返回成功回调
return Promise.resolve()
}
}
}
// 如果没有匹配到完整chunk,继续读取下一块
}
} catch (e) {
return Promise.reject(e)
}
return reader.read().then(write_stream)
}
/**
* 处理 json 响应
* @param param0
*/
const write_json = ({ done, value }: { done: boolean; value: any }) => {
if (done) {
const result_block = JSON.parse(tempResult)
if (result_block.code === 500) {
return Promise.reject(result_block.message)
} else {
if (result_block.content) {
ChatManagement.append(chat.id, result_block.content)

const write_json = async () => {
try {
while (true) {
const { done, value } = await reader.read()

if (done) {
const result_block = JSON.parse(tempResult)
if (result_block.code === 500) {
return Promise.reject(result_block.message)
} else {
if (result_block.content) {
ChatManagement.append(chat.id, result_block.content)
}
}
ChatManagement.close(chat.id)
return
}

if (value) {
const decoder = new TextDecoder('utf-8')
tempResult += decoder.decode(value)
}
}
ChatManagement.close(chat.id)
return
}
if (value) {
const decoder = new TextDecoder('utf-8')
tempResult += decoder.decode(value)
} catch (e) {
return Promise.reject(e)
}
return reader.read().then(write_json)
}

return stream ? write_stream : write_json
}
const errorWrite = (chat: any, message?: string) => {
Expand Down Expand Up @@ -646,7 +650,7 @@ const handleScroll = () => {
// 内部高度小于外部高度 就需要出滚动条
if (scrollDiv.value.wrapRef.offsetHeight < dialogScrollbar.value.scrollHeight) {
// 滚动到底部
scrollDiv.value.setScrollTop(dialogScrollbar.value.scrollHeight);
scrollDiv.value.setScrollTop(dialogScrollbar.value.scrollHeight)
}
}
}
Expand Down Expand Up @@ -758,10 +762,10 @@ watch(
chatList,
() => {
nextTick(() => {
handleScroll(); // 确保 DOM 更新后再滚动
});
handleScroll() // 确保 DOM 更新后再滚动
})
},
{deep: true, immediate: true},
{ deep: true, immediate: true },
)

defineExpose({
Expand Down