mirror of
https://github.com/index-tts/index-tts.git
synced 2026-09-01 15:10:31 +08:00
b5bd6579e3
* feat(webui): add preset save/load/manage support * fix(webui): fix preset initialization and loading issues * feat(cli/webui): expose use_accel and use_torch_compile options - Add --accel/--torch-compile flags to CLI v2 synth and batch commands - Add use_accel/use_torch_compile to persisted config keys - Pass both options through to IndexTTS2 in CLI and WebUI - Update relevant CLI tests for new runtime options * feat(webui): add UI checkboxes for use_accel and use_torch_compile in advanced settings - Add two checkboxes in the advanced settings accordion - Default values follow the --accel / --torch_compile CLI flags - Rebuild the IndexTTS2 engine when checkbox values differ from current instance * feat: expose accel/torch_compile as startup args only; add optional extras * docs: remove Windows acceleration note from README * test(cli): mock flash_attn/triton imports in no-GPU CLI tests --------- Co-authored-by: Jennie <wangyihui02@bilibili.com>
595 lines
25 KiB
Python
595 lines
25 KiB
Python
import contextlib
|
|
import io
|
|
import importlib
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest import mock
|
|
|
|
|
|
def _mock_optional_dependency_imports():
|
|
"""Make flash_attn/triton appear installed so acceleration CLI tests can run."""
|
|
original = importlib.import_module
|
|
|
|
def _fake_import(name, package=None):
|
|
if name in ("flash_attn", "triton"):
|
|
return mock.MagicMock()
|
|
return original(name, package)
|
|
|
|
return mock.patch("indextts.cli_v2.importlib.import_module", side_effect=_fake_import)
|
|
|
|
|
|
REQUIRED_MODEL_FILES = [
|
|
"config.yaml",
|
|
"bpe.model",
|
|
"gpt.pth",
|
|
"s2mel.pth",
|
|
"wav2vec2bert_stats.pt",
|
|
"feat1.pt",
|
|
"feat2.pt",
|
|
]
|
|
REQUIRED_MODEL_DIRS = [
|
|
"qwen0.6bemo4-merge",
|
|
]
|
|
AUX_MODEL_FILES = [
|
|
"hf_cache/semantic_codec_model.safetensors",
|
|
"hf_cache/campplus_cn_common.bin",
|
|
"hf_cache/bigvgan/config.json",
|
|
"hf_cache/bigvgan/bigvgan_generator.pt",
|
|
]
|
|
AUX_MODEL_DIRS = [
|
|
"hf_cache/w2v-bert-2.0",
|
|
]
|
|
|
|
|
|
def make_model_dir(path):
|
|
path.mkdir(parents=True)
|
|
for filename in REQUIRED_MODEL_FILES:
|
|
(path / filename).write_text("placeholder", encoding="utf-8")
|
|
for dirname in REQUIRED_MODEL_DIRS:
|
|
(path / dirname).mkdir()
|
|
for filename in AUX_MODEL_FILES:
|
|
target = path / filename
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
target.write_text("placeholder", encoding="utf-8")
|
|
for dirname in AUX_MODEL_DIRS:
|
|
target = path / dirname
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
(target / "config.json").write_text("placeholder", encoding="utf-8")
|
|
|
|
|
|
def fake_torch():
|
|
return SimpleNamespace(
|
|
cuda=SimpleNamespace(is_available=lambda: False, device_count=lambda: 0),
|
|
xpu=SimpleNamespace(is_available=lambda: False, device_count=lambda: 0),
|
|
backends=SimpleNamespace(mps=SimpleNamespace(is_available=lambda: False)),
|
|
)
|
|
|
|
|
|
def patched_imports():
|
|
real_import_module = importlib.import_module
|
|
|
|
def import_module(name, package=None):
|
|
if name == "torch":
|
|
return fake_torch()
|
|
if name in {"torchaudio", "indextts"}:
|
|
return SimpleNamespace(__name__=name)
|
|
return real_import_module(name, package)
|
|
|
|
return mock.patch("importlib.import_module", side_effect=import_module)
|
|
|
|
|
|
def user_state_paths(temp_path):
|
|
if sys.platform == "win32":
|
|
return {
|
|
"env": {
|
|
"APPDATA": str(temp_path / "roaming"),
|
|
"LOCALAPPDATA": str(temp_path / "local"),
|
|
},
|
|
"config_path": temp_path / "roaming" / "IndexTTS" / "config.toml",
|
|
"model_dir": temp_path / "local" / "IndexTTS" / "models" / "IndexTTS-2",
|
|
}
|
|
if sys.platform == "darwin":
|
|
app_support = temp_path / "Library" / "Application Support" / "IndexTTS"
|
|
return {
|
|
"env": {"HOME": str(temp_path)},
|
|
"config_path": app_support / "config.toml",
|
|
"model_dir": app_support / "models" / "IndexTTS-2",
|
|
}
|
|
return {
|
|
"env": {
|
|
"XDG_CONFIG_HOME": str(temp_path / "config"),
|
|
"XDG_DATA_HOME": str(temp_path / "data"),
|
|
},
|
|
"config_path": temp_path / "config" / "indextts" / "config.toml",
|
|
"model_dir": temp_path / "data" / "indextts" / "models" / "IndexTTS-2",
|
|
}
|
|
|
|
|
|
class ConfigCommandTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self._import_patch = _mock_optional_dependency_imports()
|
|
self._import_patch.start()
|
|
|
|
def tearDown(self):
|
|
self._import_patch.stop()
|
|
|
|
def run_cli(self, args, **kwargs):
|
|
from indextts.cli_v2 import main
|
|
|
|
stdout = io.StringIO()
|
|
stderr = io.StringIO()
|
|
with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
|
|
exit_code = main(args, **kwargs)
|
|
return exit_code, stdout.getvalue(), stderr.getvalue()
|
|
|
|
def test_init_creates_persistent_config_and_default_model_directory_without_model_files(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
state = user_state_paths(Path(temp_dir))
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
exit_code, stdout, stderr = self.run_cli(["init"])
|
|
|
|
config_text = state["config_path"].read_text(encoding="utf-8")
|
|
model_dir_files = list(state["model_dir"].iterdir())
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertIn(f"Config: {state['config_path']}", stdout)
|
|
self.assertIn(f"Model directory: {state['model_dir']}", stdout)
|
|
self.assertEqual(stderr, "")
|
|
self.assertIn(f'model_dir = "{state["model_dir"].as_posix()}"', config_text)
|
|
self.assertEqual(model_dir_files, [])
|
|
|
|
def test_init_with_model_dir_persists_the_requested_model_directory(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
model_dir = temp_path / "custom-models"
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
exit_code, stdout, stderr = self.run_cli(["init", "--model-dir", str(model_dir)])
|
|
|
|
config_text = state["config_path"].read_text(encoding="utf-8")
|
|
model_dir_exists = model_dir.is_dir()
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertIn(f"Model directory: {model_dir}", stdout)
|
|
self.assertEqual(stderr, "")
|
|
self.assertTrue(model_dir_exists)
|
|
self.assertIn(f'model_dir = "{model_dir.as_posix()}"', config_text)
|
|
|
|
def test_config_path_prints_the_persistent_config_file_location(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
state = user_state_paths(Path(temp_dir))
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
exit_code, stdout, stderr = self.run_cli(["config", "path"])
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(stdout, f"{state['config_path']}\n")
|
|
self.assertEqual(stderr, "")
|
|
|
|
def test_config_set_model_dir_persists_the_model_resource_directory(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
model_dir = temp_path / "persisted-models"
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
exit_code, stdout, stderr = self.run_cli(["config", "set", "model_dir", str(model_dir)])
|
|
|
|
config_text = state["config_path"].read_text(encoding="utf-8")
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(stdout, f"model_dir = {model_dir}\n")
|
|
self.assertEqual(stderr, "")
|
|
self.assertIn(f'model_dir = "{model_dir.as_posix()}"', config_text)
|
|
|
|
def test_config_set_runtime_preferences_persists_device_and_boolean_values(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
state = user_state_paths(Path(temp_dir))
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
first = self.run_cli(["config", "set", "default_device", "cuda:0"])
|
|
second = self.run_cli(["config", "set", "use_fp16", "true"])
|
|
third = self.run_cli(["config", "set", "use_deepspeed", "false"])
|
|
fourth = self.run_cli(["config", "set", "use_cuda_kernel", "true"])
|
|
fifth = self.run_cli(["config", "set", "use_accel", "true"])
|
|
sixth = self.run_cli(["config", "set", "use_torch_compile", "false"])
|
|
|
|
config_text = state["config_path"].read_text(encoding="utf-8")
|
|
|
|
self.assertEqual(first, (0, "default_device = cuda:0\n", ""))
|
|
self.assertEqual(second, (0, "use_fp16 = true\n", ""))
|
|
self.assertEqual(third, (0, "use_deepspeed = false\n", ""))
|
|
self.assertEqual(fourth, (0, "use_cuda_kernel = true\n", ""))
|
|
self.assertEqual(fifth, (0, "use_accel = true\n", ""))
|
|
self.assertEqual(sixth, (0, "use_torch_compile = false\n", ""))
|
|
self.assertIn('default_device = "cuda:0"', config_text)
|
|
self.assertIn("use_fp16 = true", config_text)
|
|
self.assertIn("use_deepspeed = false", config_text)
|
|
self.assertIn("use_cuda_kernel = true", config_text)
|
|
self.assertIn("use_accel = true", config_text)
|
|
self.assertIn("use_torch_compile = false", config_text)
|
|
|
|
def test_config_set_boolean_preference_rejects_non_boolean_values(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
state = user_state_paths(Path(temp_dir))
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
exit_code, stdout, stderr = self.run_cli(["config", "set", "use_fp16", "yes"])
|
|
config_exists = state["config_path"].exists()
|
|
|
|
self.assertEqual(exit_code, 1)
|
|
self.assertEqual(stdout, "")
|
|
self.assertIn("ERROR: use_fp16 must be true or false", stderr)
|
|
self.assertFalse(config_exists)
|
|
|
|
def test_config_get_prints_the_current_persistent_config(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
model_dir = temp_path / "models"
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
self.run_cli(["config", "set", "model_dir", str(model_dir)])
|
|
self.run_cli(["config", "set", "default_device", "cpu"])
|
|
exit_code, stdout, stderr = self.run_cli(["config", "get"])
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertIn(f'model_dir = "{model_dir.as_posix()}"', stdout)
|
|
self.assertIn('default_device = "cpu"', stdout)
|
|
self.assertEqual(stderr, "")
|
|
|
|
def test_check_uses_persisted_model_dir_when_command_line_and_environment_do_not_override_it(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
model_dir = temp_path / "persisted-models"
|
|
make_model_dir(model_dir)
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
self.run_cli(["config", "set", "model_dir", str(model_dir)])
|
|
with patched_imports():
|
|
exit_code, stdout, stderr = self.run_cli(["check"])
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertIn(f"OK: model directory {model_dir}", stdout)
|
|
self.assertEqual(stderr, "")
|
|
|
|
def test_check_model_dir_resolution_prioritizes_command_line_then_environment_then_config(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
cli_model_dir = temp_path / "cli-models"
|
|
env_model_dir = temp_path / "env-models"
|
|
persisted_model_dir = temp_path / "persisted-models"
|
|
for model_dir in [cli_model_dir, env_model_dir, persisted_model_dir]:
|
|
make_model_dir(model_dir)
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
self.run_cli(["config", "set", "model_dir", str(persisted_model_dir)])
|
|
with patched_imports():
|
|
cli_result = self.run_cli(
|
|
["check", "--model-dir", str(cli_model_dir)],
|
|
)
|
|
with mock.patch.dict(os.environ, {"INDEXTTS2_MODEL_DIR": str(env_model_dir)}, clear=False):
|
|
with patched_imports():
|
|
env_result = self.run_cli(["check"])
|
|
with patched_imports():
|
|
config_result = self.run_cli(["check"])
|
|
|
|
self.assertIn(f"OK: model directory {cli_model_dir}", cli_result[1])
|
|
self.assertIn(f"OK: model directory {env_model_dir}", env_result[1])
|
|
self.assertIn(f"OK: model directory {persisted_model_dir}", config_result[1])
|
|
|
|
def test_check_initializes_default_state_and_checks_the_platform_default_model_dir(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
state = user_state_paths(Path(temp_dir))
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
with mock.patch.dict(os.environ, {"INDEXTTS2_MODEL_DIR": ""}, clear=False):
|
|
exit_code, stdout, stderr = self.run_cli(["check"])
|
|
|
|
config_text = state["config_path"].read_text(encoding="utf-8")
|
|
model_dir_exists = state["model_dir"].is_dir()
|
|
|
|
self.assertEqual(exit_code, 2)
|
|
self.assertEqual(stdout, "")
|
|
self.assertIn("ERROR: missing required model files", stderr)
|
|
self.assertIn(state["model_dir"].as_posix(), config_text)
|
|
self.assertTrue(model_dir_exists)
|
|
|
|
def test_check_with_command_model_dir_still_initializes_default_state_without_persisting_override(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
cli_model_dir = temp_path / "cli-models"
|
|
make_model_dir(cli_model_dir)
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
with patched_imports():
|
|
exit_code, stdout, stderr = self.run_cli(["check", "--model-dir", str(cli_model_dir)])
|
|
|
|
config_text = state["config_path"].read_text(encoding="utf-8")
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertIn(f"OK: model directory {cli_model_dir}", stdout)
|
|
self.assertEqual(stderr, "")
|
|
self.assertIn(f'model_dir = "{state["model_dir"].as_posix()}"', config_text)
|
|
self.assertNotIn(cli_model_dir.as_posix(), config_text)
|
|
|
|
def test_synth_uses_persisted_model_dir_and_runtime_preferences_by_default(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
model_dir = temp_path / "models"
|
|
voice_path = temp_path / "voice.wav"
|
|
output_path = temp_path / "out.wav"
|
|
calls = []
|
|
make_model_dir(model_dir)
|
|
voice_path.write_bytes(b"voice")
|
|
|
|
class FakeIndexTTS2:
|
|
def __init__(self, **kwargs):
|
|
calls.append(("init", kwargs))
|
|
|
|
def infer(self, **kwargs):
|
|
calls.append(("infer", kwargs))
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
self.run_cli(["config", "set", "model_dir", str(model_dir)])
|
|
self.run_cli(["config", "set", "default_device", "cpu"])
|
|
self.run_cli(["config", "set", "use_fp16", "true"])
|
|
self.run_cli(["config", "set", "use_deepspeed", "true"])
|
|
self.run_cli(["config", "set", "use_cuda_kernel", "true"])
|
|
exit_code, stdout, stderr = self.run_cli(
|
|
[
|
|
"synth",
|
|
"--text",
|
|
"hello",
|
|
"--voice",
|
|
str(voice_path),
|
|
"--output",
|
|
str(output_path),
|
|
],
|
|
tts_factory=FakeIndexTTS2,
|
|
)
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(stdout, f"Generated: {output_path}\n")
|
|
self.assertEqual(stderr, "")
|
|
self.assertEqual(
|
|
calls[0][1],
|
|
{
|
|
"cfg_path": str(model_dir / "config.yaml"),
|
|
"model_dir": str(model_dir),
|
|
"use_fp16": True,
|
|
"device": "cpu",
|
|
"use_cuda_kernel": True,
|
|
"use_deepspeed": True,
|
|
"use_accel": False,
|
|
"use_torch_compile": False,
|
|
},
|
|
)
|
|
|
|
def test_batch_uses_persisted_model_dir_and_runtime_preferences_by_default(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
model_dir = temp_path / "models"
|
|
voice_path = temp_path / "voice.wav"
|
|
batch_file = temp_path / "batch.jsonl"
|
|
output_path = temp_path / "out.wav"
|
|
calls = []
|
|
make_model_dir(model_dir)
|
|
voice_path.write_bytes(b"voice")
|
|
batch_file.write_text('{"text": "hello", "voice": "voice.wav", "output": "out.wav"}\n', encoding="utf-8")
|
|
|
|
class FakeIndexTTS2:
|
|
def __init__(self, **kwargs):
|
|
calls.append(("init", kwargs))
|
|
|
|
def infer(self, **kwargs):
|
|
calls.append(("infer", kwargs))
|
|
Path(kwargs["output_path"]).write_bytes(b"audio")
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
self.run_cli(["config", "set", "model_dir", str(model_dir)])
|
|
self.run_cli(["config", "set", "default_device", "cpu"])
|
|
self.run_cli(["config", "set", "use_fp16", "true"])
|
|
self.run_cli(["config", "set", "use_deepspeed", "true"])
|
|
self.run_cli(["config", "set", "use_cuda_kernel", "true"])
|
|
exit_code, stdout, stderr = self.run_cli(
|
|
["batch", "--batch-file", str(batch_file)],
|
|
tts_factory=FakeIndexTTS2,
|
|
)
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(stdout, f"Generated: {output_path}\nBatch complete: 1 tasks generated\n")
|
|
self.assertEqual(stderr, "")
|
|
self.assertEqual(
|
|
calls[0][1],
|
|
{
|
|
"cfg_path": str(model_dir / "config.yaml"),
|
|
"model_dir": str(model_dir),
|
|
"use_fp16": True,
|
|
"device": "cpu",
|
|
"use_cuda_kernel": True,
|
|
"use_deepspeed": True,
|
|
"use_accel": False,
|
|
"use_torch_compile": False,
|
|
},
|
|
)
|
|
|
|
def test_batch_command_line_can_disable_persisted_boolean_runtime_preferences_for_one_run(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
model_dir = temp_path / "models"
|
|
voice_path = temp_path / "voice.wav"
|
|
batch_file = temp_path / "batch.jsonl"
|
|
calls = []
|
|
make_model_dir(model_dir)
|
|
voice_path.write_bytes(b"voice")
|
|
batch_file.write_text('{"text": "hello", "voice": "voice.wav", "output": "out.wav"}\n', encoding="utf-8")
|
|
|
|
class FakeIndexTTS2:
|
|
def __init__(self, **kwargs):
|
|
calls.append(("init", kwargs))
|
|
|
|
def infer(self, **kwargs):
|
|
calls.append(("infer", kwargs))
|
|
Path(kwargs["output_path"]).write_bytes(b"audio")
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
self.run_cli(["config", "set", "model_dir", str(model_dir)])
|
|
self.run_cli(["config", "set", "use_fp16", "true"])
|
|
self.run_cli(["config", "set", "use_deepspeed", "true"])
|
|
self.run_cli(["config", "set", "use_cuda_kernel", "true"])
|
|
before_config = state["config_path"].read_text(encoding="utf-8")
|
|
exit_code, stdout, stderr = self.run_cli(
|
|
[
|
|
"batch",
|
|
"--batch-file",
|
|
str(batch_file),
|
|
"--no-fp16",
|
|
"--no-deepspeed",
|
|
"--no-cuda-kernel",
|
|
"--no-accel",
|
|
"--no-torch-compile",
|
|
],
|
|
tts_factory=FakeIndexTTS2,
|
|
)
|
|
after_config = state["config_path"].read_text(encoding="utf-8")
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertIn("Batch complete: 1 tasks generated", stdout)
|
|
self.assertEqual(stderr, "")
|
|
self.assertEqual(before_config, after_config)
|
|
self.assertEqual(calls[0][1]["use_fp16"], False)
|
|
self.assertEqual(calls[0][1]["use_deepspeed"], False)
|
|
self.assertEqual(calls[0][1]["use_cuda_kernel"], False)
|
|
self.assertEqual(calls[0][1]["use_accel"], False)
|
|
self.assertEqual(calls[0][1]["use_torch_compile"], False)
|
|
|
|
def test_synth_command_line_overrides_do_not_rewrite_persistent_config(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
persisted_model_dir = temp_path / "persisted-models"
|
|
cli_model_dir = temp_path / "cli-models"
|
|
voice_path = temp_path / "voice.wav"
|
|
output_path = temp_path / "out.wav"
|
|
calls = []
|
|
make_model_dir(persisted_model_dir)
|
|
make_model_dir(cli_model_dir)
|
|
voice_path.write_bytes(b"voice")
|
|
|
|
class FakeIndexTTS2:
|
|
def __init__(self, **kwargs):
|
|
calls.append(("init", kwargs))
|
|
|
|
def infer(self, **kwargs):
|
|
calls.append(("infer", kwargs))
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
self.run_cli(["config", "set", "model_dir", str(persisted_model_dir)])
|
|
self.run_cli(["config", "set", "default_device", "cpu"])
|
|
self.run_cli(["config", "set", "use_fp16", "false"])
|
|
before_config = state["config_path"].read_text(encoding="utf-8")
|
|
exit_code, stdout, stderr = self.run_cli(
|
|
[
|
|
"synth",
|
|
"--text",
|
|
"hello",
|
|
"--voice",
|
|
str(voice_path),
|
|
"--output",
|
|
str(output_path),
|
|
"--model-dir",
|
|
str(cli_model_dir),
|
|
"--device",
|
|
"cuda:0",
|
|
"--fp16",
|
|
"--deepspeed",
|
|
"--cuda-kernel",
|
|
"--accel",
|
|
"--torch-compile",
|
|
],
|
|
tts_factory=FakeIndexTTS2,
|
|
)
|
|
after_config = state["config_path"].read_text(encoding="utf-8")
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(stdout, f"Generated: {output_path}\n")
|
|
self.assertEqual(stderr, "")
|
|
self.assertEqual(before_config, after_config)
|
|
self.assertEqual(calls[0][1]["model_dir"], str(cli_model_dir))
|
|
self.assertEqual(calls[0][1]["device"], "cuda:0")
|
|
self.assertEqual(calls[0][1]["use_fp16"], True)
|
|
self.assertEqual(calls[0][1]["use_deepspeed"], True)
|
|
self.assertEqual(calls[0][1]["use_cuda_kernel"], True)
|
|
self.assertEqual(calls[0][1]["use_accel"], True)
|
|
self.assertEqual(calls[0][1]["use_torch_compile"], True)
|
|
|
|
def test_synth_command_line_can_disable_persisted_boolean_runtime_preferences_for_one_run(self):
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
temp_path = Path(temp_dir)
|
|
state = user_state_paths(temp_path)
|
|
model_dir = temp_path / "models"
|
|
voice_path = temp_path / "voice.wav"
|
|
output_path = temp_path / "out.wav"
|
|
calls = []
|
|
make_model_dir(model_dir)
|
|
voice_path.write_bytes(b"voice")
|
|
|
|
class FakeIndexTTS2:
|
|
def __init__(self, **kwargs):
|
|
calls.append(("init", kwargs))
|
|
|
|
def infer(self, **kwargs):
|
|
calls.append(("infer", kwargs))
|
|
|
|
with mock.patch.dict(os.environ, state["env"], clear=False):
|
|
self.run_cli(["config", "set", "model_dir", str(model_dir)])
|
|
self.run_cli(["config", "set", "use_fp16", "true"])
|
|
self.run_cli(["config", "set", "use_deepspeed", "true"])
|
|
self.run_cli(["config", "set", "use_cuda_kernel", "true"])
|
|
before_config = state["config_path"].read_text(encoding="utf-8")
|
|
exit_code, stdout, stderr = self.run_cli(
|
|
[
|
|
"synth",
|
|
"--text",
|
|
"hello",
|
|
"--voice",
|
|
str(voice_path),
|
|
"--output",
|
|
str(output_path),
|
|
"--no-fp16",
|
|
"--no-deepspeed",
|
|
"--no-cuda-kernel",
|
|
"--no-accel",
|
|
"--no-torch-compile",
|
|
],
|
|
tts_factory=FakeIndexTTS2,
|
|
)
|
|
after_config = state["config_path"].read_text(encoding="utf-8")
|
|
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(stdout, f"Generated: {output_path}\n")
|
|
self.assertEqual(stderr, "")
|
|
self.assertEqual(before_config, after_config)
|
|
self.assertEqual(calls[0][1]["use_fp16"], False)
|
|
self.assertEqual(calls[0][1]["use_deepspeed"], False)
|
|
self.assertEqual(calls[0][1]["use_cuda_kernel"], False)
|
|
self.assertEqual(calls[0][1]["use_accel"], False)
|
|
self.assertEqual(calls[0][1]["use_torch_compile"], False)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|