<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gerald T Chenwi</title>
    <description>The latest articles on DEV Community by Gerald T Chenwi (@spaiboss).</description>
    <link>https://dev.to/spaiboss</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4100164%2F29340bff-8477-4a73-aa0c-297a310e1747.jpeg</url>
      <title>DEV Community: Gerald T Chenwi</title>
      <link>https://dev.to/spaiboss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/spaiboss"/>
    <language>en</language>
    <item>
      <title>We Replaced LiveKit with Cloudflare Workers for AI Voice Tutoring — and Kept the Same Brain</title>
      <dc:creator>Gerald T Chenwi</dc:creator>
      <pubDate>Sat, 29 Aug 2026 11:41:11 +0000</pubDate>
      <link>https://dev.to/spaiboss/we-replaced-livekit-with-cloudflare-workers-for-ai-voice-tutoring-and-kept-the-same-brain-2en1</link>
      <guid>https://dev.to/spaiboss/we-replaced-livekit-with-cloudflare-workers-for-ai-voice-tutoring-and-kept-the-same-brain-2en1</guid>
      <description>&lt;p&gt;We run Katush, an AI tutor for Cameroon secondary students (GCE prep). Voice tutoring was production-ready on LiveKit Cloud + a Python agent, but platform cost stacked on top of already-expensive STT/LLM/TTS APIs. We built a parallel path on Cloudflare Workers + Durable Objects + @cloudflare/voice, switched with one build flag, and kept the same BYOK stack: Deepgram, Gemini Flash Lite, Cartesia Sonic-2.&lt;/p&gt;

&lt;p&gt;Result: Cloudflare voice is live in production. We did not cut AI costs in half — we cut transport tax. Variable COGS is still ~$0.019/min. At 12 XAF/min retail, margin is thin unless the tutor stays brief.&lt;/p&gt;

&lt;p&gt;If you already pay for speech APIs and need a cheaper browser transport, this pattern works. If you need WebRTC on bad mobile networks or telephony, keep LiveKit.&lt;/p&gt;

&lt;p&gt;The problem nobody talks about in voice AI demos&lt;/p&gt;

&lt;p&gt;Voice agents look simple in a README:&lt;/p&gt;

&lt;p&gt;Mic → STT → LLM → TTS → Speaker&lt;/p&gt;

&lt;p&gt;In production there are two bills:&lt;/p&gt;

&lt;p&gt;Cognition — Deepgram, OpenAI/Google, Cartesia, etc.&lt;br&gt;
Transport — who hosts the agent loop and moves audio (LiveKit, Cloudflare, Daily, …)&lt;/p&gt;

&lt;p&gt;For Katush, cognition already eats most of our retail minute price. We sell voice at 12 XAF/min (~$0.02 USD). Students buy minute packs via mobile money (Campay). They expect a wallet: "I have 7 minutes left" — not abstract token limits.&lt;/p&gt;

&lt;p&gt;LiveKit is excellent. WebRTC, turn detection, agent framework, recording roadmap. But LiveKit Cloud agent hosting sits on top of the API vendors. In a low-ARPU market, that extra platform layer hurts.&lt;/p&gt;

&lt;p&gt;So we asked:&lt;/p&gt;

&lt;p&gt;Can Cloudflare Workers + Durable Objects replace LiveKit transport while reusing our backend tutor logic and BYOK providers?&lt;/p&gt;

&lt;p&gt;Architecture: parallel stacks, one brain&lt;/p&gt;

&lt;p&gt;We did not rewrite the tutor. Both paths call the same Render backend:&lt;/p&gt;

&lt;p&gt;/api/agent/context — student profile, exam level, subject track&lt;br&gt;
/api/agent/rag/search — past papers and curriculum grounding&lt;br&gt;
Billing via Call + UsageLog (VOICE_CALL)&lt;/p&gt;

&lt;p&gt;The only swap is how audio reaches the agent.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LiveKit (before)    Cloudflare (after)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Session start   POST /api/livekit/room  POST /api/voice/cf/session/start&lt;br&gt;
Media   WebRTC to LiveKit SFU   WebSocket to Worker DO&lt;br&gt;
Agent   Python on LiveKit Cloud TypeScript in Durable Object&lt;br&gt;
Session end DELETE /api/livekit/room    POST /api/voice/cf/session/end&lt;br&gt;
Crash cleanup   room_finished webhook   Orphan release + force-close&lt;/p&gt;

&lt;p&gt;Frontend switch — one env var at build time:&lt;/p&gt;

&lt;p&gt;typescript&lt;br&gt;
VITE_VOICE_PROVIDER=cloudflare  // or livekit&lt;/p&gt;

&lt;p&gt;Same React modal. Same minute wallet. Same upgrade toasts.&lt;/p&gt;

