Sentence modes processed all text as a whole: Pipeline.__call__ merged every
engine segment back into one (whole text, no per-token timings), producing a
single giant subtitle and whole-text progress logs.
- tts_plugin/types: add TokenTiming, AudioSegment, SynthesizedAudio.segments
- tts_plugin/utils: Pipeline yields one Segment per engine segment (with
tokens); merged fallback only when engine provides none
- kokoro engine: expose per-segment graphemes/audio + per-word token timings
- supertonic engine: expose per-segment graphemes/audio (no tokens)
- split_pattern: English Sentence/Sentence+Comma engine split is newline-only
(boundaries applied at subtitle time via spaCy); non-English Sentence+Comma
with spaCy ON uses spaCy pre-segmentation + newline engine split (no
commas); spaCy-off fallback keeps comma pattern
- tts_segments: restore inter-segment whitespace on real per-word token
boundaries only (never FakeToken fallbacks)
- _to_language_enum: accept Language enum input (str(enum) is "Language.ES",
silently resolved to EN_US and disabled spaCy pre-TTS for every language
in WebUI)
- pyqt/conversion, utils: replace print with logging
- add AGENTS.md documenting the segmentation/subtitle contract for future
sessions
- tests: update English split-pattern expectations (1566 passing)
Complete the Language enum unification (commit 0dc491e) for the PyQt
GUI, which was left resolving languages from kokoro letter codes
(voice[0]) while domain and WebUI already used Language enums.
- selected_lang is now always a Language enum: voices resolve via
language_for_voice_id(), profiles via resolve_profile_language()
- kokoro letter->Language mapping stays in the engine (new public
language_for_code()); the Language.from_code() domain shim is removed
- legacy profile files with letter codes ("a", "e", ...) are tolerated
only at the profile read boundary; new profiles save ISO codes
- conversion.py letter comparisons replaced with enum comparisons, and
SynthParams lang_code= -> language= (would TypeError at runtime)
- subtitle dropdown enabled for all languages; word-count and
highlighting modes restricted to English with auto-switch to Sentence
- tests: engine mapping + profile language resolution added; 1566 pass
- Monkey-patch transformers.AlbertModel in plugins/kokoro/__init__.py before kokoro imports it
- Works with transformers 4.x (no-op) and 5.x (adds moved symbol)
- No pinning, forks, or extra files needed
- Add .gitattributes with text=auto eol=lf for all text files
- Renormalize all files in index to LF line endings
- Fixes massive whitespace-only diffs between main and feature branch
- Add to PluginManifest
- None = not declared (use VoiceLister fallback)
- () = explicitly no static voices
- Non-empty = static catalog available without Engine instantiation
- Update get_voices() to check manifest first, fall back to Engine
- Declare 54 Kokoro voices and 10 SuperTonic voices in manifests
- Remove hardcoded voice lists from engine.py files
- Engine.listVoices() now returns [] (manifest is source of truth)
- Clean up dead create_pipeline() kwargs (sample_rate, auto_download, total_steps)
- SuperTonic plugin uses internal defaults
- total_steps is per-request parameter via Pipeline.__call__() kwargs
- Add clear_preview_pipelines() for resource cleanup
- Fix test mocks to override listVoices()
- Update Architecture Amendment #1 doc
Implement first TTS plugin using new Plugin Architecture:
- plugins/kokoro/: Plugin package with manifest and entry point
- plugins/kokoro/engine.py: KokoroEngine and KokoroSession adapters
- Wraps existing KokoroBackend without modifying it
- Implements VoiceLister capability
- Satisfies Engine/EngineSession protocol
- Passes all 163 contract tests
Tests:
- Plugin loading through Plugin Loader
- Manifest validation
- Engine creation and lifecycle
- Session synthesis and dispose
- VoiceLister capability
15 new tests for Kokoro plugin.