代码提交

This commit is contained in:
fengxiangdebaozi committed 2026-08-12 14:32:47 +08:00
1 parent 17a12bb7b6
commit 783e698b6e
10 files changed
+758 -173

No files matched your search

+81 -10
View File
@@ -44,6 +44,10 @@ import { PanelLeftOpen } from "lucide-react";
import { useAgentSelector, useAgentsPaginated } from "@/core/agents";
import { useI18n } from "@/core/i18n/hooks";
import { useModels } from "@/core/models/hooks";
import {
isSingleRoundRagProfile,
SINGLE_ROUND_RAG_MODELS,
} from "@/core/models/single-round-rag";
import { useNotification } from "@/core/notification/hooks";
import {
getLastAgentId,
@@ -52,6 +56,7 @@ import {
useThreadSettings,
} from "@/core/settings";
import { useThreadStream } from "@/core/threads/hooks";
import { useSingleRoundRagMessages } from "@/core/threads/use-single-round-rag";
import { useLayoutMode } from "@/core/system-settings/hooks";
import { useRouteBase } from "@/core/route-base";
import { textOfMessage } from "@/core/threads/utils";
@@ -226,6 +231,20 @@ export default function ChatPage() {
}
},
});
const singleRoundRag = useSingleRoundRagMessages(threadId);
const singleRoundRagSelected = isSingleRoundRagProfile(settings.context);
const displayThread = useMemo(() => {
if (singleRoundRag.messages.length === 0 && !singleRoundRag.isLoading) {
return thread;
}
const messages = [...thread.messages, ...singleRoundRag.messages];
return {
...thread,
values: { ...thread.values, messages },
messages,
isLoading: thread.isLoading || singleRoundRag.isLoading,
} as typeof thread;
}, [singleRoundRag.isLoading, singleRoundRag.messages, thread]);
const writingArtifactPath = useMemo(() => {
const isMarkdown = (path: string | null | undefined) =>
@@ -240,6 +259,46 @@ export default function ChatPage() {
const handleSubmit = useCallback(
(message: PromptInputMessage) => {
if (singleRoundRagSelected) {
if (message.files && message.files.length > 0) {
toast.error("单轮 RAG 问答暂不支持附件,请移除附件后重试。");
return;
}
setHasStartedConversation(true);
window.dispatchEvent(
new CustomEvent("workspace:sandbox-resume-autoscroll"),
);
void (async () => {
try {
let targetThreadId = threadId;
if (isNewThread) {
const created = await getAPIClient(isMock).threads.create({});
targetThreadId = created.thread_id;
setThreadId(targetThreadId);
setIsNewThread(false);
history.replaceState(
null,
"",
`${window.location.pathname}${window.location.search}#${routeBase}/chats/${targetThreadId}`,
);
const title = message.text.trim().replace(/\s+/g, " ").slice(0, 60);
if (title) {
void getAPIClient(isMock).threads
.updateState(targetThreadId, { values: { title } })
.catch(() => undefined);
}
}
await singleRoundRag.submit({
targetThreadId,
query: message.text,
modelName: settings.context.model_name ?? "OpenAI3",
});
} catch (error) {
toast.error(error instanceof Error ? error.message : "单轮 RAG 问答失败。");
}
})();
return;
}
// If the user picked an agent before sending the opening turn, hand off
// to AgentChatPage so the conversation runs against that agent. We stash
// the pending text in sessionStorage because the next/navigation shim
@@ -332,7 +391,13 @@ export default function ChatPage() {
isMock,
router,
settings.context.writing_mode,
settings.context.model_name,
writingArtifactPath,
singleRoundRag,
singleRoundRagSelected,
routeBase,
setIsNewThread,
setThreadId,
],
);
@@ -421,8 +486,12 @@ export default function ChatPage() {
saveLastAgentId(agentId);
}, []);
const handleStop = useCallback(async () => {
if (singleRoundRag.isLoading) {
singleRoundRag.stop();
return;
}
await thread.stop();
}, [thread]);
}, [singleRoundRag, thread]);
const promptPrefixProp = useMemo(() => {
if (!promptPrefixSettings?.enabled) return undefined;
@@ -468,7 +537,7 @@ export default function ChatPage() {
const compactActionsHidden = isHideChromeActive();
return (
<ThreadContext.Provider value={{ thread, isMock }}>
<ThreadContext.Provider value={{ thread: displayThread, isMock }}>
<ChatBox threadId={threadId}>
<div
className={cn(
@@ -486,7 +555,7 @@ export default function ChatPage() {
)}
>
<div className="flex w-full items-center text-sm font-medium">
<ThreadTitle threadId={threadId} thread={thread} />
<ThreadTitle threadId={threadId} thread={displayThread} />
</div>
<div className="flex items-center gap-2">
{/* 仅在收起态显示「展开历史」钮;展开后由侧栏内(新对话右侧)的收起钮负责收起。 */}
@@ -505,7 +574,7 @@ export default function ChatPage() {
)}
<TokenUsageIndicator
enabled={tokenUsageEnabled}
messages={thread.messages}
messages={displayThread.messages}
/>
<ExportTrigger threadId={threadId} />
<KnowledgeSaveTrigger threadId={threadId} />
@@ -543,7 +612,7 @@ export default function ChatPage() {
<MessageList
className={cn("size-full", "pt-10")}
threadId={threadId}
thread={thread}
thread={displayThread}
paddingBottom={messageListPaddingBottom}
tokenUsageEnabled={tokenUsageEnabled}
// 输入框为底部绝对浮层(约 160px 高),按钮上移到其上方居中。
@@ -585,9 +654,9 @@ export default function ChatPage() {
<div className="absolute right-0 bottom-0 left-0">
<TodoList
className="bg-background/5"
todos={thread.values.todos ?? []}
todos={displayThread.values.todos ?? []}
hidden={
!thread.values.todos || thread.values.todos.length === 0
!displayThread.values.todos || displayThread.values.todos.length === 0
}
/>
</div>
@@ -612,15 +681,17 @@ export default function ChatPage() {
status={
thread.error
? "error"
: thread.isLoading
: displayThread.isLoading
? "streaming"
: "ready"
}
context={settings.context}
extraModels={SINGLE_ROUND_RAG_MODELS}
disabled={
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" ||
isUploading ||
agentHandoffPending
agentHandoffPending ||
singleRoundRag.isLoading
}
onContextChange={(context) =>
setSettings("context", context)
@@ -664,7 +735,7 @@ export default function ChatPage() {
*/}
{!isLandingState && (
<QuestionNavigator
messages={thread.messages}
messages={displayThread.messages}
className="top-16 right-0 bottom-48 left-0"
/>
)}