vocal_helper.wallet module
vocal_helper.wallet
Never pay twice for the same audio.
The two paid modes place billed calls to hosted services. This module is the ledger in front of them: a paid call is memoised, across processes and across restarts, on a key derived from the audio bytes themselves, so re-running a build, re-extracting the same audio or re-rendering a report costs nothing the second time.
Extracted from notes_helper.cloud_transcribe (same author), where the
reasoning below was learned the expensive way; it belongs in the library that
owns the paid calls, not in a product on top of it.
Why the key is the audio’s bytes
The obvious ledger key is path:size:mtime. It is wrong here, and the failure
is silent.
Re-extracting the exact same audio — a rebuild, a reprocess, a second pass with a
different glossary — rewrites the file with a fresh modification time while the
bytes are identical. A size:mtime key would change, miss the ledger, and bill
the call again. Worse: when the quota is spent, the miss does not merely cost
money, it fails over and the run quietly produces a different (local) result.
Hashing the bytes makes the key survive re-extraction: identical audio means an identical key, which means a hit.
Why most keyword arguments are excluded from the key
Only the arguments that genuinely change the output belong in the key.
Transcription keys on the audio alone. A changed glossary or keyterm bias, a language relabel, a different prompt: none of these justify paying to transcribe the same bytes a second time.
Diarization keys on the audio plus an explicit speaker count, when one is forced. Auto-detection is a pure function of the audio, but a run with a forced count must not silently reuse the auto-detected result, so the two are cached separately.
A transcriber that bundles diarization (ElevenLabs Scribe does; Gladia does not) therefore keys on the diarization arguments too.
Escape hatch
VOH_WALLET_DISABLED=1 bypasses the ledger and forces a fresh, billed call. It
is off by default, deliberately: a paid call stays protected unless somebody asks
for it in so many words.
Usage example
>>> import os
>>> os.environ["VOH_WALLET_DISABLED"] = "1" # keep a doctest from touching a ledger
>>> calls = []
>>> @paid("demo.transcribe")
... def transcribe(*, audio: str) -> str:
... calls.append(audio)
... return "text"
>>> transcribe(audio=__file__), len(calls)
('text', 1)
>>> del os.environ["VOH_WALLET_DISABLED"]
- vocal_helper.wallet.audio_fingerprint(path)[source]
Content-addressed fingerprint of an audio file, for ledger keying.
- Parameters:
path (str) – Path to the audio actually sent to the paid service.
- Returns:
"sha256:<hex>". Identical bytes always give an identical string, whatever the file is called or when it was written.- Return type:
Examples
>>> fp = audio_fingerprint(__file__) >>> fp.startswith("sha256:") and len(fp) == 71 True >>> fp == audio_fingerprint(__file__) True
- vocal_helper.wallet.paid(namespace, *, key_fields=())[source]
Decorate a paid call so the same audio is never billed twice.
The decorated function must take its audio path as the keyword argument
audio; that is what the key is built from.- Parameters:
namespace (str) – Ledger namespace, e.g.
"gladia.transcribe". Distinct providers and distinct operations must not share one.key_fields (tuple of str, keyword-only, optional) – Names of the keyword arguments that genuinely change the result and must therefore join the audio in the key (typically
("num_speakers",), plus("diarize",)for a transcriber that bundles diarization). Everything else is excluded on purpose — see the module docstring.
- Returns:
A decorator.
- Return type:
callable
Notes
wallet_helperis imported lazily, on the first genuinely billed call rather than when the decorator is applied at import time, so a caller that never places a paid call does not pull the dependency in. That matters for a fast test lane installed with core extras only.Examples
>>> import os >>> os.environ["VOH_WALLET_DISABLED"] = "1" >>> @paid("demo.diarize", key_fields=("num_speakers",)) ... def diarize(*, audio: str, num_speakers: int = 0) -> int: ... return num_speakers >>> diarize(audio=__file__, num_speakers=3) 3 >>> del os.environ["VOH_WALLET_DISABLED"]
- vocal_helper.wallet.wallet_disabled()[source]
Whether
VOH_WALLET_DISABLEDasks to bypass memoisation.- Returns:
Trueonly when the variable is set to something other than"",0,falseorno. Anything unset reads as “ledger active”, which is the safe default: a typo must not start billing.- Return type:
Examples
>>> import os >>> os.environ["VOH_WALLET_DISABLED"] = "1" >>> wallet_disabled() True >>> os.environ["VOH_WALLET_DISABLED"] = "false" >>> wallet_disabled() False >>> del os.environ["VOH_WALLET_DISABLED"] >>> wallet_disabled() False
- vocal_helper.wallet.wallet_path()[source]
Resolve the ledger file, environment first.
Resolution order:
VOH_WALLET_DB, then thewallet_dbkey of thepaid:section ofsettings.yaml, then~/.vocal-helper/wallet.sqlite.Point an existing ledger here when migrating. A product that already paid for hundreds of hours keeps every one of those results only if vocal-helper reads the same file; a fresh default path is a silently empty ledger, and the next run re-bills everything.
- Returns:
The ledger path. Its parent directory is created on first use, not here.
- Return type:
Examples
>>> import os >>> os.environ["VOH_WALLET_DB"] = "/tmp/demo-ledger.sqlite" >>> wallet_path().as_posix() '/tmp/demo-ledger.sqlite' >>> del os.environ["VOH_WALLET_DB"] >>> wallet_path().name 'wallet.sqlite'