feat: complete grounded digital avatar chat experience
This commit is contained in:
@@ -90,6 +90,7 @@ export interface Avatar {
|
||||
tokenBalance: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
config?: Record<string, any>
|
||||
}
|
||||
|
||||
// 获取分身列表
|
||||
@@ -108,6 +109,14 @@ export const createAvatar = (data: Partial<Avatar>) =>
|
||||
export const updateAvatar = (id: string, data: Partial<Avatar>) =>
|
||||
request.put<Avatar>(`/avatar/${id}`, data)
|
||||
|
||||
export const uploadAvatarPhoto = (id: string, file: File) => {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
return request.post<{ photoUrl: string }>(`/avatar/${id}/photo`, form, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
}
|
||||
|
||||
// 删除分身
|
||||
export const deleteAvatar = (id: string) =>
|
||||
request.delete(`/avatar/${id}`)
|
||||
@@ -259,6 +268,63 @@ export interface ChatResponse {
|
||||
export const sendAvatarChat = (avatarId: string, payload: { message: string; history?: ChatMessage[] }) =>
|
||||
request.post<ChatResponse>(`/avatar/${avatarId}/chat`, payload)
|
||||
|
||||
export interface PublicAvatar {
|
||||
id: string
|
||||
name: string
|
||||
displayName: string
|
||||
description?: string
|
||||
photoUrl?: string
|
||||
emoji?: string
|
||||
status: 'active' | 'inactive' | 'training'
|
||||
}
|
||||
|
||||
export const createAvatarShareLink = (avatarId: string) =>
|
||||
request.post<{ shareToken: string }>(`/avatar/${avatarId}/share`)
|
||||
|
||||
export const getPublicAvatar = (shareToken: string) =>
|
||||
request.get<PublicAvatar>(`/public/avatar/${shareToken}`)
|
||||
|
||||
export const sendPublicAvatarChat = (shareToken: string, payload: { message: string; history?: ChatMessage[] }) =>
|
||||
request.post<ChatResponse>(`/public/avatar/${shareToken}/chat`, payload)
|
||||
|
||||
type ChatStreamHandlers = {
|
||||
onMeta: (meta: Pick<ChatResponse, 'source' | 'references'>) => void
|
||||
onDelta: (content: string) => void
|
||||
}
|
||||
|
||||
const streamChat = async (path: string, payload: { message: string; history?: ChatMessage[] }, handlers: ChatStreamHandlers) => {
|
||||
const headers: Record<string, string> = { 'Content-Type': 'application/json', Accept: 'text/event-stream' }
|
||||
if (_authToken) headers.Authorization = `Bearer ${_authToken}`
|
||||
const response = await fetch(`${resolveBaseURL()}${path}`, { method: 'POST', headers, body: JSON.stringify(payload) })
|
||||
if (!response.ok || !response.body) throw new Error(`对话请求失败(${response.status})`)
|
||||
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
buffer += decoder.decode(value || new Uint8Array(), { stream: !done })
|
||||
const events = buffer.split('\n\n')
|
||||
buffer = events.pop() || ''
|
||||
for (const eventBlock of events) {
|
||||
const event = eventBlock.match(/^event:\s*(.+)$/m)?.[1] || 'message'
|
||||
const data = eventBlock.match(/^data:\s*(.+)$/m)?.[1]
|
||||
if (!data) continue
|
||||
const parsed = JSON.parse(data)
|
||||
if (event === 'meta') handlers.onMeta(parsed)
|
||||
if (event === 'delta') handlers.onDelta(parsed.content || '')
|
||||
if (event === 'error') throw new Error(parsed.message || '对话暂时不可用')
|
||||
}
|
||||
if (done) break
|
||||
}
|
||||
}
|
||||
|
||||
export const streamAvatarChat = (avatarId: string, payload: { message: string; history?: ChatMessage[] }, handlers: ChatStreamHandlers) =>
|
||||
streamChat(`/avatar/${avatarId}/chat/stream`, payload, handlers)
|
||||
|
||||
export const streamPublicAvatarChat = (shareToken: string, payload: { message: string; history?: ChatMessage[] }, handlers: ChatStreamHandlers) =>
|
||||
streamChat(`/public/avatar/${shareToken}/chat/stream`, payload, handlers)
|
||||
|
||||
// ==================== 会会用户资料 API ====================
|
||||
|
||||
export interface UserProfile {
|
||||
|
||||
Reference in New Issue
Block a user