lollms-client 1.5.6__py3-none-any.whl → 1.7.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. lollms_client/__init__.py +1 -1
  2. lollms_client/llm_bindings/azure_openai/__init__.py +2 -2
  3. lollms_client/llm_bindings/claude/__init__.py +125 -35
  4. lollms_client/llm_bindings/gemini/__init__.py +261 -159
  5. lollms_client/llm_bindings/grok/__init__.py +52 -15
  6. lollms_client/llm_bindings/groq/__init__.py +2 -2
  7. lollms_client/llm_bindings/hugging_face_inference_api/__init__.py +2 -2
  8. lollms_client/llm_bindings/litellm/__init__.py +1 -1
  9. lollms_client/llm_bindings/llama_cpp_server/__init__.py +605 -0
  10. lollms_client/llm_bindings/llamacpp/__init__.py +18 -11
  11. lollms_client/llm_bindings/lollms/__init__.py +76 -21
  12. lollms_client/llm_bindings/lollms_webui/__init__.py +1 -1
  13. lollms_client/llm_bindings/mistral/__init__.py +2 -2
  14. lollms_client/llm_bindings/novita_ai/__init__.py +142 -6
  15. lollms_client/llm_bindings/ollama/__init__.py +345 -89
  16. lollms_client/llm_bindings/open_router/__init__.py +2 -2
  17. lollms_client/llm_bindings/openai/__init__.py +81 -20
  18. lollms_client/llm_bindings/openllm/__init__.py +362 -506
  19. lollms_client/llm_bindings/openwebui/__init__.py +333 -171
  20. lollms_client/llm_bindings/perplexity/__init__.py +2 -2
  21. lollms_client/llm_bindings/pythonllamacpp/__init__.py +3 -3
  22. lollms_client/llm_bindings/tensor_rt/__init__.py +1 -1
  23. lollms_client/llm_bindings/transformers/__init__.py +428 -632
  24. lollms_client/llm_bindings/vllm/__init__.py +1 -1
  25. lollms_client/lollms_agentic.py +4 -2
  26. lollms_client/lollms_base_binding.py +61 -0
  27. lollms_client/lollms_core.py +512 -1890
  28. lollms_client/lollms_discussion.py +65 -39
  29. lollms_client/lollms_llm_binding.py +126 -261
  30. lollms_client/lollms_mcp_binding.py +49 -77
  31. lollms_client/lollms_stt_binding.py +99 -52
  32. lollms_client/lollms_tti_binding.py +38 -38
  33. lollms_client/lollms_ttm_binding.py +38 -42
  34. lollms_client/lollms_tts_binding.py +43 -18
  35. lollms_client/lollms_ttv_binding.py +38 -42
  36. lollms_client/lollms_types.py +4 -2
  37. lollms_client/stt_bindings/whisper/__init__.py +108 -23
  38. lollms_client/stt_bindings/whispercpp/__init__.py +7 -1
  39. lollms_client/tti_bindings/diffusers/__init__.py +464 -803
  40. lollms_client/tti_bindings/diffusers/server/main.py +1062 -0
  41. lollms_client/tti_bindings/gemini/__init__.py +182 -239
  42. lollms_client/tti_bindings/leonardo_ai/__init__.py +6 -3
  43. lollms_client/tti_bindings/lollms/__init__.py +4 -1
  44. lollms_client/tti_bindings/novita_ai/__init__.py +5 -2
  45. lollms_client/tti_bindings/openai/__init__.py +10 -11
  46. lollms_client/tti_bindings/stability_ai/__init__.py +5 -3
  47. lollms_client/ttm_bindings/audiocraft/__init__.py +7 -12
  48. lollms_client/ttm_bindings/beatoven_ai/__init__.py +7 -3
  49. lollms_client/ttm_bindings/lollms/__init__.py +4 -17
  50. lollms_client/ttm_bindings/replicate/__init__.py +7 -4
  51. lollms_client/ttm_bindings/stability_ai/__init__.py +7 -4
  52. lollms_client/ttm_bindings/topmediai/__init__.py +6 -3
  53. lollms_client/tts_bindings/bark/__init__.py +7 -10
  54. lollms_client/tts_bindings/lollms/__init__.py +6 -1
  55. lollms_client/tts_bindings/piper_tts/__init__.py +8 -11
  56. lollms_client/tts_bindings/xtts/__init__.py +157 -74
  57. lollms_client/tts_bindings/xtts/server/main.py +241 -280
  58. {lollms_client-1.5.6.dist-info → lollms_client-1.7.13.dist-info}/METADATA +113 -5
  59. lollms_client-1.7.13.dist-info/RECORD +90 -0
  60. lollms_client-1.5.6.dist-info/RECORD +0 -87
  61. {lollms_client-1.5.6.dist-info → lollms_client-1.7.13.dist-info}/WHEEL +0 -0
  62. {lollms_client-1.5.6.dist-info → lollms_client-1.7.13.dist-info}/licenses/LICENSE +0 -0
  63. {lollms_client-1.5.6.dist-info → lollms_client-1.7.13.dist-info}/top_level.txt +0 -0
@@ -1,314 +1,275 @@
1
- import uvicorn
2
- from fastapi import FastAPI, APIRouter, HTTPException
3
- from pydantic import BaseModel
4
- import argparse
5
- import sys
6
- from pathlib import Path
7
- import asyncio
8
- import traceback
9
- import os
10
- from typing import Optional, List
11
- import io
12
- import wave
13
- import numpy as np
14
- import tempfile
15
-
16
- # --- XTTS Implementation ---
17
1
  try:
18
- print("Server: Loading XTTS dependencies...")
19
- import torch
20
- import torchaudio
21
- from TTS.api import TTS
22
- print("Server: XTTS dependencies loaded successfully")
23
-
24
- # Check for CUDA availability
25
- device = "cuda" if torch.cuda.is_available() else "cpu"
26
- print(f"Server: Using device: {device}")
27
-
28
- xtts_available = True
2
+ import uvicorn
3
+ from fastapi import FastAPI, APIRouter, HTTPException
4
+ from pydantic import BaseModel
5
+ import argparse
6
+ import sys
7
+ from pathlib import Path
8
+ import asyncio
9
+ import traceback
10
+ import os
11
+ from typing import Optional, List
12
+ import io
13
+ import wave
14
+ import numpy as np
15
+ import tempfile
29
16
 
30
- except Exception as e:
31
- print(f"Server: Failed to load XTTS dependencies: {e}")
32
- print(f"Server: Traceback:\n{traceback.format_exc()}")
33
- xtts_available = False
34
-
35
- # --- API Models ---
36
- class GenerationRequest(BaseModel):
37
- text: str
38
- voice: Optional[str] = None
39
- language: Optional[str] = "en"
40
- speaker_wav: Optional[str] = None
17
+ # Use ascii_colors for logging
18
+ from ascii_colors import ASCIIColors
41
19
 
42
- class XTTSServer:
43
- def __init__(self):
44
- self.model = None
45
- self.model_loaded = False
46
- self.model_loading = False # Flag to prevent concurrent loading
47
- self.available_voices = self._load_available_voices()
48
- self.available_models = ["xtts_v2"]
20
+ # --- XTTS Implementation ---
21
+ try:
22
+ ASCIIColors.info("Server: Loading XTTS dependencies...")
23
+ import torch
24
+ from TTS.api import TTS
25
+ ASCIIColors.green("Server: XTTS dependencies loaded successfully")
49
26
 
