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.
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/PKG-INFO +69 -75
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/README.md +32 -6
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/__main__.py +13 -1
- abstractvoice-0.3.0/abstractvoice/dependency_check.py +274 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/examples/cli_repl.py +25 -1
- abstractvoice-0.3.0/abstractvoice/examples/voice_cli.py +249 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/tts/tts_engine.py +64 -10
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/PKG-INFO +69 -75
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/SOURCES.txt +1 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/entry_points.txt +0 -1
- abstractvoice-0.3.0/abstractvoice.egg-info/requires.txt +71 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/pyproject.toml +32 -63
- abstractvoice-0.2.0/abstractvoice/examples/voice_cli.py +0 -99
- abstractvoice-0.2.0/abstractvoice.egg-info/requires.txt +0 -104
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/LICENSE +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/__init__.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/examples/__init__.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/examples/web_api.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/recognition.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/stt/__init__.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/stt/transcriber.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/tts/__init__.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/vad/__init__.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/vad/voice_detector.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice/voice_manager.py +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/dependency_links.txt +0 -0
- {abstractvoice-0.2.0 → abstractvoice-0.3.0}/abstractvoice.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
|
29
|
-
Requires-Dist: torch
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
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
|
|
43
|
-
Requires-Dist: torch
|
|
44
|
-
Requires-Dist:
|
|
45
|
-
Requires-Dist:
|
|
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:
|
|
54
|
-
Requires-Dist: sounddevice>=0.4.6; extra == "
|
|
55
|
-
Requires-Dist: webrtcvad>=2.0.10; extra == "
|
|
56
|
-
Requires-Dist: PyAudio>=0.2.13; extra == "
|
|
57
|
-
Requires-Dist: openai-whisper>=20230314; extra == "
|
|
58
|
-
Requires-Dist: coqui-tts
|
|
59
|
-
Requires-Dist: torch
|
|
60
|
-
Requires-Dist:
|
|
61
|
-
Requires-Dist:
|
|
62
|
-
Requires-Dist:
|
|
63
|
-
Requires-Dist:
|
|
64
|
-
Requires-Dist: tiktoken>=0.6.0; extra == "
|
|
65
|
-
Provides-Extra:
|
|
66
|
-
Requires-Dist:
|
|
67
|
-
Requires-Dist:
|
|
68
|
-
Requires-Dist:
|
|
69
|
-
Requires-Dist:
|
|
70
|
-
Requires-Dist:
|
|
71
|
-
|
|
72
|
-
Requires-Dist:
|
|
73
|
-
Requires-Dist:
|
|
74
|
-
|
|
75
|
-
Requires-Dist:
|
|
76
|
-
Requires-Dist:
|
|
77
|
-
|
|
78
|
-
Requires-Dist:
|
|
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
|
|
394
|
+
abstractvoice cli
|
|
404
395
|
|
|
405
396
|
# With debug mode
|
|
406
|
-
abstractvoice
|
|
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
|
|
452
|
+
abstractvoice web
|
|
459
453
|
|
|
460
454
|
# With different host and port
|
|
461
|
-
abstractvoice
|
|
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
|
|
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
|
|
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
|
|
312
|
+
abstractvoice cli
|
|
290
313
|
|
|
291
314
|
# With debug mode
|
|
292
|
-
abstractvoice
|
|
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
|
|
370
|
+
abstractvoice web
|
|
345
371
|
|
|
346
372
|
# With different host and port
|
|
347
|
-
abstractvoice
|
|
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
|
|
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
|
|
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
|
-
|
|
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()
|