mirror of
https://github.com/langgenius/webapp-conversation.git
synced 2026-02-04 01:25:30 +08:00
feat: change service code
This commit is contained in:
102
service/base.ts
102
service/base.ts
@ -1,5 +1,7 @@
|
|||||||
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 = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
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) {
|
||||||
|
// mute handle message cut off
|
||||||
|
onData('', isFirstMessage, {
|
||||||
|
conversationId: bufferObj?.conversation_id,
|
||||||
|
messageId: bufferObj?.message_id,
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
|
||||||
// mute handle message cut off
|
|
||||||
onData('', isFirstMessage, {
|
|
||||||
conversationId: bufferObj?.conversation_id,
|
|
||||||
messageId: bufferObj?.id,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
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
|
|||||||
messageId: '',
|
messageId: '',
|
||||||
errorMessage: `${e}`,
|
errorMessage: `${e}`,
|
||||||
})
|
})
|
||||||
|
hasError = true
|
||||||
|
onCompleted?.(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!hasError)
|
||||||
read()
|
read()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
read()
|
read()
|
||||||
|
|||||||
@ -1,18 +1,23 @@
|
|||||||
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 () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user