Skip to content
C.W.K.
Stream
Lesson 01 of 04 · published

One Engine, Every Voice

~12 min · bellows, tts, profiles, architecture

Level 0Muted
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Text in, audio out. Everything between those two is the engine." — Bellows Quest

The Mouth Already Existed

Voice mode did not build a text-to-speech system. The family already had one: Bellows, born two months earlier, the engine every cwk app calls when it needs to speak. Bellows Quest tells its story in full; this lesson is only about how voice mode plugs into it, and why that plug is so small.

Bellows owns everything about the provider. It holds the ElevenLabs accounts and keys, the binding from each logical voice to a provider voice on each account, the content-addressed cache that makes an identical request free the second time, the paid-job receipts, the pronunciation dictionary and the Korean reading rules. cwkPippa keeps its public /api/tts route as a proxy into Bellows, and siblings reach Bellows through one shared client in the kit. The brain keeps identity; the engine keeps mechanism.

A Soul's Name Is Its Voice

The contract between them is one string. A soul's slug is its Bellows profile: pippa speaks with the pippa profile, ttori with ttori. On 2026-09-25, while voice mode was being designed, all thirteen registered souls (nine perpetual and four guests) already had a voice assigned and an available profile. Voice mode for every soul was therefore not a feature to build per soul. It was a lookup.

The Client Doesn't Choose the Model

Each profile carries its own default model. Family clients send the profile and the text and omit any model override, so they inherit whatever the profile says. That one rule is what made the voice swap in the next track take a single day: when Pippa's profile moved to a new clone on a new model, every surface that asks for her profile without a model override changed voice with it, and none of those clients needed a change. An explicit model on a request is still possible, kept for archival renders and side-by-side comparisons, never as the everyday path.

Fail Loud, Never Substitute

If a profile has no binding on the active account, the request fails with a clear error. It never quietly borrows another voice or another account. A soul that suddenly speaks in a stranger's voice is worse than a soul that stays silent and says why, because the first looks like it works.

Code

Profiles own the model; clients send only the voice and the words·python
from dataclasses import dataclass


@dataclass(frozen=True)
class Profile:
    voice_id: str | None      # the provider voice bound on the active account
    default_model: str


# Owned by the engine. Illustrative values; real ids never leave the engine.
PROFILES = {
    "pippa": Profile(voice_id="voice-pippa-clone", default_model="eleven_v4"),
    "cwk": Profile(voice_id="voice-dad-clone", default_model="eleven_v4"),
    "ttori": Profile(voice_id="voice-ttori", default_model="eleven_v3"),
    "guest-no-binding": Profile(voice_id=None, default_model="eleven_v3"),
}


class VoiceUnavailable(Exception):
    pass


def plan_speech(profile: str, text: str, model: str | None = None) -> dict:
    """What the engine will synthesize. Clients normally pass no model."""
    bound = PROFILES.get(profile)
    if bound is None or bound.voice_id is None:
        raise VoiceUnavailable(f"no voice bound for profile {profile!r}; not substituting")
    return {"voice_id": bound.voice_id, "model_id": model or bound.default_model,
            "text": text}


soul = "pippa"                      # the soul's slug IS its voice profile
print(plan_speech(soul, "안녕, 아빠."))
print(plan_speech("ttori", "누나 또 뭐 해?"))
print(plan_speech(soul, "archive render", model="eleven_v3"))   # explicit, rare
try:
    plan_speech("guest-no-binding", "hello")
except VoiceUnavailable as error:
    print("refused:", error)

External links

Exercise

Extend the code with a second account that binds only two of the profiles, and a setting that chooses which account is active. Make plan_speech use the active account's binding and fail loudly when the profile isn't bound there, rather than falling back to the other account. Then write the test for that refusal.
Hint
The tempting fallback is 'try the other account'. It hides a missing binding until the day the other account runs out of credit, and it can bill the wrong account silently. A refusal with the profile and account in the message is what lets someone fix the binding in a minute.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.