Skip to content

Stop Processing

This content was migrated from Documentation/STOP_PROCESSING.md and verified against the current codebase. Review for accuracy.

Purpose

The Stop Processing pattern enables users to cancel an in-progress response by clicking a Stop button. The system cleanly aborts the LLM stream, restores the user's original message to the input box, and removes the partial response from the chat view.

How It Works

When the user clicks Stop, three things happen simultaneously:

  1. Frontend aborts the SSE fetch request (immediate cancellation of the network stream).
  2. Frontend restores the user's original message to the input box and removes the partial response — so they can fix typos and resend.
  3. Backend sets a Redis stop flag that the streaming service checks cooperatively.

Redis Stop Flag

The backend uses a simple Redis key to signal cancellation:

Detail Value
Key pattern chat_stop:{chat_id}
Value "1"
TTL 60 seconds (auto-cleanup if not explicitly cleared)
Service TaskTrackingService in swisper/services/task_tracking.py

The streaming service (swisper/services/streaming.py) checks the flag before yielding each chunk:

if await self.task_tracking_service.check_stop_flag(chat_id):
    await self.task_tracking_service.clear_stop_flag(chat_id)
    # Send final stop response and break

This is cooperative cancellation — the backend doesn't force-kill the LLM call. It stops yielding chunks to the frontend and clears the flag for future messages.

New Chat Handling

For new chats where the chat record doesn't exist in the database yet:

  • No backend stop call is needed (no chat ID to flag).
  • The frontend simply aborts the fetch request.
  • Navigation to the chat page only happens when done=true — so stopping early keeps the user on the "new chat" page with no 404 errors.

Message Restoration (UX)

The key UX improvement: when the user clicks Stop, their original message is restored to the input box. This lets them fix typos and resend without retyping. Previously, the message was lost on stop — frustrating for long prompts.

Key Code Locations

Component Path
Stop flag service apps/backend/swisper/services/task_tracking.pyset_stop_flag(), check_stop_flag(), clear_stop_flag()
Streaming check apps/backend/swisper/services/streaming.py — checks flag in streaming loop
Orchestration check apps/backend/swisper/services/orchestration.py — checks flag during graph execution
Frontend stop handler apps/frontend/src/features/chat/hooks/use-stream-message.tsx