Flowcat quickstart
June 16, 2026 · View on GitHub
From git clone to "I've watched the runtime move real audio and drive a
conversation" in about five minutes — no credentials, no accounts, no cloud.
What this gets you: a built Flowcat, the FrameProcessor pipeline moving audio
end-to-end, a real WebSocket media round-trip, and your conversation policy driven
from Python over the RemoteBrain seam.
Then, with one provider key:
step 5 runs a real agent you
talk to in your browser — no Rust — via the flowcat-server binary + a YAML
config. A live PSTN call adds your own embedder (carrier + control plane);
step 6 shows what that piece is and
where the live-verified path starts.
Everything in steps 1–4 is exercised in CI, so it runs on the first try.
Prerequisites
- A recent stable Rust toolchain (rustup) —
cargoon yourPATH. - Python 3 (standard library only) for step 4 — no
pip install. git.
1. Clone & build
git clone https://github.com/AreevAI/flowcat.git
cd flowcat
cargo build -p flowcat-cli # default features → no provider/network deps
The default build pulls no provider client dependencies — every STT/TTS/LLM, transport, and exporter is an opt-in Cargo feature. The first build compiles the workspace (a minute or two); after that, runs are instant.
2. Watch the pipeline move audio
cargo run -p flowcat-cli -- pipeline
A synthetic 440 Hz sine wave is pumped through a composable FrameProcessor graph
(Source → Echo → Tap → Sink) while a FrameObserver counts frames:
flowcat pipeline demo
source : 440 Hz sine, 16000 Hz mono, 320-sample frames
audio : 50 frames (~1.00 s)
chain : Source -> Echo -> Tap -> Sink
frames in : 50 (InputAudio observed)
frames out : 50 (OutputAudio observed)
echoed : 50 (counted in Echo)
wall time : 2.071 ms
result : OK (in == out == sourced)
This is Flowcat's core: each stage is its own tokio task behind a bounded channel
(natural backpressure), and the hot audio frame is an Arc — each hop moves a
pointer, not a buffer. in == out == sourced means nothing was dropped.
3. Real audio over the WebSocket transport
cargo run -p flowcat-cli -- ws-echo
This stands up the actual generic WebSocket media transport — the same one a WS-media carrier connects to — streams PCM frames through it, and echoes them back, asserting they return byte-for-byte:
ws-echo: loopback server listening on ws://127.0.0.1:<port>
ws-echo: stream started (call_id=loopback)
ws-echo: echoed frame 1 (7 samples)
ws-echo: echoed frame 2 (6 samples)
ws-echo: echoed frame 3 (64 samples)
ws-echo: stream stopped after 3 echoed frame(s)
ws-echo: loopback OK — 3 frame(s) round-tripped byte-for-byte (3 echoed server-side)
Pass --connect ws://<host>:<port> to point the echo at a live peer instead of the
in-process loopback.
4. Drive the conversation from Python
You don't have to write Rust to control the agent. Flowcat consults a "brain" at turn granularity (between turns) — your Python never touches the per-audio-frame path, so the runtime's latency profile is unaffected. Start the pure-stdlib reference server:
python3 examples/python-remote-brain/brain_server.py # http://127.0.0.1:8080
In another terminal, play the role Flowcat plays on a call — start a session, then interpret a model tool call:
curl -s -X POST http://127.0.0.1:8080/session \
-H 'Content-Type: application/json' \
-d '{"brain_config":{"graph":"demo"},"provider":"gemini"}'
{ "system_prompt": "You are a friendly receptionist. Greet the caller and ask how you can help.",
"tools": [ { "name": "book_appointment", "...": "..." }, { "name": "end_call", "...": "..." } ],
"node_id": "greeting", "collected_vars": {} }
curl -s -X POST http://127.0.0.1:8080/tool-call \
-H 'Content-Type: application/json' \
-d '{"node_id":"greeting","tool":{"name":"book_appointment","args":{"day":"Tuesday"}},"collected_vars":{}}'
{ "action": "transition",
"system_prompt": "Confirm the appointment day with the caller, then ask them to say 'confirm'.",
"tools": [ { "name": "confirm", "...": "..." }, { "name": "end_call", "...": "..." } ],
"say": "Sure — booking you for Tuesday. Shall I confirm?",
"node_id": "confirm", "collected_vars": { "requested_day": "Tuesday" }, "finished": false }
That's the whole RemoteBrain wire contract: /session seeds state, and
/tool-call returns one of transition / stay / end. Replace the decide()
function in brain_server.py with your own logic — an LLM call, a DB lookup, a
state machine. A Rust embedder wires this in with RemoteBrain::connect(...); see
examples/python-remote-brain. To expose Python
functions as model tools instead, see
examples/python-mcp-tools.
5. Talk to a real agent in your browser (no Rust)
Steps 1–4 are credential-free. To run a real agent end-to-end with no Rust,
use the flowcat-server binary: define the agent in a YAML config and serve
it; the browser playground (--features webrtc) lets you talk to it directly.
You need one provider key — the live-verified path is Gemini Live, so set
GOOGLE_API_KEY (a free key from Google AI Studio
works):
cargo build --release -p flowcat-server --features webrtc
GOOGLE_API_KEY=… ./target/release/flowcat-server --config deploy/agent.example.yaml
Open http://localhost:6210/, allow the microphone, click Start call —
and you're talking to the agent defined in
deploy/agent.example.yaml: a node/edge graph you
edit, with no control plane and no database. The live transcript renders as you
speak. The server resolves providers by name from the config and reads their
keys from the environment; the full schema is in flowcat-server/src/config.rs.
Prefer telephony? Point a Plivo number's answer webhook at the server's
/telephony/answer/plivo/{run_id}and it bridges the media WebSocket. Or run the whole thing in Docker:docker compose -f deploy/docker-compose.yml up --build— seedeploy/README.md.
6. Carry a PSTN call with your own embedder
For full control — your own routing, control plane, and in-process brain logic — you write a small embedder: a host binary that
- terminates the call — the native in-process
SipTransport(SIP/RTP, no softswitch required), or a carrier WebSocket transport if you already run one; - resolves & finalizes the call — your
SessionSource, talking to your control plane (routing, auth, recording/transcript upload); - supplies the brain — your own
AgentBrain, or theRemoteBrainfrom step 4.
Flowcat owns the media loop; you own the contract, routing, and credentials — which
is what keeps the whole call on infrastructure you control. The combination
verified end-to-end today is Gemini Live (speech-to-speech) + Plivo telephony,
so start there. The trait seams and full call lifecycle are specified in
DESIGN.md; the provider/transport surface and the "use it from
Python" model are in the README.
Fully on-prem / air-gapped? Swap the cloud providers for the local connectors (Whisper STT; Kokoro / Piper / XTTS TTS; Ollama LLM) and no call audio ever leaves your infrastructure.
Troubleshooting
cargo: command not found— install Rust via rustup and reopen your shell.- First build is slow — that's the one-time dependency compile; re-runs are instant.
- Port 8080 already in use — change
PORTnear the top ofbrain_server.py. - Run the full offline test suite —
cargo test(no network, no credentials).