&lt;p&gt;Worker URL (production): &lt;a href="https://katush-voice-cloudflare.spaitraceonline.workers.dev" rel="noopener noreferrer"&gt;https://katush-voice-cloudflare.spaitraceonline.workers.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What broke in production (and what we'd tell you to fix on day one)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Silent audio&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Symptom: Transcript updated. No sound. Cause: Gemini TTS returned raw PCM; the browser expected WAV/MP3. Fix: WAV header wrap; prefer Cartesia MP3 in production.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Eight-second pauses&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Symptom: Text fast, speech slow. Cause: TTS waited for full LLM completion. Fix: Stream Gemini by sentence; invoke Cartesia per sentence. First audio dropped from ~8–12s to ~1s on many turns.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Orphan sessions (the nasty one)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Symptom:&lt;/p&gt;

&lt;p&gt;WebSocket closed before connection established&lt;br&gt;
POST /session/start → 403 active voice call in progress&lt;/p&gt;

&lt;p&gt;Cause: Backend creates a Call row at session/start, before the WebSocket succeeds. A failed connect left a stale row for up to 2 hours.&lt;/p&gt;

&lt;p&gt;Fixes:&lt;/p&gt;

&lt;p&gt;session/end on every client-side connect failure&lt;br&gt;
Release abandoned cf_* calls after 45s&lt;br&gt;
Force-close stuck rows on retry&lt;br&gt;
WebSocket connect retry (2 attempts, 45s timeout)&lt;br&gt;
Don't bill failed connects under 15 seconds&lt;/p&gt;

&lt;p&gt;Lesson: Billing state must follow media state. LiveKit taught us this with webhooks; Cloudflare made us port the logic explicitly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quota errors only in the console&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Students hit "no minutes left" and saw a generic failure. We moved 403 reasons into toasts with upgrade links. In consumer EdTech, console.error is a silent bug.&lt;/p&gt;

&lt;p&gt;Economics (honest numbers)&lt;/p&gt;

&lt;p&gt;Assumptions: ~240 TTS characters/minute of tutor speech, 600 XAF/USD.&lt;/p&gt;

&lt;p&gt;Layer   ~USD/min&lt;br&gt;
Deepgram Nova-3 $0.0048&lt;br&gt;
Gemini 2.5 Flash Lite   ~$0.001&lt;br&gt;
Cartesia Sonic-2    ~$0.012&lt;br&gt;
Cloudflare DO + Worker  ~$0.0015&lt;br&gt;
Total variable  ~$0.019 (~11–12 XAF/min)&lt;/p&gt;

&lt;p&gt;Fixed: Cloudflare Workers Paid ~$5/month. Render backend (free tier) became our first concurrency bottleneck (~10–25 concurrent session starts) — not Cloudflare.&lt;/p&gt;

&lt;p&gt;Retail: 12 XAF/min ≈ break-even on variable COGS. TTS verbosity is a margin lever. We cap tutor replies in voice prompts to one to two short sentences.&lt;/p&gt;

&lt;p&gt;Switching transport did not remove API costs. It changed who hosts the agent loop (~$5/mo vs LiveKit agent hosting).&lt;/p&gt;

&lt;p&gt;LiveKit vs Cloudflare — quick decision guide&lt;/p&gt;

&lt;p&gt;Choose Cloudflare if:&lt;/p&gt;

&lt;p&gt;Browser-only tutoring&lt;br&gt;
TypeScript team&lt;br&gt;
Minimize platform fixed cost&lt;br&gt;
You can own WebSocket reliability + session billing&lt;/p&gt;

&lt;p&gt;Choose LiveKit if:&lt;/p&gt;

&lt;p&gt;Mobile WebRTC on lossy networks is core&lt;br&gt;
You need recording, SIP, or mature turn detection&lt;br&gt;
Rich LLM function tools out of the box&lt;/p&gt;

&lt;p&gt;We run Cloudflare primary, LiveKit one flag away for rollback.&lt;/p&gt;

&lt;p&gt;What we'd do differently&lt;br&gt;
Orphan release before the first production student&lt;br&gt;
Structured sessionId tracing across worker, backend, browser&lt;br&gt;
Mobile dropout study (MTN/Orange) before declaring victory&lt;br&gt;
Consider 14–16 XAF/min for purchased packs if the tutor runs chatty&lt;br&gt;
Try it / build your own&lt;br&gt;
Health check: GET &lt;a href="https://katush-voice-cloudflare.spaitraceonline.workers.dev/health" rel="noopener noreferrer"&gt;https://katush-voice-cloudflare.spaitraceonline.workers.dev/health&lt;/a&gt;&lt;br&gt;
Product: katush.online&lt;/p&gt;

&lt;p&gt;If you're building voice in a price-sensitive market: swap transport, not brain. The hard part is billing hygiene and TTS economics — not finding another LLM.&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>livekit</category>
      <category>edtech</category>
      <category>katushvoiceagent</category>
    </item>
  </channel>
</rss>
