feat: ElevenLabs Scribe backend with speaker diarization - #40
Merged
Conversation
Fourth transcription backend, and the first that reports who is speaking. No chunking path: the API limit is 5 GB, unlike the 25 MB and 100 MB that make the OpenAI and Vercel backends split files. Scribe timestamps every word, so words are grouped into readable cues: a pause of 0.6s or more cuts, a cue is capped at 84 characters, and a change of speaker always cuts regardless of the other two, because a cue holding two voices misattributes the line. The first two thresholds are subtitle conventions chosen to read well, not measurements. Entries with type "spacing" are filtered out; they are the gaps between words and would inflate word counts and emit empty cues. The key comes from ELEVENLABS_API_KEY first, matching the other cloud backends so CI and containers work with no Keychain, then from the macOS login Keychain so a key stored there needs no export.
--diarize and --speakers <n> reach the elevenlabs backend, and are an error on local, openai and vercel rather than being ignored: no other backend returns speaker labels, so dropping the flag quietly would hand back an undiarized transcript that looks like the request succeeded. --speakers implies --diarize, since the API only returns labels when diarization is on, and is rejected outside 1 to 32, the range the API accepts. Language validation now runs after the backend is resolved, because the accepted set depends on it: Scribe takes ISO 639-1 and ISO 639-3 alike and normalizes both to 639-3, while the existing table is the set whisper.cpp accepts and cannot be widened for every backend. doctor resolves the key through the same path a real run uses, so it cannot report missing a key that trx would have found in the Keychain.
Covers the speaker-change cut, the pause and length cuts, that spacing entries never become cues or words, that labels appear only when diarization is on, and that --diarize and --speakers are rejected where they cannot be applied. No test reaches the API.
Schemas, AGENTS.md, the bundled skill, README and the site all enumerate the backends, so each one gains elevenlabs, --diarize and --speakers. Adds the 0.9.0 changelog entry. Version left at 0.8.1: bumping it triggers the publish workflow, which is a release decision.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth transcription backend, and the first that reports who is speaking.
What it adds
--backend elevenlabs, modelsscribe_v2(default) andscribe_v1--diarizelabels every cue with its speaker and turns the.txtinto a conversation, one paragraph per turn--speakers <n>(1 to 32, the range the API accepts) implies--diarizeELEVENLABS_API_KEYfrom the environment, falling back to the macOS login Keychain entryelevenlabsesandspaboth work; Scribe normalizes both tospa)No chunking path: the API limit is 5 GB, not the 25 MB and 100 MB that make the other cloud backends split files.
Design decision worth reviewing
Scribe timestamps every word, so cues have to be grouped or the SRT is one cue per word. Three cut rules, in priority order:
speaker_idalways cuts, not subject to the other two, because a cue holding two voices misattributes the lineRules 2 and 3 are conventions picked to read well, not measurements derived from any corpus. That is the main thing to review.
Two smaller calls:
--diarizeand--speakersare an error on the other backends rather than ignored. Silently dropping them returns an undiarized transcript that looks like the request succeeded.type: "spacing"are filtered out. They are the gaps between words, and counting them inflates word counts and emits empty cues.Verification
biome checkandbun testboth exit 0. 84 pass, 0 fail, 20 new testselevenlabsblock, noELEVENLABS_API_KEY, no Keychain reachable): still 84 pass. No test reaches the API~/.trx/config.jsonwith noelevenlabskey still loads, defaults merged, existing settings preservedVersion left at
0.8.1. Bumping it triggers the publish workflow, which is a release decision rather than part of this change. The0.9.0changelog entry is included and ready for whenever that bump happens.