feat(ThreadMemoryPanel): 新增会话记忆下拉面板并完成 i18n 接入
This commit is contained in:
parent
5736a2520c
commit
c5847d3222
@ -27,7 +27,6 @@ import { IframeTestPanel } from "@/components/workspace/iframe-test-panel";
|
|||||||
import { InputBox } from "@/components/workspace/input-box";
|
import { InputBox } from "@/components/workspace/input-box";
|
||||||
import { MessageList } from "@/components/workspace/messages";
|
import { MessageList } from "@/components/workspace/messages";
|
||||||
import { ThreadContext } from "@/components/workspace/messages/context";
|
import { ThreadContext } from "@/components/workspace/messages/context";
|
||||||
import { ThreadMemoryTestPanel } from "@/components/workspace/thread-memory-test-panel";
|
|
||||||
import { ThreadTitle } from "@/components/workspace/thread-title";
|
import { ThreadTitle } from "@/components/workspace/thread-title";
|
||||||
import { Tooltip } from "@/components/workspace/tooltip";
|
import { Tooltip } from "@/components/workspace/tooltip";
|
||||||
import { useSpecificChatMode } from "@/components/workspace/use-chat-mode";
|
import { useSpecificChatMode } from "@/components/workspace/use-chat-mode";
|
||||||
@ -706,7 +705,6 @@ export default function ChatPage() {
|
|||||||
|
|
||||||
{/* MARK: 开发测试:iframe 通信功能测试面板 */}
|
{/* MARK: 开发测试:iframe 通信功能测试面板 */}
|
||||||
{/* {process.env.NODE_ENV !== "production" && <IframeTestPanel />} */}
|
{/* {process.env.NODE_ENV !== "production" && <IframeTestPanel />} */}
|
||||||
{/* <ThreadMemoryTestPanel threadId={threadId} /> */}
|
|
||||||
</div>
|
</div>
|
||||||
</ThreadContext.Provider>
|
</ThreadContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import type { ChatStatus } from "ai";
|
|||||||
import { Tour } from "antd";
|
import { Tour } from "antd";
|
||||||
import {
|
import {
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
|
BrainIcon,
|
||||||
GraduationCapIcon,
|
GraduationCapIcon,
|
||||||
LightbulbIcon,
|
LightbulbIcon,
|
||||||
Loader2Icon,
|
Loader2Icon,
|
||||||
@ -97,6 +98,7 @@ import { Suggestion, Suggestions } from "../ai-elements/suggestion";
|
|||||||
import { ScrollArea } from "../ui/scroll-area";
|
import { ScrollArea } from "../ui/scroll-area";
|
||||||
|
|
||||||
import { ModeHoverGuide } from "./mode-hover-guide";
|
import { ModeHoverGuide } from "./mode-hover-guide";
|
||||||
|
import { ThreadMemoryPanel } from "./thread-memory-panel";
|
||||||
import { Tooltip } from "./tooltip";
|
import { Tooltip } from "./tooltip";
|
||||||
|
|
||||||
|
|
||||||
@ -280,6 +282,7 @@ export function InputBox({
|
|||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
const [memoryPanelOpen, setMemoryPanelOpen] = useState(false);
|
||||||
const [references, setReferences] = useState<PromptInputReference[]>([]);
|
const [references, setReferences] = useState<PromptInputReference[]>([]);
|
||||||
const [mentionQuery, setMentionQuery] = useState("");
|
const [mentionQuery, setMentionQuery] = useState("");
|
||||||
const [mentionOpen, setMentionOpen] = useState(false);
|
const [mentionOpen, setMentionOpen] = useState(false);
|
||||||
@ -293,7 +296,8 @@ export function InputBox({
|
|||||||
const { data: referenceFilesData } = useReferenceFiles(threadIdFromProps);
|
const { data: referenceFilesData } = useReferenceFiles(threadIdFromProps);
|
||||||
|
|
||||||
// Welcome 态下禁用收缩,始终保持展开
|
// Welcome 态下禁用收缩,始终保持展开
|
||||||
const effectiveIsFocused = (showWelcomeStyle ?? false) || isFocused;
|
const effectiveIsFocused =
|
||||||
|
(showWelcomeStyle ?? false) || isFocused || memoryPanelOpen;
|
||||||
const shouldShowSuggestionList =
|
const shouldShowSuggestionList =
|
||||||
showWelcomeStyle && searchParams.get("mode") !== "skill";
|
showWelcomeStyle && searchParams.get("mode") !== "skill";
|
||||||
|
|
||||||
@ -965,6 +969,7 @@ export function InputBox({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* {!showWelcomeStyle && (
|
{/* {!showWelcomeStyle && (
|
||||||
<div className="shrink-0 h-full">
|
<div className="shrink-0 h-full">
|
||||||
<ExitChattingButton
|
<ExitChattingButton
|
||||||
@ -976,6 +981,22 @@ export function InputBox({
|
|||||||
<div ref={attachmentsButtonTourRef} className="shrink-0 h-full">
|
<div ref={attachmentsButtonTourRef} className="shrink-0 h-full">
|
||||||
<AddAttachmentsButton />
|
<AddAttachmentsButton />
|
||||||
</div>
|
</div>
|
||||||
|
{/* 记忆按钮 */}
|
||||||
|
<div className="shrink-0 h-full">
|
||||||
|
<DropdownMenu open={memoryPanelOpen} onOpenChange={setMemoryPanelOpen}>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<WorkspaceToolButton
|
||||||
|
className="h-full"
|
||||||
|
disabled={!threadIdFromProps || threadIdFromProps === "new"}
|
||||||
|
>
|
||||||
|
<BrainIcon className="size-4" />
|
||||||
|
</WorkspaceToolButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start" className="w-auto p-0">
|
||||||
|
<ThreadMemoryPanel threadId={threadIdFromProps} />
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
<div className="min-w-0 grow basis-0 h-full">
|
<div className="min-w-0 grow basis-0 h-full">
|
||||||
<IframeSkillDialogButton
|
<IframeSkillDialogButton
|
||||||
skillButtonRef={skillButtonTourRef}
|
skillButtonRef={skillButtonTourRef}
|
||||||
|
|||||||
138
frontend/src/components/workspace/thread-memory-panel.tsx
Normal file
138
frontend/src/components/workspace/thread-memory-panel.tsx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
|
import { getBackendBaseURL } from "@/core/config";
|
||||||
|
import { useI18n } from "@/core/i18n/hooks";
|
||||||
|
|
||||||
|
type ThreadMemoryPanelProps = {
|
||||||
|
threadId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ThreadMemoryPanel({ threadId }: ThreadMemoryPanelProps) {
|
||||||
|
const [memorySummary, setMemorySummary] = useState("");
|
||||||
|
const [memoryVersion, setMemoryVersion] = useState<number | null>(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 (
|
||||||
|
<div className="w-[380px] space-y-2 rounded-lg border border-ws-divider bg-ws-surface-elevated p-3 shadow-lg">
|
||||||
|
<div className="text-sm font-semibold">
|
||||||
|
<span className="hidden sm:inline">{t.threadMemoryPanel.title}</span>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
void handleLoadMemorySummary();
|
||||||
|
}}
|
||||||
|
disabled={loadingSummary}
|
||||||
|
>
|
||||||
|
{loadingSummary ? t.threadMemoryPanel.loading : t.threadMemoryPanel.load}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
void handleSaveMemorySummary();
|
||||||
|
}}
|
||||||
|
disabled={savingSummary || memoryVersion == null}
|
||||||
|
>
|
||||||
|
{savingSummary ? t.threadMemoryPanel.saving : t.threadMemoryPanel.save}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => {
|
||||||
|
void handleDeleteMemory();
|
||||||
|
}}
|
||||||
|
disabled={deletingMemory}
|
||||||
|
>
|
||||||
|
{deletingMemory ? t.threadMemoryPanel.removing : t.threadMemoryPanel.remove}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-ws-text-subtle-strong">
|
||||||
|
{t.threadMemoryPanel.threadId}: {threadId.slice(0, 8)}... |{" "}
|
||||||
|
{t.threadMemoryPanel.version}:{" "}
|
||||||
|
{memoryVersion == null ? t.threadMemoryPanel.unavailableVersion : memoryVersion}
|
||||||
|
</div>
|
||||||
|
<Textarea
|
||||||
|
value={memorySummary}
|
||||||
|
onChange={(e) => setMemorySummary(e.target.value)}
|
||||||
|
placeholder={t.threadMemoryPanel.summaryPlaceholder}
|
||||||
|
className="min-h-32 bg-white/80"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,142 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import { toast } from "sonner";
|
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { getBackendBaseURL } from "@/core/config";
|
|
||||||
|
|
||||||
type ThreadMemoryTestPanelProps = {
|
|
||||||
threadId?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function ThreadMemoryTestPanel({ threadId }: ThreadMemoryTestPanelProps) {
|
|
||||||
const [memorySummary, setMemorySummary] = useState("");
|
|
||||||
const [memoryVersion, setMemoryVersion] = useState<number | null>(null);
|
|
||||||
const [loadingSummary, setLoadingSummary] = useState(false);
|
|
||||||
const [savingSummary, setSavingSummary] = useState(false);
|
|
||||||
const [deletingMemory, setDeletingMemory] = useState(false);
|
|
||||||
const [open, setOpen] = useState(true);
|
|
||||||
|
|
||||||
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("已加载会话记忆");
|
|
||||||
} catch {
|
|
||||||
toast.error("加载会话记忆失败");
|
|
||||||
} 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("记忆已更新,请先重新加载再保存");
|
|
||||||
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("会话记忆已保存");
|
|
||||||
} catch {
|
|
||||||
toast.error("保存会话记忆失败");
|
|
||||||
} 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("当前会话记忆已删除");
|
|
||||||
} catch {
|
|
||||||
toast.error("删除会话记忆失败");
|
|
||||||
} finally {
|
|
||||||
setDeletingMemory(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed right-4 bottom-4 z-50 w-[380px] rounded-lg border border-ws-divider bg-ws-surface-elevated p-3 shadow-lg">
|
|
||||||
<div className="mb-2 flex items-center justify-between">
|
|
||||||
<div className="text-sm font-semibold">Thread Memory TestPanel</div>
|
|
||||||
<Button size="sm" variant="ghost" onClick={() => setOpen((v) => !v)}>
|
|
||||||
{open ? "收起" : "展开"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
{open && (
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
void handleLoadMemorySummary();
|
|
||||||
}}
|
|
||||||
disabled={loadingSummary}
|
|
||||||
>
|
|
||||||
{loadingSummary ? "加载中..." : "查看记忆"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
onClick={() => {
|
|
||||||
void handleSaveMemorySummary();
|
|
||||||
}}
|
|
||||||
disabled={savingSummary || memoryVersion == null}
|
|
||||||
>
|
|
||||||
{savingSummary ? "保存中..." : "保存记忆"}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
variant="destructive"
|
|
||||||
onClick={() => {
|
|
||||||
void handleDeleteMemory();
|
|
||||||
}}
|
|
||||||
disabled={deletingMemory}
|
|
||||||
>
|
|
||||||
{deletingMemory ? "删除中..." : "删除记忆"}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-ws-text-subtle-strong">
|
|
||||||
threadId: {threadId.slice(0, 8)}... | version:{" "}
|
|
||||||
{memoryVersion == null ? "-" : memoryVersion}
|
|
||||||
</div>
|
|
||||||
<Textarea
|
|
||||||
value={memorySummary}
|
|
||||||
onChange={(e) => setMemorySummary(e.target.value)}
|
|
||||||
placeholder="这里显示会话记忆总结,可编辑后保存"
|
|
||||||
className="min-h-32 bg-white/80"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -264,6 +264,27 @@ export const enUS: Translations = {
|
|||||||
scrollToBottom: "Scroll to bottom",
|
scrollToBottom: "Scroll to bottom",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
threadMemoryPanel: {
|
||||||
|
title: "Thread Memory",
|
||||||
|
load: "Load memory",
|
||||||
|
loading: "Loading...",
|
||||||
|
save: "Save memory",
|
||||||
|
saving: "Saving...",
|
||||||
|
remove: "Delete memory",
|
||||||
|
removing: "Deleting...",
|
||||||
|
threadId: "Thread ID",
|
||||||
|
version: "Version",
|
||||||
|
unavailableVersion: "-",
|
||||||
|
summaryPlaceholder: "Thread memory summary is shown here. Edit it and save.",
|
||||||
|
toastLoadSuccess: "Thread memory loaded",
|
||||||
|
toastLoadFailed: "Failed to load thread memory",
|
||||||
|
toastConflict: "Memory changed. Please reload before saving.",
|
||||||
|
toastSaveSuccess: "Thread memory saved",
|
||||||
|
toastSaveFailed: "Failed to save thread memory",
|
||||||
|
toastDeleteSuccess: "Thread memory deleted",
|
||||||
|
toastDeleteFailed: "Failed to delete thread memory",
|
||||||
|
},
|
||||||
|
|
||||||
// Workspace Chat Page
|
// Workspace Chat Page
|
||||||
chatPage: {
|
chatPage: {
|
||||||
defaultSlogan: "Let's study and work together",
|
defaultSlogan: "Let's study and work together",
|
||||||
|
|||||||
@ -194,6 +194,27 @@ export interface Translations {
|
|||||||
scrollToBottom: string;
|
scrollToBottom: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
threadMemoryPanel: {
|
||||||
|
title: string;
|
||||||
|
load: string;
|
||||||
|
loading: string;
|
||||||
|
save: string;
|
||||||
|
saving: string;
|
||||||
|
remove: string;
|
||||||
|
removing: string;
|
||||||
|
threadId: string;
|
||||||
|
version: string;
|
||||||
|
unavailableVersion: string;
|
||||||
|
summaryPlaceholder: string;
|
||||||
|
toastLoadSuccess: string;
|
||||||
|
toastLoadFailed: string;
|
||||||
|
toastConflict: string;
|
||||||
|
toastSaveSuccess: string;
|
||||||
|
toastSaveFailed: string;
|
||||||
|
toastDeleteSuccess: string;
|
||||||
|
toastDeleteFailed: string;
|
||||||
|
};
|
||||||
|
|
||||||
// Workspace Chat Page
|
// Workspace Chat Page
|
||||||
chatPage: {
|
chatPage: {
|
||||||
defaultSlogan: string;
|
defaultSlogan: string;
|
||||||
|
|||||||
@ -252,6 +252,27 @@ export const zhCN: Translations = {
|
|||||||
scrollToBottom: "滚动到底部",
|
scrollToBottom: "滚动到底部",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
threadMemoryPanel: {
|
||||||
|
title: "会话记忆",
|
||||||
|
load: "查看记忆",
|
||||||
|
loading: "加载中...",
|
||||||
|
save: "保存记忆",
|
||||||
|
saving: "保存中...",
|
||||||
|
remove: "删除记忆",
|
||||||
|
removing: "删除中...",
|
||||||
|
threadId: "threadId",
|
||||||
|
version: "版本",
|
||||||
|
unavailableVersion: "-",
|
||||||
|
summaryPlaceholder: "这里显示会话记忆总结,可编辑后保存",
|
||||||
|
toastLoadSuccess: "已加载会话记忆",
|
||||||
|
toastLoadFailed: "加载会话记忆失败",
|
||||||
|
toastConflict: "记忆已更新,请先重新加载再保存",
|
||||||
|
toastSaveSuccess: "会话记忆已保存",
|
||||||
|
toastSaveFailed: "保存会话记忆失败",
|
||||||
|
toastDeleteSuccess: "当前会话记忆已删除",
|
||||||
|
toastDeleteFailed: "删除会话记忆失败",
|
||||||
|
},
|
||||||
|
|
||||||
// Workspace Chat Page
|
// Workspace Chat Page
|
||||||
chatPage: {
|
chatPage: {
|
||||||
defaultSlogan: "来,一起学习工作吧",
|
defaultSlogan: "来,一起学习工作吧",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user