Add Seed-VC and Applio studio modes
This commit is contained in:
@@ -112,6 +112,8 @@ MUSIC_START_TIMEOUT = float(os.environ.get("MUSIC_START_TIMEOUT", "600"))
|
||||
SEPARATOR_START_TIMEOUT = float(os.environ.get("SEPARATOR_START_TIMEOUT", "600"))
|
||||
VOICE_START_TIMEOUT = float(os.environ.get("VOICE_START_TIMEOUT", "600"))
|
||||
VOICE_CHANGE_START_TIMEOUT = float(os.environ.get("VOICE_CHANGE_START_TIMEOUT", "600"))
|
||||
SEED_VC_START_TIMEOUT = float(os.environ.get("SEED_VC_START_TIMEOUT", "900"))
|
||||
APPLIO_START_TIMEOUT = float(os.environ.get("APPLIO_START_TIMEOUT", "900"))
|
||||
|
||||
# Optional worker APIs. The clean Docker baseline deliberately ships only
|
||||
# text/multimodal chat; absent workers must fail explicitly instead of trying
|
||||
@@ -405,6 +407,16 @@ def _voice_change_worker_health() -> str:
|
||||
return "unknown"
|
||||
|
||||
|
||||
def _worker_field(field: str) -> str:
|
||||
if not PROFILE_CONTROL_URL:
|
||||
return "unsupported"
|
||||
try:
|
||||
return str(_profile_controller_request("GET", "/status").get(field, "missing"))
|
||||
except Exception as exc:
|
||||
log.warning("Spezial-Worker-Status %s nicht verfügbar: %s", field, exc)
|
||||
return "unknown"
|
||||
|
||||
|
||||
def _wait_music_ready() -> None:
|
||||
deadline = time.monotonic() + MUSIC_START_TIMEOUT
|
||||
while time.monotonic() < deadline:
|
||||
@@ -461,6 +473,30 @@ def _wait_voice_change_ready() -> None:
|
||||
f"X-VC nach {VOICE_CHANGE_START_TIMEOUT:.0f} s nicht bereit")
|
||||
|
||||
|
||||
def _wait_aux_voice_ready(worker_field: str, health_field: str,
|
||||
label: str, timeout: float) -> None:
|
||||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
status = _profile_controller_request("GET", "/status")
|
||||
if (status.get(worker_field) == "running"
|
||||
and status.get(health_field) == "healthy"):
|
||||
return
|
||||
if status.get(health_field) == "unhealthy":
|
||||
raise RuntimeError(f"{label}-Container ist unhealthy")
|
||||
time.sleep(POLL_INTERVAL)
|
||||
raise RuntimeError(f"{label} nach {timeout:.0f} s nicht bereit")
|
||||
|
||||
|
||||
def _wait_seed_vc_ready() -> None:
|
||||
_wait_aux_voice_ready("seed_vc_worker", "seed_vc_health",
|
||||
"Seed-VC", SEED_VC_START_TIMEOUT)
|
||||
|
||||
|
||||
def _wait_applio_ready() -> None:
|
||||
_wait_aux_voice_ready("applio_worker", "applio_health",
|
||||
"Applio", APPLIO_START_TIMEOUT)
|
||||
|
||||
|
||||
def _special_worker(mode: str) -> tuple[str, str, callable]:
|
||||
if mode == "music":
|
||||
return "/workers/music/start", _music_worker_state(), _wait_music_ready
|
||||
@@ -471,6 +507,12 @@ def _special_worker(mode: str) -> tuple[str, str, callable]:
|
||||
if mode == "voicechange":
|
||||
return ("/workers/voice-change/start", _voice_change_worker_state(),
|
||||
_wait_voice_change_ready)
|
||||
if mode == "seedvc":
|
||||
return ("/workers/seed-vc/start", _worker_field("seed_vc_worker"),
|
||||
_wait_seed_vc_ready)
|
||||
if mode == "applio":
|
||||
return ("/workers/applio/start", _worker_field("applio_worker"),
|
||||
_wait_applio_ready)
|
||||
raise ValueError(f"unbekannter Spezialmodus: {mode}")
|
||||
|
||||
|
||||
@@ -478,11 +520,12 @@ def set_operating_mode(mode: str) -> dict:
|
||||
"""Atomarer Wechsel zwischen LLM und den exklusiven GPU-Werkzeugen."""
|
||||
if not ENABLE_MUSIC_MODE or not PROFILE_CONTROL_URL:
|
||||
raise RuntimeError("Musikmodus ist nicht konfiguriert")
|
||||
if mode not in {"llm", "music", "separation", "voice", "voicechange"}:
|
||||
raise ValueError("Modus muss 'llm', 'music', 'separation', 'voice' oder 'voicechange' sein")
|
||||
special_modes = {"music", "separation", "voice", "voicechange", "seedvc", "applio"}
|
||||
if mode not in {"llm", *special_modes}:
|
||||
raise ValueError("unbekannter Betriebsmodus")
|
||||
with STATE.lock:
|
||||
STATE.mode_error = None
|
||||
if mode in {"music", "separation", "voice", "voicechange"}:
|
||||
if mode in special_modes:
|
||||
path, worker_state, wait_ready = _special_worker(mode)
|
||||
if STATE.mode == mode and worker_state == "running":
|
||||
return {"status": "ok", "mode": mode, "changed": False}
|
||||
@@ -525,6 +568,8 @@ def set_operating_mode(mode: str) -> dict:
|
||||
_profile_controller_request("POST", "/workers/separator/stop")
|
||||
_profile_controller_request("POST", "/workers/voice/stop")
|
||||
_profile_controller_request("POST", "/workers/voice-change/stop")
|
||||
_profile_controller_request("POST", "/workers/seed-vc/stop")
|
||||
_profile_controller_request("POST", "/workers/applio/stop")
|
||||
_restore_qwen(profile)
|
||||
STATE.mode = "llm"
|
||||
STATE.mode_phase = "ready"
|
||||
@@ -543,8 +588,8 @@ def schedule_operating_mode(mode: str) -> tuple[bool, str]:
|
||||
"""Start a transition in the background so chat/UI acknowledgement is instant."""
|
||||
if not ENABLE_MUSIC_MODE or not PROFILE_CONTROL_URL:
|
||||
raise RuntimeError("Musikmodus ist nicht konfiguriert")
|
||||
if mode not in {"llm", "music", "separation", "voice", "voicechange"}:
|
||||
raise ValueError("Modus muss 'llm', 'music', 'separation', 'voice' oder 'voicechange' sein")
|
||||
if mode not in {"llm", "music", "separation", "voice", "voicechange", "seedvc", "applio"}:
|
||||
raise ValueError("unbekannter Betriebsmodus")
|
||||
with STATE.lock:
|
||||
if STATE.mode_phase not in {"ready", "error"}:
|
||||
return False, STATE.mode_phase
|
||||
@@ -582,6 +627,7 @@ def _control_command(data: dict, path: str) -> str | None:
|
||||
"/athena separation", "/athena llm",
|
||||
"/athena voice",
|
||||
"/athena voicechange", "/athena changer",
|
||||
"/athena seedvc", "/athena applio",
|
||||
"/athena status"} else None
|
||||
|
||||
|
||||
@@ -2061,6 +2107,10 @@ class Handler(BaseHTTPRequestHandler):
|
||||
"voice_health": _voice_worker_health(),
|
||||
"voice_change_worker": _voice_change_worker_state(),
|
||||
"voice_change_health": _voice_change_worker_health(),
|
||||
"seed_vc_worker": _worker_field("seed_vc_worker"),
|
||||
"seed_vc_health": _worker_field("seed_vc_health"),
|
||||
"applio_worker": _worker_field("applio_worker"),
|
||||
"applio_health": _worker_field("applio_health"),
|
||||
"return_profile": state.get("return_profile"),
|
||||
"last_error": STATE.mode_error,
|
||||
"enabled": ENABLE_MUSIC_MODE,
|
||||
@@ -2070,8 +2120,8 @@ class Handler(BaseHTTPRequestHandler):
|
||||
try:
|
||||
data = json.loads(self._read_body() or b"{}")
|
||||
mode = data.get("mode") if isinstance(data, dict) else None
|
||||
if mode not in {"llm", "music", "separation", "voice", "voicechange"}:
|
||||
raise ValueError("Feld 'mode' muss 'llm', 'music', 'separation', 'voice' oder 'voicechange' sein")
|
||||
if mode not in {"llm", "music", "separation", "voice", "voicechange", "seedvc", "applio"}:
|
||||
raise ValueError("Feld 'mode' enthält einen unbekannten Betriebsmodus")
|
||||
started, phase = schedule_operating_mode(mode)
|
||||
self._send_json(202 if started else 200, {
|
||||
"status": "accepted" if started else "ok",
|
||||
@@ -2742,6 +2792,8 @@ class Handler(BaseHTTPRequestHandler):
|
||||
"separation" if command in {"/athena stems", "/athena separation"}
|
||||
else "voice" if command == "/athena voice"
|
||||
else "voicechange" if command in {"/athena voicechange", "/athena changer"}
|
||||
else "seedvc" if command == "/athena seedvc"
|
||||
else "applio" if command == "/athena applio"
|
||||
else "llm")
|
||||
try:
|
||||
started, phase = schedule_operating_mode(target)
|
||||
@@ -2754,6 +2806,10 @@ class Handler(BaseHTTPRequestHandler):
|
||||
if target == "voice" else
|
||||
"Voice Changer wird gestartet. LLM und TTS werden entladen."
|
||||
if target == "voicechange" else
|
||||
"Seed-VC wird gestartet. LLM und TTS werden entladen."
|
||||
if target == "seedvc" else
|
||||
"Applio wird gestartet. LLM und TTS werden entladen."
|
||||
if target == "applio" else
|
||||
"Spezialmodus wird beendet und das vorherige LLM-Profil wiederhergestellt.")
|
||||
else:
|
||||
text = (f"Athena ist bereits im {target.upper()}-Modus "
|
||||
|
||||
Reference in New Issue
Block a user