代码修改
This commit is contained in:
1 parent
b7da062a4c
commit
8bdbf6ab15
3 files changed
+67
-56
No files matched your search
@@ -15,7 +15,7 @@
|
|||||||
*
|
*
|
||||||
* 状态全部来自 `useStep2Orchestration` —— 本组件不调任何后端、不持久化。
|
* 状态全部来自 `useStep2Orchestration` —— 本组件不调任何后端、不持久化。
|
||||||
*/
|
*/
|
||||||
import { useRef } from "react";
|
import { useCallback, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Cpu,
|
Cpu,
|
||||||
FileIcon,
|
FileIcon,
|
||||||
@@ -80,7 +80,6 @@ export interface Step2PanelProps {
|
|||||||
/** 主聊天里 `present_files` 会在气泡下方渲染卡片;Step 2 沿用同一交互。 */
|
/** 主聊天里 `present_files` 会在气泡下方渲染卡片;Step 2 沿用同一交互。 */
|
||||||
openArtifactPreview: (threadId: string, path: string) => void;
|
openArtifactPreview: (threadId: string, path: string) => void;
|
||||||
// ─── Setters ──────────────────────────────────────────────────
|
// ─── Setters ──────────────────────────────────────────────────
|
||||||
setIsRoundTableRunning: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
setClarificationDraft: React.Dispatch<React.SetStateAction<string>>;
|
setClarificationDraft: React.Dispatch<React.SetStateAction<string>>;
|
||||||
setCustomAgentInput: React.Dispatch<React.SetStateAction<string>>;
|
setCustomAgentInput: React.Dispatch<React.SetStateAction<string>>;
|
||||||
setAgentConfigs: React.Dispatch<
|
setAgentConfigs: React.Dispatch<
|
||||||
@@ -88,6 +87,8 @@ export interface Step2PanelProps {
|
|||||||
>;
|
>;
|
||||||
setSelectedAgentId: React.Dispatch<React.SetStateAction<number | null>>;
|
setSelectedAgentId: React.Dispatch<React.SetStateAction<number | null>>;
|
||||||
// ─── Handlers ─────────────────────────────────────────────────
|
// ─── Handlers ─────────────────────────────────────────────────
|
||||||
|
onPauseRoundtable: () => void;
|
||||||
|
onResumeRoundtable: () => void;
|
||||||
onSubmitClarification: (answer: string) => void;
|
onSubmitClarification: (answer: string) => void;
|
||||||
onInjectToRoundtable: () => void;
|
onInjectToRoundtable: () => void;
|
||||||
onSavePersona: (id: number) => void;
|
onSavePersona: (id: number) => void;
|
||||||
@@ -116,11 +117,12 @@ export function Step2Panel(props: Step2PanelProps) {
|
|||||||
seatStubMessages,
|
seatStubMessages,
|
||||||
openArtifactFromStep,
|
openArtifactFromStep,
|
||||||
openArtifactPreview,
|
openArtifactPreview,
|
||||||
setIsRoundTableRunning,
|
|
||||||
setClarificationDraft,
|
setClarificationDraft,
|
||||||
setCustomAgentInput,
|
setCustomAgentInput,
|
||||||
setAgentConfigs,
|
setAgentConfigs,
|
||||||
setSelectedAgentId,
|
setSelectedAgentId,
|
||||||
|
onPauseRoundtable,
|
||||||
|
onResumeRoundtable,
|
||||||
onSubmitClarification,
|
onSubmitClarification,
|
||||||
onInjectToRoundtable,
|
onInjectToRoundtable,
|
||||||
onSavePersona,
|
onSavePersona,
|
||||||
@@ -132,6 +134,15 @@ export function Step2Panel(props: Step2PanelProps) {
|
|||||||
const { atBottom: step2AtBottom, scrollToBottom: scrollStep2ToBottom } =
|
const { atBottom: step2AtBottom, scrollToBottom: scrollStep2ToBottom } =
|
||||||
useAutoScroll(step2ScrollRef, [step2RoundtableDialogues]);
|
useAutoScroll(step2ScrollRef, [step2RoundtableDialogues]);
|
||||||
|
|
||||||
|
// 干预研审 → 暂停会商联动反馈:点击「干预研审」瞬间让右上角按钮高亮闪烁,
|
||||||
|
// 让用户清楚看到状态已切换到「恢复会商」。
|
||||||
|
const [pauseFlash, setPauseFlash] = useState(false);
|
||||||
|
const handleInject = useCallback(() => {
|
||||||
|
onInjectToRoundtable();
|
||||||
|
setPauseFlash(true);
|
||||||
|
window.setTimeout(() => setPauseFlash(false), 1600);
|
||||||
|
}, [onInjectToRoundtable]);
|
||||||
|
|
||||||
const isSandboxStreaming =
|
const isSandboxStreaming =
|
||||||
isOrchestrating ||
|
isOrchestrating ||
|
||||||
step2RoundtableDialogues.some((dialogue) => dialogue.streaming === true);
|
step2RoundtableDialogues.some((dialogue) => dialogue.streaming === true);
|
||||||
@@ -177,10 +188,8 @@ export function Step2Panel(props: Step2PanelProps) {
|
|||||||
</select>
|
</select>
|
||||||
{isRoundTableRunning ? (
|
{isRoundTableRunning ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
type="button"
|
||||||
setIsRoundTableRunning(false);
|
onClick={onPauseRoundtable}
|
||||||
onTriggerToast("⏸️ 会商已暂停");
|
|
||||||
}}
|
|
||||||
className="bg-amber-100 hover:bg-amber-200 text-amber-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-amber-200/40 shadow-sm"
|
className="bg-amber-100 hover:bg-amber-200 text-amber-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-amber-200/40 shadow-sm"
|
||||||
>
|
>
|
||||||
<Pause className="w-3.5 h-3.5" />
|
<Pause className="w-3.5 h-3.5" />
|
||||||
@@ -188,11 +197,13 @@ export function Step2Panel(props: Step2PanelProps) {
|
|||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
type="button"
|
||||||
setIsRoundTableRunning(true);
|
onClick={onResumeRoundtable}
|
||||||
onTriggerToast("▶️ 恢复大模型并行博弈与推理决策");
|
className={`bg-emerald-100 hover:bg-emerald-200 text-emerald-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-emerald-200/40 shadow-sm ${
|
||||||
}}
|
pauseFlash
|
||||||
className="bg-emerald-100 hover:bg-emerald-200 text-emerald-800 text-[10px] font-bold px-2.5 py-1 rounded-xl flex items-center gap-1 transition border border-emerald-200/40 shadow-sm"
|
? "ring-2 ring-emerald-400 ring-offset-1 animate-pulse"
|
||||||
|
: ""
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
<Play className="w-3.5 h-3.5 fill-emerald-800" />
|
<Play className="w-3.5 h-3.5 fill-emerald-800" />
|
||||||
恢复会商
|
恢复会商
|
||||||
@@ -324,14 +335,15 @@ export function Step2Panel(props: Step2PanelProps) {
|
|||||||
value={customAgentInput}
|
value={customAgentInput}
|
||||||
onChange={(e) => setCustomAgentInput(e.target.value)}
|
onChange={(e) => setCustomAgentInput(e.target.value)}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter") onInjectToRoundtable();
|
if (e.key === "Enter") handleInject();
|
||||||
}}
|
}}
|
||||||
placeholder="作为指挥官,您可以随时插话输入指令,对智能体博弈朝向实施人工干预..."
|
placeholder="作为指挥官,您可以随时插话输入指令,对智能体博弈朝向实施人工干预..."
|
||||||
className="flex-1 bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs focus:outline-none focus:border-primary focus:ring-1 focus:ring-[var(--brand-200)]/50 font-medium"
|
className="flex-1 bg-white border border-slate-200 rounded-xl px-3.5 py-2.5 text-xs focus:outline-none focus:border-primary focus:ring-1 focus:ring-[var(--brand-200)]/50 font-medium"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={onInjectToRoundtable}
|
onClick={handleInject}
|
||||||
|
title="发送干预指令并暂停会商;点击右上角「恢复会商」继续执行"
|
||||||
className="bg-primary hover:bg-[var(--brand-900)] text-white text-xs font-bold px-4 py-2.5 rounded-xl transition shrink-0 cursor-pointer active:scale-95 shadow-md shadow-primary/10"
|
className="bg-primary hover:bg-[var(--brand-900)] text-white text-xs font-bold px-4 py-2.5 rounded-xl transition shrink-0 cursor-pointer active:scale-95 shadow-md shadow-primary/10"
|
||||||
>
|
>
|
||||||
干预研审
|
干预研审
|
||||||
|
|||||||
@@ -171,9 +171,13 @@ export interface UseStep2OrchestrationReturn {
|
|||||||
// ─── Handlers ───────────────────────────────────────────────────────
|
// ─── Handlers ───────────────────────────────────────────────────────
|
||||||
/** 中断当前编排循环 + 所有 in-flight SSE。 */
|
/** 中断当前编排循环 + 所有 in-flight SSE。 */
|
||||||
stopOrchestration: () => void;
|
stopOrchestration: () => void;
|
||||||
|
/** 与 Step2Panel「暂停会商」按钮相同 —— 挂起编排并中止 in-flight SSE。 */
|
||||||
|
pauseRoundtable: () => void;
|
||||||
|
/** 与 Step2Panel「恢复会商」按钮相同 —— 恢复自动研讨(含待处理的干预指令)。 */
|
||||||
|
resumeRoundtable: () => void;
|
||||||
/** 中断所有 in-flight SSE(给新建任务 / 加载草稿用)。 */
|
/** 中断所有 in-flight SSE(给新建任务 / 加载草稿用)。 */
|
||||||
abortAllInFlight: () => void;
|
abortAllInFlight: () => void;
|
||||||
/** 用户「干预研审」按钮 —— 追加一条人工指令气泡并触发一轮编排。 */
|
/** 用户「干预研审」—— 先暂停会商,干预指令在恢复后执行。 */
|
||||||
handleInjectToRoundtable: () => void;
|
handleInjectToRoundtable: () => void;
|
||||||
/** 总控澄清回复 —— 把回答拼到 prompt 里再起一轮编排。 */
|
/** 总控澄清回复 —— 把回答拼到 prompt 里再起一轮编排。 */
|
||||||
submitClarification: (rawAnswer: string) => void;
|
submitClarification: (rawAnswer: string) => void;
|
||||||
@@ -296,9 +300,8 @@ export function useStep2Orchestration(
|
|||||||
const orchestrationCancelRef = useRef<boolean>(false);
|
const orchestrationCancelRef = useRef<boolean>(false);
|
||||||
const step2InitRef = useRef<boolean>(false);
|
const step2InitRef = useRef<boolean>(false);
|
||||||
const activeAbortersRef = useRef<Set<AbortController>>(new Set());
|
const activeAbortersRef = useRef<Set<AbortController>>(new Set());
|
||||||
/** 干预研审:暂停当前轮次后待执行的 leader prompt。 */
|
/** 干预研审:恢复会商后待执行的 leader prompt。 */
|
||||||
const pendingInjectPromptRef = useRef<string | null>(null);
|
const pendingInjectPromptRef = useRef<string | null>(null);
|
||||||
const [injectFlushToken, setInjectFlushToken] = useState(0);
|
|
||||||
// selectedModel / agentConfigs / agentNameToRole / threadIds / coordinatorName
|
// selectedModel / agentConfigs / agentNameToRole / threadIds / coordinatorName
|
||||||
// 等等被 runOrchestration 在循环里反复读取。把它们打到 ref 上,可以让
|
// 等等被 runOrchestration 在循环里反复读取。把它们打到 ref 上,可以让
|
||||||
// runOrchestration 的 useCallback deps 大幅收窄,避免每次模型切换、agentConfigs
|
// runOrchestration 的 useCallback deps 大幅收窄,避免每次模型切换、agentConfigs
|
||||||
@@ -1112,17 +1115,11 @@ export function useStep2Orchestration(
|
|||||||
|
|
||||||
// 挂起 / 恢复:与上面的 init effect 独立。`isRoundTableRunning` toggle 时:
|
// 挂起 / 恢复:与上面的 init effect 独立。`isRoundTableRunning` toggle 时:
|
||||||
// - false → stopOrchestration
|
// - false → stopOrchestration
|
||||||
// - true 且 idle → 触发一次「请继续」的 leader cycle
|
// - true 且 idle → 触发 leader cycle(若有干预指令则优先执行)
|
||||||
// 注意 deps 故意只有 isRoundTableRunning,不再额外加 currentStep —— 回到 Step 2
|
|
||||||
// 检视讨论时不应该自动 fire 新的 leader 调用。
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentStep !== 2) return;
|
if (currentStep !== 2) return;
|
||||||
if (!isRoundTableRunning) {
|
if (!isRoundTableRunning) {
|
||||||
// 干预研审会先写入 pending 再置 false;此时 stop 已在 handler 里触发,
|
stopOrchestration();
|
||||||
// 避免 effect 再次 stop 误杀随后 flush 启动的 inject 编排。
|
|
||||||
if (!pendingInjectPromptRef.current) {
|
|
||||||
stopOrchestration();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@@ -1132,6 +1129,18 @@ export function useStep2Orchestration(
|
|||||||
!coordinatorNameRef.current
|
!coordinatorNameRef.current
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const pending = pendingInjectPromptRef.current;
|
||||||
|
if (pending) {
|
||||||
|
pendingInjectPromptRef.current = null;
|
||||||
|
void runOrchestration(
|
||||||
|
pending,
|
||||||
|
threadIdsRef.current,
|
||||||
|
coordinatorNameRef.current,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void runOrchestration(
|
void runOrchestration(
|
||||||
"用户已恢复研讨,请继续上一轮的协调工作。",
|
"用户已恢复研讨,请继续上一轮的协调工作。",
|
||||||
threadIdsRef.current,
|
threadIdsRef.current,
|
||||||
@@ -1140,20 +1149,16 @@ export function useStep2Orchestration(
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isRoundTableRunning]);
|
}, [isRoundTableRunning]);
|
||||||
|
|
||||||
// 干预研审:当前轮次停止后 flush 待执行的 inject 编排(编排期间 UI 仍为「恢复会商」)。
|
const pauseRoundtable = useCallback(() => {
|
||||||
useEffect(() => {
|
setIsRoundTableRunning(false);
|
||||||
if (isOrchestrating) return;
|
stopOrchestration();
|
||||||
const pending = pendingInjectPromptRef.current;
|
triggerToast("⏸️ 会商已暂停");
|
||||||
if (!pending) return;
|
}, [stopOrchestration, triggerToast]);
|
||||||
if (!threadIdsRef.current || !coordinatorNameRef.current) return;
|
|
||||||
pendingInjectPromptRef.current = null;
|
const resumeRoundtable = useCallback(() => {
|
||||||
triggerToast("您的人工纠偏指令已同步至圆桌节点!");
|
setIsRoundTableRunning(true);
|
||||||
void runOrchestration(
|
triggerToast("▶️ 恢复大模型并行博弈与推理决策");
|
||||||
pending,
|
}, [triggerToast]);
|
||||||
threadIdsRef.current,
|
|
||||||
coordinatorNameRef.current,
|
|
||||||
);
|
|
||||||
}, [isOrchestrating, injectFlushToken, runOrchestration, triggerToast]);
|
|
||||||
|
|
||||||
// ─── Public handlers ─────────────────────────────────────────────
|
// ─── Public handlers ─────────────────────────────────────────────
|
||||||
const handleInjectToRoundtable = useCallback(() => {
|
const handleInjectToRoundtable = useCallback(() => {
|
||||||
@@ -1166,12 +1171,8 @@ export function useStep2Orchestration(
|
|||||||
const userText = customAgentInput.trim();
|
const userText = customAgentInput.trim();
|
||||||
setCustomAgentInput("");
|
setCustomAgentInput("");
|
||||||
|
|
||||||
const prompt = `用户人工干预新指令:${userText}\n\n请结合当前讨论状态,决定是否继续派活或直接给出更新后的结论。`;
|
// 与右上角「暂停会商」完全相同的挂起逻辑。
|
||||||
pendingInjectPromptRef.current = prompt;
|
pauseRoundtable();
|
||||||
|
|
||||||
// 与 Step2Panel 右上角「暂停会商 / 恢复会商」联动:立即进入挂起态。
|
|
||||||
setIsRoundTableRunning(false);
|
|
||||||
stopOrchestration();
|
|
||||||
|
|
||||||
appendDialogue({
|
appendDialogue({
|
||||||
sender: "用户 (插话干预)",
|
sender: "用户 (插话干预)",
|
||||||
@@ -1180,13 +1181,8 @@ export function useStep2Orchestration(
|
|||||||
confidence: "最高指令",
|
confidence: "最高指令",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isOrchestratingRef.current) {
|
pendingInjectPromptRef.current = `用户人工干预新指令:${userText}\n\n请结合当前讨论状态,决定是否继续派活或直接给出更新后的结论。`;
|
||||||
triggerToast("⏸️ 会商已暂停,当前轮次结束后将处理您的干预指令");
|
}, [appendDialogue, customAgentInput, pauseRoundtable, triggerToast]);
|
||||||
} else {
|
|
||||||
triggerToast("⏸️ 会商已暂停,正在处理您的干预指令…");
|
|
||||||
}
|
|
||||||
setInjectFlushToken((n) => n + 1);
|
|
||||||
}, [appendDialogue, customAgentInput, stopOrchestration, triggerToast]);
|
|
||||||
|
|
||||||
const submitClarification = useCallback(
|
const submitClarification = useCallback(
|
||||||
(rawAnswer: string) => {
|
(rawAnswer: string) => {
|
||||||
@@ -1378,6 +1374,8 @@ export function useStep2Orchestration(
|
|||||||
setAgentConfigs,
|
setAgentConfigs,
|
||||||
resetInitGate,
|
resetInitGate,
|
||||||
stopOrchestration,
|
stopOrchestration,
|
||||||
|
pauseRoundtable,
|
||||||
|
resumeRoundtable,
|
||||||
abortAllInFlight,
|
abortAllInFlight,
|
||||||
handleInjectToRoundtable,
|
handleInjectToRoundtable,
|
||||||
submitClarification,
|
submitClarification,
|
||||||
|
|||||||
@@ -496,7 +496,8 @@ export default function RoundtablePlanningPage() {
|
|||||||
seatStubMessages={step2.seatStubMessages}
|
seatStubMessages={step2.seatStubMessages}
|
||||||
openArtifactFromStep={step2.openArtifactFromStep}
|
openArtifactFromStep={step2.openArtifactFromStep}
|
||||||
openArtifactPreview={step2.openArtifactPreview}
|
openArtifactPreview={step2.openArtifactPreview}
|
||||||
setIsRoundTableRunning={step2.setIsRoundTableRunning}
|
onPauseRoundtable={step2.pauseRoundtable}
|
||||||
|
onResumeRoundtable={step2.resumeRoundtable}
|
||||||
setClarificationDraft={step2.setClarificationDraft}
|
setClarificationDraft={step2.setClarificationDraft}
|
||||||
setCustomAgentInput={step2.setCustomAgentInput}
|
setCustomAgentInput={step2.setCustomAgentInput}
|
||||||
setAgentConfigs={step2.setAgentConfigs}
|
setAgentConfigs={step2.setAgentConfigs}
|
||||||
@@ -956,8 +957,8 @@ function LeftSidebar({
|
|||||||
<IntentSectionIcon className="h-5 w-5" />
|
<IntentSectionIcon className="h-5 w-5" />
|
||||||
</CollapsedSidebarSection>
|
</CollapsedSidebarSection>
|
||||||
|
|
||||||
<CollapsedSidebarSection title="参与角色" className="flex-1 min-h-[120px]">
|
<CollapsedSidebarSection title="参与角色">
|
||||||
<div className="flex flex-col items-center gap-2 overflow-y-auto max-h-[320px] custom-scrollbar">
|
<div className="flex flex-col items-center gap-2">
|
||||||
{roles.map((role) => (
|
{roles.map((role) => (
|
||||||
<RoleAvatarBadge
|
<RoleAvatarBadge
|
||||||
key={role.id}
|
key={role.id}
|
||||||
@@ -1245,7 +1246,7 @@ function CollapsedSidebarSection({
|
|||||||
<div
|
<div
|
||||||
title={title}
|
title={title}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full bg-white border border-slate-200/50 rounded-2xl p-2 shadow-[0_2px_8px_rgba(0,0,0,0.01)] flex flex-col items-center justify-center",
|
"w-full shrink-0 bg-white border border-slate-200/50 rounded-2xl p-2 shadow-[0_2px_8px_rgba(0,0,0,0.01)] flex flex-col items-center justify-center",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in new issue
Block a user