chore: 可以连接到流式请求

This commit is contained in:
2025-12-11 17:24:14 +08:00
parent f0d3d1986f
commit 3898518ad0
2 changed files with 227 additions and 7 deletions

View File

@@ -1,3 +1,4 @@
import Config from './config.js'
import http from './http.js' import http from './http.js'
import store from '@/store/index.js' import store from '@/store/index.js'
@@ -32,6 +33,9 @@ export default {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
} }
// ✅ 发送请求前打印日志,用于调试
console.log('Sending request to:', params.url);
console.log('Request data:', params.data);
// 发送请求 // 发送请求
const response = await http.sendRequest({ const response = await http.sendRequest({
@@ -65,17 +69,231 @@ export default {
}, },
/** /**
* HTTP流式请求 * EventSource 流式聊天
* @param {string} message 用户消息
* @param {string} conversationId 会话ID可选
* @param {Function} onMessage 消息回调
* @param {Function} onComplete 完成回调
* @param {Function} onError 错误回调
* @returns {EventSource|null} EventSource实例
*/
chatWithEventSource(message, conversationId = '', onMessage, onComplete, onError) {
// 关闭之前的连接
if (window.currentEventSource) {
window.currentEventSource.close();
}
// 构建请求参数
const params = new URLSearchParams({
uniacid: store.state.uniacid || '1',
user_id: store.state.memberInfo?.id || '123456',
query: message,
conversation_id: conversationId || '',
stream: 'true'
});
const url = `/api/kefu/chat?${params.toString()}`;
try {
const eventSource = new EventSource(url);
window.currentEventSource = eventSource;
let aiMessage = '';
// 监听消息事件
eventSource.addEventListener('message', (event) => {
try {
const data = JSON.parse(event.data);
if (data.event === 'message') {
// 更新 AI 消息
aiMessage += data.answer || '';
if (onMessage) onMessage(data.answer || '');
}
if (data.event === 'message_end') {
// 对话完成
if (onComplete) onComplete({
conversation_id: data.conversation_id,
message: aiMessage
});
}
if (data.conversation_id) {
conversationId = data.conversation_id;
}
} catch (error) {
console.error('解析消息失败:', error);
}
});
// 监听完成事件
eventSource.addEventListener('done', (event) => {
try {
const data = JSON.parse(event.data);
if (onComplete) onComplete(data);
} catch (error) {
console.error('解析完成事件失败:', error);
}
});
// 监听关闭事件
eventSource.addEventListener('close', (event) => {
try {
const data = JSON.parse(event.data);
console.log('连接正常结束:', data);
} catch (error) {
console.error('解析关闭事件失败:', error);
}
window.currentEventSource = null;
});
// 监听错误事件
eventSource.addEventListener('error', (error) => {
console.error('EventSource错误:', error);
if (onError) onError({ error: 'EventSource连接错误' });
window.currentEventSource = null;
});
return eventSource;
} catch (error) {
console.error('创建EventSource失败:', error);
if (onError) onError({ error: error.message });
return null;
}
},
/**
* Fetch API 流式聊天
* @param {string} message 用户消息
* @param {string} conversationId 会话ID可选
* @param {Function} onMessage 消息回调
* @param {Function} onComplete 完成回调
* @param {Function} onError 错误回调
*/
async chatWithFetchStream(message, conversationId = '', onMessage, onComplete, onError) {
try {
// 构建请求体 - 支持JSON和FormData两种格式
let requestData;
let headers = {
'Accept': 'text/event-stream'
};
// 优先使用JSON格式
requestData = {
uniacid: store.state.uniacid || '1',
user_id: store.state.memberInfo?.id || '123456',
query: message,
conversation_id: conversationId || '',
stream: true,
inputs: {},
response_mode: 'streaming',
user: store.state.memberInfo?.id || '123456'
};
headers['Content-Type'] = 'application/json';
const response = await fetch('/api/kefu/chat', {
method: 'POST',
headers: headers,
body: JSON.stringify(requestData)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
if (!response.body) {
throw new Error('响应体不可用');
}
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
let aiMessage = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 解码新接收的数据
buffer += decoder.decode(value, { stream: true });
// 处理缓冲的数据,按行分割
let lineEnd;
while ((lineEnd = buffer.indexOf('\n')) !== -1) {
const line = buffer.substring(0, lineEnd);
buffer = buffer.substring(lineEnd + 1);
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.substring(6));
if (data.event === 'message' || data.event === 'text_message') {
const textContent = data.answer || data.text || '';
aiMessage += textContent;
if (onMessage) onMessage(textContent);
} else if (data.event === 'message_end' || data.event === 'done') {
if (onComplete) onComplete({
conversation_id: data.conversation_id,
message: aiMessage,
...data
});
} else if (data.event === 'error' && onError) {
onError(data);
}
if (data.conversation_id) {
conversationId = data.conversation_id;
}
} catch (e) {
console.warn('解析流式数据失败:', e);
}
}
}
}
// 处理剩余的缓冲数据
if (buffer.startsWith('data: ')) {
try {
const data = JSON.parse(buffer.substring(6));
if ((data.event === 'message' || data.event === 'text_message') && onMessage) {
const textContent = data.answer || data.text || '';
onMessage(textContent);
} else if ((data.event === 'message_end' || data.event === 'done') && onComplete) {
onComplete({
conversation_id: data.conversation_id,
message: aiMessage,
...data
});
}
} catch (e) {
console.warn('解析剩余数据失败:', e);
}
}
} catch (error) {
console.error('Fetch流式聊天请求失败:', error);
if (onError) onError({ error: error.message });
}
},
/**
* HTTP流式请求现有方法保持不变
*/ */
async sendHttpStream(message, onChunk, onComplete) { async sendHttpStream(message, onChunk, onComplete) {
const params = { const params = {
url: '/api/kefu/chat', url: '/api/kefu/chat',
data: { data: {
query: message, query: message,
conversation_id: this.generateConversationId(), conversation_id: '', // 可以在外部传入或保持为空让服务器生成
user_id: store.state.memberInfo?.id || 'anonymous', user_id: store.state.memberInfo?.id || 'anonymous',
stream: true, stream: true,
uniacid: store.state.uniacid, // 保留必填参数 uniacid: store.state.uniacid, // 保留必填参数
inputs: {},
response_mode: 'streaming',
user: store.state.memberInfo?.id || 'anonymous'
}, },
header: { header: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
@@ -85,16 +303,18 @@ export default {
// 使用fetch API进行流式请求H5环境 // 使用fetch API进行流式请求H5环境
// #ifdef H5 // #ifdef H5
try { try {
const response = await fetch(`/api/kefu/chat`, { const url = Config.baseUrl + `/api/kefu/chat`;
const response = await fetch(url, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
uniacid: store.state.uniacid || '1',
inputs: {}, inputs: {},
query: message, query: message,
response_mode: 'streaming', response_mode: 'streaming',
user: store.state.memberInfo?.id || 'anonymous' user_id: store.state.memberInfo?.id || 'anonymous'
}) })
}) })

View File

@@ -395,7 +395,7 @@ export default {
// 是否启用流式响应 // 是否启用流式响应
enableStreaming: { enableStreaming: {
type: Boolean, type: Boolean,
default: false default: true
}, },
// 流式响应速度(字符/秒) // 流式响应速度(字符/秒)
streamSpeed: { streamSpeed: {