Skip to content

Commit 482122e

Browse files
authored
feat(agent-ui): optimize session status action design (#1766)
1 parent 52ae5fb commit 482122e

File tree

5 files changed

+59
-92
lines changed

5 files changed

+59
-92
lines changed

multimodal/tarko/agent-ui/src/common/hooks/useSession.ts

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
deleteSessionAction,
2222
sendMessageAction,
2323
abortQueryAction,
24-
checkSessionStatusAction,
2524
} from '../state/actions/sessionActions';
2625
import {
2726
initConnectionMonitoringAction,
@@ -64,29 +63,6 @@ export function useSession() {
6463
const abortQuery = useSetAtom(abortQueryAction);
6564
const initConnectionMonitoring = useSetAtom(initConnectionMonitoringAction);
6665
const checkServerStatus = useSetAtom(checkConnectionStatusAction);
67-
const checkSessionStatus = useSetAtom(checkSessionStatusAction);
68-
69-
const statusCheckTimeoutRef = useRef<NodeJS.Timeout | null>(null);
70-
71-
useEffect(() => {
72-
if (!activeSessionId || !connectionStatus.connected || isReplayMode) return;
73-
74-
if (statusCheckTimeoutRef.current) {
75-
clearTimeout(statusCheckTimeoutRef.current);
76-
}
77-
78-
statusCheckTimeoutRef.current = setTimeout(() => {
79-
if (activeSessionId && connectionStatus.connected && !isReplayMode) {
80-
checkSessionStatus(activeSessionId);
81-
}
82-
}, 200);
83-
84-
return () => {
85-
if (statusCheckTimeoutRef.current) {
86-
clearTimeout(statusCheckTimeoutRef.current);
87-
}
88-
};
89-
}, [activeSessionId, connectionStatus.connected, checkSessionStatus, isReplayMode]);
9066

9167
const sessionState = useMemo(
9268
() => ({
@@ -122,7 +98,7 @@ export function useSession() {
12298
initConnectionMonitoring,
12399
checkServerStatus,
124100

125-
checkSessionStatus,
101+
126102
}),
127103
[
128104
sessions,
@@ -151,7 +127,6 @@ export function useSession() {
151127
setWorkspaceDisplayState,
152128
initConnectionMonitoring,
153129
checkServerStatus,
154-
checkSessionStatus,
155130
],
156131
);
157132

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { isProcessingAtom } from '@/common/state/atoms/ui';
1+
import { sessionProcessingStatesAtom } from '@/common/state/atoms/ui';
22
import { AgentEventStream } from '@/common/types';
33
import { EventHandler, EventHandlerContext } from '../types';
4-
import { shouldUpdateProcessingState } from '../utils/panelContentUpdater';
54

65
export class AgentRunStartHandler implements EventHandler<AgentEventStream.AgentRunStartEvent> {
76
canHandle(event: AgentEventStream.Event): event is AgentEventStream.AgentRunStartEvent {
@@ -15,10 +14,11 @@ export class AgentRunStartHandler implements EventHandler<AgentEventStream.Agent
1514
): void {
1615
const { set } = context;
1716

18-
// Update processing state
19-
if (shouldUpdateProcessingState(sessionId)) {
20-
set(isProcessingAtom, true);
21-
}
17+
// Update session-isolated processing state
18+
set(sessionProcessingStatesAtom, (prev) => ({
19+
...prev,
20+
[sessionId]: true,
21+
}));
2222
}
2323
}
2424

@@ -30,9 +30,10 @@ export class AgentRunEndHandler implements EventHandler<AgentEventStream.Event>
3030
handle(context: EventHandlerContext, sessionId: string, event: AgentEventStream.Event): void {
3131
const { set } = context;
3232

33-
// Update processing state
34-
if (shouldUpdateProcessingState(sessionId)) {
35-
set(isProcessingAtom, false);
36-
}
33+
// Update session-isolated processing state
34+
set(sessionProcessingStatesAtom, (prev) => ({
35+
...prev,
36+
[sessionId]: false,
37+
}));
3738
}
3839
}

multimodal/tarko/agent-ui/src/common/state/actions/eventProcessors/utils/panelContentUpdater.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,3 @@ export function shouldUpdatePanelContent(get: Getter, sessionId: string): boolea
1717

1818
return true;
1919
}
20-
21-
/**
22-
* Helper function to determine if processing state should be updated for a session
23-
* Always allow processing state updates for the session that owns the event
24-
*/
25-
export function shouldUpdateProcessingState(sessionId: string): boolean {
26-
// Processing state is now session-isolated, so we always update for the event's session
27-
return Boolean(sessionId);
28-
}

multimodal/tarko/agent-ui/src/common/state/actions/sessionActions.ts

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { apiService } from '../../services/apiService';
44
import { sessionsAtom, activeSessionIdAtom } from '../atoms/session';
55
import { messagesAtom } from '../atoms/message';
66
import { toolResultsAtom, toolCallResultMap } from '../atoms/tool';
7-
import { sessionPanelContentAtom, isProcessingAtom } from '../atoms/ui';
7+
import { sessionPanelContentAtom, sessionProcessingStatesAtom, isProcessingAtom } from '../atoms/ui';
88
import { processEventAction } from './eventProcessors';
99
import { Message, SessionInfo } from '@/common/types';
1010
import { connectionStatusAtom } from '../atoms/ui';
@@ -150,7 +150,7 @@ export const setActiveSessionAction = atom(null, async (get, set, sessionId: str
150150
});
151151
}
152152

153-
// Processing state will be managed by SSE events
153+
154154

155155
toolCallResultMap.clear();
156156

@@ -169,6 +169,27 @@ export const setActiveSessionAction = atom(null, async (get, set, sessionId: str
169169
}
170170
}
171171

172+
// Status recovery with higher priority - always called after events processing
173+
// This ensures accurate processing state when SSE connection is established
174+
// and overrides any incorrect state from events processing
175+
if (!replayState.isActive) {
176+
try {
177+
const status = await apiService.getSessionStatus(sessionId);
178+
set(sessionProcessingStatesAtom, (prev) => ({
179+
...prev,
180+
[sessionId]: status.isProcessing,
181+
}));
182+
console.log(`Recovered processing state for session ${sessionId}: ${status.isProcessing}`);
183+
} catch (error) {
184+
console.warn(`Failed to recover session status for ${sessionId}:`, error);
185+
// Default to false on error to avoid stuck processing state
186+
set(sessionProcessingStatesAtom, (prev) => ({
187+
...prev,
188+
[sessionId]: false,
189+
}));
190+
}
191+
}
192+
172193
// Always ensure we have the latest session metadata (including modelConfig)
173194
// This is lightweight since server always provides it
174195
try {
@@ -410,46 +431,3 @@ export const abortQueryAction = atom(null, async (get, set) => {
410431
return false;
411432
}
412433
});
413-
414-
// Cache to prevent frequent status checks for the same session
415-
const statusCheckCache = new Map<string, { timestamp: number; promise?: Promise<any> }>();
416-
const STATUS_CACHE_TTL = 2000; // 2 seconds cache
417-
418-
export const checkSessionStatusAction = atom(null, async (get, set, sessionId: string) => {
419-
if (!sessionId) return;
420-
421-
const now = Date.now();
422-
const cached = statusCheckCache.get(sessionId);
423-
424-
// If we have a recent check or an ongoing request, skip
425-
if (cached) {
426-
if (cached.promise) {
427-
// There's already an ongoing request for this session
428-
return cached.promise;
429-
}
430-
if (now - cached.timestamp < STATUS_CACHE_TTL) {
431-
// Recent check, skip
432-
return;
433-
}
434-
}
435-
436-
try {
437-
// Mark that we're making a request
438-
const promise = apiService.getSessionStatus(sessionId);
439-
statusCheckCache.set(sessionId, { timestamp: now, promise });
440-
441-
const status = await promise;
442-
443-
// Update simple processing state
444-
set(isProcessingAtom, status.isProcessing);
445-
446-
// Clear the promise and update timestamp
447-
statusCheckCache.set(sessionId, { timestamp: now });
448-
449-
return status;
450-
} catch (error) {
451-
console.error('Failed to check session status:', error);
452-
// Clear the failed request
453-
statusCheckCache.delete(sessionId);
454-
}
455-
});

multimodal/tarko/agent-ui/src/common/state/atoms/ui.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,31 @@ export const sidebarCollapsedAtom = atom<boolean>(true);
5757
export const workspacePanelCollapsedAtom = atom<boolean>(false);
5858

5959
/**
60-
* Simple processing state atom based on SSE events
60+
* Session-isolated processing state atom based on SSE events
61+
* Maps session IDs to their processing states
6162
*/
62-
export const isProcessingAtom = atom<boolean>(false);
63+
export const sessionProcessingStatesAtom = atom<Record<string, boolean>>({});
64+
65+
/**
66+
* Derived atom for the current active session's processing state
67+
* Automatically isolates state by active session
68+
*/
69+
export const isProcessingAtom = atom(
70+
(get) => {
71+
const activeSessionId = get(activeSessionIdAtom);
72+
const sessionProcessingStates = get(sessionProcessingStatesAtom);
73+
return activeSessionId ? sessionProcessingStates[activeSessionId] ?? false : false;
74+
},
75+
(get, set, update: boolean) => {
76+
const activeSessionId = get(activeSessionIdAtom);
77+
if (activeSessionId) {
78+
set(sessionProcessingStatesAtom, (prev) => ({
79+
...prev,
80+
[activeSessionId]: update,
81+
}));
82+
}
83+
},
84+
);
6385

6486
/**
6587
* Workspace display mode - determines what content is shown in the workspace

0 commit comments

Comments
 (0)