|
@@ -1,7 +1,6 @@
|
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
import type { FC } from "react";
|
|
import type { FC } from "react";
|
|
|
import { traceApi } from "../../api/traceApi";
|
|
import { traceApi } from "../../api/traceApi";
|
|
|
-import { KnowledgeConfirmModal, type KnowledgeConfirmData } from "../KnowledgeConfirmModal/KnowledgeConfirmModal";
|
|
|
|
|
import styles from "./AgentControlPanel.module.css";
|
|
import styles from "./AgentControlPanel.module.css";
|
|
|
|
|
|
|
|
interface LogLine {
|
|
interface LogLine {
|
|
@@ -89,15 +88,12 @@ export const AgentControlPanel: FC<AgentControlPanelProps> = ({
|
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
const [isReflecting, setIsReflecting] = useState(false);
|
|
const [isReflecting, setIsReflecting] = useState(false);
|
|
|
const [isCompacting, setIsCompacting] = useState(false);
|
|
const [isCompacting, setIsCompacting] = useState(false);
|
|
|
- const [knowledgeConfirm, setKnowledgeConfirm] = useState<KnowledgeConfirmData | null>(null);
|
|
|
|
|
const logsEndRef = useRef<HTMLDivElement>(null);
|
|
const logsEndRef = useRef<HTMLDivElement>(null);
|
|
|
const backendLogsEndRef = useRef<HTMLDivElement>(null);
|
|
const backendLogsEndRef = useRef<HTMLDivElement>(null);
|
|
|
const wsRef = useRef<WebSocket | null>(null);
|
|
const wsRef = useRef<WebSocket | null>(null);
|
|
|
const logWsRef = useRef<WebSocket | null>(null);
|
|
const logWsRef = useRef<WebSocket | null>(null);
|
|
|
const autoScrollRef = useRef(true);
|
|
const autoScrollRef = useRef(true);
|
|
|
const logContainerRef = useRef<HTMLDivElement>(null);
|
|
const logContainerRef = useRef<HTMLDivElement>(null);
|
|
|
- const handledConfirmEventsRef = useRef<Set<number>>(new Set());
|
|
|
|
|
- const isReplayingRef = useRef(true); // 历史事件重放阶段标记
|
|
|
|
|
|
|
|
|
|
const appendLog = useCallback((line: LogLine) => {
|
|
const appendLog = useCallback((line: LogLine) => {
|
|
|
setLogs((prev) => [...prev.slice(-500), line]); // 最多保留500条
|
|
setLogs((prev) => [...prev.slice(-500), line]); // 最多保留500条
|
|
@@ -106,7 +102,6 @@ export const AgentControlPanel: FC<AgentControlPanelProps> = ({
|
|
|
// WebSocket 监听 trace 事件
|
|
// WebSocket 监听 trace 事件
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
if (!traceId) return;
|
|
if (!traceId) return;
|
|
|
- isReplayingRef.current = true; // 每次新连接重置为重放状态
|
|
|
|
|
const wsBase = API_BASE.replace(/^http(s?):\/\//, "ws$1://").replace(/\/+$/, "");
|
|
const wsBase = API_BASE.replace(/^http(s?):\/\//, "ws$1://").replace(/\/+$/, "");
|
|
|
const url = `${wsBase}/api/traces/${traceId}/watch?since_event_id=0`;
|
|
const url = `${wsBase}/api/traces/${traceId}/watch?since_event_id=0`;
|
|
|
const ws = new WebSocket(url);
|
|
const ws = new WebSocket(url);
|
|
@@ -121,35 +116,12 @@ export const AgentControlPanel: FC<AgentControlPanelProps> = ({
|
|
|
try {
|
|
try {
|
|
|
const data: TraceEvent = JSON.parse(event.data);
|
|
const data: TraceEvent = JSON.parse(event.data);
|
|
|
|
|
|
|
|
- // 连接成功事件:即根据服务端返回的真实状态初始化按钮
|
|
|
|
|
|
|
+ // 连接成功事件:竅即根据服务端返回的真实状态初始化按鈕
|
|
|
if (data.event === "connected") {
|
|
if (data.event === "connected") {
|
|
|
const realStatus = data.trace_status || (data.is_running ? "running" : "unknown");
|
|
const realStatus = data.trace_status || (data.is_running ? "running" : "unknown");
|
|
|
setTraceStatus(realStatus);
|
|
setTraceStatus(realStatus);
|
|
|
setIsStatusLoaded(true);
|
|
setIsStatusLoaded(true);
|
|
|
appendLog({ time: now(), type: "system", text: `当前状态: ${realStatus.toUpperCase()}` });
|
|
appendLog({ time: now(), type: "system", text: `当前状态: ${realStatus.toUpperCase()}` });
|
|
|
- // connected 事件之后的历史重放结束,后续为实时事件
|
|
|
|
|
- // 使用 setTimeout 确保在同批历史事件处理完后再标记
|
|
|
|
|
- setTimeout(() => { isReplayingRef.current = false; }, 0);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 知识确认请求:仅在实时运行时弹窗,跳过历史重放;同一 event_id 不重复弹
|
|
|
|
|
- if (data.event === "knowledge_confirm_request") {
|
|
|
|
|
- const eventId = (data as unknown as { event_id?: number }).event_id;
|
|
|
|
|
- if (isReplayingRef.current) return; // 历史重放阶段,跳过
|
|
|
|
|
- if (eventId != null && handledConfirmEventsRef.current.has(eventId)) return; // 已处理过
|
|
|
|
|
- if (eventId != null) handledConfirmEventsRef.current.add(eventId);
|
|
|
|
|
- const d = data as unknown as { confirm_type: string; data: Record<string, unknown> };
|
|
|
|
|
- // 先暂停执行,让状态面板感知到
|
|
|
|
|
- if (traceId) {
|
|
|
|
|
- traceApi.stopTrace(traceId).catch(() => {});
|
|
|
|
|
- }
|
|
|
|
|
- setKnowledgeConfirm({
|
|
|
|
|
- confirm_type: d.confirm_type as KnowledgeConfirmData["confirm_type"],
|
|
|
|
|
- data: d.data as KnowledgeConfirmData["data"],
|
|
|
|
|
- });
|
|
|
|
|
- const label = d.confirm_type === "knowledge_injection" ? "知识注入" : "知识保存";
|
|
|
|
|
- appendLog({ time: now(), type: "warn", text: `等待用户确认: ${label}` });
|
|
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -347,20 +319,6 @@ export const AgentControlPanel: FC<AgentControlPanelProps> = ({
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const handleKnowledgeConfirm = async (action: "confirm" | "reject", editedArgs?: Record<string, unknown>) => {
|
|
|
|
|
- if (!traceId) return;
|
|
|
|
|
- const label = knowledgeConfirm?.confirm_type === "knowledge_injection" ? "知识注入" : "知识保存";
|
|
|
|
|
- setKnowledgeConfirm(null);
|
|
|
|
|
- try {
|
|
|
|
|
- await traceApi.confirmKnowledge(traceId, { action, edited_args: editedArgs });
|
|
|
|
|
- appendLog({ time: now(), type: "system", text: `${label}: 用户${action === "confirm" ? "确认" : "拒绝"}` });
|
|
|
|
|
- // 确认/拒绝后恢复执行
|
|
|
|
|
- await traceApi.runTrace(traceId);
|
|
|
|
|
- } catch {
|
|
|
|
|
- appendLog({ time: now(), type: "error", text: `${label}确认请求失败` });
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
const handleSubmitIntervention = async () => {
|
|
const handleSubmitIntervention = async () => {
|
|
|
const text = interventionText.trim();
|
|
const text = interventionText.trim();
|
|
|
if (!text || !traceId) return;
|
|
if (!text || !traceId) return;
|
|
@@ -558,11 +516,6 @@ export const AgentControlPanel: FC<AgentControlPanelProps> = ({
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <KnowledgeConfirmModal
|
|
|
|
|
- visible={knowledgeConfirm !== null}
|
|
|
|
|
- confirmData={knowledgeConfirm}
|
|
|
|
|
- onConfirm={handleKnowledgeConfirm}
|
|
|
|
|
- />
|
|
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|