Skip to content

About

No description, website, or topics provided.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

CrewAI Conversational Flow UI Template

A reusable React + assistant-ui workspace for a deployed CrewAI conversational Flow. Configure your deployment URL and bearer token in .env, then run the UI. A conversation sidebar and central chat keep requests and explanations together. Generated code becomes a compact card in chat and streams into a dedicated code pane with Copy code and Download actions. Ordinary text replies stay in chat. Close the code pane to widen the conversation, or reopen a code card to inspect it. On mobile, switch between Conversation and Code, with history in the navigation drawer. Events retains every received runtime frame.

Messages, response chunks, and runtime events use CrewAI's WebSocket API. After three WebSocket retries, the UI falls back to HTTP message + SSE. HTTP also creates sessions and loads history.

Connect your deployed Flow

You need Node.js 22.12 or newer and a conversational Flow already deployed on CrewAI AMP.

  1. Open your automation in CrewAI AMP and find its Status tab.

  2. Copy the automation's deployment URL and bearer token. Use the same token you would use to call /kickoff.

  3. Create .env from the example in a fresh copy of this template:

    cp -n .env.example .env

    If .env already exists, edit that file instead.

  4. Set both values in .env:

    CREWAI_AMP_URL=https://your-flow-url.crewai.com
    CREWAI_BEARER_TOKEN="replace-with-your-deployment-bearer-token"
    # PORT=5173

    CREWAI_AMP_URL is the automation's base URL, not the AMP dashboard URL. Do not append /chat, /kickoff, or a session ID. Supply the token value without a Bearer prefix; the Node server adds that prefix to the Authorization header.

  5. Install and start:

    npm ci
    npm run dev

Open http://localhost:5173, send a message, and open Events to inspect the response chunks and runtime activity. The UI checks /inspect before enabling messages. The deployment must report both flow.chat.conversational: true and flow.chat.handle_turn: true.

The Node server reads .env at startup. After changing credentials or server code, stop the running process with Ctrl+C, rerun npm run dev, and refresh the browser. Frontend hot reload is disabled, so refresh after UI changes too.

The token stays on the Node server. .env is ignored by Git; .env.example contains placeholders. Keep these variables server-side, without a VITE_ prefix. Template copies and shared archives should include .env.example only.

How the UI connects

React / assistant-ui → local Node relay → deployed CrewAI Flow
                      bearer auth       WebSocket send + stream
Deployment endpoint Purpose
GET /inspect Check conversational chat capability
POST /chat/start Create a session
WS /chat/{session_id}/stream?events=*&last_event_id=0-0 Send a message or attach to an active turn; receive all runtime and text frames
POST /chat/{session_id}/message Queue an unsent message with stream: true during SSE fallback
GET /chat/{session_id}/stream/events?events=*&last_event_id=0-0 Attach to the turn over SSE after WebSocket retries are exhausted
GET /chat/{session_id}/history Load the authoritative transcript and active turn ID

The browser calls matching /api/... routes on the local server. Native WebSocket connects to /api/chat/{session_id}/stream. The relay opens a separate authenticated WebSocket to CrewAI, then completes the browser handshake only after CrewAI accepts its connection. The token stays in the server-side Authorization header, never in a browser URL.

On connection, the UI sends { "message": "...", "events": "*", "lastEventId": "0-0" } if it has a new message. Otherwise, it attaches to the active turn. The UI makes one initial WebSocket attempt and up to three retries, then switches to native EventSource for SSE. Events shows transport_retry, sse_fallback, and sse_connected so the switch is visible.

One open WebSocket is retained for the selected session. Completing or failing a turn refreshes history without closing that socket; the next message reuses it and starts with a fresh turn cursor. Events records websocket_reused instead of another connection handshake. Selecting a different session, starting a new thread, or leaving the page closes the connection. The relay closes its upstream connection gracefully.

The Enterprise implementation supports multiple turns on one socket and defaults to a five-minute idle timeout between turns (FLOW_CHAT_WEBSOCKET_IDLE_TIMEOUT_SECONDS). If the server closes an idle socket, the UI clears it and opens a new one on the next send, without retrying an already completed turn. Older deployments that close after every turn also reconnect on the next send.

Before retrying or switching transports, the UI checks authoritative history. If no message was sent, SSE fallback queues it once over HTTP. If the turn is active, SSE attaches without resending, preserving the event cursor and already displayed text. If the turn has completed, the UI loads history. If delivery is uncertain and history cannot confirm acceptance, the UI restores the draft instead of sending it again automatically.

The UI supports both text formats:

{ "type": "token", "data": { "content": "Hello " } }
{ "type": "llm_stream_chunk", "data": { "chunk": "world" } }

Each chunk updates the assistant message as received. If both formats arrive in a turn, normalized token frames take precedence to prevent duplicate text. Terminal frames refresh history, so completed replies also appear when the Flow emits no text chunks.

The Events panel retains every received JSON frame, including text chunks, with the full payload and millisecond receipt time. WebSocket ping/pong control frames are transport messages and do not appear as JSON events. Event logs are held in memory for the current conversation; the sidebar persists only session IDs and titles in this browser. Transcripts stay on CrewAI.

Only one turn runs per session. After a disconnect during a turn, the UI checks history and reattaches using the last Redis stream cursor. It never automatically resends a user message. SSE gets up to three recovery attempts; if those fail, use Reconnect. Each new turn reuses the session WebSocket when it is open; otherwise, it starts a new WebSocket attempt.

Reuse and customize

Runtime activity

The UI uses official assistant-ui Elements on top of useExternalStoreRuntime:

Element CrewAI integration
Thinking Indicator Current connection/response state
Tool Call and Tool Group MCP/CrewAI tool events become native tool-call parts, grouped behind one disclosure
Markdown Text Streaming replies, tables, lists, and links
Syntax Highlighter Lazy-loaded highlighting in the code pane and non-artifact code fences
Thread List This deployment's browser-saved session IDs, titles, and dates; selection loads CrewAI history
Reasoning Explicit llm_thinking_chunk text and agent_reasoning_completed plans become separate reasoning parts
Sources HTTP(S) references from source/sources frames, structured tool results (sources/results), or Exa's explicit Title/URL metadata rows
Message Timing Browser-measured first event, worker turn-start event, first text, and total stream time

Expand the tool group, then a call to inspect its request, result, MCP server, and available duration. Tool result panels resize when a result arrives while open. Long payloads scroll within the card. Reasoning uses its own disclosure; tool calls and ordinary logs do not become reasoning text.

Timings start when the UI accepts Send and include session creation, any history check, connection setup, and transport/worker latency. Turn started excludes the initial queued acknowledgement. First text is the first accepted answer text frame, which may precede the final public reply. Total ends at the terminal frame, or at history recovery when the terminal frame was missed. These are browser observations, not server-only execution durations. Resumed turns without a known Send time omit timings.

Tool cards, reasoning, sources, and timings are reconstructed from live events in the current tab. The deployment's text-only history does not restore these extras after reloading or switching conversations. All raw frames remain in Events for the current conversation. The history transcript stays authoritative.

The Elements are installed as source from the official registry:

npx shadcn@latest add @assistant-ui/elements-thinking-indicator @assistant-ui/elements-tool-call @assistant-ui/tool-group @assistant-ui/markdown-text @assistant-ui/syntax-highlighter @assistant-ui/elements-thread-list @assistant-ui/reasoning @assistant-ui/elements-sources @assistant-ui/elements-message-timing

Registry source lives in src/components/assistant-ui/, with shared shadcn button/tooltip components in src/components/ui/. src/ToolActivity.jsx maps CrewAI tool-call fields into the Tool Call Element's props. Tailwind compiles the supplied utilities; its base reset is scoped to the assistant-ui views. components.json records the registry and src/assistant-ui.css supplies the template's theme and Radix compatibility rules. The workspace panels and code artifact opening/downloading behavior remain template-specific.

Copy the template source into your own project, install dependencies, and configure its .env. Each copy can connect to a different conversational Flow through the two environment variables above.

File Customize
src/App.jsx Chat layout, branding, suggestions, assistant-ui runtime, and event display
src/styles.css Colors, spacing, and responsive layout
src/code-artifacts.mjs Extract top-level fenced code blocks, including code still streaming
src/assets/crewai-logo.png Top-left CrewAI logo
src/stream-text.mjs Text-frame handling
src/message-details.mjs Explicit reasoning/source fields and browser response timing
src/tool-events.mjs Correlate MCP/CrewAI tool events into tool-call message parts
src/components/assistant-ui/ Official registry Elements and shared dependencies
src/ToolActivity.jsx Map CrewAI tool-call parts into the official Tool Call Element
src/chat-stream.mjs WebSocket retries, SSE fallback, and delivery recovery
server.mjs HTTP session/history routes, SSE relay, and server configuration
websocket-relay.mjs Authenticated WebSocket handshake, message validation, and frame forwarding
index.html Page title and favicon

The connected deployment hostname appears below Flow connected. Sidebar sessions are saved separately for each deployment URL, so switching .env and restarting the server loads that deployment's own list and starts a fresh thread. The old shared session-storage entry is preserved but is not mixed into a deployment-specific list. Credentials stay server-side; the browser receives only a public hostname and a stable URL-derived identifier.

Build and verify

npm run build
npm start

npm start serves the built frontend and the Node relay on localhost, using the same environment variables. Public or multi-user hosting requires application authentication, per-user session authorization, and appropriate host/origin configuration. HITL feedback submission uses CrewAI's review workflow and is outside this template.

npm test

Tests use mock deployment data, not your real token or automation. They cover socket reuse across completed/failed turns, idle closure, three retries before SSE fallback, text chunks, safe React rendering, server-only auth, message validation, event/cursor forwarding, and forwarding text before the upstream turn completes. The TCP relay test needs local listeners. In restricted environments, run:

node --test test/chat-stream.test.mjs test/stream-text.test.mjs test/code-artifacts.test.mjs test/relay-handler.test.mjs test/websocket-relay.test.mjs test/render.test.mjs

Troubleshooting

Symptom Check
Missing configuration or unavailable deployment Set both .env values and restart the Node server.
Conversational chat unavailable Verify the deployed Flow exposes conversational: true and handle_turn: true in /inspect.
Authentication failure Use this automation's bearer token, without a Bearer prefix in .env.
Unknown API endpoint after changing transport Restart the Node process so it loads the current routes, then refresh the browser.
EADDRINUSE on port 5173 Stop the existing server in its terminal, or set PORT=5174 and open localhost on that port.
WebSocket fails before connecting Check the Node log for the upstream handshake status. HTTP 502 indicates the deployment gateway rejected the upgrade.
WebSocket closes immediately Inspect the close code in Events and deployment logs; verify the deployment supports /chat/{session_id}/stream.
Text arrives together at completion Check deployment proxy buffering and the Flow's stream emission. Compare frame timestamps with browser receipt times in Events.

The local relay forwards WebSocket frames as they arrive. Real-time text also requires the deployed Flow to emit chunks during generation. In a live Chrome test, the UI received 158 text chunks before turn completion, logged 192 event entries, and reconstructed the final response exactly. The first chunk reached the browser about 9 ms after its emission timestamp. A subsequent two-turn Chrome test completed both replies over one WebSocket connection, with websocket_reused on the second turn and no disconnect or SSE fallback.

About

No description, website, or topics provided.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages