From 9aebc1de671c9ab18fc4c0fa842363bdd49bfc5a Mon Sep 17 00:00:00 2001 From: Zamil Majdy Date: Tue, 27 Jan 2026 11:32:33 -0600 Subject: [PATCH] fix: address remaining review feedback Backend: - Make invalidate_session_cache best-effort with try-except Frontend: - Fix polling bug: use recursive scheduling so polling continues after each refetch - Add toolName fallback in helpers.ts parseToolResponse for operation_* types - Make PendingOperationWidget title dynamic based on toolName instead of hardcoded "Creating Agent" --- .../backend/api/features/chat/model.py | 11 +++-- .../Chat/components/ChatContainer/helpers.ts | 6 +-- .../PendingOperationWidget.tsx | 21 ++++++-- .../contextual/Chat/useChatSession.ts | 49 +++++++++++++------ 4 files changed, 63 insertions(+), 24 deletions(-) diff --git a/autogpt_platform/backend/backend/api/features/chat/model.py b/autogpt_platform/backend/backend/api/features/chat/model.py index 738c02cd80..7318ef88d7 100644 --- a/autogpt_platform/backend/backend/api/features/chat/model.py +++ b/autogpt_platform/backend/backend/api/features/chat/model.py @@ -299,10 +299,15 @@ async def invalidate_session_cache(session_id: str) -> None: """Invalidate a chat session from Redis cache. Used by background tasks to ensure fresh data is loaded on next access. + This is best-effort - Redis failures are logged but don't fail the operation. """ - redis_key = _get_session_cache_key(session_id) - async_redis = await get_redis_async() - await async_redis.delete(redis_key) + try: + redis_key = _get_session_cache_key(session_id) + async_redis = await get_redis_async() + await async_redis.delete(redis_key) + except Exception as e: + # Best-effort: log but don't fail - cache will expire naturally + logger.warning(f"Failed to invalidate session cache for {session_id}: {e}") async def _get_session_from_db(session_id: str) -> ChatSession | None: diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/helpers.ts b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/helpers.ts index 216671615c..9a0175d958 100644 --- a/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/helpers.ts +++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/ChatContainer/helpers.ts @@ -344,7 +344,7 @@ export function parseToolResponse( if (responseType === "operation_started") { return { type: "operation_started", - toolName: parsedResult.tool_name as string, + toolName: (parsedResult.tool_name as string) || toolName, operationId: (parsedResult.operation_id as string) || "", message: (parsedResult.message as string) || @@ -355,7 +355,7 @@ export function parseToolResponse( if (responseType === "operation_pending") { return { type: "operation_pending", - toolName: parsedResult.tool_name as string, + toolName: (parsedResult.tool_name as string) || toolName, operationId: (parsedResult.operation_id as string) || "", message: (parsedResult.message as string) || @@ -366,7 +366,7 @@ export function parseToolResponse( if (responseType === "operation_in_progress") { return { type: "operation_in_progress", - toolName: parsedResult.tool_name as string, + toolName: (parsedResult.tool_name as string) || toolName, toolCallId: (parsedResult.tool_call_id as string) || "", message: (parsedResult.message as string) || diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/components/PendingOperationWidget/PendingOperationWidget.tsx b/autogpt_platform/frontend/src/components/contextual/Chat/components/PendingOperationWidget/PendingOperationWidget.tsx index 61976cd9c6..6cfea7f327 100644 --- a/autogpt_platform/frontend/src/components/contextual/Chat/components/PendingOperationWidget/PendingOperationWidget.tsx +++ b/autogpt_platform/frontend/src/components/contextual/Chat/components/PendingOperationWidget/PendingOperationWidget.tsx @@ -19,6 +19,19 @@ interface Props { className?: string; } +function getOperationTitle(toolName?: string): string { + if (!toolName) return "Operation"; + // Convert tool name to human-readable format + // e.g., "create_agent" -> "Creating Agent", "edit_agent" -> "Editing Agent" + if (toolName === "create_agent") return "Creating Agent"; + if (toolName === "edit_agent") return "Editing Agent"; + // Default: capitalize and format tool name + return toolName + .split("_") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); +} + export function PendingOperationWidget({ status, message, @@ -30,6 +43,8 @@ export function PendingOperationWidget({ const isCompleted = status === "completed"; const isError = status === "error"; + const operationTitle = getOperationTitle(toolName); + return (
- {isPending && "Creating Agent"} - {isCompleted && "Operation Complete"} - {isError && "Operation Failed"} + {isPending && operationTitle} + {isCompleted && `${operationTitle} Complete`} + {isError && `${operationTitle} Failed`} {message} diff --git a/autogpt_platform/frontend/src/components/contextual/Chat/useChatSession.ts b/autogpt_platform/frontend/src/components/contextual/Chat/useChatSession.ts index a7b8842b89..ebdc89c161 100644 --- a/autogpt_platform/frontend/src/components/contextual/Chat/useChatSession.ts +++ b/autogpt_platform/frontend/src/components/contextual/Chat/useChatSession.ts @@ -132,6 +132,9 @@ export function useChatSession({ // Poll for updates when there are pending operations (long poll - 10s intervals with backoff) const pollAttemptRef = useRef(0); + const hasPendingOperationsRef = useRef(hasPendingOperations); + hasPendingOperationsRef.current = hasPendingOperations; + useEffect( function pollForPendingOperations() { if (!sessionId || !hasPendingOperations) { @@ -139,27 +142,43 @@ export function useChatSession({ return; } + let cancelled = false; + let timeoutId: ReturnType | null = null; + // Calculate delay with exponential backoff: 10s, 15s, 20s, 25s, 30s (max) const baseDelay = 10000; const maxDelay = 30000; - const delay = Math.min( - baseDelay + pollAttemptRef.current * 5000, - maxDelay, - ); - const timeoutId = setTimeout(async () => { - console.info( - `[useChatSession] Polling for pending operation updates (attempt ${pollAttemptRef.current + 1})`, + function schedule() { + const delay = Math.min( + baseDelay + pollAttemptRef.current * 5000, + maxDelay, ); - pollAttemptRef.current += 1; - try { - await refetch(); - } catch (err) { - console.error("[useChatSession] Poll failed:", err); - } - }, delay); + timeoutId = setTimeout(async () => { + if (cancelled) return; + console.info( + `[useChatSession] Polling for pending operation updates (attempt ${pollAttemptRef.current + 1})`, + ); + pollAttemptRef.current += 1; + try { + await refetch(); + } catch (err) { + console.error("[useChatSession] Poll failed:", err); + } finally { + // Continue polling if still pending and not cancelled + if (!cancelled && hasPendingOperationsRef.current) { + schedule(); + } + } + }, delay); + } - return () => clearTimeout(timeoutId); + schedule(); + + return () => { + cancelled = true; + if (timeoutId) clearTimeout(timeoutId); + }; }, [sessionId, hasPendingOperations, refetch], );