Commit 2019d8f3 authored by Joel's avatar Joel

feat: change service code

parent ed839618
import { API_PREFIX } from '@/config' import { API_PREFIX } from '@/config'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/chat/type'
import type { VisionFile } from '@/types/app'
const TIME_OUT = 100000 const TIME_OUT = 100000
...@@ -21,20 +23,35 @@ const baseOptions = { ...@@ -21,20 +23,35 @@ const baseOptions = {
} }
export type IOnDataMoreInfo = { export type IOnDataMoreInfo = {
conversationId: string | undefined conversationId?: string
taskId?: string
messageId: string messageId: string
errorMessage?: string errorMessage?: string
errorCode?: string
} }
export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void
export type IOnCompleted = () => void export type IOnThought = (though: ThoughtItem) => void
export type IOnError = (msg: string) => void export type IOnFile = (file: VisionFile) => void
export type IOnMessageEnd = (messageEnd: MessageEnd) => void
export type IOnMessageReplace = (messageReplace: MessageReplace) => void
export type IOnAnnotationReply = (messageReplace: AnnotationReply) => void
export type IOnCompleted = (hasError?: boolean) => void
export type IOnError = (msg: string, code?: string) => void
type IOtherOptions = { type IOtherOptions = {
isPublicAPI?: boolean
bodyStringify?: boolean
needAllResponseContent?: boolean needAllResponseContent?: boolean
deleteContentType?: boolean
onData?: IOnData // for stream onData?: IOnData // for stream
onThought?: IOnThought
onFile?: IOnFile
onMessageEnd?: IOnMessageEnd
onMessageReplace?: IOnMessageReplace
onError?: IOnError onError?: IOnError
onCompleted?: IOnCompleted // for stream onCompleted?: IOnCompleted // for stream
getAbortController?: (abortController: AbortController) => void
} }
function unicodeToChar(text: string) { function unicodeToChar(text: string) {
...@@ -43,17 +60,18 @@ function unicodeToChar(text: string) { ...@@ -43,17 +60,18 @@ function unicodeToChar(text: string) {
}) })
} }
const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted) => { const handleStream = (response: Response, onData: IOnData, onCompleted?: IOnCompleted, onThought?: IOnThought, onMessageEnd?: IOnMessageEnd, onMessageReplace?: IOnMessageReplace, onFile?: IOnFile) => {
if (!response.ok) if (!response.ok)
throw new Error('Network response was not ok') throw new Error('Network response was not ok')
const reader = response.body.getReader() const reader = response.body?.getReader()
const decoder = new TextDecoder('utf-8') const decoder = new TextDecoder('utf-8')
let buffer = '' let buffer = ''
let bufferObj: any let bufferObj: Record<string, any>
let isFirstMessage = true let isFirstMessage = true
function read() { function read() {
reader.read().then((result: any) => { let hasError = false
reader?.read().then((result: any) => {
if (result.done) { if (result.done) {
onCompleted && onCompleted() onCompleted && onCompleted()
return return
...@@ -62,27 +80,51 @@ const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted ...@@ -62,27 +80,51 @@ const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted
const lines = buffer.split('\n') const lines = buffer.split('\n')
try { try {
lines.forEach((message) => { lines.forEach((message) => {
if (!message || !message.startsWith('data: ')) if (message.startsWith('data: ')) { // check if it starts with data:
return try {
try { bufferObj = JSON.parse(message.substring(6)) as Record<string, any>// remove data: and parse as json
bufferObj = JSON.parse(message.substring(6)) // remove data: and parse as json }
} catch (e) {
catch (e) { // mute handle message cut off
// mute handle message cut off onData('', isFirstMessage, {
onData('', isFirstMessage, { conversationId: bufferObj?.conversation_id,
conversationId: bufferObj?.conversation_id, messageId: bufferObj?.message_id,
messageId: bufferObj?.id, })
}) return
return }
if (bufferObj.status === 400 || !bufferObj.event) {
onData('', false, {
conversationId: undefined,
messageId: '',
errorMessage: bufferObj?.message,
errorCode: bufferObj?.code,
})
hasError = true
onCompleted?.(true)
return
}
if (bufferObj.event === 'message' || bufferObj.event === 'agent_message') {
// can not use format here. Because message is splited.
onData(unicodeToChar(bufferObj.answer), isFirstMessage, {
conversationId: bufferObj.conversation_id,
taskId: bufferObj.task_id,
messageId: bufferObj.id,
})
isFirstMessage = false
}
else if (bufferObj.event === 'agent_thought') {
onThought?.(bufferObj as ThoughtItem)
}
else if (bufferObj.event === 'message_file') {
onFile?.(bufferObj as VisionFile)
}
else if (bufferObj.event === 'message_end') {
onMessageEnd?.(bufferObj as MessageEnd)
}
else if (bufferObj.event === 'message_replace') {
onMessageReplace?.(bufferObj as MessageReplace)
}
} }
if (bufferObj.event !== 'message')
return
onData(unicodeToChar(bufferObj.answer), isFirstMessage, {
conversationId: bufferObj.conversation_id,
messageId: bufferObj.id,
})
isFirstMessage = false
}) })
buffer = lines[lines.length - 1] buffer = lines[lines.length - 1]
} }
...@@ -92,10 +134,12 @@ const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted ...@@ -92,10 +134,12 @@ const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted
messageId: '', messageId: '',
errorMessage: `${e}`, errorMessage: `${e}`,
}) })
hasError = true
onCompleted?.(true)
return return
} }
if (!hasError)
read() read()
}) })
} }
read() read()
......
import type { IOnCompleted, IOnData, IOnError } from './base' import type { IOnCompleted, IOnData, IOnError, IOnFile, IOnMessageEnd, IOnMessageReplace, IOnThought } from './base'
import { get, post, ssePost } from './base' import { get, post, ssePost } from './base'
import type { Feedbacktype } from '@/types/app' import type { Feedbacktype } from '@/types/app'
export const sendChatMessage = async (body: Record<string, any>, { onData, onCompleted, onError }: { export const sendChatMessage = async (body: Record<string, any>, { onData, onCompleted, onThought, onFile, onError, getAbortController, onMessageEnd, onMessageReplace }: {
onData: IOnData onData: IOnData
onCompleted: IOnCompleted onCompleted: IOnCompleted
onFile: IOnFile
onThought: IOnThought
onMessageEnd: IOnMessageEnd
onMessageReplace: IOnMessageReplace
onError: IOnError onError: IOnError
getAbortController?: (abortController: AbortController) => void
}) => { }) => {
return ssePost('chat-messages', { return ssePost('chat-messages', {
body: { body: {
...body, ...body,
response_mode: 'streaming', response_mode: 'streaming',
}, },
}, { onData, onCompleted, onError }) }, { onData, onCompleted, onThought, onFile, onError, getAbortController, onMessageEnd, onMessageReplace })
} }
export const fetchConversations = async () => { export const fetchConversations = async () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment