abstractvoice 0.1.1__py3-none-any.whl → 0.2.1__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.
abstractvoice/__main__.py CHANGED
@@ -15,20 +15,27 @@ def print_examples():
15
15
  print(" cli - Command-line REPL example")
16
16
  print(" web - Web API example")
17
17
  print(" simple - Simple usage example")
18
- print("\nUsage: python -m abstractvoice <example> [args...]")
18
+ print(" check-deps - Check dependency compatibility")
19
+ print("\nUsage: python -m abstractvoice <example> [--language <lang>] [args...]")
20
+ print("\nSupported languages: en, fr, es, de, it, ru, multilingual")
21
+ print("\nExamples:")
22
+ print(" python -m abstractvoice cli --language fr # French CLI")
23
+ print(" python -m abstractvoice simple --language ru # Russian simple example")
24
+ print(" python -m abstractvoice check-deps # Check dependencies")
19
25
 
20
26
 
21
27
  def simple_example():
22
28
  """Run a simple example demonstrating basic usage."""
23
29
  from abstractvoice import VoiceManager
24
30
  import time
25
-
31
+
26
32
  print("Simple AbstractVoice Example")
27
33
  print("============================")
28
34
  print("This example demonstrates basic TTS and STT functionality.")
35
+ print("(Use --language argument to test different languages)")
29
36
  print()
30
-
31
- # Initialize voice manager
37
+
38
+ # Initialize voice manager (can be overridden with --language)
32
39
  manager = VoiceManager(debug_mode=True)
33
40
 
34
41
  try:
@@ -90,18 +97,33 @@ def simple_example():
90
97
  def main():
91
98
  """Main entry point."""
92
99
  parser = argparse.ArgumentParser(description="AbstractVoice examples")
93
- parser.add_argument("example", nargs="?", help="Example to run (cli, web, simple)")
94
-
95
- # Parse just the first argument
100
+ parser.add_argument("example", nargs="?", help="Example to run (cli, web, simple, check-deps)")
101
+ parser.add_argument("--language", "--lang", default="en",
102
+ choices=["en", "fr", "es", "de", "it", "ru", "multilingual"],
103
+ help="Voice language for examples")
104
+
105
+ # Parse just the first argument and language
96
106
  args, remaining = parser.parse_known_args()
97
-
107
+
98
108
  if not args.example:
99
109
  print_examples()
100
110
  return
101
-
102
- # Set remaining args as sys.argv for the examples
111
+
112
+ # Handle check-deps specially (doesn't need language)
113
+ if args.example == "check-deps":
114
+ from abstractvoice.dependency_check import check_dependencies
115
+ try:
116
+ check_dependencies(verbose=True)
117
+ except Exception as e:
118
+ print(f"❌ Error running dependency check: {e}")
119
+ print("This might indicate a dependency issue.")
120
+ return
121
+
122
+ # Set remaining args as sys.argv for the examples, including language
123
+ if args.language != "en":
124
+ remaining = ["--language", args.language] + remaining
103
125
  sys.argv = [sys.argv[0]] + remaining
104
-
126
+
105
127
  if args.example == "cli":
106
128
  from abstractvoice.examples.cli_repl import main
107
129
  main()
@@ -0,0 +1,274 @@
1
+ """Dependency compatibility checker for AbstractVoice.
2
+
3
+ This module provides utilities to check dependency versions and compatibility,
4
+ helping users diagnose and resolve installation issues.
5
+ """
6
+
7
+ import sys
8
+ import importlib
9
+ from typing import Dict, List, Tuple, Optional
10
+ import warnings
11
+
12
+
13
+ class DependencyChecker:
14
+ """Check and validate AbstractVoice dependencies."""
15
+
16
+ # Known compatible version ranges
17
+ PYTORCH_COMPAT = {
18
+ "torch": ("2.0.0", "2.4.0"),
19
+ "torchvision": ("0.15.0", "0.19.0"),
20
+ "torchaudio": ("2.0.0", "2.4.0"),
21
+ }
22
+
23
+ CORE_DEPS = {
24
+ "numpy": ("1.24.0", None),
25
+ "requests": ("2.31.0", None),
26
+ }
27
+
28
+ OPTIONAL_DEPS = {
29
+ "coqui-tts": ("0.27.0", "0.30.0"),
30
+ "openai-whisper": ("20230314", None),
31
+ "sounddevice": ("0.4.6", None),
32
+ "librosa": ("0.10.0", "0.11.0"),
33
+ "flask": ("2.0.0", None),
34
+ "webrtcvad": ("2.0.10", None),
35
+ "PyAudio": ("0.2.13", None),
36
+ "soundfile": ("0.12.1", None),
37
+ "tiktoken": ("0.6.0", None),
38
+ }
39
+
40
+ def __init__(self, verbose: bool = True):
41
+ self.verbose = verbose
42
+ self.results = {}
43
+
44
+ def _parse_version(self, version_str: str) -> Tuple[int, ...]:
45
+ """Parse version string into tuple of integers for comparison."""
46
+ try:
47
+ # Handle version strings like "20230314" or "2.0.0"
48
+ if version_str.isdigit():
49
+ return (int(version_str),)
50
+
51
+ # Standard semantic versioning
52
+ version_parts = version_str.split('.')
53
+ return tuple(int(part) for part in version_parts if part.isdigit())
54
+ except (ValueError, AttributeError):
55
+ return (0,)
56
+
57
+ def _check_version_range(self, current: str, min_ver: Optional[str], max_ver: Optional[str]) -> bool:
58
+ """Check if current version is within the specified range."""
59
+ current_tuple = self._parse_version(current)
60
+
61
+ if min_ver:
62
+ min_tuple = self._parse_version(min_ver)
63
+ if current_tuple < min_tuple:
64
+ return False
65
+
66
+ if max_ver:
67
+ max_tuple = self._parse_version(max_ver)
68
+ if current_tuple >= max_tuple:
69
+ return False
70
+
71
+ return True
72
+
73
+ def _check_package(self, package_name: str, min_ver: Optional[str], max_ver: Optional[str]) -> Dict:
74
+ """Check a single package installation and version."""
75
+ try:
76
+ module = importlib.import_module(package_name.replace('-', '_'))
77
+ version = getattr(module, '__version__', 'unknown')
78
+
79
+ # Special handling for packages with different version attributes
80
+ if version == 'unknown':
81
+ if hasattr(module, 'version'):
82
+ version = module.version
83
+ elif hasattr(module, 'VERSION'):
84
+ version = module.VERSION
85
+ elif package_name == 'openai-whisper':
86
+ # Whisper uses different versioning
87
+ try:
88
+ import whisper
89
+ version = getattr(whisper, '__version__', 'installed')
90
+ except:
91
+ version = 'installed'
92
+
93
+ compatible = self._check_version_range(str(version), min_ver, max_ver)
94
+
95
+ return {
96
+ 'status': 'installed',
97
+ 'version': str(version),
98
+ 'compatible': compatible,
99
+ 'min_version': min_ver,
100
+ 'max_version': max_ver
101
+ }
102
+
103
+ except ImportError:
104
+ return {
105
+ 'status': 'missing',
106
+ 'version': None,
107
+ 'compatible': False,
108
+ 'min_version': min_ver,
109
+ 'max_version': max_ver
110
+ }
111
+ except Exception as e:
112
+ return {
113
+ 'status': 'error',
114
+ 'version': None,
115
+ 'compatible': False,
116
+ 'error': str(e),
117
+ 'min_version': min_ver,
118
+ 'max_version': max_ver
119
+ }
120
+
121
+ def check_core_dependencies(self) -> Dict:
122
+ """Check core dependencies (always required)."""
123
+ results = {}
124
+ for package, (min_ver, max_ver) in self.CORE_DEPS.items():
125
+ results[package] = self._check_package(package, min_ver, max_ver)
126
+ return results
127
+
128
+ def check_pytorch_ecosystem(self) -> Dict:
129
+ """Check PyTorch ecosystem for compatibility issues."""
130
+ results = {}
131
+ for package, (min_ver, max_ver) in self.PYTORCH_COMPAT.items():
132
+ results[package] = self._check_package(package, min_ver, max_ver)
133
+ return results
134
+
135
+ def check_optional_dependencies(self) -> Dict:
136
+ """Check optional dependencies."""
137
+ results = {}
138
+ for package, (min_ver, max_ver) in self.OPTIONAL_DEPS.items():
139
+ results[package] = self._check_package(package, min_ver, max_ver)
140
+ return results
141
+
142
+ def check_pytorch_conflicts(self) -> List[str]:
143
+ """Detect specific PyTorch/TorchVision conflicts."""
144
+ conflicts = []
145
+
146
+ try:
147
+ import torch
148
+ import torchvision
149
+
150
+ torch_version = self._parse_version(torch.__version__)
151
+ tv_version = self._parse_version(torchvision.__version__)
152
+
153
+ # Check known incompatible combinations
154
+ if torch_version >= (2, 3, 0) and tv_version < (0, 18, 0):
155
+ conflicts.append(f"PyTorch {torch.__version__} is incompatible with TorchVision {torchvision.__version__}")
156
+
157
+ # Test torchvision::nms operator availability
158
+ try:
159
+ from torchvision.ops import nms
160
+ # Try to use nms to check if it actually works
161
+ import torch
162
+ boxes = torch.tensor([[0, 0, 1, 1]], dtype=torch.float32)
163
+ scores = torch.tensor([1.0], dtype=torch.float32)
164
+ nms(boxes, scores, 0.5)
165
+ except Exception as e:
166
+ if "torchvision::nms does not exist" in str(e):
167
+ conflicts.append("TorchVision NMS operator not available - version mismatch detected")
168
+
169
+ except ImportError:
170
+ pass # PyTorch not installed
171
+
172
+ return conflicts
173
+
174
+ def check_all(self) -> Dict:
175
+ """Run comprehensive dependency check."""
176
+ results = {
177
+ 'core': self.check_core_dependencies(),
178
+ 'pytorch': self.check_pytorch_ecosystem(),
179
+ 'optional': self.check_optional_dependencies(),
180
+ 'conflicts': self.check_pytorch_conflicts(),
181
+ 'python_version': sys.version,
182
+ 'platform': sys.platform
183
+ }
184
+
185
+ self.results = results
186
+ return results
187
+
188
+ def print_report(self, results: Optional[Dict] = None):
189
+ """Print a formatted dependency report."""
190
+ if results is None:
191
+ results = self.results
192
+
193
+ print("🔍 AbstractVoice Dependency Check Report")
194
+ print("=" * 50)
195
+
196
+ # Python version
197
+ print(f"\n🐍 Python: {results['python_version']}")
198
+ print(f"🖥️ Platform: {results['platform']}")
199
+
200
+ # Core dependencies
201
+ print(f"\n📦 Core Dependencies:")
202
+ for package, info in results['core'].items():
203
+ status_icon = "✅" if info['status'] == 'installed' and info['compatible'] else "❌"
204
+ version_info = f"v{info['version']}" if info['version'] else "not installed"
205
+ print(f" {status_icon} {package}: {version_info}")
206
+
207
+ # PyTorch ecosystem
208
+ print(f"\n🔥 PyTorch Ecosystem:")
209
+ pytorch_all_good = True
210
+ for package, info in results['pytorch'].items():
211
+ status_icon = "✅" if info['status'] == 'installed' and info['compatible'] else "❌"
212
+ version_info = f"v{info['version']}" if info['version'] else "not installed"
213
+ if info['status'] != 'installed' or not info['compatible']:
214
+ pytorch_all_good = False
215
+ print(f" {status_icon} {package}: {version_info}")
216
+
217
+ # Conflicts
218
+ if results['conflicts']:
219
+ print(f"\n⚠️ Detected Conflicts:")
220
+ for conflict in results['conflicts']:
221
+ print(f" ❌ {conflict}")
222
+ pytorch_all_good = False
223
+
224
+ if pytorch_all_good and all(info['status'] == 'installed' for info in results['pytorch'].values()):
225
+ print(" 🎉 PyTorch ecosystem looks compatible!")
226
+
227
+ # Optional dependencies
228
+ print(f"\n🔧 Optional Dependencies:")
229
+ installed_optional = []
230
+ missing_optional = []
231
+
232
+ for package, info in results['optional'].items():
233
+ if info['status'] == 'installed':
234
+ status_icon = "✅" if info['compatible'] else "⚠️"
235
+ installed_optional.append(f" {status_icon} {package}: v{info['version']}")
236
+ else:
237
+ missing_optional.append(f" ⭕ {package}: not installed")
238
+
239
+ if installed_optional:
240
+ print(" Installed:")
241
+ for line in installed_optional:
242
+ print(line)
243
+
244
+ if missing_optional:
245
+ print(" Missing:")
246
+ for line in missing_optional:
247
+ print(line)
248
+
249
+ # Recommendations
250
+ print(f"\n💡 Recommendations:")
251
+
252
+ if not pytorch_all_good:
253
+ print(" 🔧 Fix PyTorch conflicts with:")
254
+ print(" pip uninstall torch torchvision torchaudio")
255
+ print(" pip install abstractvoice[all]")
256
+
257
+ if not any(info['status'] == 'installed' for info in results['optional'].values()):
258
+ print(" 📦 Install voice functionality with:")
259
+ print(" pip install abstractvoice[voice-full]")
260
+
261
+ print("\n" + "=" * 50)
262
+
263
+
264
+ def check_dependencies(verbose: bool = True) -> Dict:
265
+ """Quick function to check all dependencies."""
266
+ checker = DependencyChecker(verbose=verbose)
267
+ results = checker.check_all()
268
+ if verbose:
269
+ checker.print_report(results)
270
+ return results
271
+
272
+
273
+ if __name__ == "__main__":
274
+ check_dependencies()
@@ -37,21 +37,28 @@ class VoiceREPL(cmd.Cmd):
37
37
  ruler = "" # No horizontal rule line
38
38
  use_rawinput = True
39
39
 
40
- def __init__(self, api_url="http://localhost:11434/api/chat",
41
- model="granite3.3:2b", debug_mode=False):
40
+ def __init__(self, api_url="http://localhost:11434/api/chat",
41
+ model="granite3.3:2b", debug_mode=False, language="en", tts_model=None):
42
42
  super().__init__()
43
-
43
+
44
44
  # Debug mode
45
45
  self.debug_mode = debug_mode
46
-
46
+
47
47
  # API settings
48
48
  self.api_url = api_url
49
49
  self.model = model
50
50
  self.temperature = 0.4
51
51
  self.max_tokens = 4096
52
-
53
- # Initialize voice manager
54
- self.voice_manager = VoiceManager(debug_mode=debug_mode)
52
+
53
+ # Language settings
54
+ self.current_language = language
55
+
56
+ # Initialize voice manager with language support
57
+ self.voice_manager = VoiceManager(
58
+ language=language,
59
+ tts_model=tts_model,
60
+ debug_mode=debug_mode
61
+ )
55
62
 
56
63
  # Settings
57
64
  self.use_tts = True
@@ -83,10 +90,12 @@ class VoiceREPL(cmd.Cmd):
83
90
  def _get_intro(self):
84
91
  """Generate intro message with help."""
85
92
  intro = f"\n{Colors.BOLD}Welcome to AbstractVoice CLI REPL{Colors.END}\n"
86
- intro += f"API: {self.api_url} | Model: {self.model}\n"
93
+ lang_name = self.voice_manager.get_language_name()
94
+ intro += f"API: {self.api_url} | Model: {self.model} | Voice: {lang_name}\n"
87
95
  intro += f"\n{Colors.CYAN}Quick Start:{Colors.END}\n"
88
96
  intro += " • Type messages to chat with the LLM\n"
89
97
  intro += " • Use /voice <mode> to enable voice input\n"
98
+ intro += " • Use /language <lang> to switch voice language\n"
90
99
  intro += " • Type /help for full command list\n"
91
100
  intro += " • Type /exit or /q to quit\n"
92
101
  return intro
@@ -278,7 +287,172 @@ class VoiceREPL(cmd.Cmd):
278
287
  text = re.sub(pattern, "", text, flags=re.DOTALL)
279
288
 
280
289
  return text.strip()
281
-
290
+
291
+ def do_language(self, args):
292
+ """Switch voice language.
293
+
294
+ Usage: /language <lang>
295
+ Available languages: en, fr, es, de, it
296
+ """
297
+ if not args:
298
+ current_name = self.voice_manager.get_language_name()
299
+ current_code = self.voice_manager.get_language()
300
+ print(f"Current language: {current_name} ({current_code})")
301
+
302
+ print("Available languages:")
303
+ for code in self.voice_manager.get_supported_languages():
304
+ name = self.voice_manager.get_language_name(code)
305
+ print(f" {code} - {name}")
306
+ return
307
+
308
+ language = args.strip().lower()
309
+
310
+ # Stop any current voice activity
311
+ if self.voice_mode_active:
312
+ self._voice_stop_callback()
313
+ was_active = True
314
+ else:
315
+ was_active = False
316
+
317
+ # Switch language
318
+ old_lang = self.current_language
319
+ if self.voice_manager.set_language(language):
320
+ self.current_language = language
321
+ old_name = self.voice_manager.get_language_name(old_lang)
322
+ new_name = self.voice_manager.get_language_name(language)
323
+ print(f"🌍 Language changed: {old_name} → {new_name}")
324
+
325
+ # Test the new language with localized message
326
+ test_messages = {
327
+ 'en': "Language switched to English.",
328
+ 'fr': "Langue changée en français.",
329
+ 'es': "Idioma cambiado a español.",
330
+ 'de': "Sprache auf Deutsch umgestellt.",
331
+ 'it': "Lingua cambiata in italiano."
332
+ }
333
+ test_msg = test_messages.get(language, "Language switched.")
334
+ self.voice_manager.speak(test_msg)
335
+
336
+ # Restart voice mode if it was active
337
+ if was_active:
338
+ self.do_voice(self.voice_mode)
339
+ else:
340
+ supported = ', '.join(self.voice_manager.get_supported_languages())
341
+ print(f"Failed to switch to language: {language}")
342
+ print(f"Supported languages: {supported}")
343
+ if self.debug_mode:
344
+ import traceback
345
+ traceback.print_exc()
346
+
347
+ def do_setvoice(self, args):
348
+ """Set a specific voice model.
349
+
350
+ Usage:
351
+ /setvoice # Show all available voices
352
+ /setvoice <voice_id> # Set voice (format: language.voice_id)
353
+
354
+ Examples:
355
+ /setvoice # List all voices
356
+ /setvoice fr.css10_vits # Set French CSS10 VITS voice
357
+ /setvoice it.mai_male_vits # Set Italian male VITS voice
358
+ """
359
+ if not args:
360
+ # Show all available voices organized by language
361
+ print(f"\n{Colors.CYAN}Available Voice Models:{Colors.END}")
362
+ self.voice_manager.list_voices()
363
+
364
+ print(f"\n{Colors.YELLOW}Usage:{Colors.END}")
365
+ print(" /setvoice <language>.<voice_id>")
366
+ print(" Example: /setvoice fr.css10_vits")
367
+ return
368
+
369
+ voice_spec = args.strip()
370
+
371
+ # Parse language.voice_id format
372
+ if '.' not in voice_spec:
373
+ print(f"❌ Invalid format. Use: language.voice_id")
374
+ print(f" Example: /setvoice fr.css10_vits")
375
+ print(f" Run '/setvoice' to see available voices")
376
+ return
377
+
378
+ try:
379
+ language, voice_id = voice_spec.split('.', 1)
380
+ except ValueError:
381
+ print(f"❌ Invalid format. Use: language.voice_id")
382
+ return
383
+
384
+ # Stop any current voice activity
385
+ if self.voice_mode_active:
386
+ self._voice_stop_callback()
387
+ was_active = True
388
+ else:
389
+ was_active = False
390
+
391
+ # Set the specific voice
392
+ try:
393
+ success = self.voice_manager.set_voice(language, voice_id)
394
+ if success:
395
+ # Update current language to match the voice
396
+ self.current_language = language
397
+
398
+ # Get voice info for confirmation
399
+ voice_info = self.voice_manager.VOICE_CATALOG.get(language, {}).get(voice_id, {})
400
+ lang_name = self.voice_manager.get_language_name(language)
401
+
402
+ print(f"✅ Voice changed successfully!")
403
+ print(f" Language: {lang_name} ({language})")
404
+ print(f" Voice: {voice_id}")
405
+ if voice_info:
406
+ quality_icon = "✨" if voice_info.get('quality') == 'premium' else "🔧"
407
+ gender_icon = {"male": "👨", "female": "👩", "multiple": "👥"}.get(voice_info.get('gender'), "🗣️")
408
+ print(f" Details: {quality_icon} {gender_icon} {voice_info.get('accent', 'Unknown accent')}")
409
+
410
+ # Test the new voice
411
+ test_messages = {
412
+ 'en': "Voice changed to English.",
413
+ 'fr': "Voix changée en français.",
414
+ 'es': "Voz cambiada al español.",
415
+ 'de': "Stimme auf Deutsch geändert.",
416
+ 'it': "Voce cambiata in italiano."
417
+ }
418
+ test_msg = test_messages.get(language, "Voice changed successfully.")
419
+ self.voice_manager.speak(test_msg)
420
+
421
+ # Restart voice mode if it was active
422
+ if was_active:
423
+ self.do_voice(self.voice_mode)
424
+ else:
425
+ print(f"❌ Failed to set voice: {voice_spec}")
426
+ print(f" Run '/setvoice' to see available voices")
427
+
428
+ except Exception as e:
429
+ print(f"❌ Error setting voice: {e}")
430
+ if self.debug_mode:
431
+ import traceback
432
+ traceback.print_exc()
433
+
434
+ def do_lang_info(self, args):
435
+ """Show current language information."""
436
+ info = self.voice_manager.get_language_info()
437
+ print(f"\n{Colors.CYAN}Current Language Information:{Colors.END}")
438
+ print(f" Language: {info['name']} ({info['code']})")
439
+ print(f" Model: {info['model']}")
440
+ print(f" Available models: {list(info['available_models'].keys())}")
441
+
442
+ # Check if XTTS supports multiple languages
443
+ if 'xtts' in (info['model'] or '').lower():
444
+ print(f" ✅ Supports multilingual synthesis")
445
+ else:
446
+ print(f" ℹ️ Monolingual model")
447
+
448
+ def do_list_languages(self, args):
449
+ """List all supported languages."""
450
+ print(f"\n{Colors.CYAN}Supported Languages:{Colors.END}")
451
+ for lang in self.voice_manager.get_supported_languages():
452
+ name = self.voice_manager.get_language_name(lang)
453
+ current = " (current)" if lang == self.current_language else ""
454
+ print(f" {lang} - {name}{current}")
455
+
282
456
  def do_voice(self, arg):
