voice-mode 2.32.0__py3-none-any.whl → 2.33.2__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.
- voice_mode/__version__.py +1 -1
- voice_mode/config.py +1 -1
- voice_mode/frontend/.next/BUILD_ID +1 -1
- voice_mode/frontend/.next/app-build-manifest.json +5 -5
- voice_mode/frontend/.next/build-manifest.json +3 -3
- voice_mode/frontend/.next/next-minimal-server.js.nft.json +1 -1
- voice_mode/frontend/.next/next-server.js.nft.json +1 -1
- voice_mode/frontend/.next/prerender-manifest.json +1 -1
- voice_mode/frontend/.next/required-server-files.json +1 -1
- voice_mode/frontend/.next/server/app/_not-found/page.js +1 -1
- voice_mode/frontend/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- voice_mode/frontend/.next/server/app/_not-found.html +1 -1
- voice_mode/frontend/.next/server/app/_not-found.rsc +1 -1
- voice_mode/frontend/.next/server/app/api/connection-details/route.js +2 -2
- voice_mode/frontend/.next/server/app/favicon.ico/route.js +2 -2
- voice_mode/frontend/.next/server/app/index.html +1 -1
- voice_mode/frontend/.next/server/app/index.rsc +2 -2
- voice_mode/frontend/.next/server/app/page.js +2 -2
- voice_mode/frontend/.next/server/app/page_client-reference-manifest.js +1 -1
- voice_mode/frontend/.next/server/chunks/994.js +2 -2
- voice_mode/frontend/.next/server/middleware-build-manifest.js +1 -1
- voice_mode/frontend/.next/server/next-font-manifest.js +1 -1
- voice_mode/frontend/.next/server/next-font-manifest.json +1 -1
- voice_mode/frontend/.next/server/pages/404.html +1 -1
- voice_mode/frontend/.next/server/pages/500.html +1 -1
- voice_mode/frontend/.next/server/server-reference-manifest.json +1 -1
- voice_mode/frontend/.next/standalone/.next/BUILD_ID +1 -1
- voice_mode/frontend/.next/standalone/.next/app-build-manifest.json +5 -5
- voice_mode/frontend/.next/standalone/.next/build-manifest.json +3 -3
- voice_mode/frontend/.next/standalone/.next/prerender-manifest.json +1 -1
- voice_mode/frontend/.next/standalone/.next/required-server-files.json +1 -1
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page.js +1 -1
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found.html +1 -1
- voice_mode/frontend/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- voice_mode/frontend/.next/standalone/.next/server/app/api/connection-details/route.js +2 -2
- voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico/route.js +2 -2
- voice_mode/frontend/.next/standalone/.next/server/app/index.html +1 -1
- voice_mode/frontend/.next/standalone/.next/server/app/index.rsc +2 -2
- voice_mode/frontend/.next/standalone/.next/server/app/page.js +2 -2
- voice_mode/frontend/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- voice_mode/frontend/.next/standalone/.next/server/chunks/994.js +2 -2
- voice_mode/frontend/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
- voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.js +1 -1
- voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.json +1 -1
- voice_mode/frontend/.next/standalone/.next/server/pages/404.html +1 -1
- voice_mode/frontend/.next/standalone/.next/server/pages/500.html +1 -1
- voice_mode/frontend/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- voice_mode/frontend/.next/standalone/server.js +1 -1
- voice_mode/frontend/.next/static/chunks/app/{layout-4c59da29fcf0456f.js → layout-b6b174992f2f6afd.js} +1 -1
- voice_mode/frontend/.next/static/chunks/app/{page-017e11b769f3a746.js → page-52b6f77b58ca7c9d.js} +1 -1
- voice_mode/frontend/.next/static/chunks/{main-app-822552bd94497f44.js → main-app-436d7ffcf2166712.js} +1 -1
- voice_mode/frontend/.next/trace +43 -43
- voice_mode/frontend/.next/types/app/api/connection-details/route.ts +1 -1
- voice_mode/frontend/.next/types/app/layout.ts +1 -1
- voice_mode/frontend/.next/types/app/page.ts +1 -1
- voice_mode/frontend/package-lock.json +3 -3
- voice_mode/prompts/converse.py +0 -1
- voice_mode/templates/__init__.py +1 -0
- voice_mode/templates/launchd/com.voicemode.whisper.plist +7 -13
- voice_mode/templates/scripts/__init__.py +1 -0
- voice_mode/templates/scripts/start-whisper-server.sh +80 -0
- voice_mode/tools/services/whisper/install.py +100 -132
- voice_mode/tools/services/whisper/model_install.py +38 -47
- voice_mode/tools/services/whisper/models.py +1 -1
- voice_mode/utils/services/coreml_setup.py +234 -0
- voice_mode/utils/services/whisper_helpers.py +57 -32
- {voice_mode-2.32.0.dist-info → voice_mode-2.33.2.dist-info}/METADATA +11 -12
- {voice_mode-2.32.0.dist-info → voice_mode-2.33.2.dist-info}/RECORD +73 -69
- /voice_mode/frontend/.next/static/{e8aNOVoFA4vUks2Chn7qv → gdmR4LkC2enrnvJ9K0r0_}/_buildManifest.js +0 -0
- /voice_mode/frontend/.next/static/{e8aNOVoFA4vUks2Chn7qv → gdmR4LkC2enrnvJ9K0r0_}/_ssgManifest.js +0 -0
- {voice_mode-2.32.0.dist-info → voice_mode-2.33.2.dist-info}/WHEEL +0 -0
- {voice_mode-2.32.0.dist-info → voice_mode-2.33.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,234 @@
|
|
1
|
+
"""Setup and manage CoreML Python environment for whisper.cpp."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
import subprocess
|
5
|
+
import logging
|
6
|
+
from pathlib import Path
|
7
|
+
from typing import Optional, Dict, Any
|
8
|
+
|
9
|
+
logger = logging.getLogger("voice-mode")
|
10
|
+
|
11
|
+
|
12
|
+
def setup_coreml_venv(whisper_dir: Path, force: bool = False) -> Dict[str, Any]:
|
13
|
+
"""
|
14
|
+
Setup a dedicated Python virtual environment for CoreML conversion.
|
15
|
+
|
16
|
+
Uses whisper.cpp's requirements-coreml.txt to ensure compatibility.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
whisper_dir: Path to whisper.cpp installation
|
20
|
+
force: Force recreation of venv even if it exists
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
Dict with 'success' and 'python_path' or 'error'
|
24
|
+
"""
|
25
|
+
venv_dir = whisper_dir / "venv-coreml"
|
26
|
+
venv_python = venv_dir / "bin" / "python"
|
27
|
+
requirements_file = whisper_dir / "models" / "requirements-coreml.txt"
|
28
|
+
|
29
|
+
# Check if requirements file exists
|
30
|
+
if not requirements_file.exists():
|
31
|
+
return {
|
32
|
+
"success": False,
|
33
|
+
"error": f"CoreML requirements file not found at {requirements_file}"
|
34
|
+
}
|
35
|
+
|
36
|
+
# Check if venv already exists and is valid
|
37
|
+
if venv_python.exists() and not force:
|
38
|
+
# Test if the venv has the required packages
|
39
|
+
try:
|
40
|
+
result = subprocess.run(
|
41
|
+
[str(venv_python), "-c", "import torch, coremltools, whisper, ane_transformers"],
|
42
|
+
capture_output=True,
|
43
|
+
text=True,
|
44
|
+
timeout=5
|
45
|
+
)
|
46
|
+
if result.returncode == 0:
|
47
|
+
logger.info(f"CoreML venv already exists and is valid at {venv_dir}")
|
48
|
+
return {
|
49
|
+
"success": True,
|
50
|
+
"python_path": str(venv_python),
|
51
|
+
"message": "Using existing CoreML virtual environment"
|
52
|
+
}
|
53
|
+
except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
|
54
|
+
logger.info("Existing CoreML venv is incomplete, will recreate")
|
55
|
+
|
56
|
+
# Create or recreate venv
|
57
|
+
logger.info(f"Creating CoreML virtual environment at {venv_dir}")
|
58
|
+
|
59
|
+
try:
|
60
|
+
# Remove existing venv if force or invalid
|
61
|
+
if venv_dir.exists() and (force or not venv_python.exists()):
|
62
|
+
import shutil
|
63
|
+
shutil.rmtree(venv_dir, ignore_errors=True)
|
64
|
+
|
65
|
+
# Try to use Python 3.11 as recommended by whisper.cpp
|
66
|
+
python_cmd = None
|
67
|
+
for python_version in ["python3.11", "python3.10", "python3.9", "python3"]:
|
68
|
+
if subprocess.run(["which", python_version], capture_output=True).returncode == 0:
|
69
|
+
# Check actual version
|
70
|
+
version_result = subprocess.run(
|
71
|
+
[python_version, "--version"],
|
72
|
+
capture_output=True,
|
73
|
+
text=True
|
74
|
+
)
|
75
|
+
if version_result.returncode == 0:
|
76
|
+
version = version_result.stdout.strip()
|
77
|
+
logger.info(f"Found {version}")
|
78
|
+
# Strongly prefer 3.11 as recommended
|
79
|
+
if "3.11" in version:
|
80
|
+
python_cmd = python_version
|
81
|
+
logger.info("Using Python 3.11 (recommended for CoreML)")
|
82
|
+
break
|
83
|
+
elif "3.10" in version or "3.9" in version:
|
84
|
+
if python_cmd is None: # Use as fallback if no 3.11
|
85
|
+
python_cmd = python_version
|
86
|
+
elif python_cmd is None:
|
87
|
+
python_cmd = python_version # Use as last resort
|
88
|
+
|
89
|
+
if python_cmd is None:
|
90
|
+
return {
|
91
|
+
"success": False,
|
92
|
+
"error": "No suitable Python version found. Python 3.9-3.11 recommended for CoreML."
|
93
|
+
}
|
94
|
+
|
95
|
+
# Create venv
|
96
|
+
logger.info(f"Creating venv with {python_cmd}")
|
97
|
+
result = subprocess.run(
|
98
|
+
[python_cmd, "-m", "venv", str(venv_dir)],
|
99
|
+
capture_output=True,
|
100
|
+
text=True
|
101
|
+
)
|
102
|
+
|
103
|
+
if result.returncode != 0:
|
104
|
+
return {
|
105
|
+
"success": False,
|
106
|
+
"error": f"Failed to create venv: {result.stderr}"
|
107
|
+
}
|
108
|
+
|
109
|
+
# Upgrade pip
|
110
|
+
logger.info("Upgrading pip in CoreML venv")
|
111
|
+
subprocess.run(
|
112
|
+
[str(venv_python), "-m", "pip", "install", "--upgrade", "pip"],
|
113
|
+
capture_output=True,
|
114
|
+
text=True
|
115
|
+
)
|
116
|
+
|
117
|
+
# Install requirements with proper versions
|
118
|
+
# Based on whisper.cpp documentation and coremltools compatibility
|
119
|
+
# Python 3.11 is recommended, torch 2.5.0 is known to work with coremltools
|
120
|
+
logger.info("Installing CoreML requirements with compatible versions")
|
121
|
+
packages = [
|
122
|
+
"torch==2.5.0", # Specific version mentioned in whisper.cpp for coremltools compatibility
|
123
|
+
"coremltools>=7.0",
|
124
|
+
"openai-whisper",
|
125
|
+
"ane_transformers"
|
126
|
+
]
|
127
|
+
|
128
|
+
# Try installing all at once first
|
129
|
+
result = subprocess.run(
|
130
|
+
[str(venv_python), "-m", "pip", "install"] + packages,
|
131
|
+
capture_output=True,
|
132
|
+
text=True
|
133
|
+
)
|
134
|
+
|
135
|
+
if result.returncode != 0:
|
136
|
+
# Try installing packages one by one if bulk install fails
|
137
|
+
logger.warning("Bulk install failed, trying packages individually")
|
138
|
+
|
139
|
+
failed_packages = []
|
140
|
+
for package in packages:
|
141
|
+
logger.info(f"Installing {package}")
|
142
|
+
result = subprocess.run(
|
143
|
+
[str(venv_python), "-m", "pip", "install", package],
|
144
|
+
capture_output=True,
|
145
|
+
text=True
|
146
|
+
)
|
147
|
+
if result.returncode != 0:
|
148
|
+
logger.warning(f"Failed to install {package}: {result.stderr}")
|
149
|
+
failed_packages.append(package)
|
150
|
+
|
151
|
+
if failed_packages:
|
152
|
+
return {
|
153
|
+
"success": False,
|
154
|
+
"error": f"Failed to install packages: {', '.join(failed_packages)}",
|
155
|
+
"partial": True,
|
156
|
+
"python_path": str(venv_python)
|
157
|
+
}
|
158
|
+
|
159
|
+
# Verify installation
|
160
|
+
logger.info("Verifying CoreML dependencies")
|
161
|
+
result = subprocess.run(
|
162
|
+
[str(venv_python), "-c", "import torch, coremltools, whisper, ane_transformers; print('All packages imported successfully')"],
|
163
|
+
capture_output=True,
|
164
|
+
text=True
|
165
|
+
)
|
166
|
+
|
167
|
+
if result.returncode == 0:
|
168
|
+
logger.info("CoreML virtual environment created successfully")
|
169
|
+
return {
|
170
|
+
"success": True,
|
171
|
+
"python_path": str(venv_python),
|
172
|
+
"message": "CoreML virtual environment created with all dependencies"
|
173
|
+
}
|
174
|
+
else:
|
175
|
+
return {
|
176
|
+
"success": True, # Partial success
|
177
|
+
"python_path": str(venv_python),
|
178
|
+
"warning": "Some packages may be missing but environment was created",
|
179
|
+
"verification_error": result.stderr
|
180
|
+
}
|
181
|
+
|
182
|
+
except Exception as e:
|
183
|
+
logger.error(f"Error setting up CoreML venv: {e}")
|
184
|
+
return {
|
185
|
+
"success": False,
|
186
|
+
"error": str(e)
|
187
|
+
}
|
188
|
+
|
189
|
+
|
190
|
+
def get_coreml_python(whisper_dir: Path) -> Optional[str]:
|
191
|
+
"""
|
192
|
+
Get the path to Python executable with CoreML dependencies.
|
193
|
+
|
194
|
+
Checks in order:
|
195
|
+
1. Dedicated venv-coreml environment
|
196
|
+
2. Existing venv environment (if it has CoreML packages)
|
197
|
+
3. None if no suitable environment found
|
198
|
+
|
199
|
+
Args:
|
200
|
+
whisper_dir: Path to whisper.cpp installation
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
Path to Python executable or None
|
204
|
+
"""
|
205
|
+
# Check dedicated CoreML venv first
|
206
|
+
venv_coreml_python = whisper_dir / "venv-coreml" / "bin" / "python"
|
207
|
+
if venv_coreml_python.exists():
|
208
|
+
# Quick check if it has required packages
|
209
|
+
try:
|
210
|
+
result = subprocess.run(
|
211
|
+
[str(venv_coreml_python), "-c", "import torch, coremltools"],
|
212
|
+
capture_output=True,
|
213
|
+
timeout=5
|
214
|
+
)
|
215
|
+
if result.returncode == 0:
|
216
|
+
return str(venv_coreml_python)
|
217
|
+
except:
|
218
|
+
pass
|
219
|
+
|
220
|
+
# Check existing venv as fallback
|
221
|
+
venv_python = whisper_dir / "venv" / "bin" / "python"
|
222
|
+
if venv_python.exists():
|
223
|
+
try:
|
224
|
+
result = subprocess.run(
|
225
|
+
[str(venv_python), "-c", "import torch, coremltools, whisper"],
|
226
|
+
capture_output=True,
|
227
|
+
timeout=5
|
228
|
+
)
|
229
|
+
if result.returncode == 0:
|
230
|
+
return str(venv_python)
|
231
|
+
except:
|
232
|
+
pass
|
233
|
+
|
234
|
+
return None
|
@@ -9,6 +9,8 @@ import shutil
|
|
9
9
|
from pathlib import Path
|
10
10
|
from typing import Optional, List, Dict, Union
|
11
11
|
|
12
|
+
from .coreml_setup import setup_coreml_venv, get_coreml_python
|
13
|
+
|
12
14
|
logger = logging.getLogger("voice-mode")
|
13
15
|
|
14
16
|
def find_whisper_server() -> Optional[str]:
|
@@ -36,14 +38,23 @@ def find_whisper_server() -> Optional[str]:
|
|
36
38
|
|
37
39
|
|
38
40
|
def find_whisper_model() -> Optional[str]:
|
39
|
-
"""Find
|
40
|
-
from voice_mode.config import WHISPER_MODEL_PATH
|
41
|
+
"""Find the active whisper model file based on VOICEMODE_WHISPER_MODEL setting."""
|
42
|
+
from voice_mode.config import WHISPER_MODEL_PATH, WHISPER_MODEL
|
43
|
+
|
44
|
+
# First try to find the specific model configured in VOICEMODE_WHISPER_MODEL
|
45
|
+
model_name = WHISPER_MODEL # This reads from env/config
|
46
|
+
model_filename = f"ggml-{model_name}.bin"
|
41
47
|
|
42
48
|
# Check configured model path
|
43
49
|
model_dir = Path(WHISPER_MODEL_PATH)
|
44
50
|
if model_dir.exists():
|
45
|
-
|
51
|
+
specific_model = model_dir / model_filename
|
52
|
+
if specific_model.exists():
|
53
|
+
return str(specific_model)
|
54
|
+
|
55
|
+
# Fall back to any model if configured model not found
|
46
56
|
for model_file in model_dir.glob("ggml-*.bin"):
|
57
|
+
logger.warning(f"Configured model {model_name} not found, using {model_file.name}")
|
47
58
|
return str(model_file)
|
48
59
|
|
49
60
|
# Check default installation paths
|
@@ -54,7 +65,13 @@ def find_whisper_model() -> Optional[str]:
|
|
54
65
|
|
55
66
|
for default_path in default_paths:
|
56
67
|
if default_path.exists():
|
68
|
+
specific_model = default_path / model_filename
|
69
|
+
if specific_model.exists():
|
70
|
+
return str(specific_model)
|
71
|
+
|
72
|
+
# Fall back to any model
|
57
73
|
for model_file in default_path.glob("ggml-*.bin"):
|
74
|
+
logger.warning(f"Configured model {model_name} not found, using {model_file.name}")
|
58
75
|
return str(model_file)
|
59
76
|
|
60
77
|
return None
|
@@ -63,7 +80,8 @@ def find_whisper_model() -> Optional[str]:
|
|
63
80
|
async def download_whisper_model(
|
64
81
|
model: str,
|
65
82
|
models_dir: Union[str, Path],
|
66
|
-
force_download: bool = False
|
83
|
+
force_download: bool = False,
|
84
|
+
skip_core_ml: bool = False
|
67
85
|
) -> Dict[str, Union[bool, str]]:
|
68
86
|
"""
|
69
87
|
Download a single Whisper model.
|
@@ -72,6 +90,7 @@ async def download_whisper_model(
|
|
72
90
|
model: Model name (e.g., 'large-v2', 'base.en')
|
73
91
|
models_dir: Directory to download models to
|
74
92
|
force_download: Re-download even if model exists
|
93
|
+
skip_core_ml: Skip Core ML conversion even on Apple Silicon
|
75
94
|
|
76
95
|
Returns:
|
77
96
|
Dict with 'success' and optional 'error' or 'path'
|
@@ -144,8 +163,11 @@ async def download_whisper_model(
|
|
144
163
|
"error": f"Model file not found after download: {model_path}"
|
145
164
|
}
|
146
165
|
|
147
|
-
#
|
148
|
-
|
166
|
+
# Initialize core_ml_result
|
167
|
+
core_ml_result = None
|
168
|
+
|
169
|
+
# Check for Core ML support on Apple Silicon (unless explicitly skipped)
|
170
|
+
if platform.system() == "Darwin" and platform.machine() == "arm64" and not skip_core_ml:
|
149
171
|
# Check if Core ML dependencies are needed
|
150
172
|
requirements_file = Path(models_dir) / "requirements-coreml.txt"
|
151
173
|
if requirements_file.exists() and shutil.which("uv"):
|
@@ -185,15 +207,22 @@ async def download_whisper_model(
|
|
185
207
|
else:
|
186
208
|
logger.warning(f"Core ML conversion failed ({error_category}): {core_ml_result.get('error', 'Unknown error')}")
|
187
209
|
|
188
|
-
#
|
189
|
-
|
210
|
+
# Build response with appropriate status
|
211
|
+
response = {
|
190
212
|
"success": True,
|
191
213
|
"path": str(model_path),
|
192
|
-
"message": f"Model {model} downloaded successfully"
|
193
|
-
"core_ml_status": core_ml_result,
|
194
|
-
"acceleration": "coreml" if core_ml_result.get("success") else "metal"
|
214
|
+
"message": f"Model {model} downloaded successfully"
|
195
215
|
}
|
196
216
|
|
217
|
+
# Add Core ML status if attempted
|
218
|
+
if core_ml_result:
|
219
|
+
response["core_ml_status"] = core_ml_result
|
220
|
+
response["acceleration"] = "coreml" if core_ml_result.get("success") else "metal"
|
221
|
+
else:
|
222
|
+
response["acceleration"] = "metal"
|
223
|
+
|
224
|
+
return response
|
225
|
+
|
197
226
|
except subprocess.CalledProcessError as e:
|
198
227
|
logger.error(f"Failed to download model {model}: {e.stderr}")
|
199
228
|
return {
|
@@ -255,28 +284,24 @@ async def convert_to_coreml(
|
|
255
284
|
logger.info(f"Converting {model} to Core ML format...")
|
256
285
|
|
257
286
|
try:
|
258
|
-
#
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
current = current.parent
|
287
|
+
# First, try to get existing CoreML Python environment
|
288
|
+
coreml_python = get_coreml_python(whisper_dir)
|
289
|
+
|
290
|
+
# If no suitable environment exists, set one up
|
291
|
+
if not coreml_python:
|
292
|
+
logger.info("Setting up CoreML Python environment...")
|
293
|
+
setup_result = setup_coreml_venv(whisper_dir)
|
294
|
+
if setup_result["success"]:
|
295
|
+
coreml_python = setup_result.get("python_path")
|
296
|
+
else:
|
297
|
+
logger.warning(f"Could not setup CoreML environment: {setup_result.get('error')}")
|
270
298
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
logger.info("Using uv for Core ML conversion with Python dependencies")
|
275
|
-
# Run from the whisper models directory
|
299
|
+
if coreml_python:
|
300
|
+
# Use the CoreML-enabled Python environment
|
301
|
+
logger.info(f"Using CoreML Python environment: {coreml_python}")
|
276
302
|
script_path = whisper_dir / "models" / "convert-whisper-to-coreml.py"
|
277
303
|
result = subprocess.run(
|
278
|
-
[
|
279
|
-
str(script_path),
|
304
|
+
[coreml_python, str(script_path),
|
280
305
|
"--model", model, "--encoder-only", "True", "--optimize-ane", "True"],
|
281
306
|
cwd=str(whisper_dir / "models"),
|
282
307
|
capture_output=True,
|
@@ -301,8 +326,8 @@ async def convert_to_coreml(
|
|
301
326
|
shutil.rmtree(coreml_path, ignore_errors=True)
|
302
327
|
shutil.move(str(compiled_path), str(coreml_path))
|
303
328
|
else:
|
304
|
-
#
|
305
|
-
logger.
|
329
|
+
# No suitable Python environment available
|
330
|
+
logger.warning("No suitable Python environment for CoreML conversion")
|
306
331
|
# Run from the whisper models directory where the script is located
|
307
332
|
script_dir = convert_script.parent
|
308
333
|
result = subprocess.run(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: voice-mode
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.33.2
|
4
4
|
Summary: VoiceMode - Voice interaction capabilities for AI assistants (formerly voice-mcp)
|
5
5
|
Project-URL: Homepage, https://github.com/mbailey/voicemode
|
6
6
|
Project-URL: Repository, https://github.com/mbailey/voicemode
|
@@ -116,7 +116,15 @@ Natural voice conversations for AI assistants. Voice Mode brings human-like voic
|
|
116
116
|
Install Claude Code with Voice Mode configured and ready to run on Linux, macOS, and Windows WSL:
|
117
117
|
|
118
118
|
```bash
|
119
|
+
# Download and run the installer
|
119
120
|
curl -O https://getvoicemode.com/install.sh && bash install.sh
|
121
|
+
|
122
|
+
# While local voice services can be installed automatically, we recommend
|
123
|
+
# providing an OpenAI API key as a fallback in case local services are unavailable
|
124
|
+
export OPENAI_API_KEY=your-openai-key # Optional but recommended
|
125
|
+
|
126
|
+
# Start a voice conversation
|
127
|
+
claude /voicemode:converse
|
120
128
|
```
|
121
129
|
|
122
130
|
This installer will:
|
@@ -124,16 +132,7 @@ This installer will:
|
|
124
132
|
- Install Claude Code if not already installed
|
125
133
|
- Configure Voice Mode as an MCP server
|
126
134
|
- Set up your system for voice conversations
|
127
|
-
|
128
|
-
After installation, just run:
|
129
|
-
```bash
|
130
|
-
# With OpenAI API (cloud-based, requires API key)
|
131
|
-
export OPENAI_API_KEY=your-openai-key
|
132
|
-
claude converse
|
133
|
-
|
134
|
-
# Or use free local services (Voice Mode will offer to install them)
|
135
|
-
claude converse
|
136
|
-
```
|
135
|
+
- Offer to install free local STT/TTS services if no API key is provided
|
137
136
|
|
138
137
|
### Manual Installation
|
139
138
|
|
@@ -693,7 +692,7 @@ To save all audio files (both TTS output and STT input):
|
|
693
692
|
export VOICEMODE_SAVE_AUDIO=true
|
694
693
|
```
|
695
694
|
|
696
|
-
Audio files are saved to:
|
695
|
+
Audio files are saved to: `~/.voicemode/audio/YYYY/MM/` with timestamps in the filename.
|
697
696
|
|
698
697
|
## Documentation
|
699
698
|
|