fix: propagate real message IDs via SSE and fix regenerate model parameter

- Add message_id and user_message_id to SSE done events so frontend
  can replace temp IDs with real DB IDs, fixing "消息不存在" on regenerate
- Replace Body(default=None) with RegenerateRequest Pydantic model for
  proper JSON body parsing, fixing 422 Unprocessable Content
- Frontend always sends model in regenerate request body
- Pass selected model through message-item → regenerateMessage chain
- Various UI refinements to chat sidebar, quick questions, and layout

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 09:00:31 +08:00
parent 53a9380d7a
commit 0251a5e3ff
10 changed files with 549 additions and 858 deletions
+8 -21
View File
@@ -40,39 +40,25 @@ async function apiRequest<T>(
};
try {
console.log(`🌐 API请求: ${endpoint}`, { url, headers: defaultHeaders });
const response = await fetch(url, config);
console.log(`📡 API响应: ${endpoint}`, { status: response.status, ok: response.ok });
if (!response.ok) {
// 401错误:token无效或过期,跳转到登录页
if (response.status === 401) {
console.warn("🚨 收到401错误,token可能无效或过期");
console.log("🔍 当前路径:", window.location.pathname);
console.log("🔍 当前token:", localStorage.getItem("auth_token") ? "存在" : "不存在");
// 清除本地存储的token
localStorage.removeItem("auth_token");
// 只在非登录/注册页面才跳转,避免死循环
if (typeof window !== 'undefined' &&
if (typeof window !== 'undefined' &&
!window.location.pathname.includes('/login') &&
!window.location.pathname.includes('/register')) {
console.warn("🔄 Token无效或已过期,跳转到登录页");
// 使用replace而不是href,避免在历史记录中留下当前页面
window.location.replace("/login");
}
throw new Error("认证已过期,请重新登录");
}
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || `HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`API请求失败 ${endpoint}:`, error);
throw error;
}
}
@@ -443,7 +429,7 @@ export const chatAPI = {
knowledgeBaseIds?: string[],
signal?: AbortSignal,
onChunk?: (chunk: string) => void,
onComplete?: (sessionId: number) => void,
onComplete?: (sessionId: number, messageId?: number, userMessageId?: number) => void,
onError?: (error: string) => void,
onStatus?: (status: string) => void,
onThinking?: (step: { step: string; message: string; details?: Record<string, any>; timestamp?: string }) => void,
@@ -598,8 +584,8 @@ export const chatAPI = {
console.log("[DEBUG-STREAM] 接收chunk:", data.content);
onChunk?.(data.content);
} else if (data.type === "done") {
console.log("[DEBUG-STREAM] 流式完成, session_id:", data.session_id);
onComplete?.(data.session_id);
console.log("[DEBUG-STREAM] 流式完成, session_id:", data.session_id, "message_id:", data.message_id, "user_message_id:", data.user_message_id);
onComplete?.(data.session_id, data.message_id, data.user_message_id);
} else if (data.error) {
console.log("[DEBUG-STREAM] 流式错误:", data.error);
onError?.(data.error);
@@ -685,13 +671,14 @@ export const chatAPI = {
});
},
async regenerateMessage(messageId: number) {
async regenerateMessage(messageId: number, model?: string) {
return apiRequest<{
message: string;
new_message_id: number;
content: string;
}>(`/chat/messages/${messageId}/regenerate`, {
method: "POST",
body: JSON.stringify({ model: model || null }),
});
},