fix: 对话格式兼容改造
This commit is contained in:
@@ -11,9 +11,10 @@ import { useHookFetch } from 'hook-fetch/vue';
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { Sender } from 'vue-element-plus-x';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { send } from '@/api';
|
||||
import { unifiedSend } from '@/api';
|
||||
import ModelSelect from '@/components/ModelSelect/index.vue';
|
||||
import Collapse from '@/layouts/components/Header/components/Collapse.vue';
|
||||
import { parseStreamChunk, convertToApiFormat, type UnifiedMessage } from '@/utils/apiFormatConverter';
|
||||
|
||||
import CreateChat from '@/layouts/components/Header/components/CreateChat.vue';
|
||||
import TitleEditing from '@/layouts/components/Header/components/TitleEditing.vue';
|
||||
@@ -63,8 +64,17 @@ const bubbleItems = ref<MessageItem[]>([]);
|
||||
const bubbleListRef = ref<BubbleListInstance | null>(null);
|
||||
const isSending = ref(false);
|
||||
|
||||
// 记录当前请求使用的 API 格式类型,用于正确解析响应
|
||||
const currentRequestApiType = ref<string>('');
|
||||
|
||||
// 创建统一发送请求的包装函数
|
||||
const unifiedSendWrapper = (params: any) => {
|
||||
const { data, apiType, modelId, sessionId } = params;
|
||||
return unifiedSend(data, apiType, modelId, sessionId);
|
||||
};
|
||||
|
||||
const { stream, loading: isLoading, cancel } = useHookFetch({
|
||||
request: send,
|
||||
request: unifiedSendWrapper,
|
||||
onError: async (error) => {
|
||||
isLoading.value = false;
|
||||
|
||||
@@ -133,51 +143,49 @@ watch(
|
||||
*/
|
||||
function handleDataChunk(chunk: AnyObject) {
|
||||
try {
|
||||
// 安全获取 delta 和 content
|
||||
const delta = chunk.choices?.[0]?.delta;
|
||||
const reasoningChunk = delta?.reasoning_content;
|
||||
const parsedChunk = delta?.content;
|
||||
// 使用统一的解析器,根据当前请求的 API 格式类型解析响应
|
||||
const parsed = parseStreamChunk(chunk, currentRequestApiType.value || 'Completions');
|
||||
console.log('✅ [解析结果]:', parsed);
|
||||
|
||||
// usage 处理(可以移动到 startSSE 里也可以写这里)
|
||||
if (chunk.usage) {
|
||||
const { prompt_tokens, completion_tokens, total_tokens } = chunk.usage;
|
||||
const latest = bubbleItems.value[bubbleItems.value.length - 1];
|
||||
const latest = bubbleItems.value[bubbleItems.value.length - 1];
|
||||
|
||||
// 处理 token 使用情况
|
||||
if (parsed.usage) {
|
||||
latest.tokenUsage = {
|
||||
prompt: prompt_tokens,
|
||||
completion: completion_tokens,
|
||||
total: total_tokens,
|
||||
prompt: parsed.usage.prompt_tokens || 0,
|
||||
completion: parsed.usage.completion_tokens || 0,
|
||||
total: parsed.usage.total_tokens || 0,
|
||||
};
|
||||
}
|
||||
|
||||
if (reasoningChunk) {
|
||||
const latest = bubbleItems.value[bubbleItems.value.length - 1];
|
||||
// 处理推理内容
|
||||
if (parsed.reasoning_content) {
|
||||
latest.thinkingStatus = 'thinking';
|
||||
latest.loading = true;
|
||||
latest.thinlCollapse = true;
|
||||
latest.reasoning_content += reasoningChunk;
|
||||
latest.reasoning_content += parsed.reasoning_content;
|
||||
}
|
||||
|
||||
if (parsedChunk) {
|
||||
const thinkStart = parsedChunk.includes('<think>');
|
||||
const thinkEnd = parsedChunk.includes('</think>');
|
||||
// 处理普通内容
|
||||
if (parsed.content) {
|
||||
const thinkStart = parsed.content.includes('<think>');
|
||||
const thinkEnd = parsed.content.includes('</think>');
|
||||
|
||||
if (thinkStart)
|
||||
isThinking = true;
|
||||
if (thinkEnd)
|
||||
isThinking = false;
|
||||
|
||||
const latest = bubbleItems.value[bubbleItems.value.length - 1];
|
||||
|
||||
if (isThinking) {
|
||||
latest.thinkingStatus = 'thinking';
|
||||
latest.loading = true;
|
||||
latest.thinlCollapse = true;
|
||||
latest.reasoning_content += parsedChunk.replace('<think>', '').replace('</think>', '');
|
||||
latest.reasoning_content += parsed.content.replace('<think>', '').replace('</think>', '');
|
||||
}
|
||||
else {
|
||||
latest.thinkingStatus = 'end';
|
||||
latest.loading = false;
|
||||
latest.content += parsedChunk;
|
||||
latest.content += parsed.content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,6 +220,15 @@ async function startSSE(chatContent: string) {
|
||||
|
||||
isSending.value = true;
|
||||
|
||||
// 记录当前请求使用的 API 格式类型,用于后续正确解析响应
|
||||
currentRequestApiType.value = modelStore.currentModelInfo.modelApiType || 'Completions';
|
||||
console.log('🚀 [发送请求] 当前模型信息:', {
|
||||
modelId: modelStore.currentModelInfo.modelId,
|
||||
modelName: modelStore.currentModelInfo.modelName,
|
||||
modelApiType: modelStore.currentModelInfo.modelApiType,
|
||||
currentRequestApiType: currentRequestApiType.value,
|
||||
});
|
||||
|
||||
try {
|
||||
// 清空输入框
|
||||
inputValue.value = '';
|
||||
@@ -309,14 +326,33 @@ async function startSSE(chatContent: string) {
|
||||
return baseMessage;
|
||||
});
|
||||
|
||||
// 使用 for-await 处理流式响应
|
||||
// 获取当前模型的 API 类型
|
||||
const apiType = modelStore.currentModelInfo.modelApiType || 'Completions';
|
||||
console.log('📤 [转换请求] API 类型:', apiType);
|
||||
console.log('📤 [转换前] 消息内容:', messagesContent);
|
||||
|
||||
// 根据 API 类型转换请求格式
|
||||
const convertedRequest = convertToApiFormat(
|
||||
messagesContent as UnifiedMessage[],
|
||||
apiType,
|
||||
modelStore.currentModelInfo.modelId ?? '',
|
||||
true,
|
||||
);
|
||||
|
||||
console.log('📤 [转换后] 请求体:', convertedRequest);
|
||||
|
||||
// 使用新的统一接口发送请求
|
||||
const sessionId = route.params?.id !== 'not_login' ? String(route.params?.id) : 'not_login';
|
||||
const modelId = modelStore.currentModelInfo.modelId ?? '';
|
||||
|
||||
for await (const chunk of stream({
|
||||
messages: messagesContent,
|
||||
sessionId: route.params?.id !== 'not_login' ? String(route.params?.id) : 'not_login',
|
||||
stream: true,
|
||||
userId: userStore.userInfo?.userId,
|
||||
model: modelStore.currentModelInfo.modelId ?? '',
|
||||
data: convertedRequest,
|
||||
apiType,
|
||||
modelId,
|
||||
sessionId,
|
||||
})) {
|
||||
console.log('📦 [接收响应] 原始数据块:', chunk.result);
|
||||
console.log('🔧 [解析响应] 使用格式:', currentRequestApiType.value);
|
||||
handleDataChunk(chunk.result as AnyObject);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user