aboutsummaryrefslogtreecommitdiff
path: root/app/backends
diff options
context:
space:
mode:
Diffstat (limited to 'app/backends')
-rw-r--r--app/backends/audiocpp/__init__.py6
-rw-r--r--app/backends/audiocpp/configsync.py42
-rw-r--r--app/backends/audiocpp/wizard.py35
-rwxr-xr-xapp/backends/faster.py18
-rw-r--r--app/backends/qwen.py44
5 files changed, 35 insertions, 110 deletions
diff --git a/app/backends/audiocpp/__init__.py b/app/backends/audiocpp/__init__.py
index f55cf35..02fe967 100644
--- a/app/backends/audiocpp/__init__.py
+++ b/app/backends/audiocpp/__init__.py
@@ -9,7 +9,7 @@ Modules:
catalog the model_specs catalog + server.json building/selections
models install state on disk, missing-model guidance, downloads
voices reference-.wav transcription planning and execution
- configsync app/converter/config.py + server.json port/id/backend sync
+ configsync app/converter/config.py + server.json port/backend sync
build checkout lifecycle: ggml patches, binary build, update,
uninstall
remote querying a running server for its models/voices
@@ -56,7 +56,6 @@ from .voices import (
from .configsync import (
config_port,
update_config_api_url_port,
- update_config_model_ids,
update_server_backend,
update_server_config_port,
)
@@ -97,8 +96,7 @@ __all__ = [
"transcribe_wav_dir", "print_empty_transcript_warning",
# configsync
"config_port", "update_config_api_url_port",
- "update_config_model_ids", "update_server_config_port",
- "update_server_backend",
+ "update_server_config_port", "update_server_backend",
# build
"find_local_checkout", "find_audiocpp_server_bin", "find_build_script",
"apply_ggml_patches", "build_audiocpp", "uninstall", "update",
diff --git a/app/backends/audiocpp/configsync.py b/app/backends/audiocpp/configsync.py
index 0a161c0..bce8142 100644
--- a/app/backends/audiocpp/configsync.py
+++ b/app/backends/audiocpp/configsync.py
@@ -75,25 +75,6 @@ def update_server_config_port(port: int) -> bool:
return True
-def update_config_model_ids(model_id: str,
- clone_model_id: Optional[str] = None,
- config_path: Optional[Path] = None) -> bool:
- """Rewrite AUDIOCPP_MODEL_ID (and AUDIOCPP_CLONE_MODEL_ID when given).
-
- Goes through ``common.update_config_value`` so the imported config
- module mirrors the change immediately. Returns True when every named
- key now holds its value in the file.
- """
- path = Path(config_path) if config_path is not None else CONFIG_PATH
- ok = common.update_config_value("AUDIOCPP_MODEL_ID", model_id,
- config_path=path)
- if clone_model_id is not None:
- ok = common.update_config_value("AUDIOCPP_CLONE_MODEL_ID",
- clone_model_id,
- config_path=path) and ok
- return ok
-
-
def _apply_port_sync(port: int, accepted: bool) -> None:
"""Write the port into app/converter/config.py, or report when declined."""
if accepted:
@@ -106,29 +87,6 @@ def _apply_port_sync(port: int, accepted: bool) -> None:
f"will still use port {config_port()}")
-def _offer_config_model_id_sync(model_id: str, accepted: Optional[bool]) -> None:
- """Point app/converter/config.py at a single hosted model entry.
-
- The converter requests the model id configured in AUDIOCPP_MODEL_ID,
- and single-model servers use the same id for the clone entry, so both
- ids are rewritten together. ACCEPTED is True/False (apply/skip the
- rewrite) or None when no single-entry sync applies (nothing to do).
- """
- if config.AUDIOCPP_MODEL_ID == model_id \
- and config.AUDIOCPP_CLONE_MODEL_ID == model_id:
- return
- if accepted is None:
- return
- if accepted:
- if not update_config_model_ids(model_id, model_id):
- print(f"[WARNING] Could not update {CONFIG_PATH}; edit "
- "AUDIOCPP_MODEL_ID and AUDIOCPP_CLONE_MODEL_ID by hand so "
- "audiobook.py uses this model")
- else:
- print("[WARNING] Left the model ids unchanged; audiobook.py will "
- f"still request model '{config.AUDIOCPP_MODEL_ID}'")
-
-
def update_server_backend(backend: str) -> bool:
"""Rewrite the 'backend' in the checkout's server.json, or True when none.
diff --git a/app/backends/audiocpp/wizard.py b/app/backends/audiocpp/wizard.py
index d85076a..6d2d33a 100644
--- a/app/backends/audiocpp/wizard.py
+++ b/app/backends/audiocpp/wizard.py
@@ -20,7 +20,6 @@ from backends.common import (
resolve_wav_dir_arg,
write_prompt_text,
)
-from converter import config
from ui import taskview, tui
from . import build as _build
from . import configsync as _configsync
@@ -288,7 +287,6 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser
"build": build,
"lazy_load": True,
"sync_port": None,
- "sync_model_ids": s["sync_model_ids"],
"wav_dir": s["wav_dir"],
"plan": s["plan"],
"download": s["download"],
@@ -385,7 +383,6 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser
s["download"] = bool(result.get("download")) and (
_models.download_applicable(s["audiocpp_dir"],
s["model_entries"]))
- s["sync_model_ids"] = result.get("sync_model_ids")
s["delete_unused"] = bool(result.get("delete_unused")) \
and bool(s["unused_entries"])
return _finalize()
@@ -395,9 +392,9 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser
The Generate-audiobooks-style form replaces the old one-question-
per-screen chain (host, port, port sync, backend, build offer,
- wav directory, transcription plan, model-id sync, delete unused,
- download). Rows whose question does not apply are hidden rather
- than skipped silently. Esc or Cancel pops back to the model tree.
+ wav directory, transcription plan, delete unused, download). Rows
+ whose question does not apply are hidden rather than skipped
+ silently. Esc or Cancel pops back to the model tree.
"""
try:
_compute_entries()
@@ -487,16 +484,6 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser
"kind": "bool", "value": True,
})
- model_sync_relevant = len(s["entry_ids"]) == 1 and not (
- config.AUDIOCPP_MODEL_ID == s["entry_ids"][0]
- and config.AUDIOCPP_CLONE_MODEL_ID == s["entry_ids"][0])
- if model_sync_relevant:
- fields.append({
- "key": "sync_model_ids",
- "label": f"Make '{s['entry_ids'][0]}' the default model?",
- "kind": "bool", "value": True,
- })
-
new_paths = {entry["path"] for entry in s["model_entries"]}
s["unused_entries"] = _models.unused_installed_entries(
s["output_path"], new_paths) \
@@ -636,9 +623,6 @@ def _execute_lanes(settings: dict,
print(f"[OK] Deleted {removed} unused model "
f"{'entry' if removed == 1 else 'entries'} from disk.")
- if len(settings["entry_ids"]) == 1:
- _configsync._offer_config_model_id_sync(settings["entry_ids"][0],
- settings["sync_model_ids"])
_voices.print_empty_transcript_warning(state["transcripts"])
return 0
@@ -927,13 +911,6 @@ def _collect_from_flags(args: argparse.Namespace,
print("[INFO] Aborted; existing server.json kept")
return None
- # Config sync decisions (auto-apply unless explicitly declined).
- sync_model_ids: Optional[bool] = None
- if len(entry_ids) == 1 and not (
- config.AUDIOCPP_MODEL_ID == entry_ids[0]
- and config.AUDIOCPP_CLONE_MODEL_ID == entry_ids[0]):
- sync_model_ids = not args.no_sync_model_ids
-
# Wav dir + transcription plan (defaults to the project's voices/ dir).
wav_dir = args.input_dir if args.input_dir is not None else VOICES_DIR
plan: Optional[dict] = None
@@ -961,7 +938,6 @@ def _collect_from_flags(args: argparse.Namespace,
"build": build,
"lazy_load": lazy_load,
"sync_port": None,
- "sync_model_ids": sync_model_ids,
"wav_dir": wav_dir,
"plan": plan,
"download": args.download,
@@ -972,7 +948,7 @@ def build_parser() -> argparse.ArgumentParser:
"""The audio.cpp setup CLI (also used to build a default namespace)."""
parser = argparse.ArgumentParser(
description="Set up the audio.cpp TTS backend: clone/build, pick "
- "models, write server.json, and sync app/converter/config.py.")
+ "models, write server.json, and sync the configured port.")
parser.add_argument("--wavs", type=resolve_wav_dir_arg, default=None,
dest="input_dir", metavar="WAV_DIR",
help="Directory with .wav reference files to publish as "
@@ -1017,9 +993,6 @@ def build_parser() -> argparse.ArgumentParser:
help="Run model_manager_v2.py install for each hosted "
"model automatically (default: print the commands "
"only)")
- parser.add_argument("--no-sync-model-ids", action="store_true",
- help="Do not rewrite AUDIOCPP_MODEL_ID/"
- "AUDIOCPP_CLONE_MODEL_ID for a single-entry server")
return parser
diff --git a/app/backends/faster.py b/app/backends/faster.py
index 285d865..2d0cad3 100755
--- a/app/backends/faster.py
+++ b/app/backends/faster.py
@@ -337,27 +337,19 @@ def _execute_steps(settings: dict) -> List[taskview.TaskStep]:
if voices is None:
return 1
- # Sync app/converter/config.py default voice. (The server port is
- # not touched here: it lives in FASTER_API_URL, edited in the
- # Settings screen.)
- default_voice = next(iter(voices))
- if default_voice != config.FASTER_VOICE:
- if common.update_config_value("FASTER_VOICE", default_voice):
- print(f"[OK] Updated FASTER_VOICE to {default_voice}")
- else:
- print("[WARNING] Could not update FASTER_VOICE; edit "
- "app/converter/config.py by hand")
-
+ # (The server port is not touched here: it lives in
+ # FASTER_API_URL, edited in the Settings screen. Voices are picked
+ # per run with --voice / on the Generate-audiobooks screen.)
_print_launch_hint(settings["output_path"])
return 0
steps.append(taskview.TaskStep(
- "Write voices.json & sync config", write))
+ "Write voices.json", write))
return steps
def _execute(settings: dict) -> int:
- """Console tail: install, clone, write voices.json, sync, advise."""
+ """Console tail: install, clone, write voices.json, advise."""
return taskview.run_steps_inline(_execute_steps(settings))
diff --git a/app/backends/qwen.py b/app/backends/qwen.py
index d6dd2e0..a4cddb5 100644
--- a/app/backends/qwen.py
+++ b/app/backends/qwen.py
@@ -6,9 +6,10 @@ ONE Qwen3-TTS model per process — CustomVoice (built-in speakers), Base
(voice cloning) or VoiceDesign (described voice). This module sets it up
end-to-end: pip-install the package into its own managed venv
(``app/envs/qwen`` — the app venv and the faster backend's never receive
-it). There are no questions to ask — the port and which model to run live in
-``app/converter/config.py`` (the model is chosen per run on the hub's
-Generate-audiobooks screen), and only one server runs at a time.
+it). There are no questions to ask — the port lives in
+``app/converter/config.py`` (the hub's Settings screen) and the model is
+chosen per run on the hub's Generate-audiobooks screen; only one server
+runs at a time.
Model weights are not part of the install: each demo lazily fetches its
~4GB snapshot from HuggingFace into the standard hub cache the first time a
@@ -52,9 +53,9 @@ QWEN_PIP_PKG = "qwen-tts"
QWEN_ENV = envs.QWEN_ENV_DIR
DEFAULT_PORT = 7860
-# The models a single demo server can host, by config.QWEN_MODEL name.
-# A running server identifies itself via its probe identity (backends.probe),
-# so "which model is up" is always read off the server, never assumed.
+# The models a single demo server can host. A running server identifies
+# itself via its probe identity (backends.probe), so "which model is up" is
+# always read off the server, never assumed.
MODEL_REPOS = {
"CustomVoice": "Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice",
"Base": "Qwen/Qwen3-TTS-12Hz-1.7B-Base",
@@ -66,11 +67,13 @@ IDENTITY_TO_MODEL = {
probe.IDENTITY_QWEN_CLONE: "Base",
probe.IDENTITY_QWEN_DESIGN: "VoiceDesign",
}
+# The model a fresh managed start (Start/Stop Backend Servers menu) hosts;
+# Generate-audiobooks runs needing another model restart the server.
DEFAULT_MODEL = "CustomVoice"
-# Built-in CustomVoice speakers (see app/converter/config.py SPEAKER). The
-# canonical list lives in converter.clients.speakers (shared with the
-# audio.cpp backend's Convert-form Speaker picker).
+# Built-in CustomVoice speakers. The canonical list lives in
+# converter.clients.speakers (shared with the audio.cpp backend's
+# Convert-form Speaker picker); a run's speaker is picked per run.
QWEN_SPEAKERS = QWEN3_TTS_SPEAKERS
@@ -179,8 +182,8 @@ def _config_port(url: str, fallback: int) -> int:
def current_model() -> str:
- """The configured model to host (a MODEL_REPOS key; DEFAULT_MODEL on typos)."""
- return config.QWEN_MODEL if config.QWEN_MODEL in MODEL_REPOS else DEFAULT_MODEL
+ """The model a fresh managed start hosts (DEFAULT_MODEL)."""
+ return DEFAULT_MODEL
def model_for_identity(identity: Optional[str]) -> Optional[str]:
@@ -202,8 +205,9 @@ def _wizard(stdscr, args: argparse.Namespace) -> dict:
The qwen backend has no per-install choices: install happens when the
package is missing (and not skipped by flag), and every other value —
- port, speaker, which model runs — lives in app/converter/config.py /
- the hub's Settings and Generate-audiobooks screens.
+ port — lives in app/converter/config.py (the hub's Settings screen);
+ the model and voice are picked per run on the Generate-audiobooks
+ screen.
"""
return {
"do_install": (not _is_installed()) and not args.skip_install,
@@ -306,11 +310,12 @@ def _managed_running_model() -> Optional[str]:
def detect() -> BackendStatus:
"""Detect whether qwen-tts is installed, plus the launch command.
- One managed spec exists, hosting ``config.QWEN_MODEL`` on the single
- configured port. Which model currently answers there is read via the
- probe (local pid alive => check our own URL; otherwise the remote URL)
- so the status names the *running* model even when it differs from the
- configured one.
+ One managed spec exists, hosting the default model on the single
+ configured port (Generate-audiobooks runs needing another model boot
+ it via their own spec). Which model currently answers there is read
+ via the probe (local pid alive => check our own URL; otherwise the
+ remote URL) so the status names the *running* model even when it
+ differs from the default one.
"""
installed = _is_installed()
model = current_model()
@@ -319,8 +324,7 @@ def detect() -> BackendStatus:
details.append("pip: installed" if installed else
"not installed — run setup to pip install qwen-tts")
details.append(f"port: {_config_port(url, DEFAULT_PORT)}")
- details.append(f"model: {model}")
- details.append(f"speaker: {config.SPEAKER}")
+ details.append(f"default model: {model}")
specs = [_build_spec(model)]
managed = servers.manages(specs)
# A locally-managed server names its running model via the probe of the