fix: 对话格式兼容改造

This commit is contained in:
Gsh
2026-01-11 00:15:31 +08:00
parent a9a9e45b7c
commit 53d70ef9d7
7 changed files with 651 additions and 64 deletions

View File

@@ -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);
}
}