ChatDev REST API vs WebSocket Execution Modes: Architecture and Implementation Guide

ChatDev offers two distinct execution pathways—synchronous HTTP REST endpoints and persistent WebSocket connections—that share the same GraphExecutor engine but differ fundamentally in real-time feedback capabilities, interactive human-in-the-loop support, and state persistence models.

The OpenBMB/ChatDev framework provides flexible deployment options for AI-driven software development workflows. When implementing production deployments, architects must evaluate the ChatDev REST API vs WebSocket execution modes to determine whether simple request-response patterns or interactive, long-running sessions better serve their use case. Both modes ultimately invoke the core graph traversal logic, yet they diverge significantly in concurrency models, progress reporting, and session lifecycle management.

Execution Architecture Fundamentals

REST Synchronous Path (server/routes/execute_sync.py)

In server/routes/execute_sync.py, the run_workflow_sync function handles HTTP POST requests to /api/workflow/run. This route constructs a GraphContext, instantiates a stream-only subclass of GraphExecutor (often _StreamingExecutor), and executes the workflow via runtime/sdk.py's run_workflow helper. The execution blocks within a thread pool using GraphExecutor._execute, returning only after the graph traversal completes. This synchronous design suits batch processing where clients require final results rather than incremental updates.

WebSocket Asynchronous Path (server/routes/websocket.py)

The WebSocket handler in server/routes/websocket.py upgrades HTTP connections through websocket_endpoint and registers them via WebSocketManager.connect, generating a unique session_id. Execution flows through WorkflowRunService.start_workflow, which creates a WebSocketGraphExecutor—an async subclass of GraphExecutor defined in server/services/websocket_executor.py. This executor runs via execute_graph_async on the asyncio event loop, maintaining the socket connection throughout the workflow lifecycle while pushing real-time JSON messages.

Real-Time Progress Reporting Patterns

REST Response Models

The synchronous endpoint returns final results only after workflow completion, including final_message, token_usage, and output_dir. For streaming requirements, clients can set Accept: text/event-stream to receive Server-Sent Events (SSE) via _run_workflow_with_logger, which enqueues started, log, and completed events through a queue.Queue mechanism before the HTTP response closes.

WebSocket Message Protocol

WebSocket mode leverages WebSocketManager.send_message to push typed JSON messages immediately. Event types include workflow_started, log, human_input, workflow_completed, and workflow_cancelled. This persistent channel enables clients to monitor incremental progress without polling, as the WebSocketGraphExecutor emits logs and intermediate results during graph traversal.

Interactive Capabilities and State Management

Human-in-the-Loop Support

Only the WebSocket implementation supports pausing execution for human feedback. When the executor encounters a HumanNode, it pauses and awaits input via MessageHandler._handle_human_input, which forwards data to SessionExecutionController.provide_human_input. The WorkflowSessionStore maintains session state (including RUNNING, WAITING_FOR_INPUT, and CANCELLED statuses) across multiple interactions, enabling complex multi-turn conversations impossible in REST mode.

Cancellation Mechanisms

REST workflows can only be terminated by client-side HTTP request abortion or timeouts, with no server-side cancellation channel. Conversely, WebSocket clients trigger cancellation through WebSocketManager.disconnect or explicit cancel messages, which invoke WorkflowRunService.request_cancel to propagate WorkflowCancelledError to the running executor, allowing graceful termination and resource cleanup.

Scalability and Resource Models

Thread-Based vs Event-Driven Concurrency

Synchronous REST execution occupies a worker thread for the entire workflow duration, limiting concurrent throughput to the thread pool size. The WebSocket architecture runs on the asyncio event loop, allowing thousands of sessions to coexist within a single process. Sessions waiting for human input consume negligible CPU resources, freeing the event loop for active computations.

State Persistence Scope

REST execution maintains GraphContext only within the request thread's memory; once the HTTP response completes, intermediate state is discarded. WebSocket sessions persist in WorkflowSessionStore across multiple messages, enabling workflow inspection via get_status commands and recovery from temporary disconnections without data loss.