50
- # Don't initialize model here - do it lazily on first request
51
- print("Server: XTTS server initialized (model will be loaded on first request)")
52
-
53
- async def _ensure_model_loaded(self):
54
- """Ensure the XTTS model is loaded (lazy loading)"""
55
- if self.model_loaded:
56
- return
57
-
58
- if self.model_loading:
59
- # Another request is already loading the model, wait for it
60
- while self.model_loading and not self.model_loaded:
61
- await asyncio.sleep(0.1)
62
- return
63
-
64
- if not xtts_available:
65
- raise RuntimeError("XTTS library not available")
66
-
67
- try:
68
- self.model_loading = True
69
- print("Server: Loading XTTS model for the first time (this may take a few minutes)...")
70
-
71
- # Initialize XTTS model
72
- self.model = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device)
73
-
74
- self.model_loaded = True
75
- print("Server: XTTS model loaded successfully")
76
-
77
- except Exception as e:
78
- print(f"Server: Error loading XTTS model: {e}")
79
- print(f"Server: Traceback:\n{traceback.format_exc()}")
27
+ # Check for CUDA availability
28
+ device = "cuda" if torch.cuda.is_available() else "cpu"
29
+ ASCIIColors.info(f"Server: Using device: {device}")
30
+
31
+ xtts_available = True
32
+
33
+ except Exception as e:
34
+ ASCIIColors.error(f"Server: Failed to load XTTS dependencies: {e}")
35
+ ASCIIColors.error(f"Server: Traceback:\n{traceback.format_exc()}")
36
+ xtts_available = False
37
+
38
+ # --- API Models ---
39
+ class GenerationRequest(BaseModel):
40
+ text: str
41
+ voice: Optional[str] = None
42
+ language: Optional[str] = "en"
43
+ # speaker_wav is kept for backward compatibility but voice is preferred
44
+ speaker_wav: Optional[str] = None
45
+ split_sentences: Optional[bool] = True
46
+
47
+ class XTTSServer:
48
+ def __init__(self):
49
+ self.model = None
80
50
  self.model_loaded = False
81
- raise
82
- finally:
83
- self.model_loading = False
84
-
85
- def _load_available_voices(self) -> List[str]:
86
- """Load and return available voices"""
87
- try:
88
- # Look for voice files in voices directory
89
- voices_dir = Path(__file__).parent / "voices"
90
- voices = []
51
+ self.model_loading = False # Flag to prevent concurrent loading
52
+ self.available_models = ["tts_models/multilingual/multi-dataset/xtts_v2"]
53
+ self.voices_dir = Path(__file__).parent / "voices"
54
+ self.voices_dir.mkdir(exist_ok=True)
55
+ self.available_voices = self._load_available_voices()
91
56
 
92
- if voices_dir.exists():
93
- # Look for WAV files in voices directory
94
- for voice_file in voices_dir.glob("*.wav"):
95
- voices.append(voice_file.stem)
96
-
97
- # If no custom voices found, provide some default names
98
- if not voices:
99
- voices = ["default", "female", "male"]
57
+ ASCIIColors.info("Server: XTTS server initialized (model will be loaded on first request)")
58
+
59
+ async def _ensure_model_loaded(self):
60
+ """Ensure the XTTS model is loaded (lazy loading)"""
61
+ if self.model_loaded:
62
+ return
100
63
 
101
- return voices
102
-
103
- except Exception as e:
104
- print(f"Server: Error loading voices: {e}")
105
- return ["default"]
106
-
107
- async def generate_audio(self, text: str, voice: Optional[str] = None,
108
- language: str = "en", speaker_wav: Optional[str] = None) -> bytes:
109
- """Generate audio from text using XTTS"""
110
- # Ensure model is loaded before proceeding
111
- await self._ensure_model_loaded()
64
+ if self.model_loading:
65
+ # Another request is already loading the model, wait for it
66
+ while self.model_loading and not self.model_loaded:
67
+ await asyncio.sleep(0.1)
68
+ return
69
+
70
+ if not xtts_available:
71
+ raise RuntimeError("XTTS library not available. Please ensure all dependencies are installed correctly in the venv.")
72
+
73
+ try:
74
+ self.model_loading = True
75
+ ASCIIColors.yellow("Server: Loading XTTS model for the first time (this may take a few minutes)...")
76
+
77
+ # Initialize XTTS model
78
+ self.model = TTS(self.available_models[0]).to(device)
79
+
80
+ self.model_loaded = True
81
+ ASCIIColors.green("Server: XTTS model loaded successfully")
82
+
83
+ except Exception as e:
84
+ ASCIIColors.error(f"Server: Error loading XTTS model: {e}")
85
+ ASCIIColors.error(f"Server: Traceback:\n{traceback.format_exc()}")
86
+ self.model_loaded = False
87
+ raise
88
+ finally:
89
+ self.model_loading = False
112
90
 
