2.3 KiB
Voice archetype gallery
Code: core/voice_archetypes.py (catalog + filter), routes/archetypes.py, UI static/js/voice-archetypes.js (core module) + card in static/sections/s-design.html.
Catalog: 40 entries authored here (no third-party data or code). Fields: id, name_de, name_en, instruct (free-text Qwen3 VoiceDesign description; German prompts transliterated to ASCII like the existing design presets), category (7 fixed), language (German/English), facets {gender, age, pitch, accent, tone} from the DESIGN_CHIPS vocabulary, sample_script_de/en (per category). Validated at import: unknown facet, category, language or duplicate/odd id raises ValueError (unit-tested).
API
GET /api/archetypes?gender&age&pitch&accent&language&category&q&offset&limit->{total, items, categories, cached}; limit capped at 100, q at 100 chars.POST /api/archetypes/{id}/preview-> WAV. Cache<CONFIG_DIR>/archetype_previews/<sha256(instruct|language|engine|seed)[:32]>.wav; engine is the configured voice-design model, seed fixed to 42 (injected as extra param for this call only). Rendering goes through_voice_design_request_audioinside the shared bounded TTS queue, 240 s timeout, per-key lock, size cap 8 MB, blank/too-short audio rejected (502), atomic write. Cache is naturally bounded by the catalog size.POST /api/archetypes/{id}/use-> saves voicearchetype_<id>(normalized WAV from the cached preview, reference text = sample script, metaorigin: designed,voice_design_prompt). Idempotent: an existing voice returnscreated: false.
Provenance choice: core/provenance.BASES has no "synthetic" value, so basis is unknown (closest; nothing to consent to), source archetype/<id> (a : would be parsed as a URL scheme and rejected), note "synthetic designed voice".
UI: the card is a collapsed <details>; nothing is fetched or rendered until opened (DOM budget). 24 cards per page ("Load more"), favourites in localStorage (guarded), DE translations in i18n.js.
Pre-render: scripts/render_archetype_previews.py calls the preview endpoint for every entry (sequential, idempotent). No API key needed: the API-key gate accepts same-origin requests, so the script sends Origin: <base>; only local/LAN bases accepted.
Not done: per-archetype audio shipped in the repo, user-defined archetypes, community import.