Skip to content

(expressive mode): register inference replaces presets - #6571

Open
cshape wants to merge 5 commits into
livekit:tina/refactor-expressive-modefrom
cshape:cale/expressive-simplify-steering
Open

(expressive mode): register inference replaces presets#6571
cshape wants to merge 5 commits into
livekit:tina/refactor-expressive-modefrom
cshape:cale/expressive-simplify-steering

Conversation

@cshape

@cshape cshape commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

stacked on #6528 β€” the first two commits are that PR; review the last three.

our evals (~2,000 generations, 5 personas Γ— serious/neutral/casual moments, 4 llms) suggest the agent persona and conversational context choose the appropriate register on their own, so this replaces the formal/casual presets with:

  • a provider-neutral register rule in the shared preamble (composed when the moment is heavy, loose when it's casual, reassessed every turn), plus fish-specific guidance built conditionally so an opted-out option is never mentioned
  • preset removal β€” on fish the two presets resolved to byte-identical instructions, and neither moved behavior the persona didn't already move
  • nonverbal_sounds accepts a bool, and dicts are sparse opt-outs: {"laughing": False} removes laughter and nothing else

measured at the default (everything on): 0.00 laughs/turn at serious moments across all four llms, 0.67–0.83 at casual ones, judge fit 4.59/5 overall. {"nonverbal_sounds": False, "disfluencies": False} produced 0 sounds and 1 filler across 360 generations.

one semantic change to sign off on: NonverbalOptions() (empty dict) now means "no opt-outs" β€” everything stays on; nonverbal_sounds=False is the all-off form.

full findings + reproducible eval harness shared separately.

cshape added 5 commits July 23, 2026 11:31
Registers Fish Audio (s2 family) across the expr pipeline so inference
fishaudio/* voices work in expressive mode:

- llm_instructions: sounds-filtered builder β€” discrete emotion vocabulary
  (regretful, hopeful, ... sarcastic), sounds (laughing, clear throat),
  breaks, and an emphasis-only wrapping prosody
- convert_markup: expr lowers to Fish's native brackets β€” [very EMOTION]
  (intensified so the emotion lands harder), [SOUND], [break]/[long-break]
  by duration, and [emphasis] word
- steering: laughing/reflex_sounds govern the two sounds; disabled labels
  vanish from the advertised vocabulary and examples
- transcript: expr, hallucinated native XML, and Fish's own bracket form
  are all stripped; brackets=True since Fish's native dialect IS brackets
- chunking: no _MAX_INPUT_LEN entry on purpose β€” Fish's markers are
  sentence-scoped, so per-sentence emission loses no steering and keeps
  time-to-first-audio low
gemma keeps labels english on its own, but a weaker llm could write
label="嬉しい", which lowers to a bracket fish doesn't recognize β€”
make it explicit like the pre-refactor prompts did.
The shared preamble now tells every markup-capable provider's LLM to match
delivery to the register of the moment β€” composed and restrained when the
moment is professional or emotionally heavy, loose and bright when it is
casual β€” reassessed every turn. This is the mechanism that lets a single
default configuration serve a debt collector and a kids' tutor alike.

The fish block adds vocabulary-specific guidance on top, built
conditionally so an opted-out option is never referenced, not even
prohibitively: laughter guidance only when laugh sounds are advertised,
filler guidance only when disfluencies are enabled, and label-polarity
guidance (empathetic/sad/regretful/hopeful at heavy moments, never
happy/excited against hard news) always.
Register-inference evals showed the presets moved almost nothing the
agent's own persona didn't already move: with the full vocabulary
advertised and the register rule in the shared preamble, models place
laughter and fillers by conversational moment on their own (0.83
laughs/turn at casual moments vs 0.00 at serious, same settings, and
0.00 serious-moment laughs across four llms).

formal/casual also carried an implied delivery contract the data doesn't
support β€” on fish the two presets resolved to byte-identical
instructions. A locked-down agent is now expressed directly:
{"nonverbal_sounds": {}, "disfluencies": False}.
Two semantic fixes to the steering surface, from review:

- nonverbal_sounds accepts a plain bool: True keeps the provider's full
  vocabulary (same as omitting the key), False disables every sound.
- A NonverbalOptions dict is now a genuinely sparse opt-out: omitted
  categories default to ENABLED, so {"laughing": False} removes laughter
  and nothing else. Previously omitted categories defaulted to off, which
  made every nested dict silently disable the whole vocabulary β€” the same
  atomic-mapping footgun previously flagged on the preset merge.

The omitted-off default existed for the presets, which restricted from
zero; with presets gone the default is the full vocabulary and every
control is subtractive.
@cshape
cshape requested a review from a team as a code owner July 27, 2026 19:33

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

βœ… Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant