Merge branch 'master-nj' into 'master'
Master nj See merge request knowledge-base-bj/deerflow-code!23
This commit is contained in:
commit
96bfdacee5
6 files changed
+2906
-1014
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -22,10 +22,10 @@ import {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
type ComponentProps,
|
type ComponentProps,
|
||||||
type ReactNode,
|
type ReactNode,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
PromptInput,
|
PromptInput,
|
||||||
@@ -62,10 +62,10 @@ import {
|
|||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { apiFetch } from "@/core/api";
|
import { apiFetch } from "@/core/api";
|
||||||
import { getBackendBaseURL } from "@/core/config";
|
import { getBackendBaseURL } from "@/core/config";
|
||||||
import { useI18n } from "@/core/i18n/hooks";
|
import { useI18n } from "@/core/i18n/hooks";
|
||||||
import { isHiddenFromUIMessage } from "@/core/messages/utils";
|
import { isHiddenFromUIMessage } from "@/core/messages/utils";
|
||||||
import { useModels } from "@/core/models/hooks";
|
import { useModels } from "@/core/models/hooks";
|
||||||
import type { Model } from "@/core/models/types";
|
import type { Model } from "@/core/models/types";
|
||||||
import type { AgentThreadContext } from "@/core/threads";
|
import type { AgentThreadContext } from "@/core/threads";
|
||||||
import { textOfMessage } from "@/core/threads/utils";
|
import { textOfMessage } from "@/core/threads/utils";
|
||||||
import { desensitize } from "@/lib/desensitize";
|
import { desensitize } from "@/lib/desensitize";
|
||||||
@@ -134,11 +134,11 @@ export function InputBox({
|
|||||||
onFollowupsVisibilityChange,
|
onFollowupsVisibilityChange,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onStop,
|
onStop,
|
||||||
promptPrefix,
|
promptPrefix,
|
||||||
agentSelector,
|
agentSelector,
|
||||||
knowledgeSelector,
|
knowledgeSelector,
|
||||||
extraModels = [],
|
extraModels = [],
|
||||||
assistantId,
|
assistantId,
|
||||||
hideAdminRecommendedQuestions,
|
hideAdminRecommendedQuestions,
|
||||||
compact,
|
compact,
|
||||||
...props
|
...props
|
||||||
@@ -194,28 +194,28 @@ export function InputBox({
|
|||||||
* homepage (``ChatPage``) passes this; ``AgentChatPage`` omits it because the
|
* homepage (``ChatPage``) passes this; ``AgentChatPage`` omits it because the
|
||||||
* thread is already bound to one agent.
|
* thread is already bound to one agent.
|
||||||
*/
|
*/
|
||||||
agentSelector?: {
|
agentSelector?: {
|
||||||
selectedAgentId: string | null;
|
selectedAgentId: string | null;
|
||||||
onAgentChange: (agentId: string | null) => void;
|
onAgentChange: (agentId: string | null) => void;
|
||||||
};
|
};
|
||||||
/** WeKnora-style knowledge-space picker rendered inside the composer toolbar. */
|
/** WeKnora-style knowledge-space picker rendered inside the composer toolbar. */
|
||||||
knowledgeSelector?: ReactNode;
|
knowledgeSelector?: ReactNode;
|
||||||
/** Page-scoped model choices implemented outside the DeerFlow model API. */
|
/** Page-scoped model choices implemented outside the DeerFlow model API. */
|
||||||
extraModels?: Model[];
|
extraModels?: Model[];
|
||||||
}) {
|
}) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const [localSettings, setLocalSettings] = useLocalSettings();
|
const [localSettings, setLocalSettings] = useLocalSettings();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [modelDialogOpen, setModelDialogOpen] = useState(false);
|
const [modelDialogOpen, setModelDialogOpen] = useState(false);
|
||||||
const { models } = useModels();
|
const { models } = useModels();
|
||||||
const singleRoundRagActive =
|
const singleRoundRagActive =
|
||||||
context.execution_profile === "single_round_rag";
|
context.execution_profile === "single_round_rag";
|
||||||
const multiRoundRagActive =
|
const multiRoundRagActive =
|
||||||
context.execution_profile === "multi_round_rag";
|
context.execution_profile === "multi_round_rag";
|
||||||
const frontendRagActive = singleRoundRagActive || multiRoundRagActive;
|
const frontendRagActive = singleRoundRagActive || multiRoundRagActive;
|
||||||
const modelOptions = useMemo(() => {
|
const modelOptions = useMemo(() => {
|
||||||
return singleRoundRagActive ? extraModels : models;
|
return singleRoundRagActive ? extraModels : models;
|
||||||
}, [extraModels, models, singleRoundRagActive]);
|
}, [extraModels, models, singleRoundRagActive]);
|
||||||
const { thread, isMock } = useThread();
|
const { thread, isMock } = useThread();
|
||||||
const { textInput } = usePromptInputController();
|
const { textInput } = usePromptInputController();
|
||||||
const promptRootRef = useRef<HTMLDivElement | null>(null);
|
const promptRootRef = useRef<HTMLDivElement | null>(null);
|
||||||
@@ -238,46 +238,46 @@ export function InputBox({
|
|||||||
!userPreferences.hide_admin_prompt_prefix &&
|
!userPreferences.hide_admin_prompt_prefix &&
|
||||||
recommendedQuestions.length > 0;
|
recommendedQuestions.length > 0;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Multi-round RAG is a frontend execution profile, not a model option.
|
// Multi-round RAG is a frontend execution profile, not a model option.
|
||||||
// Do not let the normal model fallback clear it on every render.
|
// Do not let the normal model fallback clear it on every render.
|
||||||
if (multiRoundRagActive) {
|
if (multiRoundRagActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (modelOptions.length === 0) {
|
if (modelOptions.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const currentModel = modelOptions.find((m) => m.name === context.model_name);
|
const currentModel = modelOptions.find((m) => m.name === context.model_name);
|
||||||
const fallbackModel = currentModel ?? modelOptions[0]!;
|
const fallbackModel = currentModel ?? modelOptions[0]!;
|
||||||
const supportsThinking = fallbackModel.supports_thinking ?? false;
|
const supportsThinking = fallbackModel.supports_thinking ?? false;
|
||||||
const nextModelName = fallbackModel.name;
|
const nextModelName = fallbackModel.name;
|
||||||
const nextMode = getResolvedMode(context.mode, supportsThinking);
|
const nextMode = getResolvedMode(context.mode, supportsThinking);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
context.model_name === nextModelName &&
|
context.model_name === nextModelName &&
|
||||||
context.mode === nextMode &&
|
context.mode === nextMode &&
|
||||||
context.execution_profile === fallbackModel.execution_profile
|
context.execution_profile === fallbackModel.execution_profile
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onContextChange?.({
|
onContextChange?.({
|
||||||
...context,
|
...context,
|
||||||
model_name: nextModelName,
|
model_name: nextModelName,
|
||||||
mode: nextMode,
|
mode: nextMode,
|
||||||
execution_profile: fallbackModel.execution_profile,
|
execution_profile: fallbackModel.execution_profile,
|
||||||
});
|
});
|
||||||
}, [context, modelOptions, multiRoundRagActive, onContextChange]);
|
}, [context, modelOptions, multiRoundRagActive, onContextChange]);
|
||||||
|
|
||||||
const selectedModel = useMemo(() => {
|
|
||||||
if (modelOptions.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return modelOptions.find((m) => m.name === context.model_name) ?? modelOptions[0];
|
|
||||||
}, [context.model_name, modelOptions]);
|
|
||||||
|
|
||||||
const resolvedModelName = selectedModel?.name;
|
const selectedModel = useMemo(() => {
|
||||||
const selectedSingleRoundRag = singleRoundRagActive;
|
if (modelOptions.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return modelOptions.find((m) => m.name === context.model_name) ?? modelOptions[0];
|
||||||
|
}, [context.model_name, modelOptions]);
|
||||||
|
|
||||||
|
const resolvedModelName = selectedModel?.name;
|
||||||
|
const selectedSingleRoundRag = singleRoundRagActive;
|
||||||
|
|
||||||
const supportThinking = useMemo(
|
const supportThinking = useMemo(
|
||||||
() => selectedModel?.supports_thinking ?? false,
|
() => selectedModel?.supports_thinking ?? false,
|
||||||
@@ -291,72 +291,72 @@ export function InputBox({
|
|||||||
|
|
||||||
const handleModelSelect = useCallback(
|
const handleModelSelect = useCallback(
|
||||||
(model_name: string) => {
|
(model_name: string) => {
|
||||||
const model = modelOptions.find((m) => m.name === model_name);
|
const model = modelOptions.find((m) => m.name === model_name);
|
||||||
if (!model) {
|
if (!model) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onContextChange?.({
|
onContextChange?.({
|
||||||
...context,
|
...context,
|
||||||
model_name,
|
model_name,
|
||||||
mode: getResolvedMode(context.mode, model.supports_thinking ?? false),
|
mode: getResolvedMode(context.mode, model.supports_thinking ?? false),
|
||||||
reasoning_effort: context.reasoning_effort,
|
reasoning_effort: context.reasoning_effort,
|
||||||
execution_profile: model.execution_profile,
|
execution_profile: model.execution_profile,
|
||||||
});
|
});
|
||||||
setModelDialogOpen(false);
|
setModelDialogOpen(false);
|
||||||
},
|
},
|
||||||
[onContextChange, context, modelOptions],
|
[onContextChange, context, modelOptions],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleModeSelect = useCallback(
|
const handleModeSelect = useCallback(
|
||||||
(mode: InputMode) => {
|
(mode: InputMode) => {
|
||||||
const ordinaryModel =
|
const ordinaryModel =
|
||||||
models.find((model) => model.name === context.model_name) ?? models[0];
|
models.find((model) => model.name === context.model_name) ?? models[0];
|
||||||
onContextChange?.({
|
onContextChange?.({
|
||||||
...context,
|
...context,
|
||||||
model_name: ordinaryModel?.name ?? context.model_name,
|
model_name: ordinaryModel?.name ?? context.model_name,
|
||||||
mode: getResolvedMode(
|
mode: getResolvedMode(
|
||||||
mode,
|
mode,
|
||||||
ordinaryModel?.supports_thinking ?? supportThinking,
|
ordinaryModel?.supports_thinking ?? supportThinking,
|
||||||
),
|
),
|
||||||
reasoning_effort:
|
reasoning_effort:
|
||||||
mode === "ultra"
|
mode === "ultra"
|
||||||
? "high"
|
? "high"
|
||||||
: mode === "pro"
|
: mode === "pro"
|
||||||
? "medium"
|
? "medium"
|
||||||
: mode === "thinking"
|
: mode === "thinking"
|
||||||
? "low"
|
? "low"
|
||||||
: "minimal",
|
: "minimal",
|
||||||
execution_profile: undefined,
|
execution_profile: undefined,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[onContextChange, context, models, supportThinking],
|
[onContextChange, context, models, supportThinking],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSingleRoundRagSelect = useCallback(() => {
|
const handleSingleRoundRagSelect = useCallback(() => {
|
||||||
const ragModel =
|
const ragModel =
|
||||||
extraModels.find((model) => model.name === context.model_name) ??
|
extraModels.find((model) => model.name === context.model_name) ??
|
||||||
extraModels[0];
|
extraModels[0];
|
||||||
if (!ragModel) {
|
if (!ragModel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onContextChange?.({
|
onContextChange?.({
|
||||||
...context,
|
...context,
|
||||||
model_name: ragModel.name,
|
model_name: ragModel.name,
|
||||||
mode: getResolvedMode(
|
mode: getResolvedMode(
|
||||||
context.mode,
|
context.mode,
|
||||||
ragModel.supports_thinking ?? false,
|
ragModel.supports_thinking ?? false,
|
||||||
),
|
),
|
||||||
execution_profile: "single_round_rag",
|
execution_profile: "single_round_rag",
|
||||||
});
|
});
|
||||||
}, [context, extraModels, onContextChange]);
|
}, [context, extraModels, onContextChange]);
|
||||||
|
|
||||||
const handleMultiRoundRagSelect = useCallback(() => {
|
const handleMultiRoundRagSelect = useCallback(() => {
|
||||||
onContextChange?.({
|
onContextChange?.({
|
||||||
...context,
|
...context,
|
||||||
execution_profile: "multi_round_rag",
|
execution_profile: "multi_round_rag",
|
||||||
});
|
});
|
||||||
}, [context, onContextChange]);
|
}, [context, onContextChange]);
|
||||||
|
|
||||||
const handleReasoningEffortSelect = useCallback(
|
const handleReasoningEffortSelect = useCallback(
|
||||||
(effort: "minimal" | "low" | "medium" | "high") => {
|
(effort: "minimal" | "low" | "medium" | "high") => {
|
||||||
@@ -422,11 +422,11 @@ export function InputBox({
|
|||||||
onContextChange?.({
|
onContextChange?.({
|
||||||
...context,
|
...context,
|
||||||
model_name: resolvedModelName,
|
model_name: resolvedModelName,
|
||||||
mode: getResolvedMode(
|
mode: getResolvedMode(
|
||||||
context.mode,
|
context.mode,
|
||||||
selectedModel?.supports_thinking ?? false,
|
selectedModel?.supports_thinking ?? false,
|
||||||
),
|
),
|
||||||
execution_profile: selectedModel?.execution_profile,
|
execution_profile: selectedModel?.execution_profile,
|
||||||
});
|
});
|
||||||
setTimeout(() => onSubmit?.(message), 0);
|
setTimeout(() => onSubmit?.(message), 0);
|
||||||
return;
|
return;
|
||||||
@@ -441,8 +441,8 @@ export function InputBox({
|
|||||||
onStop,
|
onStop,
|
||||||
resolvedModelName,
|
resolvedModelName,
|
||||||
ragModeEnabled,
|
ragModeEnabled,
|
||||||
selectedModel?.supports_thinking,
|
selectedModel?.supports_thinking,
|
||||||
selectedModel?.execution_profile,
|
selectedModel?.execution_profile,
|
||||||
status,
|
status,
|
||||||
threadId,
|
threadId,
|
||||||
],
|
],
|
||||||
@@ -500,9 +500,9 @@ export function InputBox({
|
|||||||
}, [pendingSuggestion, requestFormSubmit, textInput]);
|
}, [pendingSuggestion, requestFormSubmit, textInput]);
|
||||||
|
|
||||||
const showFollowups =
|
const showFollowups =
|
||||||
!disabled &&
|
!disabled &&
|
||||||
!isNewThread &&
|
!isNewThread &&
|
||||||
!selectedSingleRoundRag &&
|
!selectedSingleRoundRag &&
|
||||||
!followupsHidden &&
|
!followupsHidden &&
|
||||||
(followupsLoading || followups.length > 0);
|
(followupsLoading || followups.length > 0);
|
||||||
|
|
||||||
@@ -528,19 +528,19 @@ export function InputBox({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disabled || isMock || selectedSingleRoundRag) {
|
if (disabled || isMock || selectedSingleRoundRag) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const visibleMessages = thread.messages.filter((m) => !isHiddenFromUIMessage(m));
|
const visibleMessages = thread.messages.filter((m) => !isHiddenFromUIMessage(m));
|
||||||
const lastAi = [...visibleMessages].reverse().find((m) => m.type === "ai");
|
const lastAi = [...visibleMessages].reverse().find((m) => m.type === "ai");
|
||||||
const lastAiId = lastAi?.id ?? null;
|
const lastAiId = lastAi?.id ?? null;
|
||||||
if (!lastAiId || lastAiId === lastGeneratedForAiIdRef.current) {
|
if (!lastAiId || lastAiId === lastGeneratedForAiIdRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lastGeneratedForAiIdRef.current = lastAiId;
|
lastGeneratedForAiIdRef.current = lastAiId;
|
||||||
|
|
||||||
const recent = visibleMessages
|
const recent = visibleMessages
|
||||||
.filter((m) => m.type === "human" || m.type === "ai")
|
.filter((m) => m.type === "human" || m.type === "ai")
|
||||||
.map((m) => {
|
.map((m) => {
|
||||||
const role = m.type === "human" ? "user" : "assistant";
|
const role = m.type === "human" ? "user" : "assistant";
|
||||||
@@ -590,7 +590,7 @@ export function InputBox({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => controller.abort();
|
return () => controller.abort();
|
||||||
}, [context.model_name, disabled, isMock, selectedSingleRoundRag, status, thread.messages, threadId]);
|
}, [context.model_name, disabled, isMock, selectedSingleRoundRag, status, thread.messages, threadId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={promptRootRef} className="relative flex flex-col gap-6">
|
<div ref={promptRootRef} className="relative flex flex-col gap-6">
|
||||||
@@ -677,7 +677,7 @@ export function InputBox({
|
|||||||
: "*:data-[slot='input-group']:rounded-3xl",
|
: "*:data-[slot='input-group']:rounded-3xl",
|
||||||
// 紧凑落地态(CM 参考图):白卡 + 细边 + 轻阴影 + 主色聚焦环(无旋转彩边)
|
// 紧凑落地态(CM 参考图):白卡 + 细边 + 轻阴影 + 主色聚焦环(无旋转彩边)
|
||||||
compact && isNewThread
|
compact && isNewThread
|
||||||
? "rounded-2xl border border-gray-200/70 bg-white overflow-hidden shadow-[0_4.5px_22px_rgba(0,0,0,0.025)] transition-all duration-300 focus-within:border-[rgb(var(--legacy-primary-rgb)/35%)] focus-within:ring-4 focus-within:ring-[rgb(var(--legacy-primary-rgb)/5%)] focus-within:shadow-[0_10px_35px_rgb(var(--legacy-primary-rgb)/4%)] dark:border-slate-700 dark:bg-slate-800"
|
? "rounded-2xl border border-gray-200/70 bg-white overflow-hidden shadow-[0_4.5px_22px_rgba(0,0,0,0.025)] transition-all duration-300 focus-within:border-[rgb(var(--legacy-primary-rgb)/35%)] focus-within:ring-4 focus-within:ring-[rgb(var(--legacy-primary-rgb)/5%)] focus-within:shadow-[0_10px_35px_rgb(var(--legacy-primary-rgb)/4%)] dark:border-slate-700 dark:bg-slate-800"
|
||||||
: // landing 态:透明,让外层 gemini-gradient-border 提供白卡 + 旋转描边
|
: // landing 态:透明,让外层 gemini-gradient-border 提供白卡 + 旋转描边
|
||||||
// 对话态:常规白卡 + 中性边框 + 轻阴影
|
// 对话态:常规白卡 + 中性边框 + 轻阴影
|
||||||
isNewThread
|
isNewThread
|
||||||
@@ -727,16 +727,16 @@ export function InputBox({
|
|||||||
/>
|
/>
|
||||||
</PromptInputActionMenuContent>
|
</PromptInputActionMenuContent>
|
||||||
</PromptInputActionMenu> */}
|
</PromptInputActionMenu> */}
|
||||||
{!frontendRagActive && (
|
{!frontendRagActive && (
|
||||||
<AddAttachmentsButton
|
<AddAttachmentsButton
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-full border-0 bg-transparent px-2.5! shadow-none text-muted-foreground hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100",
|
"rounded-full border-0 bg-transparent px-2.5! shadow-none text-muted-foreground hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100",
|
||||||
compact &&
|
compact &&
|
||||||
"gap-1.5! rounded-lg px-2.5! py-1! text-[11px] font-medium",
|
"gap-1.5! rounded-lg px-2.5! py-1! text-[11px] font-medium",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!frontendRagActive && <Tooltip
|
{!frontendRagActive && <Tooltip
|
||||||
content={
|
content={
|
||||||
context.writing_mode
|
context.writing_mode
|
||||||
? "写作模式已开启:最终输出会强制保存为 Markdown 文件"
|
? "写作模式已开启:最终输出会强制保存为 Markdown 文件"
|
||||||
@@ -746,23 +746,23 @@ export function InputBox({
|
|||||||
<PromptInputButton
|
<PromptInputButton
|
||||||
className={cn(
|
className={cn(
|
||||||
"gap-1.5! rounded-full px-3! shadow-none",
|
"gap-1.5! rounded-full px-3! shadow-none",
|
||||||
compact && "rounded-lg px-2.5! py-1! text-[11px] font-medium",
|
compact && "rounded-lg px-2.5! py-1! text-[11px] font-medium",
|
||||||
context.writing_mode
|
context.writing_mode
|
||||||
? "border border-amber-300 bg-amber-50 text-amber-700 ring-1 ring-amber-300/35 hover:bg-amber-100 dark:border-amber-300/70 dark:bg-amber-400/20 dark:text-amber-100 dark:ring-amber-300/35 dark:shadow-[0_0_0_1px_rgba(251,191,36,0.18),0_0_18px_rgba(251,191,36,0.18)] dark:hover:bg-amber-400/25"
|
? "border border-amber-300 bg-amber-50 text-amber-700 ring-1 ring-amber-300/35 hover:bg-amber-100 dark:border-amber-300/70 dark:bg-amber-400/20 dark:text-amber-100 dark:ring-amber-300/35 dark:shadow-[0_0_0_1px_rgba(251,191,36,0.18),0_0_18px_rgba(251,191,36,0.18)] dark:hover:bg-amber-400/25"
|
||||||
: compact
|
: compact
|
||||||
? "border border-transparent bg-muted/40 text-muted-foreground hover:bg-muted hover:text-foreground"
|
? "border border-transparent bg-muted/40 text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||||
: "border-0 bg-transparent text-muted-foreground hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100",
|
: "border-0 bg-transparent text-muted-foreground hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100",
|
||||||
)}
|
)}
|
||||||
onClick={handleWritingModeToggle}
|
onClick={handleWritingModeToggle}
|
||||||
>
|
>
|
||||||
{context.writing_mode && (
|
{context.writing_mode && (
|
||||||
<CheckIcon className="size-3 text-amber-700 dark:text-amber-100" />
|
<CheckIcon className="size-3 text-amber-700 dark:text-amber-100" />
|
||||||
)}
|
)}
|
||||||
<FileTextIcon className="size-3" />
|
<FileTextIcon className="size-3" />
|
||||||
<span className="text-xs font-normal">写作</span>
|
<span className="text-xs font-normal">写作</span>
|
||||||
</PromptInputButton>
|
</PromptInputButton>
|
||||||
</Tooltip>}
|
</Tooltip>}
|
||||||
{!frontendRagActive && <Tooltip
|
{!frontendRagActive && <Tooltip
|
||||||
content={
|
content={
|
||||||
memoryInjectionEnabled
|
memoryInjectionEnabled
|
||||||
? "记忆注入已开启:对话前会注入历史记忆"
|
? "记忆注入已开启:对话前会注入历史记忆"
|
||||||
@@ -771,24 +771,24 @@ export function InputBox({
|
|||||||
>
|
>
|
||||||
<PromptInputButton
|
<PromptInputButton
|
||||||
className={cn(
|
className={cn(
|
||||||
"gap-1.5! rounded-full px-3! shadow-none",
|
"gap-1.5! rounded-full px-3! shadow-none",
|
||||||
compact && "rounded-lg px-2.5! py-1! text-[11px] font-medium",
|
compact && "rounded-lg px-2.5! py-1! text-[11px] font-medium",
|
||||||
memoryInjectionEnabled
|
memoryInjectionEnabled
|
||||||
? "border border-blue-300 bg-blue-50 text-blue-700 ring-1 ring-blue-300/35 hover:bg-blue-100 dark:border-cyan-300/70 dark:bg-cyan-400/20 dark:text-cyan-50 dark:ring-cyan-300/35 dark:shadow-[0_0_0_1px_rgba(34,211,238,0.18),0_0_18px_rgba(34,211,238,0.18)] dark:hover:bg-cyan-400/25"
|
? "border border-blue-300 bg-blue-50 text-blue-700 ring-1 ring-blue-300/35 hover:bg-blue-100 dark:border-cyan-300/70 dark:bg-cyan-400/20 dark:text-cyan-50 dark:ring-cyan-300/35 dark:shadow-[0_0_0_1px_rgba(34,211,238,0.18),0_0_18px_rgba(34,211,238,0.18)] dark:hover:bg-cyan-400/25"
|
||||||
: compact
|
: compact
|
||||||
? "border border-transparent bg-muted/40 text-muted-foreground hover:bg-muted hover:text-foreground"
|
? "border border-transparent bg-muted/40 text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||||
: "border-0 bg-transparent text-muted-foreground hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100",
|
: "border-0 bg-transparent text-muted-foreground hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100",
|
||||||
)}
|
)}
|
||||||
onClick={handleMemoryInjectionToggle}
|
onClick={handleMemoryInjectionToggle}
|
||||||
>
|
>
|
||||||
{memoryInjectionEnabled && (
|
{memoryInjectionEnabled && (
|
||||||
<CheckIcon className="size-3 text-blue-700 dark:text-cyan-50" />
|
<CheckIcon className="size-3 text-blue-700 dark:text-cyan-50" />
|
||||||
)}
|
)}
|
||||||
<BrainCircuitIcon className="size-3" />
|
<BrainCircuitIcon className="size-3" />
|
||||||
<span className="text-xs font-normal">记忆</span>
|
<span className="text-xs font-normal">记忆</span>
|
||||||
</PromptInputButton>
|
</PromptInputButton>
|
||||||
</Tooltip>}
|
</Tooltip>}
|
||||||
{!frontendRagActive && <Tooltip
|
{!frontendRagActive && <Tooltip
|
||||||
content={
|
content={
|
||||||
ragModeEnabled
|
ragModeEnabled
|
||||||
? "参考文献模式已开启:检索类结果会按编号引用展示"
|
? "参考文献模式已开启:检索类结果会按编号引用展示"
|
||||||
@@ -817,8 +817,8 @@ export function InputBox({
|
|||||||
<BookMarkedIcon className="size-3" />
|
<BookMarkedIcon className="size-3" />
|
||||||
<span className="text-xs font-normal">参考文献</span>
|
<span className="text-xs font-normal">参考文献</span>
|
||||||
</PromptInputButton>
|
</PromptInputButton>
|
||||||
</Tooltip>}
|
</Tooltip>}
|
||||||
{!frontendRagActive && <Tooltip
|
{!frontendRagActive && <Tooltip
|
||||||
content={
|
content={
|
||||||
contextCompressionEnabled
|
contextCompressionEnabled
|
||||||
? "上下文压缩已开启:超长对话会自动压缩历史以节省 token"
|
? "上下文压缩已开启:超长对话会自动压缩历史以节省 token"
|
||||||
@@ -846,70 +846,70 @@ export function InputBox({
|
|||||||
<ShrinkIcon className="size-3" />
|
<ShrinkIcon className="size-3" />
|
||||||
<span className="text-xs font-normal">上下文压缩</span>
|
<span className="text-xs font-normal">上下文压缩</span>
|
||||||
</PromptInputButton>
|
</PromptInputButton>
|
||||||
</Tooltip>}
|
</Tooltip>}
|
||||||
<PromptInputActionMenu>
|
<PromptInputActionMenu>
|
||||||
<ModeHoverGuide
|
<ModeHoverGuide
|
||||||
mode={
|
mode={
|
||||||
context.mode === "flash" ||
|
context.mode === "flash" ||
|
||||||
context.mode === "thinking" ||
|
context.mode === "thinking" ||
|
||||||
context.mode === "pro" ||
|
context.mode === "pro" ||
|
||||||
context.mode === "ultra"
|
context.mode === "ultra"
|
||||||
? context.mode
|
? context.mode
|
||||||
: "flash"
|
: "flash"
|
||||||
}
|
}
|
||||||
tooltipContent={
|
tooltipContent={
|
||||||
singleRoundRagActive
|
singleRoundRagActive
|
||||||
? "单轮 RAG 问答:基于知识库检索生成答案,并展示参考资料"
|
? "单轮 RAG 问答:基于知识库检索生成答案,并展示参考资料"
|
||||||
: multiRoundRagActive
|
: multiRoundRagActive
|
||||||
? "多轮 RAG 问答:多次检索并展示检索过程与参考资料"
|
? "多轮 RAG 问答:多次检索并展示检索过程与参考资料"
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<PromptInputActionMenuTrigger
|
<PromptInputActionMenuTrigger
|
||||||
className={cn(
|
className={cn(
|
||||||
"gap-1.5! rounded-full border-0 bg-transparent px-3! text-muted-foreground shadow-none hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100",
|
"gap-1.5! rounded-full border-0 bg-transparent px-3! text-muted-foreground shadow-none hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100",
|
||||||
compact && "rounded-lg border px-2.5! py-1! text-[11px] font-medium",
|
compact && "rounded-lg border px-2.5! py-1! text-[11px] font-medium",
|
||||||
compact &&
|
compact &&
|
||||||
(frontendRagActive
|
(frontendRagActive
|
||||||
? "border-emerald-200 bg-emerald-50/60 text-emerald-700 hover:bg-emerald-100 dark:border-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-200"
|
? "border-emerald-200 bg-emerald-50/60 text-emerald-700 hover:bg-emerald-100 dark:border-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-200"
|
||||||
: context.mode === "thinking"
|
: context.mode === "thinking"
|
||||||
? "border-indigo-200 bg-indigo-50/60 text-indigo-700 hover:bg-indigo-100 dark:border-indigo-700 dark:bg-indigo-950/40 dark:text-indigo-200"
|
? "border-indigo-200 bg-indigo-50/60 text-indigo-700 hover:bg-indigo-100 dark:border-indigo-700 dark:bg-indigo-950/40 dark:text-indigo-200"
|
||||||
: context.mode === "pro"
|
: context.mode === "pro"
|
||||||
? "border-violet-200 bg-violet-50/60 text-violet-700 hover:bg-violet-100 dark:border-violet-700 dark:bg-violet-950/40 dark:text-violet-200"
|
? "border-violet-200 bg-violet-50/60 text-violet-700 hover:bg-violet-100 dark:border-violet-700 dark:bg-violet-950/40 dark:text-violet-200"
|
||||||
: context.mode === "ultra"
|
: context.mode === "ultra"
|
||||||
? "border-amber-200 bg-amber-50/60 text-amber-700 hover:bg-amber-100 dark:border-amber-700 dark:bg-amber-950/40 dark:text-amber-200"
|
? "border-amber-200 bg-amber-50/60 text-amber-700 hover:bg-amber-100 dark:border-amber-700 dark:bg-amber-950/40 dark:text-amber-200"
|
||||||
: "border-transparent bg-muted/40 hover:bg-muted hover:text-foreground"),
|
: "border-transparent bg-muted/40 hover:bg-muted hover:text-foreground"),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{frontendRagActive ? (
|
{frontendRagActive ? (
|
||||||
<BookMarkedIcon className="size-3" />
|
<BookMarkedIcon className="size-3" />
|
||||||
) : context.mode === "flash" ? (
|
) : context.mode === "flash" ? (
|
||||||
<ZapIcon className="size-3" />
|
<ZapIcon className="size-3" />
|
||||||
) : context.mode === "thinking" ? (
|
) : context.mode === "thinking" ? (
|
||||||
<LightbulbIcon className="size-3" />
|
<LightbulbIcon className="size-3" />
|
||||||
) : context.mode === "pro" ? (
|
) : context.mode === "pro" ? (
|
||||||
<GraduationCapIcon className="size-3" />
|
<GraduationCapIcon className="size-3" />
|
||||||
) : context.mode === "ultra" ? (
|
) : context.mode === "ultra" ? (
|
||||||
<RocketIcon className="size-3 text-[#dabb5e]" />
|
<RocketIcon className="size-3 text-[#dabb5e]" />
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-xs font-normal",
|
"text-xs font-normal",
|
||||||
context.mode === "ultra" ? "golden-text" : "",
|
context.mode === "ultra" ? "golden-text" : "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{frontendRagActive
|
{frontendRagActive
|
||||||
? multiRoundRagActive
|
? multiRoundRagActive
|
||||||
? "多轮 RAG"
|
? "多轮 RAG"
|
||||||
: "单轮 RAG"
|
: "单轮 RAG"
|
||||||
: (context.mode === "flash" && t.inputBox.flashMode) ||
|
: (context.mode === "flash" && t.inputBox.flashMode) ||
|
||||||
(context.mode === "thinking" &&
|
(context.mode === "thinking" &&
|
||||||
t.inputBox.reasoningMode) ||
|
t.inputBox.reasoningMode) ||
|
||||||
(context.mode === "pro" && t.inputBox.proMode) ||
|
(context.mode === "pro" && t.inputBox.proMode) ||
|
||||||
(context.mode === "ultra" && t.inputBox.ultraMode)}
|
(context.mode === "ultra" && t.inputBox.ultraMode)}
|
||||||
</div>
|
</div>
|
||||||
</PromptInputActionMenuTrigger>
|
</PromptInputActionMenuTrigger>
|
||||||
</ModeHoverGuide>
|
</ModeHoverGuide>
|
||||||
<PromptInputActionMenuContent className="w-80">
|
<PromptInputActionMenuContent className="w-80">
|
||||||
@@ -918,11 +918,11 @@ export function InputBox({
|
|||||||
{t.inputBox.mode}
|
{t.inputBox.mode}
|
||||||
</DropdownMenuLabel>
|
</DropdownMenuLabel>
|
||||||
<PromptInputActionMenu>
|
<PromptInputActionMenu>
|
||||||
<PromptInputActionMenuItem
|
<PromptInputActionMenuItem
|
||||||
className={cn(
|
className={cn(
|
||||||
!frontendRagActive && context.mode === "flash"
|
!frontendRagActive && context.mode === "flash"
|
||||||
? "text-accent-foreground"
|
? "text-accent-foreground"
|
||||||
: "text-muted-foreground/65",
|
: "text-muted-foreground/65",
|
||||||
)}
|
)}
|
||||||
onSelect={() => handleModeSelect("flash")}
|
onSelect={() => handleModeSelect("flash")}
|
||||||
>
|
>
|
||||||
@@ -930,9 +930,9 @@ export function InputBox({
|
|||||||
<div className="flex items-center gap-1 font-bold">
|
<div className="flex items-center gap-1 font-bold">
|
||||||
<ZapIcon
|
<ZapIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"mr-2 size-4",
|
"mr-2 size-4",
|
||||||
!frontendRagActive && context.mode === "flash" &&
|
!frontendRagActive && context.mode === "flash" &&
|
||||||
"text-accent-foreground",
|
"text-accent-foreground",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{t.inputBox.flashMode}
|
{t.inputBox.flashMode}
|
||||||
@@ -941,7 +941,7 @@ export function InputBox({
|
|||||||
{t.inputBox.flashModeDescription}
|
{t.inputBox.flashModeDescription}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!frontendRagActive && context.mode === "flash" ? (
|
{!frontendRagActive && context.mode === "flash" ? (
|
||||||
<CheckIcon className="ml-auto size-4" />
|
<CheckIcon className="ml-auto size-4" />
|
||||||
) : (
|
) : (
|
||||||
<div className="ml-auto size-4" />
|
<div className="ml-auto size-4" />
|
||||||
@@ -949,9 +949,9 @@ export function InputBox({
|
|||||||
</PromptInputActionMenuItem>
|
</PromptInputActionMenuItem>
|
||||||
{supportThinking && (
|
{supportThinking && (
|
||||||
<PromptInputActionMenuItem
|
<PromptInputActionMenuItem
|
||||||
className={cn(
|
className={cn(
|
||||||
!frontendRagActive && context.mode === "thinking"
|
!frontendRagActive && context.mode === "thinking"
|
||||||
? "text-accent-foreground"
|
? "text-accent-foreground"
|
||||||
: "text-muted-foreground/65",
|
: "text-muted-foreground/65",
|
||||||
)}
|
)}
|
||||||
onSelect={() => handleModeSelect("thinking")}
|
onSelect={() => handleModeSelect("thinking")}
|
||||||
@@ -960,9 +960,9 @@ export function InputBox({
|
|||||||
<div className="flex items-center gap-1 font-bold">
|
<div className="flex items-center gap-1 font-bold">
|
||||||
<LightbulbIcon
|
<LightbulbIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"mr-2 size-4",
|
"mr-2 size-4",
|
||||||
!frontendRagActive && context.mode === "thinking" &&
|
!frontendRagActive && context.mode === "thinking" &&
|
||||||
"text-accent-foreground",
|
"text-accent-foreground",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{t.inputBox.reasoningMode}
|
{t.inputBox.reasoningMode}
|
||||||
@@ -971,7 +971,7 @@ export function InputBox({
|
|||||||
{t.inputBox.reasoningModeDescription}
|
{t.inputBox.reasoningModeDescription}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!frontendRagActive && context.mode === "thinking" ? (
|
{!frontendRagActive && context.mode === "thinking" ? (
|
||||||
<CheckIcon className="ml-auto size-4" />
|
<CheckIcon className="ml-auto size-4" />
|
||||||
) : (
|
) : (
|
||||||
<div className="ml-auto size-4" />
|
<div className="ml-auto size-4" />
|
||||||
@@ -979,8 +979,8 @@ export function InputBox({
|
|||||||
</PromptInputActionMenuItem>
|
</PromptInputActionMenuItem>
|
||||||
)}
|
)}
|
||||||
<PromptInputActionMenuItem
|
<PromptInputActionMenuItem
|
||||||
className={cn(
|
className={cn(
|
||||||
!frontendRagActive && context.mode === "pro"
|
!frontendRagActive && context.mode === "pro"
|
||||||
? "text-accent-foreground"
|
? "text-accent-foreground"
|
||||||
: "text-muted-foreground/65",
|
: "text-muted-foreground/65",
|
||||||
)}
|
)}
|
||||||
@@ -990,8 +990,8 @@ export function InputBox({
|
|||||||
<div className="flex items-center gap-1 font-bold">
|
<div className="flex items-center gap-1 font-bold">
|
||||||
<GraduationCapIcon
|
<GraduationCapIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"mr-2 size-4",
|
"mr-2 size-4",
|
||||||
!frontendRagActive && context.mode === "pro" &&
|
!frontendRagActive && context.mode === "pro" &&
|
||||||
"text-accent-foreground",
|
"text-accent-foreground",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -1001,15 +1001,15 @@ export function InputBox({
|
|||||||
{t.inputBox.proModeDescription}
|
{t.inputBox.proModeDescription}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!frontendRagActive && context.mode === "pro" ? (
|
{!frontendRagActive && context.mode === "pro" ? (
|
||||||
<CheckIcon className="ml-auto size-4" />
|
<CheckIcon className="ml-auto size-4" />
|
||||||
) : (
|
) : (
|
||||||
<div className="ml-auto size-4" />
|
<div className="ml-auto size-4" />
|
||||||
)}
|
)}
|
||||||
</PromptInputActionMenuItem>
|
</PromptInputActionMenuItem>
|
||||||
<PromptInputActionMenuItem
|
<PromptInputActionMenuItem
|
||||||
className={cn(
|
className={cn(
|
||||||
!frontendRagActive && context.mode === "ultra"
|
!frontendRagActive && context.mode === "ultra"
|
||||||
? "text-accent-foreground"
|
? "text-accent-foreground"
|
||||||
: "text-muted-foreground/65",
|
: "text-muted-foreground/65",
|
||||||
)}
|
)}
|
||||||
@@ -1019,17 +1019,17 @@ export function InputBox({
|
|||||||
<div className="flex items-center gap-1 font-bold">
|
<div className="flex items-center gap-1 font-bold">
|
||||||
<RocketIcon
|
<RocketIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"mr-2 size-4",
|
"mr-2 size-4",
|
||||||
!frontendRagActive &&
|
!frontendRagActive &&
|
||||||
context.mode === "ultra" &&
|
context.mode === "ultra" &&
|
||||||
"text-[#dabb5e]",
|
"text-[#dabb5e]",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
!frontendRagActive &&
|
!frontendRagActive &&
|
||||||
context.mode === "ultra" &&
|
context.mode === "ultra" &&
|
||||||
"golden-text",
|
"golden-text",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{t.inputBox.ultraMode}
|
{t.inputBox.ultraMode}
|
||||||
@@ -1039,78 +1039,78 @@ export function InputBox({
|
|||||||
{t.inputBox.ultraModeDescription}
|
{t.inputBox.ultraModeDescription}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!frontendRagActive && context.mode === "ultra" ? (
|
{!frontendRagActive && context.mode === "ultra" ? (
|
||||||
<CheckIcon className="ml-auto size-4" />
|
<CheckIcon className="ml-auto size-4" />
|
||||||
) : (
|
) : (
|
||||||
<div className="ml-auto size-4" />
|
<div className="ml-auto size-4" />
|
||||||
)}
|
)}
|
||||||
</PromptInputActionMenuItem>
|
</PromptInputActionMenuItem>
|
||||||
{extraModels.length > 0 && (
|
{extraModels.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<DropdownMenuSeparator className="my-2" />
|
<DropdownMenuSeparator className="my-2" />
|
||||||
<PromptInputActionMenuItem
|
<PromptInputActionMenuItem
|
||||||
className={cn(
|
className={cn(
|
||||||
selectedSingleRoundRag
|
selectedSingleRoundRag
|
||||||
? "bg-emerald-50 text-emerald-800 hover:bg-emerald-100 focus:bg-emerald-100 dark:bg-emerald-950/40 dark:text-emerald-200 dark:hover:bg-emerald-950/60 dark:focus:bg-emerald-950/60"
|
? "bg-emerald-50 text-emerald-800 hover:bg-emerald-100 focus:bg-emerald-100 dark:bg-emerald-950/40 dark:text-emerald-200 dark:hover:bg-emerald-950/60 dark:focus:bg-emerald-950/60"
|
||||||
: "text-muted-foreground/65",
|
: "text-muted-foreground/65",
|
||||||
)}
|
)}
|
||||||
onSelect={handleSingleRoundRagSelect}
|
onSelect={handleSingleRoundRagSelect}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex items-center gap-1 font-bold">
|
<div className="flex items-center gap-1 font-bold">
|
||||||
<BookMarkedIcon
|
<BookMarkedIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"mr-2 size-4",
|
"mr-2 size-4",
|
||||||
selectedSingleRoundRag && "text-emerald-700 dark:text-emerald-200",
|
selectedSingleRoundRag && "text-emerald-700 dark:text-emerald-200",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
单轮 RAG 问答
|
单轮 RAG 问答
|
||||||
</div>
|
</div>
|
||||||
<div className="pl-7 text-xs">
|
<div className="pl-7 text-xs">
|
||||||
基于知识库检索生成答案,并展示思考内容与参考资料
|
基于知识库检索生成答案,并展示思考内容与参考资料
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{selectedSingleRoundRag ? (
|
{selectedSingleRoundRag ? (
|
||||||
<CheckIcon className="ml-auto size-4" />
|
<CheckIcon className="ml-auto size-4" />
|
||||||
) : (
|
) : (
|
||||||
<div className="ml-auto size-4" />
|
<div className="ml-auto size-4" />
|
||||||
)}
|
)}
|
||||||
</PromptInputActionMenuItem>
|
</PromptInputActionMenuItem>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<PromptInputActionMenuItem
|
<PromptInputActionMenuItem
|
||||||
className={cn(
|
className={cn(
|
||||||
multiRoundRagActive
|
multiRoundRagActive
|
||||||
? "bg-emerald-50 text-emerald-800 hover:bg-emerald-100 focus:bg-emerald-100 dark:bg-emerald-950/40 dark:text-emerald-200 dark:hover:bg-emerald-950/60 dark:focus:bg-emerald-950/60"
|
? "bg-emerald-50 text-emerald-800 hover:bg-emerald-100 focus:bg-emerald-100 dark:bg-emerald-950/40 dark:text-emerald-200 dark:hover:bg-emerald-950/60 dark:focus:bg-emerald-950/60"
|
||||||
: "text-muted-foreground/65",
|
: "text-muted-foreground/65",
|
||||||
)}
|
)}
|
||||||
onSelect={handleMultiRoundRagSelect}
|
onSelect={handleMultiRoundRagSelect}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex items-center gap-1 font-bold">
|
<div className="flex items-center gap-1 font-bold">
|
||||||
<BookMarkedIcon
|
<BookMarkedIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"mr-2 size-4",
|
"mr-2 size-4",
|
||||||
multiRoundRagActive && "text-emerald-700 dark:text-emerald-200",
|
multiRoundRagActive && "text-emerald-700 dark:text-emerald-200",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
多轮 RAG 问答
|
多轮 RAG 问答
|
||||||
</div>
|
</div>
|
||||||
<div className="pl-7 text-xs">
|
<div className="pl-7 text-xs">
|
||||||
多次检索、展示过程与参考资料,适合复杂问题
|
多次检索、展示过程与参考资料,适合复杂问题
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{multiRoundRagActive ? (
|
{multiRoundRagActive ? (
|
||||||
<CheckIcon className="ml-auto size-4" />
|
<CheckIcon className="ml-auto size-4" />
|
||||||
) : (
|
) : (
|
||||||
<div className="ml-auto size-4" />
|
<div className="ml-auto size-4" />
|
||||||
)}
|
)}
|
||||||
</PromptInputActionMenuItem>
|
</PromptInputActionMenuItem>
|
||||||
</PromptInputActionMenu>
|
</PromptInputActionMenu>
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
</PromptInputActionMenuContent>
|
</PromptInputActionMenuContent>
|
||||||
</PromptInputActionMenu>
|
</PromptInputActionMenu>
|
||||||
{!frontendRagActive && supportReasoningEffort && context.mode !== "flash" && (
|
{!frontendRagActive && supportReasoningEffort && context.mode !== "flash" && (
|
||||||
<PromptInputActionMenu>
|
<PromptInputActionMenu>
|
||||||
<PromptInputActionMenuTrigger className={cn("gap-1.5! rounded-full border-0 bg-transparent px-3! text-muted-foreground shadow-none hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100", compact && "rounded-lg border border-border px-2.5! py-1! text-[13px]")}>
|
<PromptInputActionMenuTrigger className={cn("gap-1.5! rounded-full border-0 bg-transparent px-3! text-muted-foreground shadow-none hover:bg-muted/70 hover:text-foreground dark:text-slate-400 dark:hover:bg-slate-700/70 dark:hover:text-slate-100", compact && "rounded-lg border border-border px-2.5! py-1! text-[13px]")}>
|
||||||
<div className="text-xs font-normal">
|
<div className="text-xs font-normal">
|
||||||
@@ -1226,10 +1226,9 @@ export function InputBox({
|
|||||||
</PromptInputActionMenuContent>
|
</PromptInputActionMenuContent>
|
||||||
</PromptInputActionMenu>
|
</PromptInputActionMenu>
|
||||||
)}
|
)}
|
||||||
</PromptInputTools>
|
</PromptInputTools>
|
||||||
<PromptInputTools className={cn("gap-1.5", compact && "gap-2")}>
|
<PromptInputTools className={cn("gap-1.5", compact && "gap-2")}>
|
||||||
{!frontendRagActive && agentSelector && (
|
{!frontendRagActive && agentSelector && (
|
||||||
{!frontendRagActive && knowledgeSelector}
|
|
||||||
<AgentSelector
|
<AgentSelector
|
||||||
selectedAgentId={agentSelector.selectedAgentId}
|
selectedAgentId={agentSelector.selectedAgentId}
|
||||||
onAgentChange={agentSelector.onAgentChange}
|
onAgentChange={agentSelector.onAgentChange}
|
||||||
@@ -1237,7 +1236,8 @@ export function InputBox({
|
|||||||
compact={compact}
|
compact={compact}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!frontendRagActive && promptPrefix && (
|
{!frontendRagActive && knowledgeSelector}
|
||||||
|
{!frontendRagActive && promptPrefix && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={
|
content={
|
||||||
promptPrefix.on
|
promptPrefix.on
|
||||||
@@ -1265,7 +1265,7 @@ export function InputBox({
|
|||||||
</PromptInputButton>
|
</PromptInputButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!multiRoundRagActive && <ModelSelector
|
{!multiRoundRagActive && <ModelSelector
|
||||||
open={modelDialogOpen}
|
open={modelDialogOpen}
|
||||||
onOpenChange={setModelDialogOpen}
|
onOpenChange={setModelDialogOpen}
|
||||||
>
|
>
|
||||||
@@ -1292,7 +1292,7 @@ export function InputBox({
|
|||||||
<ModelSelectorContent>
|
<ModelSelectorContent>
|
||||||
<ModelSelectorInput placeholder={t.inputBox.searchModels} />
|
<ModelSelectorInput placeholder={t.inputBox.searchModels} />
|
||||||
<ModelSelectorList>
|
<ModelSelectorList>
|
||||||
{modelOptions.map((m) => (
|
{modelOptions.map((m) => (
|
||||||
<ModelSelectorItem
|
<ModelSelectorItem
|
||||||
key={m.name}
|
key={m.name}
|
||||||
value={m.name}
|
value={m.name}
|
||||||
@@ -1300,11 +1300,11 @@ export function InputBox({
|
|||||||
>
|
>
|
||||||
<div className="flex min-w-0 flex-1 flex-col">
|
<div className="flex min-w-0 flex-1 flex-col">
|
||||||
<ModelSelectorName>{m.display_name}</ModelSelectorName>
|
<ModelSelectorName>{m.display_name}</ModelSelectorName>
|
||||||
<span className="text-muted-foreground truncate text-[10px]">
|
<span className="text-muted-foreground truncate text-[10px]">
|
||||||
{selectedSingleRoundRag
|
{selectedSingleRoundRag
|
||||||
? `单轮知识库问答 · ${m.name}`
|
? `单轮知识库问答 · ${m.name}`
|
||||||
: m.model}
|
: m.model}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{m.name === context.model_name ? (
|
{m.name === context.model_name ? (
|
||||||
<CheckIcon className="ml-auto size-4" />
|
<CheckIcon className="ml-auto size-4" />
|
||||||
@@ -1312,15 +1312,15 @@ export function InputBox({
|
|||||||
<div className="ml-auto size-4" />
|
<div className="ml-auto size-4" />
|
||||||
)}
|
)}
|
||||||
</ModelSelectorItem>
|
</ModelSelectorItem>
|
||||||
))}
|
))}
|
||||||
</ModelSelectorList>
|
</ModelSelectorList>
|
||||||
</ModelSelectorContent>
|
</ModelSelectorContent>
|
||||||
</ModelSelector>}
|
</ModelSelector>}
|
||||||
<PromptInputSubmit
|
<PromptInputSubmit
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-full border-border bg-background shadow-sm hover:bg-muted dark:bg-blue-500 dark:border-blue-500 dark:text-white dark:hover:bg-blue-400 dark:shadow-none",
|
"rounded-full border-border bg-background shadow-sm hover:bg-muted dark:bg-blue-500 dark:border-blue-500 dark:text-white dark:hover:bg-blue-400 dark:shadow-none",
|
||||||
compact &&
|
compact &&
|
||||||
"size-7 rounded-lg border-0 bg-[var(--primary)] text-[var(--button-foreground,var(--primary-foreground))] hover:opacity-90 disabled:bg-muted disabled:text-muted-foreground/40 dark:bg-[var(--primary)] dark:border-0 dark:text-[var(--button-foreground,var(--primary-foreground))] dark:hover:opacity-90 dark:disabled:bg-muted",
|
"size-7 rounded-lg border-0 bg-[var(--primary)] text-[var(--button-foreground,var(--primary-foreground))] hover:opacity-90 disabled:bg-muted disabled:text-muted-foreground/40 dark:bg-[var(--primary)] dark:border-0 dark:text-[var(--button-foreground,var(--primary-foreground))] dark:hover:opacity-90 dark:disabled:bg-muted",
|
||||||
)}
|
)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -30,12 +30,13 @@ import { useSkills } from "@/core/skills/hooks";
|
|||||||
import {
|
import {
|
||||||
buildSkillDisplayMap,
|
buildSkillDisplayMap,
|
||||||
collectRuntimeDisplaySources,
|
collectRuntimeDisplaySources,
|
||||||
|
type RuntimeDisplaySource,
|
||||||
} from "@/core/skill-display/runtime";
|
} from "@/core/skill-display/runtime";
|
||||||
import type { Subtask } from "@/core/tasks";
|
import type { Subtask } from "@/core/tasks";
|
||||||
import { useUpdateSubtask } from "@/core/tasks/context";
|
import { useUpdateSubtask } from "@/core/tasks/context";
|
||||||
import type { AgentThreadState } from "@/core/threads";
|
import type { AgentThreadState } from "@/core/threads";
|
||||||
import { isSingleRoundRagMessage } from "@/core/threads/use-single-round-rag";
|
import { isSingleRoundRagMessage } from "@/core/threads/use-single-round-rag";
|
||||||
import { isMultiRoundRagMessage } from "@/core/threads/use-multi-round-rag";
|
import { isMultiRoundRagMessage } from "@/core/threads/use-multi-round-rag";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { resolveAllowMultiple } from "@/roundtable-planning/lib/clarification";
|
import { resolveAllowMultiple } from "@/roundtable-planning/lib/clarification";
|
||||||
|
|
||||||
@@ -99,18 +100,18 @@ export function MessageList({
|
|||||||
tokenUsageEnabled = false,
|
tokenUsageEnabled = false,
|
||||||
extraRehypePlugins,
|
extraRehypePlugins,
|
||||||
scrollButtonClassName,
|
scrollButtonClassName,
|
||||||
onClarificationSubmit,
|
onClarificationSubmit,
|
||||||
onEditResubmit,
|
onEditResubmit,
|
||||||
onEditSave,
|
onEditSave,
|
||||||
messageContentOverrides,
|
messageContentOverrides,
|
||||||
externallyHiddenMessageIds,
|
externallyHiddenMessageIds,
|
||||||
onSetupAgentApprove,
|
onSetupAgentApprove,
|
||||||
onSetupAgentReject,
|
onSetupAgentReject,
|
||||||
onSetupWritingApprove,
|
onSetupWritingApprove,
|
||||||
onSetupWritingReject,
|
onSetupWritingReject,
|
||||||
onSetupWritingStart,
|
onSetupWritingStart,
|
||||||
onOpenArtifact,
|
onOpenArtifact,
|
||||||
}: {
|
}: {
|
||||||
className?: string;
|
className?: string;
|
||||||
contentClassName?: string;
|
contentClassName?: string;
|
||||||
fitMessageRowsToContent?: boolean;
|
fitMessageRowsToContent?: boolean;
|
||||||
@@ -121,19 +122,19 @@ export function MessageList({
|
|||||||
/** 额外定位「回到最新」悬浮按钮——不同页面输入框高度不同,需上移让位。 */
|
/** 额外定位「回到最新」悬浮按钮——不同页面输入框高度不同,需上移让位。 */
|
||||||
scrollButtonClassName?: string;
|
scrollButtonClassName?: string;
|
||||||
extraRehypePlugins?: ReturnType<typeof useRehypeSplitWordsIntoSpans>;
|
extraRehypePlugins?: ReturnType<typeof useRehypeSplitWordsIntoSpans>;
|
||||||
onClarificationSubmit?: (answer: string) => void | Promise<void>;
|
onClarificationSubmit?: (answer: string) => void | Promise<void>;
|
||||||
onEditResubmit?: (text: string) => void;
|
onEditResubmit?: (text: string) => void;
|
||||||
onEditSave?: (messageId: string, text: string) => void | Promise<void>;
|
onEditSave?: (messageId: string, text: string) => void | Promise<void>;
|
||||||
messageContentOverrides?: Record<string, string>;
|
messageContentOverrides?: Record<string, string>;
|
||||||
externallyHiddenMessageIds?: Set<string>;
|
externallyHiddenMessageIds?: Set<string>;
|
||||||
onSetupAgentApprove?: (payload: SetupAgentApprovalPayload) => void | Promise<void>;
|
onSetupAgentApprove?: (payload: SetupAgentApprovalPayload) => void | Promise<void>;
|
||||||
onSetupAgentReject?: (payload: SetupAgentApprovalPayload) => void | Promise<void>;
|
onSetupAgentReject?: (payload: SetupAgentApprovalPayload) => void | Promise<void>;
|
||||||
onSetupWritingApprove?: (payload: SetupWritingApprovalPayload) => void | Promise<void>;
|
onSetupWritingApprove?: (payload: SetupWritingApprovalPayload) => void | Promise<void>;
|
||||||
onSetupWritingReject?: (payload: SetupWritingApprovalPayload) => void | Promise<void>;
|
onSetupWritingReject?: (payload: SetupWritingApprovalPayload) => void | Promise<void>;
|
||||||
/** 审批卡「直接开始写作」回调:应用配置(含已确认大纲)并立即启动写作流程(仅写作页提供,模板编辑等场景缺省隐藏该按钮)。 */
|
/** 审批卡「直接开始写作」回调:应用配置(含已确认大纲)并立即启动写作流程(仅写作页提供,模板编辑等场景缺省隐藏该按钮)。 */
|
||||||
onSetupWritingStart?: (payload: SetupWritingApprovalPayload) => void | Promise<void>;
|
onSetupWritingStart?: (payload: SetupWritingApprovalPayload) => void | Promise<void>;
|
||||||
onOpenArtifact?: (filepath: string) => void;
|
onOpenArtifact?: (filepath: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const baseRehypePlugins = useRehypeSplitWordsIntoSpans(thread.isLoading);
|
const baseRehypePlugins = useRehypeSplitWordsIntoSpans(thread.isLoading);
|
||||||
const rehypePlugins = useMemo(
|
const rehypePlugins = useMemo(
|
||||||
() => (extraRehypePlugins?.length ? [...baseRehypePlugins, ...extraRehypePlugins] : baseRehypePlugins),
|
() => (extraRehypePlugins?.length ? [...baseRehypePlugins, ...extraRehypePlugins] : baseRehypePlugins),
|
||||||
@@ -165,33 +166,33 @@ export function MessageList({
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const allMessages = useMemo(() => {
|
const allMessages = useMemo(() => {
|
||||||
const hasOverrides = messageContentOverrides && Object.keys(messageContentOverrides).length > 0;
|
const hasOverrides = messageContentOverrides && Object.keys(messageContentOverrides).length > 0;
|
||||||
return thread.messages
|
return thread.messages
|
||||||
.filter((m) => !isHiddenFromUIMessage(m))
|
.filter((m) => !isHiddenFromUIMessage(m))
|
||||||
.map((message) => {
|
.map((message) => {
|
||||||
const override =
|
const override =
|
||||||
hasOverrides && message.id
|
hasOverrides && message.id
|
||||||
? messageContentOverrides?.[message.id]
|
? messageContentOverrides?.[message.id]
|
||||||
: undefined;
|
: undefined;
|
||||||
return override === undefined
|
return override === undefined
|
||||||
? message
|
? message
|
||||||
: ({ ...message, content: override } as Message);
|
: ({ ...message, content: override } as Message);
|
||||||
});
|
});
|
||||||
}, [messageContentOverrides, thread.messages]);
|
}, [messageContentOverrides, thread.messages]);
|
||||||
const messages = useMemo(
|
const messages = useMemo(
|
||||||
() => {
|
() => {
|
||||||
const externalHidden = externallyHiddenMessageIds ?? new Set<string>();
|
const externalHidden = externallyHiddenMessageIds ?? new Set<string>();
|
||||||
return hiddenMessageIds.size === 0 && externalHidden.size === 0
|
return hiddenMessageIds.size === 0 && externalHidden.size === 0
|
||||||
? allMessages
|
? allMessages
|
||||||
: allMessages.filter(
|
: allMessages.filter(
|
||||||
(m) =>
|
(m) =>
|
||||||
!m.id ||
|
!m.id ||
|
||||||
(!hiddenMessageIds.has(m.id) && !externalHidden.has(m.id)),
|
(!hiddenMessageIds.has(m.id) && !externalHidden.has(m.id)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[allMessages, externallyHiddenMessageIds, hiddenMessageIds],
|
[allMessages, externallyHiddenMessageIds, hiddenMessageIds],
|
||||||
);
|
);
|
||||||
|
|
||||||
const askTimes = useChatAskTimes();
|
const askTimes = useChatAskTimes();
|
||||||
|
|
||||||
@@ -237,21 +238,21 @@ export function MessageList({
|
|||||||
}, [messages, askTimes]);
|
}, [messages, askTimes]);
|
||||||
|
|
||||||
const [settings] = useLocalSettings();
|
const [settings] = useLocalSettings();
|
||||||
const ragModeEnabled =
|
const ragModeEnabled =
|
||||||
settings.display.ragModeEnabled || isThreadRagModeLocked(threadId);
|
settings.display.ragModeEnabled || isThreadRagModeLocked(threadId);
|
||||||
const hasSingleRoundRag = useMemo(
|
const hasSingleRoundRag = useMemo(
|
||||||
() =>
|
() =>
|
||||||
messages.some(
|
messages.some(
|
||||||
(message) =>
|
(message) =>
|
||||||
isSingleRoundRagMessage(message) || isMultiRoundRagMessage(message),
|
isSingleRoundRagMessage(message) || isMultiRoundRagMessage(message),
|
||||||
),
|
),
|
||||||
[messages],
|
[messages],
|
||||||
);
|
);
|
||||||
const referencesEnabled = ragModeEnabled || hasSingleRoundRag;
|
const referencesEnabled = ragModeEnabled || hasSingleRoundRag;
|
||||||
const { skills } = useSkills();
|
const { skills } = useSkills();
|
||||||
const skillDisplayMap = useMemo(
|
const skillDisplayMap = useMemo(
|
||||||
() => (referencesEnabled ? buildSkillDisplayMap(skills) : new Map()),
|
() => (referencesEnabled ? buildSkillDisplayMap(skills) : new Map()),
|
||||||
[referencesEnabled, skills],
|
[referencesEnabled, skills],
|
||||||
);
|
);
|
||||||
// Same per-turn reference batches the right-side panel renders (backend
|
// Same per-turn reference batches the right-side panel renders (backend
|
||||||
// batches preferred). Inline [n] tokens must resolve against these so badge
|
// batches preferred). Inline [n] tokens must resolve against these so badge
|
||||||
@@ -259,6 +260,53 @@ export function MessageList({
|
|||||||
// frontend tool-message scan below comes up empty (e.g. after a reload).
|
// frontend tool-message scan below comes up empty (e.g. after a reload).
|
||||||
const referenceBatches = useContext(ReferenceBatchesContext);
|
const referenceBatches = useContext(ReferenceBatchesContext);
|
||||||
|
|
||||||
|
// 单轮/多轮 RAG 的参考资料存放在一条 hide_from_ui 的 tool 消息里(紧挨在
|
||||||
|
// assistant 消息之前)。allMessages 会把 hide_from_ui 消息过滤掉,导致通用
|
||||||
|
// 的 collectRuntimeDisplaySources 扫描不到它们。这里直接在原始消息流里按
|
||||||
|
// assistant 消息提取其对应的参考资料,保证 `[1]` 等编号能解析成可点击引用。
|
||||||
|
const frontendRagSourcesByAiId = useMemo(() => {
|
||||||
|
const map = new Map<string, RuntimeDisplaySource[]>();
|
||||||
|
const raw = thread.messages;
|
||||||
|
for (let i = 0; i < raw.length; i++) {
|
||||||
|
const msg = raw[i];
|
||||||
|
if (!msg || msg.type !== "ai" || !msg.id) continue;
|
||||||
|
const kwargs = msg.additional_kwargs;
|
||||||
|
if (!kwargs || (kwargs.single_round_rag !== true && kwargs.multi_round_rag !== true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let sources: RuntimeDisplaySource[] = [];
|
||||||
|
for (let j = i - 1; j >= 0; j--) {
|
||||||
|
const prev = raw[j];
|
||||||
|
if (!prev) break;
|
||||||
|
// 前一条 human/ai 消息即为本轮边界,跨边界即停止。
|
||||||
|
if (prev.type === "human" || prev.type === "ai") break;
|
||||||
|
if (prev.type !== "tool") continue;
|
||||||
|
const prevKwargs = prev.additional_kwargs;
|
||||||
|
if (
|
||||||
|
!prevKwargs ||
|
||||||
|
(prevKwargs.single_round_rag !== true && prevKwargs.multi_round_rag !== true)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const results = extractFrontendRagResults(prev.content);
|
||||||
|
if (results && results.length > 0) {
|
||||||
|
const toolName = String(
|
||||||
|
(prev as { name?: unknown }).name ??
|
||||||
|
(prevKwargs.multi_round_rag === true
|
||||||
|
? "multi_round_rag_references"
|
||||||
|
: "knowledge_base_search"),
|
||||||
|
);
|
||||||
|
sources = results.map((item, index) =>
|
||||||
|
toRuntimeDisplaySource(item, index + 1, toolName),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
map.set(msg.id, sources);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [thread.messages]);
|
||||||
|
|
||||||
// Track which clarification messages are still awaiting an answer.
|
// Track which clarification messages are still awaiting an answer.
|
||||||
// Pending = clarification tool messages that appear after the last human message
|
// Pending = clarification tool messages that appear after the last human message
|
||||||
// (no user reply has been sent for them yet).
|
// (no user reply has been sent for them yet).
|
||||||
@@ -384,7 +432,7 @@ export function MessageList({
|
|||||||
if (group.type === "human" || group.type === "assistant") {
|
if (group.type === "human" || group.type === "assistant") {
|
||||||
return group.messages.map((msg) => {
|
return group.messages.map((msg) => {
|
||||||
const isAssistantMessage =
|
const isAssistantMessage =
|
||||||
referencesEnabled &&
|
referencesEnabled &&
|
||||||
group.type === "assistant" &&
|
group.type === "assistant" &&
|
||||||
msg.type === "ai";
|
msg.type === "ai";
|
||||||
const batchSources = isAssistantMessage
|
const batchSources = isAssistantMessage
|
||||||
@@ -392,14 +440,21 @@ export function MessageList({
|
|||||||
(batch) => batch.assistant_message_id === msg.id,
|
(batch) => batch.assistant_message_id === msg.id,
|
||||||
)?.sources ?? []
|
)?.sources ?? []
|
||||||
: [];
|
: [];
|
||||||
|
// 单轮/多轮 RAG 的参考资料来自隐藏的 tool 消息,优先于通用扫描。
|
||||||
|
const frontendRagSources =
|
||||||
|
isAssistantMessage && msg.id
|
||||||
|
? frontendRagSourcesByAiId.get(msg.id) ?? []
|
||||||
|
: [];
|
||||||
const displaySources = isAssistantMessage
|
const displaySources = isAssistantMessage
|
||||||
? batchSources.length > 0
|
? batchSources.length > 0
|
||||||
? batchSources
|
? batchSources
|
||||||
: collectRuntimeDisplaySources(messages, skillDisplayMap, {
|
: frontendRagSources.length > 0
|
||||||
afterMessageId: findPreviousHumanMessageId(messages, msg.id),
|
? frontendRagSources
|
||||||
beforeMessageId: msg.id,
|
: collectRuntimeDisplaySources(messages, skillDisplayMap, {
|
||||||
includeBeforeMessage: true,
|
afterMessageId: findPreviousHumanMessageId(messages, msg.id),
|
||||||
})
|
beforeMessageId: msg.id,
|
||||||
|
includeBeforeMessage: true,
|
||||||
|
})
|
||||||
: [];
|
: [];
|
||||||
return (
|
return (
|
||||||
<MessageListItem
|
<MessageListItem
|
||||||
@@ -415,11 +470,11 @@ export function MessageList({
|
|||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
tokenUsageEnabled={tokenUsageEnabled}
|
tokenUsageEnabled={tokenUsageEnabled}
|
||||||
displaySources={displaySources}
|
displaySources={displaySources}
|
||||||
extraRehypePlugins={extraRehypePlugins}
|
extraRehypePlugins={extraRehypePlugins}
|
||||||
onEditResubmit={onEditResubmit}
|
onEditResubmit={onEditResubmit}
|
||||||
onEditSave={onEditSave}
|
onEditSave={onEditSave}
|
||||||
onMessageDeleted={
|
onMessageDeleted={
|
||||||
allowDelete ? handleMessageDeleted : undefined
|
allowDelete ? handleMessageDeleted : undefined
|
||||||
}
|
}
|
||||||
turnTime={msg.id ? turnTimes.get(msg.id) : undefined}
|
turnTime={msg.id ? turnTimes.get(msg.id) : undefined}
|
||||||
/>
|
/>
|
||||||
@@ -524,11 +579,11 @@ export function MessageList({
|
|||||||
className="mb-4"
|
className="mb-4"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ArtifactFileList
|
<ArtifactFileList
|
||||||
files={files}
|
files={files}
|
||||||
threadId={threadId}
|
threadId={threadId}
|
||||||
onOpenArtifact={onOpenArtifact}
|
onOpenArtifact={onOpenArtifact}
|
||||||
/>
|
/>
|
||||||
<MessageTokenUsageList
|
<MessageTokenUsageList
|
||||||
enabled={tokenUsageEnabled}
|
enabled={tokenUsageEnabled}
|
||||||
isLoading={thread.isLoading}
|
isLoading={thread.isLoading}
|
||||||
@@ -728,6 +783,46 @@ function findPreviousHumanMessageId(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 从单轮/多轮 RAG 的 tool 消息体里取出 `content.results`。 */
|
||||||
|
function extractFrontendRagResults(content: unknown): unknown[] | null {
|
||||||
|
if (content && typeof content === "object" && !Array.isArray(content)) {
|
||||||
|
const results = (content as { results?: unknown }).results;
|
||||||
|
if (Array.isArray(results)) return results;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 把单轮/多轮 RAG 的一条参考资料归一化为展示用的 RuntimeDisplaySource。 */
|
||||||
|
function toRuntimeDisplaySource(
|
||||||
|
item: unknown,
|
||||||
|
index: number,
|
||||||
|
skillName: string,
|
||||||
|
): RuntimeDisplaySource {
|
||||||
|
const record =
|
||||||
|
item && typeof item === "object" && !Array.isArray(item)
|
||||||
|
? (item as Record<string, unknown>)
|
||||||
|
: {};
|
||||||
|
const raw =
|
||||||
|
(record.raw as Record<string, unknown> | undefined) ?? record;
|
||||||
|
const content = String(record.content ?? record.snippet ?? "");
|
||||||
|
return {
|
||||||
|
id: typeof record.id === "string" && record.id ? record.id : undefined,
|
||||||
|
title: String(record.title ?? `参考资料 ${index}`),
|
||||||
|
type: String(record.type ?? "result"),
|
||||||
|
snippet: String(record.snippet ?? content),
|
||||||
|
content,
|
||||||
|
source:
|
||||||
|
typeof record.source === "string" && record.source ? record.source : undefined,
|
||||||
|
author: undefined,
|
||||||
|
time: typeof record.time === "string" && record.time ? record.time : undefined,
|
||||||
|
url: typeof record.url === "string" && record.url ? record.url : undefined,
|
||||||
|
raw,
|
||||||
|
index,
|
||||||
|
skillName,
|
||||||
|
mode: "citation",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getSetupAgentApprovalPayload(
|
function getSetupAgentApprovalPayload(
|
||||||
message: unknown,
|
message: unknown,
|
||||||
): SetupAgentApprovalPayload | null {
|
): SetupAgentApprovalPayload | null {
|
||||||
|
|||||||
@@ -815,11 +815,7 @@ export default function ChatPage() {
|
|||||||
: "ready"
|
: "ready"
|
||||||
}
|
}
|
||||||
context={settings.context}
|
context={settings.context}
|
||||||
extraModels={
|
extraModels={SINGLE_ROUND_RAG_MODELS}
|
||||||
singleRoundRagSelected
|
|
||||||
? SINGLE_ROUND_RAG_MODELS
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
disabled={
|
disabled={
|
||||||
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" ||
|
env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" ||
|
||||||
isUploading ||
|
isUploading ||
|
||||||
|
|||||||
@@ -364,8 +364,7 @@ auth_login:
|
|||||||
# 点击后在新标签页打开下面的 url。enabled 关掉则不显示该入口。
|
# 点击后在新标签页打开下面的 url。enabled 关掉则不显示该入口。
|
||||||
|
|
||||||
database:
|
database:
|
||||||
backend: postgres
|
backend: sqlite
|
||||||
postgres_url: $POSTGRES_DATABASE_URL
|
|
||||||
sqlite_dir: .deer-flow/data
|
sqlite_dir: .deer-flow/data
|
||||||
pool_size: 20 # 基础连接池(postgres/mysql 生效;sqlite 忽略)
|
pool_size: 20 # 基础连接池(postgres/mysql 生效;sqlite 忽略)
|
||||||
max_overflow: 20 # 高峰期可临时超出 pool_size 的额外连接数,真实上限 = pool_size + max_overflow
|
max_overflow: 20 # 高峰期可临时超出 pool_size 的额外连接数,真实上限 = pool_size + max_overflow
|
||||||
@@ -374,13 +373,8 @@ database:
|
|||||||
echo_sql: false
|
echo_sql: false
|
||||||
|
|
||||||
checkpointer:
|
checkpointer:
|
||||||
type: postgres
|
type: sqlite
|
||||||
connection_string: $POSTGRES_CHECKPOINT_URL
|
connection_string: .deer-flow/data/deerflow.db
|
||||||
use_postgres: true
|
|
||||||
# PostgreSQL 优先;SQLite 仅作历史记录读取兜底,确认稳定后可移除。
|
|
||||||
dual_read_enabled: true
|
|
||||||
dual_read_sqlite_paths:
|
|
||||||
- .deer-flow/data/deerflow.db
|
|
||||||
|
|
||||||
# 记忆子系统:跨会话持久化用户画像与 agent 私有记忆。
|
# 记忆子系统:跨会话持久化用户画像与 agent 私有记忆。
|
||||||
# 三层覆盖优先级(高->低):
|
# 三层覆盖优先级(高->低):
|
||||||
|
|||||||
Reference in new issue
Block a user