113
- if not self.model_loaded or self.model is None:
114
- raise RuntimeError("XTTS model failed to load")
91
+ def _load_available_voices(self) -> List[str]:
92
+ """Load and return available voices, ensuring 'default_voice' is always present."""
93
+ try:
94
+ self.voices_dir.mkdir(exist_ok=True)
95
+
96
+ # Scan for case-insensitive .wav and .mp3 files and get their stems
97
+ found_voices = {p.stem for p in self.voices_dir.glob("*.[wW][aA][vV]")}
98
+ found_voices.update({p.stem for p in self.voices_dir.glob("*.[mM][pP]3")})
99
+
100
+ # GUARANTEE 'default_voice' is in the list for UI consistency.
101
+ all_voices = {"default_voice"}.union(found_voices)
102
+
103
+ sorted_voices = sorted(list(all_voices))
104
+ ASCIIColors.info(f"Discovered voices: {sorted_voices}")
105
+ return sorted_voices
106
+
107
+ except Exception as e:
108
+ ASCIIColors.error(f"Server: Error scanning voices directory: {e}")
109
+ # If scanning fails, it's crucial to still return the default.
110
+ return ["default_voice"]
115
111
 
116
- try:
117
- print(f"Server: Generating audio for: '{text[:50]}{'...' if len(text) > 50 else ''}'")
118
- print(f"Server: Using voice: {voice}, language: {language}")
112
+ def _get_speaker_wav_path(self, voice_name: str) -> Optional[str]:
113
+ """Find the path to a speaker wav/mp3 file from its name."""
114
+ if not voice_name:
115
+ return None
119
116
 
120
- # Handle voice/speaker selection
121
- speaker_wav_path = None
117
+ # Case 1: voice_name is an absolute path that exists
118
+ if os.path.isabs(voice_name) and os.path.exists(voice_name):
119
+ return voice_name
122
120
 
123
- # First priority: use provided speaker_wav parameter
124
- if speaker_wav:
125
- speaker_wav_path = speaker_wav
126
- print(f"Server: Using provided speaker_wav: {speaker_wav_path}")
121
+ # Case 2: voice_name is a name in the voices directory (check for .mp3 then .wav)
122
+ mp3_path = self.voices_dir / f"{voice_name}.mp3"
123
+ if mp3_path.exists():
124
+ return str(mp3_path)
125
+
126
+ wav_path = self.voices_dir / f"{voice_name}.wav"
127
+ if wav_path.exists():
128
+ return str(wav_path)
127
129
 
128
- # Second priority: check if voice parameter is a file path
129
- elif voice and voice != "default":
130
- if os.path.exists(voice):
131
- # Voice parameter is a full file path
132
- speaker_wav_path = voice
133
- print(f"Server: Using voice as file path: {speaker_wav_path}")
134
- else:
135
- # Look for voice file in voices directory
136
- voices_dir = Path(__file__).parent / "voices"
137
- potential_voice_path = voices_dir / f"{voice}.wav"
138
- if potential_voice_path.exists():
139
- speaker_wav_path = str(potential_voice_path)
140
- print(f"Server: Using custom voice file: {speaker_wav_path}")
141
- else:
142
- print(f"Server: Voice '{voice}' not found in voices directory")
130
+ return None
131
+
132
+ async def generate_audio(self, req: GenerationRequest) -> bytes:
133
+ """Generate audio from text using XTTS"""
134
+ await self._ensure_model_loaded()
143
135
 
144
- # Create a temporary file for output
145
- with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_file:
146
- temp_output_path = temp_file.name
136
+ if not self.model_loaded or self.model is None:
137
+ raise RuntimeError("XTTS model failed to load or is not available.")
147
138
 
148
139
  try:
