DEV Community

Sylvain Boily
Sylvain Boily

Posted on • Edited on • Originally published at roomkit.live

I Built a Multi-Channel Conversation Framework in Python. Here's Why.

If you've ever integrated SMS, email, voice, and chat into the same app, you know the pain. Each channel has its own SDK, its own webhooks, its own quirks. A customer starts on SMS, continues on email, finishes on chat — and your system treats them as three strangers.

I spent 20 years building telecom infrastructure. After the third time rebuilding the same "route messages between channels" plumbing, I extracted the pattern into a library.

It's called RoomKit.


The Problem

Every conversation system I've worked on had the same architecture smell: channel-specific code scattered everywhere, identity stitched together with duct tape, and zero shared context between channels.

The typical approach looks like this:

# The "just add another if-statement" pattern
if source == "sms":
    handle_sms(message)
elif source == "email":
    handle_email(message)
elif source == "whatsapp":
    handle_whatsapp(message)
# ... repeat for every new channel
Enter fullscreen mode Exit fullscreen mode

Each handler has its own storage, its own user lookup, its own response logic. Switching from Twilio to Telnyx means rewriting half the codebase. Adding AI to the conversation means threading it through every handler.

The Idea: Rooms, Not Channels

RoomKit introduces a single abstraction: the room. A room is a conversation. Channels attach to rooms. Messages flow in, get processed through hooks, and broadcast to all attached channels.

[SMS] ──┐
[Email] ─┤──→ Room ──→ Hooks ──→ Broadcast ──→ [All Channels]
[AI]  ───┘
Enter fullscreen mode Exit fullscreen mode

The channel doesn't matter. The room is the conversation.

Show Me the Code

Install:

pip install roomkit
Enter fullscreen mode Exit fullscreen mode

Here's a working example — a support room where a customer on WebSocket talks to an AI assistant:

import asyncio
from roomkit import (
    RoomKit, WebSocketChannel, AIChannel, MockAIProvider,
    ChannelCategory, InboundMessage, TextContent,
)

async def main():
    kit = RoomKit()

    # Register channels
    kit.register_channel(WebSocketChannel("customer-ws"))
    kit.register_channel(AIChannel("assistant", provider=MockAIProvider(
        responses=["I found your order — it shipped yesterday."]
    )))

    # Create room and attach channels
    await kit.create_room(room_id="support-42")
    await kit.attach_channel("support-42", "customer-ws")
    await kit.attach_channel("support-42", "assistant",
                              category=ChannelCategory.INTELLIGENCE)

    # Process an inbound message
    await kit.process_inbound(InboundMessage(
        channel_id="customer-ws",
        sender_id="customer-1",
        content=TextContent(body="Where is my order?"),
    ))

    # Check the conversation timeline
    for event in await kit.store.list_events("support-42"):
        print(f"[{event.source.channel_id}] {event.content.body}")

asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

Output:

[customer-ws] Where is my order?
[assistant] I found your order — it shipped yesterday.
Enter fullscreen mode Exit fullscreen mode

That's it. The customer's message enters the room, the AI channel picks it up, responds, and everything is stored in a unified timeline. Replace MockAIProvider with AnthropicAIProvider or OpenAIAIProvider for production.

Hooks: Where Your Logic Lives

The hook system is where RoomKit gets interesting. Instead of scattering logic across handlers, you intercept events at well-defined points:

from roomkit import HookTrigger, HookResult

@kit.hook(HookTrigger.BEFORE_BROADCAST)
async def moderate_content(event, ctx):
    if contains_profanity(event.content.body):
        return HookResult.block("Content policy violation")
    return HookResult.allow()

@kit.hook(HookTrigger.BEFORE_BROADCAST)
async def route_to_ai(event, ctx):
    if needs_ai_response(event, ctx):
        return HookResult.inject_to(["ai-channel"])
    return HookResult.allow()
Enter fullscreen mode Exit fullscreen mode

Content moderation, AI routing, analytics, transformations — all in one place, applied uniformly regardless of which channel the message came from.

