Browse Source

merge: 合并新代码

master
Senor-Liu 7 hours ago
parent
commit
75ae782f92
  1. 16
      web/client/src/jsonRender/registry.jsx
  2. 2
      web/client/src/layout/index.jsx
  3. 103
      web/client/src/sections/superAgent/hooks/useSuperAgentStream.js

16
web/client/src/jsonRender/registry.jsx

@ -1049,9 +1049,10 @@ const { registry, handlers, executeAction } = defineRegistry(catalog, {
// //
Table: ({ props, bindings }) => { Table: ({ props, bindings }) => {
const schema = props.__inPlaceEditSchema || {}; const schema = props.__inPlaceEditSchema || {};
const isInPlaceEditEnabled = Boolean(props.__inPlaceEditEnabled);
const [editableValue, setEditableValue] = useEditableProp( const [editableValue, setEditableValue] = useEditableProp(
props.__inPlaceEditValue, props.dataSource || [],
bindings?.__inPlaceEditValue, isInPlaceEditEnabled ? bindings?.__inPlaceEditValue : undefined,
props.dataSource || [] props.dataSource || []
); );
const itemFields = schema.item_fields || []; const itemFields = schema.item_fields || [];
@ -1059,7 +1060,7 @@ const { registry, handlers, executeAction } = defineRegistry(catalog, {
itemFields.map((field) => [String(field.name), field]) itemFields.map((field) => [String(field.name), field])
); );
const isInlineEditing = Boolean( const isInlineEditing = Boolean(
props.__inPlaceEditEnabled isInPlaceEditEnabled
&& bindings?.__inPlaceEditValue && bindings?.__inPlaceEditValue
&& Array.isArray(editableValue) && Array.isArray(editableValue)
&& schema.kind === "list" && schema.kind === "list"
@ -1109,17 +1110,18 @@ const { registry, handlers, executeAction } = defineRegistry(catalog, {
Tree: ({ props, bindings }) => { Tree: ({ props, bindings }) => {
const schema = props.__inPlaceEditSchema || {}; const schema = props.__inPlaceEditSchema || {};
const childrenKey = schema.children_key || "children"; const childrenKey = schema.children_key || "children";
const isInPlaceEditEnabled = Boolean(props.__inPlaceEditEnabled);
const [editableValue, setEditableValue] = useEditableProp( const [editableValue, setEditableValue] = useEditableProp(
props.__inPlaceEditValue, props.treeData || [],
bindings?.__inPlaceEditValue, isInPlaceEditEnabled ? bindings?.__inPlaceEditValue : undefined,
[] props.treeData || []
); );
const itemFields = schema.item_fields || []; const itemFields = schema.item_fields || [];
const titleField = itemFields.find( const titleField = itemFields.find(
(field) => field.name === schema.item_title_field (field) => field.name === schema.item_title_field
); );
const isInlineEditing = Boolean( const isInlineEditing = Boolean(
props.__inPlaceEditEnabled isInPlaceEditEnabled
&& bindings?.__inPlaceEditValue && bindings?.__inPlaceEditValue
&& Array.isArray(editableValue) && Array.isArray(editableValue)
&& schema.kind === "tree" && schema.kind === "tree"

2
web/client/src/layout/index.jsx

@ -95,7 +95,7 @@ export default function index(props) {
<Routes> <Routes>
{outerRoutes} {outerRoutes}
<Route path="/" element={<Layout routerNav={routerNav} />}> <Route path="/" element={<Layout routerNav={routerNav} />}>
<Route index element={<Navigate to="/superAgent" replace />} /> <Route index element={<Navigate to="/aichat" replace />} />
{innerRoutes} {innerRoutes}
</Route> </Route>
</Routes> </Routes>

103
web/client/src/sections/superAgent/hooks/useSuperAgentStream.js

@ -73,6 +73,51 @@ const getInterruptStatus = (item = {}) => {
const isTerminalInterrupt = (item = {}) => const isTerminalInterrupt = (item = {}) =>
TERMINAL_INTERRUPT_STATUSES.has(getInterruptStatus(item)); TERMINAL_INTERRUPT_STATUSES.has(getInterruptStatus(item));
const getInterruptInteractionKey = (item = {}) => {
const payload = item?.value || item?.payload || item || {};
const interactionId = String(
payload.interaction_id ||
payload.interactionId ||
item.interaction_id ||
item.interactionId ||
""
).trim();
if (!interactionId) return "";
const revision = payload.revision ?? item.revision ?? "";
return `${interactionId}:${String(revision)}`;
};
const parseMessageContent = (content) => {
if (content && typeof content === "object") return content;
if (typeof content !== "string") return null;
try {
return JSON.parse(content);
} catch (_) {
return null;
}
};
const collectResolvedInterruptInteractions = (messages = []) => {
const resolvedInteractions = new Set();
(Array.isArray(messages) ? messages : []).forEach((message) => {
if (String(message?.type || "").toLowerCase() !== "tool") return;
const result = parseMessageContent(message.content);
const decisionType = String(result?.type || "").toLowerCase();
const interactionId = String(result?.interaction_id || "").trim();
const revision = result?.revision ?? "";
if (
interactionId &&
["approve", "edit", "reject"].includes(decisionType)
) {
resolvedInteractions.add(`${interactionId}:${String(revision)}`);
}
});
return resolvedInteractions;
};
const isSuperAgentEventRequest = (input) => { const isSuperAgentEventRequest = (input) => {
const requestUrl = String(input?.url || input || ""); const requestUrl = String(input?.url || input || "");
return /\/threads\/[^/]+\/stream\/events(?:\?|$)/.test(requestUrl); return /\/threads\/[^/]+\/stream\/events(?:\?|$)/.test(requestUrl);
@ -394,6 +439,7 @@ const useSuperAgentStream = ({
const [runtimeInterrupts, setRuntimeInterrupts] = useState([]); const [runtimeInterrupts, setRuntimeInterrupts] = useState([]);
const [dismissedInterruptIds, setDismissedInterruptIds] = useState(new Set()); const [dismissedInterruptIds, setDismissedInterruptIds] = useState(new Set());
const resolvedInterruptIdsRef = useRef(new Set()); const resolvedInterruptIdsRef = useRef(new Set());
const resolvedInterruptInteractionsRef = useRef(new Set());
const [currentInterruptId, setCurrentInterruptId] = useState(""); const [currentInterruptId, setCurrentInterruptId] = useState("");
const [hydratedThreadId, setHydratedThreadId] = useState(""); const [hydratedThreadId, setHydratedThreadId] = useState("");
const [transportStateRevision, setTransportStateRevision] = useState(0); const [transportStateRevision, setTransportStateRevision] = useState(0);
@ -580,6 +626,7 @@ const useSuperAgentStream = ({
setRuntimeInterrupts([]); setRuntimeInterrupts([]);
setDismissedInterruptIds(new Set()); setDismissedInterruptIds(new Set());
resolvedInterruptIdsRef.current.clear(); resolvedInterruptIdsRef.current.clear();
resolvedInterruptInteractionsRef.current.clear();
setCurrentInterruptId(""); setCurrentInterruptId("");
setHydratedThreadId(""); setHydratedThreadId("");
}, [activeSessionId]); }, [activeSessionId]);
@ -671,7 +718,12 @@ const useSuperAgentStream = ({
if (event?.method === "input.requested") { if (event?.method === "input.requested") {
const data = event.params?.data || {}; const data = event.params?.data || {};
const interruptId = String(data.interrupt_id || "").trim(); const interruptId = String(data.interrupt_id || "").trim();
if (interruptId && !resolvedInterruptIdsRef.current.has(interruptId)) { const interactionKey = getInterruptInteractionKey(data.payload);
const isResolvedInterrupt =
resolvedInterruptIdsRef.current.has(interruptId) ||
(interactionKey &&
resolvedInterruptInteractionsRef.current.has(interactionKey));
if (interruptId && !isResolvedInterrupt) {
localRunActiveRef.current = false; localRunActiveRef.current = false;
setIsLocalRunActive(false); setIsLocalRunActive(false);
setCurrentInterruptId(interruptId); setCurrentInterruptId(interruptId);
@ -763,6 +815,7 @@ const useSuperAgentStream = ({
setIsBusinessRunning(false); setIsBusinessRunning(false);
setRuntimeInterrupts([]); setRuntimeInterrupts([]);
resolvedInterruptIdsRef.current.clear(); resolvedInterruptIdsRef.current.clear();
resolvedInterruptInteractionsRef.current.clear();
// 会话切换只断开当前浏览器订阅,不能发送 stop/cancel 命令。 // 会话切换只断开当前浏览器订阅,不能发送 stop/cancel 命令。
// stopRun 只由用户点击停止按钮触发,否则后端会把原会话改为 suspended。 // stopRun 只由用户点击停止按钮触发,否则后端会把原会话改为 suspended。
@ -862,6 +915,12 @@ const useSuperAgentStream = ({
}).then((result) => { }).then((result) => {
if (interruptId) { if (interruptId) {
resolvedInterruptIdsRef.current.add(interruptId); resolvedInterruptIdsRef.current.add(interruptId);
const interactionKey = getInterruptInteractionKey(
response?.value || response?.payload || response
);
if (interactionKey) {
resolvedInterruptInteractionsRef.current.add(interactionKey);
}
setRuntimeInterrupts((currentInterrupts) => setRuntimeInterrupts((currentInterrupts) =>
currentInterrupts.filter( currentInterrupts.filter(
(item) => String(item?.id || "") !== interruptId (item) => String(item?.id || "") !== interruptId
@ -952,6 +1011,19 @@ const useSuperAgentStream = ({
const hydratedMessages = Array.isArray(hydratedState?.values?.messages) const hydratedMessages = Array.isArray(hydratedState?.values?.messages)
? hydratedState.values.messages ? hydratedState.values.messages
: []; : [];
const historicalResolvedInteractionKeys = useMemo(
() =>
collectResolvedInterruptInteractions([
...hydratedMessages,
...rawStreamMessages,
]),
[hydratedMessages, rawStreamMessages]
);
useEffect(() => {
historicalResolvedInteractionKeys.forEach((interactionKey) => {
resolvedInterruptInteractionsRef.current.add(interactionKey);
});
}, [historicalResolvedInteractionKeys]);
// 只有 hydrate 真正拿到消息后,才能解除首次提交的 thread 投影保护。 // 只有 hydrate 真正拿到消息后,才能解除首次提交的 thread 投影保护。
// 仅有 task 快照不代表消息已完成落库,否则 task 先到时会让当前消息再次被隐藏。 // 仅有 task 快照不代表消息已完成落库,否则 task 先到时会让当前消息再次被隐藏。
const hasHydratedSessionContent = hydratedMessages.length > 0; const hasHydratedSessionContent = hydratedMessages.length > 0;
@ -1058,10 +1130,18 @@ const useSuperAgentStream = ({
})) }))
: []), : []),
]; ];
const hasPersistedInterrupts = hydratedInterrupts.length > 0; const pendingHydratedInterrupts = hydratedInterrupts.filter((item) => {
const interruptId = String(item?.id || item?.interruptId || "").trim();
const interactionKey = getInterruptInteractionKey(item);
return (
!resolvedInterruptIdsRef.current.has(interruptId) &&
!historicalResolvedInteractionKeys.has(interactionKey)
);
});
const hasPersistedInterrupts = pendingHydratedInterrupts.length > 0;
const persistedInterrupts = const persistedInterrupts =
hydratedState hydratedState
? hydratedInterrupts ? pendingHydratedInterrupts
: isStreamProjectionReady : isStreamProjectionReady
? stream.interrupts ? stream.interrupts
: []; : [];
@ -1132,14 +1212,25 @@ const useSuperAgentStream = ({
streamInterrupts.forEach((item) => appendInterrupt(item)); streamInterrupts.forEach((item) => appendInterrupt(item));
return [...merged.values()].filter( return [...merged.values()].filter(
(item) => (item) => {
!dismissedInterruptIds.has(String(item.id || "")) && const interruptId = String(item.id || "");
const interactionKey = getInterruptInteractionKey(item);
const isHistoricalResolved =
historicalResolvedInteractionKeys.has(interactionKey) ||
resolvedInterruptInteractionsRef.current.has(interactionKey);
return (
!dismissedInterruptIds.has(interruptId) &&
!resolvedInterruptIdsRef.current.has(interruptId) &&
!isHistoricalResolved &&
!isTerminalInterrupt(item) !isTerminalInterrupt(item)
); );
}
);
}, [ }, [
dismissedInterruptIds, dismissedInterruptIds,
isInterruptProjectionReady, isInterruptProjectionReady,
isStreamProjectionReady, isStreamProjectionReady,
historicalResolvedInteractionKeys,
streamInterrupts, streamInterrupts,
]); ]);
@ -1170,7 +1261,7 @@ const useSuperAgentStream = ({
isPersistedThreadActive, isPersistedThreadActive,
currentInterruptId: currentInterruptId:
hydratedState && hydratedState &&
hydratedInterrupts.length === 0 && pendingHydratedInterrupts.length === 0 &&
runtimeInterrupts.length === 0 runtimeInterrupts.length === 0
? "" ? ""
: currentInterruptId, : currentInterruptId,

Loading…
Cancel
Save