Files
abogen/docs/architecture-amendment-001.md
T
Artem Akymenko 735098d7cd feat: add static voice catalog to PluginManifest
- 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
2026-07-12 16:20:16 +03:00

3.5 KiB

Architecture Amendment #1: EngineConfig — lang_code field

Date: 2026-07-12 Status: Accepted PR: #12 (Normalize Pipeline Public API)

Summary

Add lang_code: str = "a" to EngineConfig and update its definition to clarify the architectural contract.

Background

During migration from the old KokoroBackend to the Plugin Architecture, the lang_code parameter became a dead argument. The old backend read it from **kwargs and passed it to KPipeline(lang_code=...). The new KokoroPlugin.create_engine() hardcodes lang_code="a", ignoring the config entirely. Callers continued passing lang_code to create_pipeline(), unaware it had no effect.

This is a functional regression relative to the pre-Plugin Architecture behavior.

Decision

1. Updated EngineConfig definition

Before:

Immutable value object for engine initialization settings.
Contains only engine-specific settings, no resource references.

After:

Immutable configuration of an Engine instance.
Contains parameters that define how a particular Engine instance is
created and that remain constant throughout the lifetime of that Engine.
Plugin implementations may ignore fields that are not applicable to them.

2. New field

@dataclass(frozen=True)
class EngineConfig:
    device: str = "cpu"
    lang_code: str = "a"

3. Architectural rules

  • Fields in EngineConfig are optional unless explicitly required by a plugin.
  • Plugins MUST ignore unsupported EngineConfig fields.
  • All parameters that may vary between individual synthesis requests must remain in SynthesisRequest.parameters.

Rationale

Analysis of real TTS engines (Kokoro, Piper, XTTS, Coqui, StyleTTS2, Fish Speech) confirmed:

Parameter type Where it belongs Example
Engine instance config (immutable) EngineConfig device, lang_code
Synthesis parameters (per-request) SynthesisRequest.parameters speed, split_pattern, total_steps

lang_code determines the engine's behavior at creation time and cannot be changed during the engine's lifetime. It is not a synthesis parameter.

Impact on existing plugins

Plugin device lang_code Notes
Kokoro Reads ✓ Reads ✓ (was hardcoded, now from config) Regression fixed
SuperTonic Ignores Ignores No change — no language concept
Future plugins May read May ignore Field-ignoring rule applies

Contract tests added

class TestEngineConfigContract:
    def test_default_lang_code(self)       # EngineConfig().lang_code == "a"
    def test_custom_lang_code(self)        # EngineConfig(lang_code="j").lang_code == "j"
    def test_immutability_lang_code(self)  # frozen — cannot reassign
    def test_plugins_may_ignore_irrelevant_fields(self)  # field-ignoring rule
    def test_engine_config_contains_engine_instance_configuration(self)  # definition

Files changed

File Change
abogen/tts_plugin/types.py Updated docstring, added lang_code: str = "a"
plugins/kokoro/__init__.py Reads config.lang_code instead of hardcoded "a"
abogen/tts_plugin/utils.py create_pipeline() passes lang_code to EngineConfig
tests/contracts/test_types_contract.py 5 new contract tests
tests/contracts/test_plugin_manager_contract.py Updated assertion for lang_code
tests/test_behavioral_regression.py Updated test_engine_config_defaults