What RoomKit Is (and Isn't)

RoomKit is not a platform. There's no dashboard, no hosted infrastructure, no per-message fees. It's a Python library — primitives you compose into your own system.

Think of it as the missing layer between your channels and your logic:

  • vs. Twilio/Telnyx: RoomKit doesn't send messages. It orchestrates them. Use Twilio as a provider inside RoomKit.
  • vs. Chatwoot/Intercom: Those are full applications. RoomKit is what you'd use to build one.
  • vs. Rasa/Dialogflow: Those focus on NLP. RoomKit focuses on routing messages to AI (or humans) across channels.
  • vs. LiveKit: LiveKit is WebRTC infrastructure for real-time media. RoomKit is conversation orchestration for any channel.

The Channel Matrix

Built-in channel types with pluggable providers:

Channel Providers
SMS Twilio, Telnyx, Sinch, VoiceMeUp
RCS Twilio, Telnyx
Email ElasticEmail, SMTP
WhatsApp Business & Personal
Messenger Facebook
Teams Bot Framework
Voice Deepgram STT, ElevenLabs TTS, FastRTC
Realtime Voice Gemini Live, OpenAI Realtime
WebSocket Built-in
AI Anthropic, OpenAI, Gemini
HTTP Generic webhooks

Swap providers without touching application logic. Twilio today, Telnyx tomorrow — your hooks don't change.

Production-Ready Patterns

RoomKit ships with the resilience patterns you'd eventually build yourself:

  • Pluggable storage: In-memory for dev, Redis/PostgreSQL for production
  • Circuit breakers to isolate failing providers
  • Rate limiting with token buckets
  • Retry with exponential backoff
  • Identity resolution across channels (the same person on SMS and email becomes one participant)
  • Event sources with auto-restart, health monitoring, and backpressure

AI-Native

This is 2026. Every conversation system needs AI integration. RoomKit was designed for it:

  • AI channels are first-class citizens, not bolted on
  • Two voice modes: STT/TTS pipeline or speech-to-speech (Gemini Live, OpenAI Realtime)
  • llms.txt and AGENTS.md built into the package so AI coding assistants understand the codebase
  • MCP integration — use RoomKit as a tool in Claude, Cursor, or any MCP-compatible agent
  • Programmatic AI context: get_llms_txt() and get_agents_md() for feeding documentation into LLM context windows
from roomkit import get_llms_txt, get_agents_md

# Give your AI assistant full context on RoomKit
llms_content = get_llms_txt()
agents_guidelines = get_agents_md()
Enter fullscreen mode Exit fullscreen mode

Protocol-First: The RFC

This is the part I'm most excited about. RoomKit isn't just a Python library — it's a protocol.

Early on, I made a deliberate choice: write the specification before locking in the implementation. The result is roomkit-rfc.md — a language-agnostic RFC that defines rooms, channels, hooks, identity resolution, event schemas, and the full message lifecycle. The Python library is the reference implementation, but the spec stands on its own.

Why does this matter? Because conversation orchestration shouldn't be a Python-only problem. The same room/channel/hook model makes sense in Go, Rust, TypeScript, Java — anywhere you're building multi-channel systems.

This is where you come in. The spec is stable and ready for other language bindings. If you're building conversation systems in Go and want a RoomKit SDK, the RFC gives you everything you need to build one that's compatible with the Python implementation. Same concepts, same semantics, interoperable by design.

What's available today:

I'd love to see a roomkit-go, roomkit-ts, or roomkit-rust emerge from the community. The protocol is designed to make that possible — and I'm happy to support anyone who wants to take it on.

Getting Started

# Core library (only dependency: Pydantic)
pip install roomkit

# With AI providers
pip install roomkit[anthropic]
pip install roomkit[openai]

# Everything
pip install roomkit[all]
Enter fullscreen mode Exit fullscreen mode

The library is fully typed, async-first, and runs on Python 3.12+. The API is stable, the test suite is comprehensive, and the documentation covers everything from quickstart to production deployment.


RoomKit is open source, MIT licensed, and looking for early adopters. If you're building anything that involves conversations across multiple channels, I'd love to hear how it fits your use case.

Star the repo, try the quickstart, open an issue. Let's build this together.

Top comments (0)