braidio.cost#

Cost model for braidio’s paid operations (ElevenLabs TTS).

braidio’s only spend is ElevenLabs synthesis — braidio.narrate() / braidio.text_to_dialogue() and the renderers built on them — billed by ElevenLabs per character of submitted text. Everything else (ffmpeg extraction, weaving, loudness normalization) is local and free.

This module turns text into a USD figure so renders can attribute a cost onto lacing.Artifact.cost_usd and a free estimate_cost() can preview a whole braidio.Script before any synthesis is paid for.

The figure is an ESTIMATE, priced at a configurable per-1000-character rate (env RATE_ENV_VAR, the per-model MODEL_USD_PER_1K_CHARS table, or DEFAULT_USD_PER_1K_CHARS). It equals what ElevenLabs bills for a live synthesis at that rate — not the provider’s exact invoice. braidio does not yet see the lower-level mixing on-disk cache, so a render served from that cache still reports its rate estimate rather than $0 (a safe over-estimate for a spend ledger; see thorwhalen/braidio#8 to make it exact).

This differs from falaw (which has no default rate and returns None whenever a real price is unknown): braidio deliberately supplies a default so a ledger has a number out of the box — while still returning None (unpriced, never a fake 0.0 for real text) when the rate is explicitly disabled or invalid. Characters are always counted exactly, so a dollar figure can be recomputed if the rate changes.

Module Attributes

RATE_ENV_VAR

Env var overriding the USD-per-1000-characters rate (global default for models not in MODEL_USD_PER_1K_CHARS).

DEFAULT_USD_PER_1K_CHARS

Default USD per 1000 characters when nothing else is configured.

MODEL_USD_PER_1K_CHARS

Confirmed per-model rate overrides (USD per 1000 chars).

Functions

billable_chars(text)

Characters ElevenLabs bills for text (the whole submitted string).

estimate_cost(source, *[, model_id])

Estimate ElevenLabs spend for a braidio.Script or a raw string.

tts_cost_usd(text, *[, model_id])

Estimated USD to synthesize text; 0.0 for empty, None if unpriced.

usd_per_1k_chars([model_id])

Resolved USD-per-1000-characters rate (most specific source first).

Classes

CostLine(label, kind, characters, usd, model_id)

One billable line of an estimate (a narration beat or a dialogue beat).

CostRollup(characters, usd, unpriced[, lines])

A production's estimated TTS spend, exact on characters, honest on dollars.

class braidio.cost.CostLine(label, kind, characters, usd, model_id)[source]#

Bases: object

One billable line of an estimate (a narration beat or a dialogue beat).

Named to echo falaw.CostLine (a line within a rollup) rather than falaw.CostEstimate (which means a per-call price spec) — so the vocabulary is consistent across the federation.

class braidio.cost.CostRollup(characters, usd, unpriced, lines=())[source]#

Bases: object

A production’s estimated TTS spend, exact on characters, honest on dollars.

usd is the sum of the priced lines; unpriced is True when some billable text had no configured rate (so usd is a lower bound). No billable lines (e.g. an all-segment script) gives characters=0, usd=0.0, unpriced=False. Named CostRollup to match falaw.CostRollup.

property summary: str#

One-line human summary (handy for a CLI/MCP preview).

braidio.cost.DEFAULT_USD_PER_1K_CHARS: float = 0.3#

Default USD per 1000 characters when nothing else is configured. Approximate: ElevenLabs bills per credit and the $/credit depends on your plan, so set RATE_ENV_VAR to your plan’s real rate. Kept slightly conservative so a budget over- rather than under-estimates spend.

braidio.cost.MODEL_USD_PER_1K_CHARS: dict[str, float] = {}#

Confirmed per-model rate overrides (USD per 1000 chars). A model listed here is priced at its own rate — winning over the env override and the default — because a confirmed price is more accurate than a global guess. Empty until ElevenLabs per-model rates are confirmed (e.g. eleven_v3 dialogue vs eleven_multilingual_v2).

braidio.cost.RATE_ENV_VAR = 'BRAIDIO_TTS_USD_PER_1K_CHARS'#

Env var overriding the USD-per-1000-characters rate (global default for models not in MODEL_USD_PER_1K_CHARS). Set it to your ElevenLabs plan’s real rate for exact ledgers; set it to none to mark spend as unpriced.

braidio.cost.billable_chars(text)[source]#

Characters ElevenLabs bills for text (the whole submitted string).

ElevenLabs charges for everything sent — including eleven_v3 audio tags like [excited] — so this is just len(text). A named function keeps the billing definition in one place if it ever needs to change.

Return type:

int

>>> billable_chars("hello")
5
>>> billable_chars(None)
0
braidio.cost.estimate_cost(source, *, model_id=None)[source]#

Estimate ElevenLabs spend for a braidio.Script or a raw string.

Free/local work (segment extraction, weaving) contributes nothing. The returned CostRollup reports exact characters and an honest dollar sum (a lower bound when some text is unpriced; see usd_per_1k_chars()).

Return type:

CostRollup

>>> from braidio import Script, Narration, SegmentBeat
>>> s = Script(title="x", id_slug="01", beats=[
...     Narration(text="a" * 500), SegmentBeat(reference="clip:1")])
>>> estimate_cost(s).characters  # only the narration counts; the clip is free
500
braidio.cost.tts_cost_usd(text, *, model_id=None)[source]#

Estimated USD to synthesize text; 0.0 for empty, None if unpriced.

Empty text is genuinely free (0.0) regardless of the rate; non-empty text is None only when usd_per_1k_chars() is unpriced.

Return type:

Optional[float]

>>> tts_cost_usd("")
0.0
braidio.cost.usd_per_1k_chars(model_id=None)[source]#

Resolved USD-per-1000-characters rate (most specific source first).

Resolution: a confirmed per-model rate in MODEL_USD_PER_1K_CHARS → the env override RATE_ENV_VAR → DEFAULT_USD_PER_1K_CHARS. Returns None (unpriced, not free) when the env override is explicitly disabled (one of _UNPRICED_SENTINELS) or is not a finite, non-negative number — a bad rate must never silently become a dishonest negative/NaN spend.

Return type:

Optional[float]