Coverage for wrapper/xtts/wrapper_xtts.py: 75%
84 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
1import logging
3import torch
4from torch import inference_mode
6from typing_extensions import override # Python 3.11
7from typing import Optional
8from typing import Dict
9from typing import Any
10from typing import Union
12from wrapper_model import ModelGeneration
14import numpy as np
16from TTS.tts.configs.xtts_config import XttsConfig
17from TTS.tts.models.xtts import Xtts
20class XTTSGeneration(ModelGeneration):
21 def __init__(self) -> None:
22 super().__init__("xtts")
24 # Model components
25 self.xtts_config: Optional[XttsConfig] = None
26 self.xtts: Optional[Xtts] = None
28 def __del__(self) -> None:
29 if self.xtts is not None:
30 self.xtts = None
32 def init_parallelism(self) -> None:
33 self.load_timer.start("torch_dist")
34 # No real parallelism as it runs with a single GPU or CPU
35 if torch.cuda.is_available():
36 self.rank = 0
37 self.local_rank = 0
38 self.world_size = 1
39 self.device_id: Union[int, str] = self.local_rank
40 self.device = torch.device(f"cuda:{self.device_id}")
41 torch.cuda.set_device(self.local_rank)
42 else:
43 self.device_id = "cpu"
44 self.device = torch.device(self.device_id)
45 self.load_timer.end("torch_dist")
47 def load_model(self) -> None:
48 self.load_timer.start("xtts")
49 self.xtts_config = XttsConfig()
50 # TODO fix this with the DockerFile
51 self.xtts_config.load_json("coqui/XTTS-v2/config.json")
52 self.xtts = Xtts.init_from_config(self.xtts_config)
53 self.xtts.load_checkpoint(
54 self.xtts_config,
55 checkpoint_dir="coqui/XTTS-v2",
56 eval=True)
57 self.xtts.cuda()
58 self.load_timer.end("xtts")
60 def init_model_parallelism(self) -> None:
61 if self.world_size > 1:
62 logging.warning("XTTS does not support distributed parallelism.")
64 def model_compile(self) -> None:
65 if not self.torch_compile:
66 return
67 self.load_timer.start("compile")
68 self.xtts = torch.compile(
69 self.xtts,
70 mode="reduce-overhead")
71 self.load_timer.end("compile")
73 async def get_rest_args(
74 self,
75 data_json: Dict[str, Union[str, int, float]]
76 ) -> Dict[str, Any]:
77 if data_json is None:
78 raise ValueError("Missing JSON body")
79 text = data_json.get("text", None)
80 if text is None:
81 raise ValueError("Missing 'text' parameter")
82 return {
83 "task": self.model_name,
84 "args": {
85 "text": text,
86 }
87 }
89 @inference_mode()
90 async def warmup(self) -> None:
91 logging.info("Warmup for XTTS generation")
92 await self.generate(text="Warmup")
94 def _assert_model_init(self) -> None:
95 super()._assert_model_init()
96 if not self.xtts:
97 raise ValueError("XTTS not loaded.")
99 @override
100 @inference_mode()
101 async def generate(
102 self,
103 text: str,
104 job_id: Optional[str] = None,
105 ) -> np.ndarray:
106 gen_timer = self._new_gen_timer(job_id)
108 self._assert_model_init()
110 self.running = True # We can run in parallel but good to know if we are running
112 try:
113 # Clean up text
114 text = text.replace("*", "")
116 if self.xtts is None:
117 raise RuntimeError("Model not loaded")
118 outputs = self.xtts.synthesize(
119 text,
120 self.xtts_config,
121 speaker_wav="tests/data/ljspeech/wavs/LJ001-0001.wav",
122 gpt_cond_len=3,
123 language="en",
124 )
125 wav_np = outputs["wav"]
126 return wav_np
127 finally:
128 self.running = False
129 gen_timer.end("total")
131 def get_health(self) -> Dict[str, Any]:
132 ret = super().get_health()
133 if torch.cuda.is_available():
134 ret["gpu"] = torch.cuda.get_device_name(0)
135 return ret