283
457
  """Control voice input mode.
284
458
 
@@ -554,6 +728,10 @@ class VoiceREPL(cmd.Cmd):
554
728
  print(" /clear Clear history")
555
729
  print(" /tts on|off Toggle TTS")
556
730
  print(" /voice <mode> Voice input: off|full|wait|stop|ptt")
731
+ print(" /language <lang> Switch voice language (en, fr, es, de, it)")
732
+ print(" /setvoice [id] List voices or set specific voice (lang.voice_id)")
733
+ print(" /lang_info Show current language information")
734
+ print(" /list_languages List all supported languages")
557
735
  print(" /speed <number> Set TTS speed (0.5-2.0, default: 1.0, pitch preserved)")
558
736
  print(" /tts_model <model> Switch TTS model: vits(best)|fast_pitch|glow-tts|tacotron2-DDC")
559
737
  print(" /whisper <model> Switch Whisper model: tiny|base|small|medium|large")
@@ -831,10 +1009,15 @@ def parse_args():
831
1009
  """Parse command line arguments."""
832
1010
  parser = argparse.ArgumentParser(description="AbstractVoice CLI Example")
833
1011
  parser.add_argument("--debug", action="store_true", help="Enable debug mode")
834
- parser.add_argument("--api", default="http://localhost:11434/api/chat",
1012
+ parser.add_argument("--api", default="http://localhost:11434/api/chat",
835
1013
  help="LLM API URL")
836
- parser.add_argument("--model", default="granite3.3:2b",
1014
+ parser.add_argument("--model", default="granite3.3:2b",
837
1015
  help="LLM model name")
1016
+ parser.add_argument("--language", "--lang", default="en",
1017
+ choices=["en", "fr", "es", "de", "it", "ru", "multilingual"],
1018
+ help="Voice language (en=English, fr=French, es=Spanish, de=German, it=Italian, ru=Russian, multilingual=All)")
1019
+ parser.add_argument("--tts-model",
1020
+ help="Specific TTS model to use (overrides language default)")
838
1021
  return parser.parse_args()
839
1022
 
840
1023
 
@@ -844,11 +1027,13 @@ def main():
844
1027
  # Parse command line arguments
845
1028
  args = parse_args()
846
1029
 
847
- # Initialize and run REPL
1030
+ # Initialize and run REPL with language support
848
1031
  repl = VoiceREPL(
849
1032
  api_url=args.api,
850
1033
  model=args.model,
851
- debug_mode=args.debug
1034
+ debug_mode=args.debug,
1035
+ language=args.language,
1036
+ tts_model=args.tts_model
852
1037
  )
853
1038
  repl.cmdloop()
854
1039
  except KeyboardInterrupt:
@@ -13,20 +13,25 @@ def parse_args():
13
13
  """Parse command line arguments."""
14
14
  parser = argparse.ArgumentParser(description="AbstractVoice Voice Mode")
15
15
  parser.add_argument("--debug", action="store_true", help="Enable debug mode")
16
- parser.add_argument("--api", default="http://localhost:11434/api/chat",
16
+ parser.add_argument("--api", default="http://localhost:11434/api/chat",
17
17
  help="LLM API URL")
18
- parser.add_argument("--model", default="granite3.3:2b",
18
+ parser.add_argument("--model", default="granite3.3:2b",
19
19
  help="LLM model name")
20
20
  parser.add_argument("--whisper", default="tiny",
21
21
  help="Whisper model to use (tiny, base, small, medium, large)")
22
22
  parser.add_argument("--no-listening", action="store_true",
23
23
  help="Disable speech-to-text (listening), TTS still works")
24
- parser.add_argument("--system",
24
+ parser.add_argument("--system",
25
25
  help="Custom system prompt")
26
26
  parser.add_argument("--temperature", type=float, default=0.4,
27
27
  help="Set temperature (0.0-2.0) for the LLM")
28
28
  parser.add_argument("--max-tokens", type=int, default=4096,
29
29
  help="Set maximum tokens for the LLM response")
30
+ parser.add_argument("--language", "--lang", default="en",
31
+ choices=["en", "fr", "es", "de", "it", "ru", "multilingual"],
32
+ help="Voice language (en=English, fr=French, es=Spanish, de=German, it=Italian, ru=Russian, multilingual=All)")
33
+ parser.add_argument("--tts-model",
34
+ help="Specific TTS model to use (overrides language default)")
30
35
  return parser.parse_args()
31
36
 
32
37
  def main():
@@ -35,13 +40,22 @@ def main():
35
40
  # Parse command line arguments
36
41
  args = parse_args()
37
42
 
38
- print("Starting AbstractVoice voice interface...")
43
+ # Show language information
44
+ language_names = {
45
+ 'en': 'English', 'fr': 'French', 'es': 'Spanish',
46
+ 'de': 'German', 'it': 'Italian', 'ru': 'Russian',
47
+ 'multilingual': 'Multilingual'
48
+ }
49
+ lang_name = language_names.get(args.language, args.language)
50
+ print(f"Starting AbstractVoice voice interface ({lang_name})...")
39
51
 
40
- # Initialize REPL
52
+ # Initialize REPL with language support
41
53
  repl = VoiceREPL(
42
54
  api_url=args.api,
43
55
  model=args.model,
44
- debug_mode=args.debug
56
+ debug_mode=args.debug,
57
+ language=args.language,
58
+ tts_model=args.tts_model
45
59
  )
46
60
 
47
61
  # Set custom system prompt if provided