149
- # Generate audio using XTTS
150
- if speaker_wav_path and os.path.exists(speaker_wav_path):
151
- print(f"Server: Generating with speaker reference: {speaker_wav_path}")
152
- self.model.tts_to_file(
153
- text=text,
154
- speaker_wav=speaker_wav_path,
155
- language=language,
156
- file_path=temp_output_path
140
+ text_to_generate = req.text
141
+ ASCIIColors.info(f"Server: Generating audio for: '{text_to_generate[:50]}{'...' if len(text_to_generate) > 50 else ''}'")
142
+ ASCIIColors.info(f"Server: Language: {req.language}, Requested Voice: {req.voice}")
143
+
144
+ # Determine which voice name to use. Priority: speaker_wav > voice > 'default_voice'
145
+ voice_to_find = req.speaker_wav or req.voice or "default_voice"
146
+ speaker_wav_path = self._get_speaker_wav_path(voice_to_find)
147
+
148
+ # If the chosen voice wasn't found and it wasn't the default, try the default as a fallback.
149
+ if not speaker_wav_path and voice_to_find != "default_voice":
150
+ ASCIIColors.warning(f"Voice '{voice_to_find}' not found. Falling back to 'default_voice'.")
151
+ speaker_wav_path = self._get_speaker_wav_path("default_voice")
152
+
153
+ # If still no path, it's a critical error because even the default is missing.
154
+ if not speaker_wav_path:
155
+ available = self._get_all_available_voice_files()
156
+ raise RuntimeError(
157
+ f"XTTS requires a speaker reference file, but none could be found.\n"
158
+ f"Attempted to use '{voice_to_find}' but it was not found, and the fallback 'default_voice.mp3' is also missing from the voices folder.\n"
159
+ f"Please add audio files to the '{self.voices_dir.resolve()}' directory. Available files: {available or 'None'}"
157
160
  )
158
- else:
159
- print("Server: No valid speaker reference found, trying default")
160
- # For XTTS without speaker reference, try to find a default
161
- default_speaker = self._get_default_speaker_file()
162
- if default_speaker and os.path.exists(default_speaker):
163
- print(f"Server: Using default speaker: {default_speaker}")
164
- self.model.tts_to_file(
165
- text=text,
166
- speaker_wav=default_speaker,
167
- language=language,
168
- file_path=temp_output_path
169
- )
170
- else:
171
- # Create a more helpful error message
172
- available_voices = self._get_all_available_voice_files()
173
- error_msg = f"No speaker reference available. XTTS requires a speaker reference file.\n"
174
- error_msg += f"Attempted to use: {speaker_wav_path if speaker_wav_path else 'None'}\n"
175
- error_msg += f"Available voice files: {available_voices}"
176
- raise RuntimeError(error_msg)
177
161
 
178
- # Read the generated audio file
179
- with open(temp_output_path, 'rb') as f:
180
- audio_bytes = f.read()
162
+ ASCIIColors.info(f"Server: Using speaker reference: {speaker_wav_path}")
163
+
164
+ # Generate audio using XTTS
165
+ wav_chunks = self.model.tts(
166
+ text=text_to_generate,
167
+ speaker_wav=speaker_wav_path,
168
+ language=req.language,
169
+ split_sentences=req.split_sentences
170
+ )
171
+
172
+ # Combine chunks into a single audio stream
173
+ audio_data = np.array(wav_chunks, dtype=np.float32)
181
174
 
182
- print(f"Server: Generated {len(audio_bytes)} bytes of audio")
175
+ buffer = io.BytesIO()
176
+ with wave.open(buffer, 'wb') as wf:
177
+ wf.setnchannels(1)
178
+ wf.setsampwidth(2) # 16-bit
179
+ wf.setframerate(self.model.synthesizer.output_sample_rate)
180
+ wf.writeframes((audio_data * 32767).astype(np.int16).tobytes())
181
+
182
+ audio_bytes = buffer.getvalue()
183
+
184
+ ASCIIColors.green(f"Server: Generated {len(audio_bytes)} bytes of audio.")
183
185
  return audio_bytes
184
186
 
185
- finally:
186
- # Clean up temporary file
187
- if os.path.exists(temp_output_path):
188
- os.unlink(temp_output_path)
189
-
190
- except Exception as e:
191
- print(f"Server: Error generating audio: {e}")
192
- print(f"Server: Traceback:\n{traceback.format_exc()}")
193
- raise
194
-
195
- def _get_all_available_voice_files(self) -> List[str]:
196
- """Get list of all available voice files for debugging"""
197
- voices_dir = Path(__file__).parent / "voices"
198
- voice_files = []
187
+ except Exception as e:
188
+ ASCIIColors.error(f"Server: Error generating audio: {e}")
189
+ ASCIIColors.error(f"Server: Traceback:\n{traceback.format_exc()}")
190
+ raise
199
191
 
200
- if voices_dir.exists():
201
- voice_files = [str(f) for f in voices_dir.glob("*.wav")]
202
-
203
- return voice_files
204
-
205
- def _get_default_speaker_file(self) -> Optional[str]:
206
- """Get path to default speaker file"""
207
- voices_dir = Path(__file__).parent / "voices"
192
+ def _get_all_available_voice_files(self) -> List[str]:
193
+ """Get list of all available voice files for debugging"""
194
+ return [f.name for f in self.voices_dir.glob("*.*")]
208
195
 
209
- # Look for a default speaker file
210
- for filename in ["default.wav", "speaker.wav", "reference.wav"]:
211
- potential_path = voices_dir / filename
212
- if potential_path.exists():
213
- return str(potential_path)
196
+ def list_voices(self) -> List[str]:
197
+ """Return list of available voices"""
198
+ return self.available_voices
214
199
 
215
- # If no default found, look for any wav file
216
- wav_files = list(voices_dir.glob("*.wav"))
217
- if wav_files:
218
- return str(wav_files[0])
219
-
220
- return None
221
-
222
- def list_voices(self) -> List[str]:
223
- """Return list of available voices"""
224
- return self.available_voices
225
-
226
- def list_models(self) -> List[str]:
227
- """Return list of available models"""
228
- return self.available_models
200
+ def list_models(self) -> List[str]:
201
+ """Return list of available models"""
202
+ return self.available_models
203
+
204
+ # --- Globals ---
205
+ app = FastAPI(title="XTTS Server")
206
+ router = APIRouter()
207
+ xtts_server = XTTSServer()
208
+ model_lock = asyncio.Lock() # Ensure only one generation happens at a time on the model
229
209
 
230
- # --- Globals ---
231
- app = FastAPI(title="XTTS Server")
232
- router = APIRouter()
233
- xtts_server = XTTSServer()
234
- model_lock = asyncio.Lock() # Ensure thread-safe access
210
+ # --- API Endpoints ---
211
+ @router.post("/generate_audio")
212
+ async def api_generate_audio(request: GenerationRequest):
213
+ async with model_lock:
214
+ try:
215
+ from fastapi.responses import Response
216
+ audio_bytes = await xtts_server.generate_audio(request)
217
+ return Response(content=audio_bytes, media_type="audio/wav")
218
+ except Exception as e:
219
+ ASCIIColors.error(f"Server: ERROR in generate_audio endpoint: {e}")
220
+ raise HTTPException(status_code=500, detail=str(e))
235
221
 
236
- # --- API Endpoints ---
237
- @router.post("/generate_audio")
238
- async def generate_audio(request: GenerationRequest):
239
- async with model_lock:
222
+ @router.get("/list_voices")
223
+ async def api_list_voices():
240
224
  try:
241
- audio_bytes = await xtts_server.generate_audio(
242
- text=request.text,
243
- voice=request.voice,
244
- language=request.language,
245
- speaker_wav=request.speaker_wav
246
- )
247
- from fastapi.responses import Response
248
- return Response(content=audio_bytes, media_type="audio/wav")
225
+ voices = xtts_server.list_voices()
226
+ return {"voices": voices}
249
227
  except Exception as e:
250
- print(f"Server: ERROR in generate_audio endpoint: {e}")
251
- print(f"Server: ERROR traceback:\n{traceback.format_exc()}")
228
+ ASCIIColors.error(f"Server: ERROR in list_voices endpoint: {e}")
252
229
  raise HTTPException(status_code=500, detail=str(e))
253
230
 
254
- @router.get("/list_voices")
255
- async def list_voices():
256
- try:
257
- voices = xtts_server.list_voices()
258
- print(f"Server: Returning {len(voices)} voices: {voices}")
259
- return {"voices": voices}
260
- except Exception as e:
261
- print(f"Server: ERROR in list_voices endpoint: {e}")
262
- print(f"Server: ERROR traceback:\n{traceback.format_exc()}")
263
- raise HTTPException(status_code=500, detail=str(e))
264
-
265
- @router.get("/list_models")
266
- async def list_models():
267
- try:
268
- models = xtts_server.list_models()
269
- print(f"Server: Returning {len(models)} models: {models}")
270
- return {"models": models}
271
- except Exception as e:
272
- print(f"Server: ERROR in list_models endpoint: {e}")
273
- print(f"Server: ERROR traceback:\n{traceback.format_exc()}")
274
- raise HTTPException(status_code=500, detail=str(e))
231
+ @router.get("/list_models")
232
+ async def api_list_models():
233
+ try:
234
+ models = xtts_server.list_models()
235
+ return {"models": models}
236
+ except Exception as e:
237
+ ASCIIColors.error(f"Server: ERROR in list_models endpoint: {e}")
238
+ raise HTTPException(status_code=500, detail=str(e))
275
239
 
276
- @router.get("/status")
277
- async def status():
278
- return {
279
- "status": "running",
280
- "xtts_available": xtts_available,
281
- "model_loaded": xtts_server.model_loaded,
282
- "model_loading": xtts_server.model_loading,
283
- "voices_count": len(xtts_server.available_voices),
284
- "device": torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU"
285
- }
240
+ @router.get("/status")
241
+ async def status():
242
+ return {
243
+ "status": "running",
244
+ "xtts_available": xtts_available,
245
+ "model_loaded": xtts_server.model_loaded,
246
+ "device": device if xtts_available else "N/A"
247
+ }
286
248
 
287
- # Add a health check endpoint that responds immediately
288
- @router.get("/health")
289
- async def health_check():
290
- return {"status": "healthy", "ready": True}
249
+ app.include_router(router)
291
250
 
292
- app.include_router(router)
251
+ # --- Server Startup ---
252
+ if __name__ == '__main__':
253
+ parser = argparse.ArgumentParser(description="LoLLMs XTTS Server")
254
+ parser.add_argument("--host", type=str, default="localhost", help="Host to bind the server to.")
255
+ parser.add_argument("--port", type=int, default=8081, help="Port to bind the server to.")
256
+
257
+ args = parser.parse_args()
293
258
 
294
- # --- Server Startup ---
295
- if __name__ == '__main__':
296
- parser = argparse.ArgumentParser(description="XTTS TTS Server")
297
- parser.add_argument("--host", type=str, default="localhost", help="Host to bind the server to.")
298
- parser.add_argument("--port", type=int, default="8081", help="Port to bind the server to.")
299
-
300
- args = parser.parse_args()
259
+ ASCIIColors.cyan("--- LoLLMs XTTS Server ---")
260
+ ASCIIColors.green(f"Starting server on http://{args.host}:{args.port}")
261
+ ASCIIColors.info(f"Voices directory: {xtts_server.voices_dir.resolve()}")
262
+
263
+ if not xtts_available:
264
+ ASCIIColors.red("Warning: XTTS dependencies not found. Server will run but generation will fail.")
265
+ else:
266
+ ASCIIColors.info(f"Detected device: {device}")
267
+
268
+ uvicorn.run(app, host=args.host, port=args.port)
301
269
 
302
- print(f"Server: Starting XTTS server on {args.host}:{args.port}")
303
- print(f"Server: XTTS available: {xtts_available}")
304
- print(f"Server: Model will be loaded on first audio generation request")
305
- print(f"Server: Available voices: {len(xtts_server.available_voices)}")
306
- if xtts_available:
307
- print(f"Server: Device: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU'}")
308
-
309
- # Create voices directory if it doesn't exist
310
- voices_dir = Path(__file__).parent / "voices"
311
- voices_dir.mkdir(exist_ok=True)
312
- print(f"Server: Voices directory: {voices_dir}")
313
-
314
- uvicorn.run(app, host=args.host, port=args.port)
270
+ except Exception as e:
271
+ # This will catch errors during initial imports
272
+ from ascii_colors import ASCIIColors
273
+ ASCIIColors.red(f"Server: CRITICAL ERROR during startup: {e}")
274
+ import traceback
275
+ ASCIIColors.red(f"Server: Traceback:\n{traceback.format_exc()}")