feat: add general chat feature (without project/agent concept)

This commit is contained in:
AI Clinical Dev Team
2025-10-12 09:48:49 +08:00
parent 07704817ba
commit 348af7fee9
9 changed files with 688 additions and 0 deletions

139
frontend/src/api/chatApi.ts Normal file
View 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,
}