Skip to content

Cooperative execution cancellation from Chat UI #439

@kovtcharov

Description

@kovtcharov

Summary

Allow users to cancel an in-progress agent response from the Chat UI. Currently, clicking "Stop" aborts the SSE stream (via AbortController), but the agent thread continues running in the background — wasting compute and potentially executing unwanted tool calls.

Motivation

When the agent is taking a long time (e.g., multi-step plan with tool calls), the user has no way to actually stop it. The frontend can close the stream, but the backend thread keeps going. This wastes resources and can lead to confusing state where a "cancelled" message still gets saved.

Design

Agent Framework (src/gaia/agents/base/agent.py)

  1. Add _cancelled flag to the Agent base class:

    def __init__(self):
        self._cancelled = threading.Event()
    
    def cancel(self):
        """Request cooperative cancellation of the current query."""
        self._cancelled.set()
    
    def _check_cancelled(self):
        """Check if cancellation was requested. Call at safe points."""
        if self._cancelled.is_set():
            raise AgentCancelled("Execution cancelled by user")
  2. Add cancellation checkpoints in process_query():

    • Before each step iteration
    • Before _execute_tool()
    • Before LLM calls
    • After tool completion
  3. AgentCancelled exception — New exception type in errors.py that the streaming loop catches gracefully.

Server (src/gaia/chat/ui/server.py)

  1. Store active agent reference per session:

    _active_agents: Dict[str, ChatAgent] = {}
  2. POST /api/chat/cancel endpoint:

    @app.post("/api/chat/cancel")
    async def cancel_chat(request: CancelRequest):
        agent = _active_agents.get(request.session_id)
        if agent:
            agent.cancel()
        return {"cancelled": True}
  3. Cleanup — Remove agent from _active_agents when streaming completes.

Frontend

  1. api.tscancelChat(sessionId) function
  2. ChatView — "Stop" button calls cancelChat() in addition to AbortController.abort()

Acceptance Criteria

  • Clicking "Stop" in the UI actually stops the agent (not just the stream)
  • Agent stops at the next safe checkpoint (doesn't cut mid-tool)
  • Partial response is preserved and shown to the user
  • No orphan agent threads after cancellation
  • Works on Windows and Linux/macOS
  • CLI agent (gaia chat) handles Ctrl+C gracefully via the same mechanism

Files to Modify

  • src/gaia/agents/base/agent.py_cancelled flag, cancel(), checkpoints
  • src/gaia/agents/base/errors.pyAgentCancelled exception
  • src/gaia/chat/ui/server.py — Active agent tracking, /api/chat/cancel
  • src/gaia/apps/chat/webui/src/services/api.tscancelChat()
  • src/gaia/apps/chat/webui/src/components/ChatView.tsx — Wire stop button

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentchatChat SDK changesenhancementNew feature or requestp1medium priority

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions