fix: async document processing in background threads, improve chat UX

- Fix knowledge_base_service calling async process_document synchronously
  in background threads — use asyncio.new_event_loop().run_until_complete()
  instead of bare call that returned unawaited coroutine
- Same fix for _update_document and reindex_document
- Replace ModeSelector with auto-detect: RAG mode when knowledge bases selected
- Convert regenerateMessage to streaming (was synchronous API call)
- Show "知识库检索模式" indicator above input when KB selected
- Remove loadSessions side-effect from streaming onComplete
- Fix chat page loading state to avoid flash
- Improve forum page spacing and sizing

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 11:33:32 +08:00
parent 50faf7ad4e
commit 62da8b3c58
5 changed files with 250 additions and 157 deletions
+10 -5
View File
@@ -4,10 +4,9 @@ import { useState, useRef, useEffect } from "react";
import { useChatStore } from "@/store/chat";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Send, Square, Bot, ChevronDown, Settings2 } from "lucide-react";
import { Send, Square, Bot, ChevronDown, Settings2, Database } from "lucide-react";
import MessageList from "./message-list";
import QuickQuestions from "./quick-questions";
import ModeSelector, { ChatMode } from "./mode-selector";
import ModelSelector from "./model-selector";
import KnowledgeSelector, { KnowledgeBase } from "./knowledge-selector";
import { cn } from "@/lib/utils";
@@ -16,7 +15,6 @@ import { knowledgeBaseAPI } from "@/lib/api";
export default function ChatInterface() {
const [inputMessage, setInputMessage] = useState("");
const [isComposing, setIsComposing] = useState(false);
const [chatMode, setChatMode] = useState<ChatMode>("normal");
const [selectedModel, setSelectedModel] = useState("deepseek-ai/DeepSeek-V3");
const [selectedKnowledgeBases, setSelectedKnowledgeBases] = useState<string[]>([]);
const [systemKnowledgeBases, setSystemKnowledgeBases] = useState<KnowledgeBase[]>([]);
@@ -72,7 +70,8 @@ export default function ChatInterface() {
await selectSession(newSession.id);
}
}
await streamMessage(message, chatMode, selectedKnowledgeBases, selectedModel);
const mode = selectedKnowledgeBases.length > 0 ? "rag" : "normal";
await streamMessage(message, mode, selectedKnowledgeBases, selectedModel);
};
const handleKeyPress = (e: React.KeyboardEvent) => {
@@ -124,7 +123,6 @@ export default function ChatInterface() {
{/* Settings panel — collapsible */}
{showSettings && (
<div className="flex items-center gap-2 mb-3 pb-3 border-b border-border/30">
<ModeSelector mode={chatMode} onModeChange={setChatMode} />
<ModelSelector
selectedModel={selectedModel}
onModelChange={setSelectedModel}
@@ -156,6 +154,12 @@ export default function ChatInterface() {
</Button>
<div className="flex-1 relative">
{selectedKnowledgeBases.length > 0 && (
<div className="absolute -top-0.5 left-3 right-3 flex items-center gap-1 text-[10px] text-primary font-medium pointer-events-none">
<Database className="w-3 h-3" />
<span></span>
</div>
)}
<input
ref={inputRef}
value={inputMessage}
@@ -167,6 +171,7 @@ export default function ChatInterface() {
disabled={isLoading || isStreaming}
className={cn(
"w-full h-10 px-4 rounded-xl text-sm",
selectedKnowledgeBases.length > 0 && "pt-4",
"bg-muted/40 border border-border/30",
"placeholder:text-muted-foreground/60",
"focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary/30",