Skip to content

DRU-660 -- The calls server: Pipecat carries the call - #735

Merged
czpython merged 1 commit into
mainfrom
codex/dru-660-voice-server
Oct 4, 2026
Merged

czpython merged 1 commit into
mainfrom
codex/dru-660-voice-server

Conversation

@czpython

@czpython czpython commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

What changed

The calls server carries a phone call. It takes Twilio's audio stream, asks Druks to pick up the call, holds the call with the speech-to-speech model, runs the Bot's tools through /mcp, and posts each line to Druks. It is its own program on Pipecat, with its own image and its own release cycle.

  • The project. druks-calls/ holds the package calls, with its own pyproject.toml and uv.lock. It pins pipecat-ai 1.12.0 with the Google, MCP, OpenAI, and WebSocket extras. The backend does not depend on it, and it imports nothing from Druks.
  • The server. app.py serves GET /healthz and a WebSocket at /_calls/{token}. At the handshake it posts pickup to {DRUKS_URL}/_external/calls/events/ with the token and the stream's X-Twilio-Signature. It closes the stream when Druks refuses, and it refuses a stream beyond MAX_CALLS live calls.
  • A call. Call.run builds one pipeline for each call: the Twilio transport, the realtime service of the model's vendor (openai/… or google/…), and one MCP client with the pickup's key and conversation header. The model gets the pickup's prompt, then the facts, then the tools. A model that cannot connect ends the call.
  • The lines. The context aggregators give the caller's and the assistant's lines. Pipecat adds the caller's words after the reply has started, so each assistant turn numbers the caller's line first and the reply second. The server posts each line under its number, and Druks orders a call by those numbers.
  • The end. end_call is the model's tool to end the call. A call also ends after no_speech_seconds of silence, at max_call_seconds, or when the caller hangs up. The server then posts ended.
  • Names on the Druks side. The server's webhook is CallsEvents at /_external/calls/events/, and a call's stream URL is wss://<webhook host>/_calls/<call token>. Twilio.link takes the address for a number's calls as calls_url.
  • The Voice card. It refuses a model whose vendor is not openai or google, because the calls server runs only those two. It has a new optional field, the transcription model, which an OpenAI model uses to write the caller's words as text.
  • Its own release cycle. deploy/calls/Dockerfile builds the image from druks-calls/, with goss.yaml beside it. build-calls-image.yml publishes ghcr.io/czpython/druks-calls only when the server changes. on-pull-request-calls.yml runs its tests, builds the image, and runs the goss checks, also only when it changes. The backend PR workflow lints druks-calls/.
  • Not in the default stack. Most installs do not need calls, so deploy/compose.yaml has no calls service and druks.toml has no switch for it. docs/deployment.md gives the service to add to compose.override.yaml. A Druks upgrade restarts it only when a new calls image exists.
  • Caddy. deploy/caddy/Caddyfile passes /_calls/* to the calls server, on the webhook host and on the :8000 listener.
  • Docs. docs/deployment.md covers the calls server and how to add it, docs/full-local.md the tunnel for calls, docs/chat.md when a call ends, and AGENTS.md the project.

@czpython
czpython force-pushed the codex/dru-660-voice-server branch from bedef93 to 994d6f1 Compare September 27, 2026 14:37
@czpython
czpython force-pushed the codex/dru-659-druks-answers-a-call branch 4 times, most recently from 6c8108d to e698e63 Compare October 4, 2026 11:06
Base automatically changed from codex/dru-659-druks-answers-a-call to main October 4, 2026 11:14
@mintlify

mintlify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
druks 🟢 Ready View Preview Oct 4, 2026, 2:19 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

@czpython
czpython force-pushed the codex/dru-660-voice-server branch from 994d6f1 to 39c6d5e Compare October 4, 2026 11:19
@czpython czpython changed the title DRU-660 -- The voice server: Pipecat carries the call DRU-660 -- The calls server: Pipecat carries the call Oct 4, 2026
@czpython
czpython force-pushed the codex/dru-660-voice-server branch from 39c6d5e to 87a9dae Compare October 4, 2026 13:30
@czpython
czpython force-pushed the codex/dru-660-voice-server branch from 87a9dae to 90970ce Compare October 4, 2026 13:32
@czpython
czpython force-pushed the codex/dru-660-voice-server branch from 90970ce to bf47513 Compare October 4, 2026 13:42
@czpython
czpython force-pushed the codex/dru-660-voice-server branch from bf47513 to 1b71381 Compare October 4, 2026 13:46
@czpython
czpython force-pushed the codex/dru-660-voice-server branch from 1b71381 to 5d1c193 Compare October 4, 2026 14:09
@czpython
czpython force-pushed the codex/dru-660-voice-server branch from 5d1c193 to 8dfb607 Compare October 4, 2026 14:13
@czpython
czpython force-pushed the codex/dru-660-voice-server branch from 8dfb607 to fa22f19 Compare October 4, 2026 14:16
@czpython
czpython force-pushed the codex/dru-660-voice-server branch from fa22f19 to 3d276fd Compare October 4, 2026 14:19
@czpython
czpython enabled auto-merge (squash) October 4, 2026 14:22
@czpython
czpython merged commit 3e2ce44 into main Oct 4, 2026
7 checks passed
@czpython
czpython deleted the codex/dru-660-voice-server branch October 4, 2026 14:25

This branch was successfully deployed

1 active deployment
staging - docs — 3d276fde Deployed Oct 4, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant