fix: personality preset crash in Design tab (closes #89) - #111
Conversation
Selecting any personality preset in the Design tab (e.g. "News Anchor")
made the next Synthesize call fail with a 400 ValueError that took the
generation pipeline down — the user had to restart the app.
Root cause
==========
backend/core/personalities.py shipped human-readable prose as each
preset's `instruct` value, e.g.:
"Speak clearly and professionally like a television news presenter"
OmniVoice's model.generate(instruct=...) runs every instruct string
through _resolve_instruct (omnivoice/models/omnivoice.py:1351), which
splits on commas and validates each item against a fixed taxonomy in
omnivoice/utils/voice_design.py (gender, age, pitch, accent, dialect,
"whisper"). Prose like "Speak clearly..." has zero tokens in that
vocabulary, so the model raises:
ValueError: Unsupported instruct items found in
Speak clearly and professionally like a television news presenter:
'Speak clearly and professionally like a television news presenter'
-> ... (unsupported)
The frontend (CloneDesignTab.jsx applyPersonality) writes the preset's
`instruct` straight into the synth form, so every one of the six
personalities triggered the crash — verified all six raise ValueError.
Fix
===
Map each personality to a comma-separated bundle of valid taxonomy
tokens that _resolve_instruct accepts. Kept the original prose as a new
`description` field for any future UI tooltips and so the design intent
isn't lost.
Verified personalities now round-trip cleanly:
narrator -> "middle-aged, low pitch"
casual -> "young adult, moderate pitch"
news_anchor -> "middle-aged, moderate pitch, american accent"
storyteller -> "middle-aged, moderate pitch, british accent"
corporate -> "middle-aged, moderate pitch"
energetic -> "young adult, high pitch"
Regression test
===============
tests/backend/core/test_personalities.py exercises the exact failing
path: every personality is fed through the same _resolve_instruct that
the runtime calls. The test would have failed on every shipped
personality before this commit.
Cross-platform / data compatibility
===================================
Pure-Python data change — same on macOS / Windows / Linux. No DB
schema or omnivoice_data/ migration: voice_profiles.instruct is
untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR updates the personality registry in ChangesPersonality Data Structure and Validation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/backend/core/test_personalities.py (1)
59-63: ⚡ Quick winAssert
descriptionin the schema contract test.The registry test should validate the new
descriptionfield too, otherwise this PR’s schema addition can regress silently.Patch suggestion
- for key in ("id", "name", "instruct", "icon"): + for key in ("id", "name", "instruct", "description", "icon"): assert key in p, f"personality {p.get('name')!r} missing {key!r}" assert isinstance(p[key], str), ( f"personality {p.get('name')!r} field {key!r} must be a string" )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/backend/core/test_personalities.py` around lines 59 - 63, The test loop validating personality schema omits the new "description" field; update the check that iterates keys ("id","name","instruct","icon") to also include "description" and add the same type assertion for p["description"] (i.e., assert the key exists on p and assert isinstance(p["description"], str)) so the registry test covers the new schema field; locate the loop using variable p in the test_personalities test and modify it accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/backend/core/test_personalities.py`:
- Around line 59-63: The test loop validating personality schema omits the new
"description" field; update the check that iterates keys
("id","name","instruct","icon") to also include "description" and add the same
type assertion for p["description"] (i.e., assert the key exists on p and assert
isinstance(p["description"], str)) so the registry test covers the new schema
field; locate the loop using variable p in the test_personalities test and
modify it accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 495c2f5b-fa5c-44d0-a583-432ab573f765
📒 Files selected for processing (2)
backend/core/personalities.pytests/backend/core/test_personalities.py
Summary
Selecting any personality preset on the Design tab (e.g. News Anchor) made the next Synthesize Audio call crash with HTTP 400 from the generate router — the user had to restart the app to recover. Affects all 6 shipped personalities.
This PR ships a minimal data fix to
backend/core/personalities.pyplus a regression test that pins the failing path.Reproduction steps (per issue #89)
Confirmed all 6 prose personality strings raise
ValueErroragainst the same code path the runtime uses:Root cause
backend/core/personalities.pyshipped human-readable prose as each personality'sinstructvalue, e.g.OmniVoice's
model.generate(instruct=...)runs every instruct string through_resolve_instruct(omnivoice/models/omnivoice.py:1351), which splits on commas and validates each item against a fixed taxonomy defined inomnivoice/utils/voice_design.py(gender, age, pitch, accent, dialect,whisper). Prose like "Speak clearly…" has zero tokens in that vocabulary, so the model raises:The frontend (
CloneDesignTab.jsx→applyPersonality) writes the preset'sinstructstraight into the synth form (useTTS.jsline 108–110 joins it with the comma-separatedvdStates), so every personality triggered the crash.Fix
Map each personality to a comma-separated bundle of valid taxonomy tokens that
_resolve_instructaccepts. The original prose moves into a newdescriptionfield so design intent isn't lost and future UI tooltips can use it. The frontend doesn't renderdescriptiontoday (onlyname+icon), so this is purely additive.instructmiddle-aged, low pitchyoung adult, moderate pitchmiddle-aged, moderate pitch, american accentmiddle-aged, moderate pitch, british accentmiddle-aged, moderate pitchyoung adult, high pitchRegression test
tests/backend/core/test_personalities.pyruns every personality'sinstructthrough the exact same_resolve_instructthe runtime calls. The test would have failed on all 6 personalities before this commit; after the fix it passes for all of them.Also confirmed the wider
tests/backend/core/slice still passes (25 passed).Cross-platform / data compatibility
voice_profiles.instructcolumn is untouched. No DB migration needed; existingomnivoice_data/works unchanged.Test plan
pytest tests/backend/core/test_personalities.py -v(3/3 pass)_resolve_instructpytest tests/backend/core/— 25/25 pass)🤖 Generated with Claude Code
Summary by CodeRabbit
Documentation
Tests