Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
"""``POST /api/audio/tts-lease`` — desktop speech toggles as TTS warm-up/release.
|
||||
|
||||
The desktop's "Read replies aloud" and voice-conversation toggles call this so
|
||||
the backend can pre-load the configured TTS engine when speech is about to be
|
||||
needed and unload resident local models once no surface holds a lease.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def isolated_profiles(tmp_path, monkeypatch, _isolate_hermes_home):
|
||||
from hermes_constants import get_hermes_home
|
||||
from hermes_cli import profiles
|
||||
|
||||
default_home = get_hermes_home()
|
||||
profiles_root = default_home / "profiles"
|
||||
worker_home = profiles_root / "worker_beta"
|
||||
for home in (default_home, worker_home):
|
||||
home.mkdir(parents=True, exist_ok=True)
|
||||
(home / "config.yaml").write_text("{}\n", encoding="utf-8")
|
||||
(worker_home / ".env").write_text("", encoding="utf-8")
|
||||
|
||||
monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: default_home)
|
||||
monkeypatch.setattr(profiles, "_get_profiles_root", lambda: profiles_root)
|
||||
return {"default": default_home, "worker_beta": worker_home}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(monkeypatch, isolated_profiles):
|
||||
try:
|
||||
from starlette.testclient import TestClient
|
||||
except ImportError:
|
||||
pytest.skip("fastapi/starlette not installed")
|
||||
|
||||
import hermes_state
|
||||
from hermes_constants import get_hermes_home
|
||||
from hermes_cli.web_server import app, _SESSION_HEADER_NAME, _SESSION_TOKEN
|
||||
|
||||
monkeypatch.setattr(hermes_state, "DEFAULT_DB_PATH", get_hermes_home() / "state.db")
|
||||
c = TestClient(app)
|
||||
c.headers[_SESSION_HEADER_NAME] = _SESSION_TOKEN
|
||||
return c
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clean_leases():
|
||||
from tools import tts_tool
|
||||
|
||||
tts_tool._reset_tts_leases_for_tests()
|
||||
for cache in tts_tool._LOCAL_TTS_MODEL_CACHES.values():
|
||||
cache.clear()
|
||||
yield
|
||||
tts_tool._reset_tts_leases_for_tests()
|
||||
for cache in tts_tool._LOCAL_TTS_MODEL_CACHES.values():
|
||||
cache.clear()
|
||||
|
||||
|
||||
def test_active_acquires_and_warms(client, monkeypatch):
|
||||
from tools import tts_tool
|
||||
|
||||
warmed = []
|
||||
monkeypatch.setattr(
|
||||
tts_tool,
|
||||
"warm_tts_provider",
|
||||
lambda cfg=None, provider=None: warmed.append(1) or {"provider": "piper", "warmed": True, "action": "loaded"},
|
||||
)
|
||||
|
||||
resp = client.post("/api/audio/tts-lease", json={"lease": "desktop:read-aloud", "active": True})
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["ok"] is True
|
||||
assert body["lease"] == "desktop:read-aloud"
|
||||
assert body["active"] is True
|
||||
assert body["leases"] == 1
|
||||
assert body["action"] == "loaded"
|
||||
assert warmed == [1]
|
||||
assert tts_tool.tts_lease_holders() == ["desktop:read-aloud"]
|
||||
|
||||
|
||||
def test_inactive_releases_and_unloads_when_last(client, monkeypatch):
|
||||
from tools import tts_tool
|
||||
|
||||
monkeypatch.setattr(tts_tool, "warm_tts_provider", lambda cfg=None, provider=None: {"action": "noop", "warmed": False, "provider": "piper"})
|
||||
client.post("/api/audio/tts-lease", json={"lease": "desktop:read-aloud", "active": True})
|
||||
client.post("/api/audio/tts-lease", json={"lease": "desktop:conversation:abc", "active": True})
|
||||
tts_tool._piper_voice_cache["voice"] = object()
|
||||
|
||||
first = client.post("/api/audio/tts-lease", json={"lease": "desktop:read-aloud", "active": False}).json()
|
||||
assert first["leases"] == 1
|
||||
assert first["released"] == 0
|
||||
assert len(tts_tool._piper_voice_cache) == 1
|
||||
|
||||
last = client.post("/api/audio/tts-lease", json={"lease": "desktop:conversation:abc", "active": False}).json()
|
||||
assert last["leases"] == 0
|
||||
assert last["released"] == 1
|
||||
assert tts_tool._piper_voice_cache == {}
|
||||
|
||||
|
||||
def test_warm_failure_is_reported_not_an_http_error(client, monkeypatch):
|
||||
from tools import tts_tool
|
||||
|
||||
def _boom(cfg=None, provider=None):
|
||||
raise RuntimeError("engine exploded")
|
||||
|
||||
monkeypatch.setattr(tts_tool, "warm_tts_provider", _boom)
|
||||
resp = client.post("/api/audio/tts-lease", json={"lease": "desktop:read-aloud", "active": True})
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["ok"] is True
|
||||
assert body["action"] == "error"
|
||||
assert "engine exploded" in body["error"]
|
||||
|
||||
|
||||
def test_blank_lease_rejected(client):
|
||||
resp = client.post("/api/audio/tts-lease", json={"lease": " ", "active": True})
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
def test_active_default_true(client, monkeypatch):
|
||||
from tools import tts_tool
|
||||
|
||||
monkeypatch.setattr(tts_tool, "warm_tts_provider", lambda cfg=None, provider=None: {"action": "noop", "warmed": False, "provider": "x"})
|
||||
resp = client.post("/api/audio/tts-lease", json={"lease": "tui:x"})
|
||||
assert resp.json()["active"] is True
|
||||
assert tts_tool.tts_lease_holders() == ["tui:x"]
|
||||
|
||||
|
||||
def test_acquire_resolves_provider_inside_target_profile(client, isolated_profiles, monkeypatch):
|
||||
"""Warm-up must read the REQUESTING profile's tts config, like /api/audio/speak."""
|
||||
import yaml
|
||||
from tools import tts_tool
|
||||
|
||||
(isolated_profiles["worker_beta"] / "config.yaml").write_text(
|
||||
yaml.safe_dump({"tts": {"provider": "kittentts"}}), encoding="utf-8"
|
||||
)
|
||||
seen = {}
|
||||
|
||||
def _fake_warm(cfg=None, provider=None):
|
||||
from hermes_constants import get_hermes_home
|
||||
|
||||
seen["home"] = str(get_hermes_home())
|
||||
seen["provider"] = tts_tool._get_provider(tts_tool._load_tts_config())
|
||||
return {"action": "noop", "warmed": False, "provider": seen["provider"]}
|
||||
|
||||
monkeypatch.setattr(tts_tool, "warm_tts_provider", _fake_warm)
|
||||
resp = client.post("/api/audio/tts-lease?profile=worker_beta", json={"lease": "desktop:read-aloud", "active": True})
|
||||
assert resp.status_code == 200
|
||||
assert seen["home"] == str(isolated_profiles["worker_beta"])
|
||||
assert seen["provider"] == "kittentts"
|
||||
|
||||
|
||||
def test_unknown_profile_404(client):
|
||||
resp = client.post("/api/audio/tts-lease?profile=ghost", json={"lease": "desktop:read-aloud", "active": True})
|
||||
assert resp.status_code == 404
|
||||
Reference in New Issue
Block a user