对话功能

main
wangyunfei 1 day ago
parent f4e7f3ebd9
commit 309ddb6e4a

@ -80,13 +80,85 @@ export const callDeepSeekAPI = async (messages, options = {}) => {
return content
}
// 本地服务调用(调试版:打印详细日志,方便逐步排查)
export const callLocalChatAPI = async (prompt) => {
const base = '/api/chat/stream'
const url = `${base}?prompt=${encodeURIComponent(prompt)}`
// DeepSeek 流式调用
// 逐步调试日志
// eslint-disable-next-line no-console
console.debug('[callLocalChatAPI] url:', url)
try {
// eslint-disable-next-line no-console
console.debug('[callLocalChatAPI] fetch start')
const resp = await fetch(url, { method: 'GET' })
// eslint-disable-next-line no-console
console.debug('[callLocalChatAPI] fetch done', {
ok: resp.ok,
status: resp.status,
statusText: resp.statusText,
headers: Object.fromEntries(resp.headers.entries())
})
if (!resp.ok) {
const txt = await resp.text().catch(() => null)
// eslint-disable-next-line no-console
console.error('[callLocalChatAPI] non-OK response body:', txt)
const msg = txt || resp.statusText || `HTTP_${resp.status}`
const err = new Error(msg)
err.status = resp.status
throw err
}
// 最简单:一次性读取完整响应文本(非流式),然后从 SSE 格式中提取第一个 event:message 的 data
const raw = await resp.text().catch(e => {
// eslint-disable-next-line no-console
console.error('[callLocalChatAPI] text read error:', e)
return ''
})
// eslint-disable-next-line no-console
console.debug('[callLocalChatAPI] raw length:', raw?.length || 0, 'preview:', (raw || '').slice(0, 500))
if (!raw) throw new Error('EMPTY_RESPONSE')
// 按空行分块,优先找到 event:message 的 data
const blocks = raw.split(/\r?\n\r?\n/).map(b => b.trim()).filter(Boolean)
for (const block of blocks) {
const lines = block.split(/\r?\n/).map(l => l.trim())
let eventType = ''
const dataLines = []
for (const line of lines) {
if (/^event\s*:/i.test(line)) {
eventType = line.split(':').slice(1).join(':').trim()
} else if (/^data\s*:/i.test(line)) {
dataLines.push(line.split(':').slice(1).join(':'))
}
}
if (eventType === 'message' && dataLines.length > 0) {
const message = dataLines.join('\n').trim()
// eslint-disable-next-line no-console
console.debug('[callLocalChatAPI] parsed message data preview:', message.slice(0, 300))
if (!message) throw new Error('EMPTY_RESPONSE')
return message
}
}
// 回退:取第一个 data: 行的内容
const firstDataMatch = raw.match(/^[ \t]*data\s*:(.*)$/im)
if (firstDataMatch && firstDataMatch[1]) {
const fallback = firstDataMatch[1].trim()
// eslint-disable-next-line no-console
console.debug('[callLocalChatAPI] fallback data:', fallback.slice(0, 300))
return fallback
}
// 最后回退:返回原始文本
return raw
} catch (err) {
// eslint-disable-next-line no-console
console.error('[callLocalChatAPI] ERROR:', err)
throw err
}
}
// 错误处理函数
export const handleAPIError = (error) => {

@ -25,7 +25,7 @@ import {
ReloadOutlined,
PaperClipOutlined,
} from '@ant-design/icons'
import { callDeepSeekAPI, handleAPIError } from './models/api'
import { callLocalChatAPI, handleAPIError } from '@/config/api'
import { conversationStore } from '@/utils/pageConversationStore'
import './ChatConversation.less'
@ -136,6 +136,7 @@ function ChatConversation() {
setInputValue('')
setLoading(true)
try {
// 准备发送给API的消息历史
const apiMessages = currentMessages.map(msg => ({
@ -146,8 +147,9 @@ function ChatConversation() {
content: messageText
})
// 调用DeepSeek API
const assistantResponse = await callDeepSeekAPI(apiMessages)
// 调用本地后端接口(把用户输入作为 prompt 传给后端)
// 注意callLocalChatAPI 目前只发送 messageText如果后端需要历史可改为序列化 apiMessages 并后端解析
const assistantResponse = await callLocalChatAPI(messageText)
// 添加助手回复到独立存储
conversationStore.addMessage('assistant', assistantResponse)
@ -394,9 +396,9 @@ function ChatConversation() {
<Select
className='ds-select'
dropdownClassName="ds-select-dropdown"
defaultValue="模型反馈"
defaultValue="模型反馈"
style={{
minWidth: 120, maxWidth: 200, marginLeft: 'auto', marginRight: 8
minWidth: 120, maxWidth: 200, marginLeft: 'auto',marginRight: 8
}}
onchange={handleChange}
options={[

Loading…
Cancel
Save