abstractvoice 0.2.0__tar.gz → 0.3.0__tar.gz

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 (28) hide show
  1. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/PKG-INFO +69 -75
  2. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/README.md +32 -6
  3. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/__main__.py +13 -1
  4. abstractvoice-0.3.0/abstractvoice/dependency_check.py +274 -0
  5. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/examples/cli_repl.py +25 -1
  6. abstractvoice-0.3.0/abstractvoice/examples/voice_cli.py +249 -0
  7. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/tts/tts_engine.py +64 -10
  8. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/PKG-INFO +69 -75
  9. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/SOURCES.txt +1 -0
  10. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/entry_points.txt +0 -1
  11. abstractvoice-0.3.0/abstractvoice.egg-info/requires.txt +71 -0
  12. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/pyproject.toml +32 -63
  13. abstractvoice-0.2.0/abstractvoice/examples/voice_cli.py +0 -99
  14. abstractvoice-0.2.0/abstractvoice.egg-info/requires.txt +0 -104
  15. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/LICENSE +0 -0
  16. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/__init__.py +0 -0
  17. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/examples/__init__.py +0 -0
  18. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/examples/web_api.py +0 -0
  19. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/recognition.py +0 -0
  20. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/stt/__init__.py +0 -0
  21. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/stt/transcriber.py +0 -0
  22. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/tts/__init__.py +0 -0
  23. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/vad/__init__.py +0 -0
  24. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/vad/voice_detector.py +0 -0
  25. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/voice_manager.py +0 -0
  26. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/dependency_links.txt +0 -0
  27. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/top_level.txt +0 -0
  28. {abstractvoice-0.2.0 → abstractvoice-0.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractvoice
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: A modular Python library for voice interactions with AI systems
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
6
  License-Expression: MIT
@@ -25,10 +25,11 @@ Requires-Dist: webrtcvad>=2.0.10; extra == "voice"
25
25
  Requires-Dist: PyAudio>=0.2.13; extra == "voice"
26
26
  Requires-Dist: soundfile>=0.12.1; extra == "voice"
27
27
  Provides-Extra: tts
28
- Requires-Dist: coqui-tts>=0.27.0; extra == "tts"
29
- Requires-Dist: torch>=2.0.0; extra == "tts"
30
- Requires-Dist: torchaudio>=2.0.0; extra == "tts"
31
- Requires-Dist: librosa>=0.10.0; extra == "tts"
28
+ Requires-Dist: coqui-tts<0.30.0,>=0.27.0; extra == "tts"
29
+ Requires-Dist: torch<2.4.0,>=2.0.0; extra == "tts"
30
+ Requires-Dist: torchvision<0.19.0,>=0.15.0; extra == "tts"
31
+ Requires-Dist: torchaudio<2.4.0,>=2.0.0; extra == "tts"
32
+ Requires-Dist: librosa<0.11.0,>=0.10.0; extra == "tts"
32
33
  Provides-Extra: stt
33
34
  Requires-Dist: openai-whisper>=20230314; extra == "stt"
34
35
  Requires-Dist: tiktoken>=0.6.0; extra == "stt"
@@ -39,10 +40,11 @@ Requires-Dist: sounddevice>=0.4.6; extra == "all"
39
40
  Requires-Dist: webrtcvad>=2.0.10; extra == "all"
40
41
  Requires-Dist: PyAudio>=0.2.13; extra == "all"
41
42
  Requires-Dist: openai-whisper>=20230314; extra == "all"
42
- Requires-Dist: coqui-tts>=0.27.0; extra == "all"
43
- Requires-Dist: torch>=2.0.0; extra == "all"
44
- Requires-Dist: torchaudio>=2.0.0; extra == "all"
45
- Requires-Dist: librosa>=0.10.0; extra == "all"
43
+ Requires-Dist: coqui-tts<0.30.0,>=0.27.0; extra == "all"
44
+ Requires-Dist: torch<2.4.0,>=2.0.0; extra == "all"
45
+ Requires-Dist: torchvision<0.19.0,>=0.15.0; extra == "all"
46
+ Requires-Dist: torchaudio<2.4.0,>=2.0.0; extra == "all"
47
+ Requires-Dist: librosa<0.11.0,>=0.10.0; extra == "all"
46
48
  Requires-Dist: soundfile>=0.12.1; extra == "all"
47
49
  Requires-Dist: flask>=2.0.0; extra == "all"
48
50
  Requires-Dist: tiktoken>=0.6.0; extra == "all"
@@ -50,66 +52,32 @@ Provides-Extra: dev
50
52
  Requires-Dist: pytest>=7.0.0; extra == "dev"
51
53
  Requires-Dist: black>=22.0.0; extra == "dev"
52
54
  Requires-Dist: flake8>=5.0.0; extra == "dev"
53
- Provides-Extra: languages
54
- Requires-Dist: sounddevice>=0.4.6; extra == "languages"
55
- Requires-Dist: webrtcvad>=2.0.10; extra == "languages"
56
- Requires-Dist: PyAudio>=0.2.13; extra == "languages"
57
- Requires-Dist: openai-whisper>=20230314; extra == "languages"
58
- Requires-Dist: coqui-tts>=0.27.0; extra == "languages"
59
- Requires-Dist: torch>=2.0.0; extra == "languages"
60
- Requires-Dist: torchaudio>=2.0.0; extra == "languages"
61
- Requires-Dist: librosa>=0.10.0; extra == "languages"
62
- Requires-Dist: soundfile>=0.12.1; extra == "languages"
63
- Requires-Dist: flask>=2.0.0; extra == "languages"
64
- Requires-Dist: tiktoken>=0.6.0; extra == "languages"
65
- Provides-Extra: fr
66
- Requires-Dist: sounddevice>=0.4.6; extra == "fr"
67
- Requires-Dist: webrtcvad>=2.0.10; extra == "fr"
68
- Requires-Dist: PyAudio>=0.2.13; extra == "fr"
69
- Requires-Dist: openai-whisper>=20230314; extra == "fr"
70
- Requires-Dist: coqui-tts>=0.27.0; extra == "fr"
71
- Requires-Dist: torch>=2.0.0; extra == "fr"
72
- Requires-Dist: torchaudio>=2.0.0; extra == "fr"
73
- Requires-Dist: librosa>=0.10.0; extra == "fr"
74
- Requires-Dist: soundfile>=0.12.1; extra == "fr"
75
- Requires-Dist: flask>=2.0.0; extra == "fr"
76
- Requires-Dist: tiktoken>=0.6.0; extra == "fr"
77
- Provides-Extra: es
78
- Requires-Dist: sounddevice>=0.4.6; extra == "es"
79
- Requires-Dist: webrtcvad>=2.0.10; extra == "es"
80
- Requires-Dist: PyAudio>=0.2.13; extra == "es"
81
- Requires-Dist: openai-whisper>=20230314; extra == "es"
82
- Requires-Dist: coqui-tts>=0.27.0; extra == "es"
83
- Requires-Dist: torch>=2.0.0; extra == "es"
84
- Requires-Dist: torchaudio>=2.0.0; extra == "es"
85
- Requires-Dist: librosa>=0.10.0; extra == "es"
86
- Requires-Dist: soundfile>=0.12.1; extra == "es"
87
- Requires-Dist: flask>=2.0.0; extra == "es"
88
- Requires-Dist: tiktoken>=0.6.0; extra == "es"
89
- Provides-Extra: de
90
- Requires-Dist: sounddevice>=0.4.6; extra == "de"
91
- Requires-Dist: webrtcvad>=2.0.10; extra == "de"
92
- Requires-Dist: PyAudio>=0.2.13; extra == "de"
93
- Requires-Dist: openai-whisper>=20230314; extra == "de"
94
- Requires-Dist: coqui-tts>=0.27.0; extra == "de"
95
- Requires-Dist: torch>=2.0.0; extra == "de"
96
- Requires-Dist: torchaudio>=2.0.0; extra == "de"
97
- Requires-Dist: librosa>=0.10.0; extra == "de"
98
- Requires-Dist: soundfile>=0.12.1; extra == "de"
99
- Requires-Dist: flask>=2.0.0; extra == "de"
100
- Requires-Dist: tiktoken>=0.6.0; extra == "de"
101
- Provides-Extra: it
102
- Requires-Dist: sounddevice>=0.4.6; extra == "it"
103
- Requires-Dist: webrtcvad>=2.0.10; extra == "it"
104
- Requires-Dist: PyAudio>=0.2.13; extra == "it"
105
- Requires-Dist: openai-whisper>=20230314; extra == "it"
106
- Requires-Dist: coqui-tts>=0.27.0; extra == "it"
107
- Requires-Dist: torch>=2.0.0; extra == "it"
108
- Requires-Dist: torchaudio>=2.0.0; extra == "it"
109
- Requires-Dist: librosa>=0.10.0; extra == "it"
110
- Requires-Dist: soundfile>=0.12.1; extra == "it"
111
- Requires-Dist: flask>=2.0.0; extra == "it"
112
- Requires-Dist: tiktoken>=0.6.0; extra == "it"
55
+ Provides-Extra: voice-full
56
+ Requires-Dist: sounddevice>=0.4.6; extra == "voice-full"
57
+ Requires-Dist: webrtcvad>=2.0.10; extra == "voice-full"
58
+ Requires-Dist: PyAudio>=0.2.13; extra == "voice-full"
59
+ Requires-Dist: openai-whisper>=20230314; extra == "voice-full"
60
+ Requires-Dist: coqui-tts<0.30.0,>=0.27.0; extra == "voice-full"
61
+ Requires-Dist: torch<2.4.0,>=2.0.0; extra == "voice-full"
62
+ Requires-Dist: torchvision<0.19.0,>=0.15.0; extra == "voice-full"
63
+ Requires-Dist: torchaudio<2.4.0,>=2.0.0; extra == "voice-full"
64
+ Requires-Dist: librosa<0.11.0,>=0.10.0; extra == "voice-full"
65
+ Requires-Dist: soundfile>=0.12.1; extra == "voice-full"
66
+ Requires-Dist: tiktoken>=0.6.0; extra == "voice-full"
67
+ Provides-Extra: core-tts
68
+ Requires-Dist: coqui-tts<0.30.0,>=0.27.0; extra == "core-tts"
69
+ Requires-Dist: torch<2.4.0,>=2.0.0; extra == "core-tts"
70
+ Requires-Dist: torchvision<0.19.0,>=0.15.0; extra == "core-tts"
71
+ Requires-Dist: torchaudio<2.4.0,>=2.0.0; extra == "core-tts"
72
+ Requires-Dist: librosa<0.11.0,>=0.10.0; extra == "core-tts"
73
+ Provides-Extra: core-stt
74
+ Requires-Dist: openai-whisper>=20230314; extra == "core-stt"
75
+ Requires-Dist: tiktoken>=0.6.0; extra == "core-stt"
76
+ Provides-Extra: audio-only
77
+ Requires-Dist: sounddevice>=0.4.6; extra == "audio-only"
78
+ Requires-Dist: webrtcvad>=2.0.10; extra == "audio-only"
79
+ Requires-Dist: PyAudio>=0.2.13; extra == "audio-only"
80
+ Requires-Dist: soundfile>=0.12.1; extra == "audio-only"
113
81
  Dynamic: license-file
114
82
 
115
83
  # AbstractVoice
@@ -396,14 +364,40 @@ abstractvoice --debug --whisper base --model gemma3:latest --api http://localhos
396
364
  - **Note**: This creates a "TTS-only" mode where you type and the AI speaks back
397
365
  - `--system <prompt>` - Custom system prompt
398
366
 
367
+ ### 🎯 Complete CLI Interface (v0.3.0+)
368
+
369
+ AbstractVoice provides a unified command interface for all functionality:
370
+
371
+ ```bash
372
+ # Voice mode (default)
373
+ abstractvoice # Interactive voice mode with AI
374
+ abstractvoice --model cogito:3b # With custom Ollama model
375
+ abstractvoice --language fr # French voice mode
376
+
377
+ # Examples and utilities
378
+ abstractvoice cli # CLI REPL for text interaction
379
+ abstractvoice web # Web API server
380
+ abstractvoice simple # Simple TTS/STT demonstration
381
+ abstractvoice check-deps # Check dependency compatibility
382
+ abstractvoice help # Show available commands
383
+
384
+ # Get help
385
+ abstractvoice --help # Complete help with all options
386
+ ```
387
+
388
+ **All functionality through one command!** No more confusion between different entry points.
389
+
399
390
  ### Command-Line REPL
400
391
 
401
392
  ```bash
402
393
  # Run the CLI example (TTS ON, STT OFF)
403
- abstractvoice-cli cli
394
+ abstractvoice cli
404
395
 
405
396
  # With debug mode
406
- abstractvoice-cli cli --debug
397
+ abstractvoice cli --debug
398
+
399
+ # With specific language
400
+ abstractvoice cli --language fr
407
401
  ```
408
402
 
409
403
  #### REPL Commands
@@ -455,17 +449,17 @@ All commands must start with `/` except `stop`:
455
449
 
456
450
  ```bash
457
451
  # Run the web API example
458
- abstractvoice-cli web
452
+ abstractvoice web
459
453
 
460
454
  # With different host and port
461
- abstractvoice-cli web --host 0.0.0.0 --port 8000
455
+ abstractvoice web --host 0.0.0.0 --port 8000
462
456
  ```
463
457
 
464
458
  You can also run a simplified version that doesn't load the full models:
465
459
 
466
460
  ```bash
467
461
  # Run the web API with simulation mode
468
- abstractvoice-cli web --simulate
462
+ abstractvoice web --simulate
469
463
  ```
470
464
 
471
465
  #### Troubleshooting Web API
@@ -493,7 +487,7 @@ If you encounter issues with the web API:
493
487
 
494
488
  ```bash
495
489
  # Run the simple example
496
- abstractvoice-cli simple
490
+ abstractvoice simple
497
491
  ```
498
492
 
499
493
  ## Documentation
@@ -282,14 +282,40 @@ abstractvoice --debug --whisper base --model gemma3:latest --api http://localhos
282
282
  - **Note**: This creates a "TTS-only" mode where you type and the AI speaks back
283
283
  - `--system <prompt>` - Custom system prompt
284
284
 
285
+ ### 🎯 Complete CLI Interface (v0.3.0+)
286
+
287
+ AbstractVoice provides a unified command interface for all functionality:
288
+
289
+ ```bash
290
+ # Voice mode (default)
291
+ abstractvoice # Interactive voice mode with AI
292
+ abstractvoice --model cogito:3b # With custom Ollama model
293
+ abstractvoice --language fr # French voice mode
294
+
295
+ # Examples and utilities
296
+ abstractvoice cli # CLI REPL for text interaction
297
+ abstractvoice web # Web API server
298
+ abstractvoice simple # Simple TTS/STT demonstration
299
+ abstractvoice check-deps # Check dependency compatibility
300
+ abstractvoice help # Show available commands
301
+
302
+ # Get help
303
+ abstractvoice --help # Complete help with all options
304
+ ```
305
+
306
+ **All functionality through one command!** No more confusion between different entry points.
307
+
285
308
  ### Command-Line REPL
286
309
 
287
310
  ```bash
288
311
  # Run the CLI example (TTS ON, STT OFF)
289
- abstractvoice-cli cli
312
+ abstractvoice cli
290
313
 
291
314
  # With debug mode
292
- abstractvoice-cli cli --debug
315
+ abstractvoice cli --debug
316
+
317
+ # With specific language
318
+ abstractvoice cli --language fr
293
319
  ```
294
320
 
295
321
  #### REPL Commands
@@ -341,17 +367,17 @@ All commands must start with `/` except `stop`:
341
367
 
342
368
  ```bash
343
369
  # Run the web API example
344
- abstractvoice-cli web
370
+ abstractvoice web
345
371
 
346
372
  # With different host and port
347
- abstractvoice-cli web --host 0.0.0.0 --port 8000
373
+ abstractvoice web --host 0.0.0.0 --port 8000
348
374
  ```
349
375
 
350
376
  You can also run a simplified version that doesn't load the full models:
351
377
 
352
378
  ```bash
353
379
  # Run the web API with simulation mode
354
- abstractvoice-cli web --simulate
380
+ abstractvoice web --simulate
355
381
  ```
356
382
 
357
383
  #### Troubleshooting Web API
@@ -379,7 +405,7 @@ If you encounter issues with the web API:
379
405
 
380
406
  ```bash
381
407
  # Run the simple example
382
- abstractvoice-cli simple
408
+ abstractvoice simple
383
409
  ```
384
410
 
385
411
  ## Documentation
@@ -15,11 +15,13 @@ 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(" check-deps - Check dependency compatibility")
18
19
  print("\nUsage: python -m abstractvoice <example> [--language <lang>] [args...]")
19
20
  print("\nSupported languages: en, fr, es, de, it, ru, multilingual")
20
21
  print("\nExamples:")
21
22
  print(" python -m abstractvoice cli --language fr # French CLI")
22
23
  print(" python -m abstractvoice simple --language ru # Russian simple example")
24
+ print(" python -m abstractvoice check-deps # Check dependencies")
23
25
 
24
26
 
25
27
  def simple_example():
@@ -95,7 +97,7 @@ def simple_example():
95
97
  def main():
96
98
  """Main entry point."""
97
99
  parser = argparse.ArgumentParser(description="AbstractVoice examples")
98
- parser.add_argument("example", nargs="?", help="Example to run (cli, web, simple)")
100
+ parser.add_argument("example", nargs="?", help="Example to run (cli, web, simple, check-deps)")
99
101
  parser.add_argument("--language", "--lang", default="en",
100
102
  choices=["en", "fr", "es", "de", "it", "ru", "multilingual"],
101
103
  help="Voice language for examples")
@@ -107,6 +109,16 @@ def main():
107
109
  print_examples()
108
110
  return
109
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
+
110
122
  # Set remaining args as sys.argv for the examples, including language
111
123
  if args.language != "en":
112
124
  remaining = ["--language", args.language] + remaining
@@ -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()
@@ -235,8 +235,32 @@ class VoiceREPL(cmd.Cmd):
235
235
  if self.voice_manager:
236
236
  self.voice_manager.speak(response_text)
237
237
 
238
+ except requests.exceptions.ConnectionError as e:
239
+ print(f"❌ Cannot connect to Ollama API at {self.api_url}")
240
+ print(f" Please check that Ollama is running and accessible")
241
+ print(f" Try: ollama serve")
242
+ if self.debug_mode:
243
+ print(f" Connection error: {e}")
244
+ except requests.exceptions.HTTPError as e:
245
+ if "404" in str(e):
246
+ print(f"❌ Model '{self.model}' not found on Ollama server")
247
+ print(f" Available models: Try 'ollama list' to see installed models")
248
+ print(f" To install a model: ollama pull {self.model}")
249
+ else:
250
+ print(f"❌ HTTP error from Ollama API: {e}")
251
+ if self.debug_mode:
252
+ print(f" Full error: {e}")
238
253
  except Exception as e:
239
- print(f"Error: {e}")
254
+ error_msg = str(e).lower()
255
+ if "model file not found" in error_msg or "no such file" in error_msg:
256
+ print(f"❌ Model '{self.model}' not found or not fully downloaded")
257
+ print(f" Try: ollama pull {self.model}")
258
+ print(f" Or use an existing model: ollama list")
259
+ elif "connection" in error_msg or "refused" in error_msg:
260
+ print(f"❌ Cannot connect to Ollama at {self.api_url}")
261
+ print(f" Make sure Ollama is running: ollama serve")
262
+ else:
263
+ print(f"❌ Error: {e}")
240
264
  if self.debug_mode:
241
265
  import traceback
242
266
  traceback.print_exc()