diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index dcd226e8..b8e1f3c6 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -27,7 +27,6 @@ import { IframeTestPanel } from "@/components/workspace/iframe-test-panel"; import { InputBox } from "@/components/workspace/input-box"; import { MessageList } from "@/components/workspace/messages"; import { ThreadContext } from "@/components/workspace/messages/context"; -import { ThreadMemoryTestPanel } from "@/components/workspace/thread-memory-test-panel"; import { ThreadTitle } from "@/components/workspace/thread-title"; import { Tooltip } from "@/components/workspace/tooltip"; import { useSpecificChatMode } from "@/components/workspace/use-chat-mode"; @@ -706,7 +705,6 @@ export default function ChatPage() { {/* MARK: 开发测试:iframe 通信功能测试面板 */} {/* {process.env.NODE_ENV !== "production" && } */} - {/* */} ); diff --git a/frontend/src/components/workspace/input-box.tsx b/frontend/src/components/workspace/input-box.tsx index 2c93afe7..32f2347c 100644 --- a/frontend/src/components/workspace/input-box.tsx +++ b/frontend/src/components/workspace/input-box.tsx @@ -4,6 +4,7 @@ import type { ChatStatus } from "ai"; import { Tour } from "antd"; import { CheckIcon, + BrainIcon, GraduationCapIcon, LightbulbIcon, Loader2Icon, @@ -97,6 +98,7 @@ import { Suggestion, Suggestions } from "../ai-elements/suggestion"; import { ScrollArea } from "../ui/scroll-area"; import { ModeHoverGuide } from "./mode-hover-guide"; +import { ThreadMemoryPanel } from "./thread-memory-panel"; import { Tooltip } from "./tooltip"; @@ -280,6 +282,7 @@ export function InputBox({ null, ); const [isFocused, setIsFocused] = useState(false); + const [memoryPanelOpen, setMemoryPanelOpen] = useState(false); const [references, setReferences] = useState([]); const [mentionQuery, setMentionQuery] = useState(""); const [mentionOpen, setMentionOpen] = useState(false); @@ -293,7 +296,8 @@ export function InputBox({ const { data: referenceFilesData } = useReferenceFiles(threadIdFromProps); // Welcome 态下禁用收缩,始终保持展开 - const effectiveIsFocused = (showWelcomeStyle ?? false) || isFocused; + const effectiveIsFocused = + (showWelcomeStyle ?? false) || isFocused || memoryPanelOpen; const shouldShowSuggestionList = showWelcomeStyle && searchParams.get("mode") !== "skill"; @@ -965,6 +969,7 @@ export function InputBox({ /> )} + {/* {!showWelcomeStyle && (
+ {/* 记忆按钮 */} +
+ + + + + + + + + + +
(null); + const [loadingSummary, setLoadingSummary] = useState(false); + const [savingSummary, setSavingSummary] = useState(false); + const [deletingMemory, setDeletingMemory] = useState(false); + const { t } = useI18n(); + if (!threadId || threadId === "new") return null; + + const handleLoadMemorySummary = async () => { + setLoadingSummary(true); + try { + const res = await fetch( + `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/memory-summary`, + ); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const data = (await res.json()) as { summary: string; memoryVersion: number }; + setMemorySummary(data.summary ?? ""); + setMemoryVersion(data.memoryVersion ?? 0); + toast.success(t.threadMemoryPanel.toastLoadSuccess); + } catch { + toast.error(t.threadMemoryPanel.toastLoadFailed); + } finally { + setLoadingSummary(false); + } + }; + + const handleSaveMemorySummary = async () => { + if (memoryVersion == null) return; + setSavingSummary(true); + try { + const res = await fetch( + `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/memory-summary`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ summary: memorySummary, memoryVersion }), + }, + ); + if (res.status === 409) { + toast.error(t.threadMemoryPanel.toastConflict); + return; + } + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const data = (await res.json()) as { memoryVersion?: number }; + if (typeof data.memoryVersion === "number") setMemoryVersion(data.memoryVersion); + toast.success(t.threadMemoryPanel.toastSaveSuccess); + } catch { + toast.error(t.threadMemoryPanel.toastSaveFailed); + } finally { + setSavingSummary(false); + } + }; + + const handleDeleteMemory = async () => { + setDeletingMemory(true); + try { + const res = await fetch( + `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/memory`, + { method: "DELETE" }, + ); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + setMemorySummary(""); + setMemoryVersion(0); + toast.success(t.threadMemoryPanel.toastDeleteSuccess); + } catch { + toast.error(t.threadMemoryPanel.toastDeleteFailed); + } finally { + setDeletingMemory(false); + } + }; + + return ( +
+
+ {t.threadMemoryPanel.title} +
+
+
+ + + +
+
+ {t.threadMemoryPanel.threadId}: {threadId.slice(0, 8)}... |{" "} + {t.threadMemoryPanel.version}:{" "} + {memoryVersion == null ? t.threadMemoryPanel.unavailableVersion : memoryVersion} +
+