Activate when the user explicitly names the narrator skill or requests production-ready narration in either case: (1) numbered takes fitted to fixed video windows, or (2) one continuous long-form story read in a locked voice that must be measured and retried for timbre or internal pauses. Collect missing text, voice_id, or voice_type after activation; their absence is not a reason to skip this workflow. Own pacing, speech-duration gates, retries, and ready audio delivery; never time-stretch. Do not use for ordinary TTS, "read this aloud", an unconstrained voiceover, voice browsing, music, SFX, singing, dubbing existing speech, or voice cloning.
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Text in → narration audio out, with the length and voice guarantees a video assembler needs. The caller picks the voice; this skill makes the takes fit.
Required input: the lines to speak (numbered, in order) and the voice pair
voice_id + voice_type (preset | element) chosen by the caller.
Optional input: target window per line (default 9.4–9.8s of speech for a 10s
block), delivery direction, per-line mood, language (inferred from the text).
Output: one completed audio generation per line, in order, carrying its job_id
and result URL; download it as voiceNN.wav inside sandbox_exec when a file is
needed. Continuous mode returns one or more completed jobs plus
narration.wav when sandbox joining is available. Report measured speech length
only when it was actually measured.
Use the current Higgsfield voice tools directly:
list_voices; preserve the exact
voice_id + voice_type pair.generate_audio_batch. Every item is
{index, params:{model:"seed_audio", prompt, voice_id, voice_type, format:"wav", count:1}}; index is the stable line/chunk number.{index, job_id} and call jobs_wait on that group with
timeout_seconds:25. If all_terminal:false, wait
poll_after_seconds and call it again only for active or retryable lookup
failures. Freeze completed indices. If the group shows no status change for
20 minutes, return its pending indices/job ids to the caller instead of
looping silently.submission_failed item without a job_id to jobs_wait.
After a concurrent-job/rate-limit failure, finish the active group and retry
only rejected indices in a smaller later group. Retry only failed takes; never
resubmit completed ones.job_display, job_status, show_generations, or
show_generation_by_ids. The caller owns the post-stage
show_generation_by_ids review using the exact final audio ledger.job_id values for the caller's exact stage ledger.
Download result URLs only inside sandbox_exec when a preinstalled workflow
script needs a file.Do not call legacy AskUserQuestion. If a required voice pair or line list is
missing, return that missing-input error to the caller; the caller owns intake.
One line = one take that FILLS its window. For a 10s block: target 9.4–9.8s of speech.
voice.lock, one line:
voice_id voice_type) and re-read that file before EVERY call — never pass
a pair from memory. A remembered-not-reread pair is exactly how a video ends up
with different voices per block.[ {DELIVERY}, {optional line mood}, starts speaking immediately] [00:00-00:09] {line}{DELIVERY} is ONE direction phrase composed once for the whole job and repeated
VERBATIM on every line (that is what keeps the timbre stable), e.g.
wry conversational explainer, neutral accent, bright dry timbre, lively pace.[scoffs], [giggles]) cost ~1s each; count them.sandbox_exec({
command:"bash ${HF_WORKFLOWS}/faceless-channel-video/scripts/narrator/speech_metrics.sh work/voices/voice01.wav"
})speech= must land in the window; pauses= must be 0 (no internal silence
≥0.8s). The script trims the provider's head/tail padding, so it reports what the
assembler will actually centre. If the runtime cannot download a completed result,
keep the completed job_id, report that the local speech gate was unavailable,
and let the downstream fixed-window assembler center the take. Never invent metrics.atempo, never
speed/pitch-shift, never touch speech_rate (unless the caller explicitly
asked for a rate change).
--continuous)For flows that time visuals to the audio afterwards (e.g. still-frame stories): generate the WHOLE script as one flowing read instead of per-line snippets.
{DELIVERY} verbatim on each. Submit independent chunks through
generate_audio_batch with stable reading-order indices, wait them as above,
then join in index order losslessly inside sandbox_exec:
ffmpeg -f concat -safe 0 -i parts.txt -c copy work/voices/narration.wav.voice_id + voice_type on every call of a
job, re-read from voice.lock.voice_type (preset vs
element is the usual culprit) and retry the same id. Only if the id truly does
not exist, hand the problem back to the caller — do not silently substitute
another voice.Return, per line: completed job_id, result URL when present, local file name when
downloaded in the sandbox, measured speech when available, whether it passed the measurable gate,
and any rewritten final wording so the caller can keep its manifest and captions in
sync.
openai/plugins
0722921
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.