Implementation Examples

Executing via REST API

Standard blocking execution returning final results:

curl -X POST https://your-host/api/workflow/run \
  -H "Content-Type: application/json" \
  -d '{
        "yaml_file": "my_workflow.yaml",
        "task_prompt": "Summarize the attached code.",
        "attachments": ["./src/main.py"],
        "log_level": "INFO"
      }'

For real-time streaming via Server-Sent Events:

curl -N -X POST https://your-host/api/workflow/run \
  -H "Accept: text/event-stream" \
  -H "Content-Type: application/json" \
  -d '{"yaml_file":"my_workflow.yaml","task_prompt":"Explain the graph structure."}'

Interactive WebSocket Client

JavaScript implementation handling bidirectional communication:

const ws = new WebSocket("wss://your-host/ws");

ws.onopen = () => {
  console.log("WebSocket established");
  ws.send(JSON.stringify({
    type: "workflow_start",
    data: {
      yaml_file: "design_workflow.yaml",
      task_prompt: "Generate a design document.",
      log_level: "DEBUG"
    }
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  console.log("Received:", msg.type);
  
  // Handle human-in-the-loop prompts
  if (msg.type === "human_input") {
    ws.send(JSON.stringify({
      type: "human_input",
      data: { 
        input: "Clarification provided by user.",
        attachments: []
      }
    }));
  }
};

ws.onerror = (e) => console.error("WebSocket error:", e);

Cancelling a WebSocket Session

// Method 1: Close connection to trigger disconnect handlers
ws.close();

// Method 2: Send explicit cancel message
ws.send(JSON.stringify({ type: "cancel", data: {} }));

The WebSocketManager.disconnect routine invokes WorkflowRunService.request_cancel, setting cancellation flags that the WebSocketGraphExecutor checks during graph traversal.

Summary

  • REST API provides simple HTTP request-response semantics suitable for batch jobs and CI pipelines, with optional SSE streaming but no mid-execution interaction or server-side cancellation.
  • WebSocket mode enables real-time bidirectional communication, supporting human-in-the-loop workflows via HumanNode pause/resume and explicit server-side cancellation mechanisms.
  • Both modes utilize the core GraphExecutor class from workflow/graph.py, but differ in subclass implementations (_StreamingExecutor for REST vs WebSocketGraphExecutor for WebSocket).
  • Thread pool blocking in REST limits concurrency compared to the asyncio-based WebSocket architecture that handles thousands of concurrent sessions.
  • Session persistence through WorkflowSessionStore exists only in WebSocket mode, enabling workflow inspection, reconnection, and multi-turn conversations.

Frequently Asked Questions

Can I switch from REST to WebSocket mid-execution?

No. The execution mode is determined at session initialization. REST workflows run in run_workflow_sync with thread-local state that terminates after the HTTP response, while WebSocket workflows require the persistent session management established during the initial websocket_endpoint handshake. To change modes, you must cancel the current execution and start a new session via the desired protocol.

Which mode should I use for automated CI/CD pipelines?

Use the REST API. The synchronous endpoint in server/routes/execute_sync.py provides deterministic completion signals and simple error handling via HTTP status codes. Since CI pipelines typically require final artifacts rather than incremental logs or human feedback, the blocking request-response model eliminates the complexity of managing persistent connections and session state.

How does error handling differ between the two modes?

REST execution returns HTTP 4xx/5xx status codes for validation or execution errors, with details in the JSON response body. WebSocket mode transmits errors as typed messages through the active socket while keeping the connection alive, allowing clients to handle failures without reconnection. Critical errors in both modes ultimately derive from WorkflowExecutionError raised in the underlying GraphExecutor engine.

Is session state shared between REST requests?

No. REST execution maintains state only within the request thread's memory; once the HTTP response completes, the GraphContext and intermediate results are discarded. WebSocket sessions persist in WorkflowSessionStore across multiple messages and connections, enabling status inspection via MessageHandler commands and recovery from temporary network interruptions without restarting the workflow.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →