feat: auto-register existing TTS backends

- Add create_kokoro_backend() factory in kokoro.py
- Add create_supertonic_backend() factory in supertonic.py
- Auto-discover backend modules in __init__.py via pkgutil
- Both backends register themselves on import
- Tests verify registration and factory callables
This commit is contained in:
Artem Akymenko
2026-07-06 15:04:49 +03:00
parent 58fe22e3d5
commit 57fec453e2
4 changed files with 237 additions and 104 deletions
+20
View File
@@ -0,0 +1,20 @@
"""TTS backends package.
Backend modules are auto-discovered and imported here.
Each backend module registers itself with the global registry
when imported.
"""
import importlib
import pkgutil
def _discover_backends():
"""Import all modules in this package to trigger their registration."""
package = __name__
for _importer, modname, _ispkg in pkgutil.iter_modules(path=__path__):
importlib.import_module(f"{package}.{modname}")
_discover_backends()
+32
View File
@@ -3,3 +3,35 @@ def load_numpy_kpipeline():
from kokoro import KPipeline # type: ignore[import-not-found] from kokoro import KPipeline # type: ignore[import-not-found]
return np, KPipeline return np, KPipeline
def create_kokoro_backend(**kwargs):
"""Create a Kokoro TTS backend instance.
Args:
lang_code: Language code (e.g. "a" for American English).
repo_id: HuggingFace repo id. Defaults to "hexgrad/Kokoro-82M".
device: Device to use ("cpu", "cuda", "mps"). Defaults to "cpu".
Returns:
KPipeline instance.
"""
_np, KPipeline = load_numpy_kpipeline()
return KPipeline(
lang_code=kwargs["lang_code"],
repo_id=kwargs.get("repo_id", "hexgrad/Kokoro-82M"),
device=kwargs.get("device", "cpu"),
)
from abogen.tts_backend import TTSBackendMetadata
from abogen.tts_backend_registry import register_backend
register_backend(
metadata=TTSBackendMetadata(
id="kokoro",
name="Kokoro",
description="Kokoro TTS engine",
),
factory=create_kokoro_backend,
)
+31
View File
@@ -273,3 +273,34 @@ class SupertonicPipeline:
audio = _resample_linear(audio, src_rate, self.sample_rate) audio = _resample_linear(audio, src_rate, self.sample_rate)
yield SupertonicSegment(graphemes=chunk_to_speak, audio=audio) yield SupertonicSegment(graphemes=chunk_to_speak, audio=audio)
def create_supertonic_backend(**kwargs):
"""Create a SuperTonic TTS backend instance.
Args:
sample_rate: Audio sample rate. Defaults to 24000.
auto_download: Auto-download models. Defaults to True.
total_steps: Inference steps. Defaults to 5.
Returns:
SupertonicPipeline instance.
"""
return SupertonicPipeline(
sample_rate=kwargs.get("sample_rate", 24000),
auto_download=kwargs.get("auto_download", True),
total_steps=kwargs.get("total_steps", 5),
)
from abogen.tts_backend import TTSBackendMetadata
from abogen.tts_backend_registry import register_backend
register_backend(
metadata=TTSBackendMetadata(
id="supertonic",
name="SuperTonic",
description="SuperTonic TTS engine",
),
factory=create_supertonic_backend,
)
+50
View File
@@ -97,3 +97,53 @@ class TestTTSBackendRegistry:
result = registry.get_metadata("x") result = registry.get_metadata("x")
assert result.name == "V2" assert result.name == "V2"
assert registry.create_backend("x") == "v2" assert registry.create_backend("x") == "v2"
class TestBackendRegistration:
"""Tests that existing backends are auto-registered."""
def test_import_triggers_registration(self):
import abogen.tts_backends # noqa: F401
from abogen.tts_backend_registry import _registry
backends = _registry.list_backends()
ids = [b.id for b in backends]
assert "kokoro" in ids
assert "supertonic" in ids
def test_kokoro_metadata(self):
import abogen.tts_backends # noqa: F401
from abogen.tts_backend_registry import _registry
meta = _registry.get_metadata("kokoro")
assert meta.id == "kokoro"
assert meta.name == "Kokoro"
assert "Kokoro" in meta.description
def test_supertonic_metadata(self):
import abogen.tts_backends # noqa: F401
from abogen.tts_backend_registry import _registry
meta = _registry.get_metadata("supertonic")
assert meta.id == "supertonic"
assert meta.name == "SuperTonic"
assert "SuperTonic" in meta.description
def test_kokoro_factory_callable(self):
import abogen.tts_backends # noqa: F401
from abogen.tts_backend_registry import _registry
factory = _registry._factories["kokoro"]
assert callable(factory)
def test_supertonic_factory_callable(self):
import abogen.tts_backends # noqa: F401
from abogen.tts_backend_registry import _registry
factory = _registry._factories["supertonic"]
assert callable(factory)