feat: add general chat feature (without project/agent concept)
This commit is contained in:
139
frontend/src/api/chatApi.ts
Normal file
139
frontend/src/api/chatApi.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* 通用对话API
|
||||
* 无需项目和智能体概念,纯大模型对话
|
||||
*/
|
||||
|
||||
export interface GeneralConversation {
|
||||
id: string
|
||||
userId: string
|
||||
title: string
|
||||
modelName?: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface GeneralMessage {
|
||||
id: string
|
||||
conversationId: string
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
model?: string
|
||||
tokens?: number
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface SendChatMessageData {
|
||||
content: string
|
||||
modelType: string
|
||||
knowledgeBaseIds?: string[]
|
||||
conversationId?: string
|
||||
}
|
||||
|
||||
export interface ApiResponse<T = any> {
|
||||
success: boolean
|
||||
data?: T
|
||||
message?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息(流式输出)
|
||||
*/
|
||||
export const sendMessageStream = async (
|
||||
data: SendChatMessageData,
|
||||
onChunk: (content: string) => void,
|
||||
onComplete: (conversationId: string) => void,
|
||||
onError: (error: Error) => void
|
||||
) => {
|
||||
try {
|
||||
console.log('🚀 [chatApi] 发送通用对话消息', data)
|
||||
|
||||
const response = await fetch('/api/v1/chat/stream', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader()
|
||||
if (!reader) {
|
||||
throw new Error('Response body is null')
|
||||
}
|
||||
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ''
|
||||
let conversationId = data.conversationId || ''
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
|
||||
if (done) {
|
||||
break
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
const lines = buffer.split('\n')
|
||||
buffer = lines.pop() || ''
|
||||
|
||||
for (const line of lines) {
|
||||
const trimmedLine = line.trim()
|
||||
if (!trimmedLine || !trimmedLine.startsWith('data:')) {
|
||||
continue
|
||||
}
|
||||
|
||||
const dataStr = trimmedLine.slice(5).trim()
|
||||
|
||||
if (dataStr === '[DONE]') {
|
||||
console.log('✅ [chatApi] 流式输出完成')
|
||||
onComplete(conversationId)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const chunk = JSON.parse(dataStr)
|
||||
if (chunk.content) {
|
||||
onChunk(chunk.content)
|
||||
}
|
||||
// 从第一个chunk中提取conversationId(如果是新对话)
|
||||
if (!conversationId && chunk.conversationId) {
|
||||
conversationId = chunk.conversationId
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse SSE chunk:', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ [chatApi] 发送消息失败:', error)
|
||||
onError(error as Error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对话列表
|
||||
*/
|
||||
export const getConversations = async (): Promise<ApiResponse<GeneralConversation[]>> => {
|
||||
const response = await fetch('/api/v1/chat/conversations')
|
||||
return response.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除对话
|
||||
*/
|
||||
export const deleteConversation = async (id: string): Promise<ApiResponse> => {
|
||||
const response = await fetch(`/api/v1/chat/conversations/${id}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
return response.json()
|
||||
}
|
||||
|
||||
export default {
|
||||
sendMessageStream,
|
||||
getConversations,
|
||||
deleteConversation,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user