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
@@ -1,4 +1,4 @@
|
|
1
|
-
// File: /tmp/build-via-sdist-
|
1
|
+
// File: /tmp/build-via-sdist-tlxizmx7/voice_mode-2.33.2/voice_mode/frontend/app/api/connection-details/route.ts
|
2
2
|
import * as entry from '../../../../../app/api/connection-details/route.js'
|
3
3
|
import type { NextRequest } from 'next/server.js'
|
4
4
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
// File: /tmp/build-via-sdist-
|
1
|
+
// File: /tmp/build-via-sdist-tlxizmx7/voice_mode-2.33.2/voice_mode/frontend/app/layout.tsx
|
2
2
|
import * as entry from '../../../app/layout.js'
|
3
3
|
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
|
4
4
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
// File: /tmp/build-via-sdist-
|
1
|
+
// File: /tmp/build-via-sdist-tlxizmx7/voice_mode-2.33.2/voice_mode/frontend/app/page.tsx
|
2
2
|
import * as entry from '../../../app/page.js'
|
3
3
|
import type { ResolvingMetadata, ResolvingViewport } from 'next/dist/lib/metadata/types/metadata-interface.js'
|
4
4
|
|
@@ -1774,9 +1774,9 @@
|
|
1774
1774
|
"license": "MIT"
|
1775
1775
|
},
|
1776
1776
|
"node_modules/electron-to-chromium": {
|
1777
|
-
"version": "1.5.
|
1778
|
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.
|
1779
|
-
"integrity": "sha512-
|
1777
|
+
"version": "1.5.209",
|
1778
|
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz",
|
1779
|
+
"integrity": "sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==",
|
1780
1780
|
"dev": true,
|
1781
1781
|
"license": "ISC"
|
1782
1782
|
},
|
voice_mode/prompts/converse.py
CHANGED
@@ -10,7 +10,6 @@ def converse() -> str:
|
|
10
10
|
"Using tools from voice-mode, have an ongoing two-way conversation",
|
11
11
|
"End the chat when the user indicates they want to end it",
|
12
12
|
"Keep your utterances brief unless a longer response is requested or necessary",
|
13
|
-
"Listen for up to 120 seconds per response"
|
14
13
|
]
|
15
14
|
|
16
15
|
return "\n".join(f"- {instruction}" for instruction in instructions)
|
@@ -0,0 +1 @@
|
|
1
|
+
# Templates package for Voice Mode
|
@@ -1,32 +1,26 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
2
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
-
<!-- com.voicemode.whisper.plist v1.
|
4
|
-
<!-- Last updated: 2025-
|
5
|
-
<!--
|
3
|
+
<!-- com.voicemode.whisper.plist v1.1.0 -->
|
4
|
+
<!-- Last updated: 2025-08-25 -->
|
5
|
+
<!-- Uses unified startup script for dynamic model selection -->
|
6
6
|
<plist version="1.0">
|
7
7
|
<dict>
|
8
8
|
<key>Label</key>
|
9
9
|
<string>com.voicemode.whisper</string>
|
10
10
|
<key>ProgramArguments</key>
|
11
11
|
<array>
|
12
|
-
<string>{
|
13
|
-
<string>--host</string>
|
14
|
-
<string>0.0.0.0</string>
|
15
|
-
<string>--port</string>
|
16
|
-
<string>{WHISPER_PORT}</string>
|
17
|
-
<string>--model</string>
|
18
|
-
<string>{MODEL_FILE}</string>
|
12
|
+
<string>{START_SCRIPT_PATH}</string>
|
19
13
|
</array>
|
20
14
|
<key>RunAtLoad</key>
|
21
15
|
<true/>
|
22
16
|
<key>KeepAlive</key>
|
23
17
|
<true/>
|
24
18
|
<key>StandardOutPath</key>
|
25
|
-
<string>{LOG_DIR}/whisper.out.log</string>
|
19
|
+
<string>{LOG_DIR}/whisper/whisper.out.log</string>
|
26
20
|
<key>StandardErrorPath</key>
|
27
|
-
<string>{LOG_DIR}/whisper.err.log</string>
|
21
|
+
<string>{LOG_DIR}/whisper/whisper.err.log</string>
|
28
22
|
<key>WorkingDirectory</key>
|
29
|
-
<string>{
|
23
|
+
<string>{INSTALL_DIR}</string>
|
30
24
|
<key>EnvironmentVariables</key>
|
31
25
|
<dict>
|
32
26
|
<key>PATH</key>
|
@@ -0,0 +1 @@
|
|
1
|
+
# Script templates for Voice Mode services
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Whisper Service Startup Script
|
4
|
+
# This script is used by both macOS (launchd) and Linux (systemd) to start the whisper service
|
5
|
+
# It sources the voicemode.env file to get configuration, especially VOICEMODE_WHISPER_MODEL
|
6
|
+
|
7
|
+
# Determine whisper directory (script is in bin/, whisper root is parent)
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
9
|
+
WHISPER_DIR="$(dirname "$SCRIPT_DIR")"
|
10
|
+
|
11
|
+
# Voicemode configuration directory
|
12
|
+
VOICEMODE_DIR="$HOME/.voicemode"
|
13
|
+
LOG_DIR="$VOICEMODE_DIR/logs/whisper"
|
14
|
+
|
15
|
+
# Create log directory if it doesn't exist
|
16
|
+
mkdir -p "$LOG_DIR"
|
17
|
+
|
18
|
+
# Log file for this script (separate from whisper server logs)
|
19
|
+
STARTUP_LOG="$LOG_DIR/startup.log"
|
20
|
+
|
21
|
+
# Source voicemode configuration if it exists
|
22
|
+
if [ -f "$VOICEMODE_DIR/voicemode.env" ]; then
|
23
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Sourcing voicemode.env" >> "$STARTUP_LOG"
|
24
|
+
source "$VOICEMODE_DIR/voicemode.env"
|
25
|
+
else
|
26
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Warning: voicemode.env not found" >> "$STARTUP_LOG"
|
27
|
+
fi
|
28
|
+
|
29
|
+
# Model selection with environment variable support
|
30
|
+
MODEL_NAME="${VOICEMODE_WHISPER_MODEL:-base}"
|
31
|
+
MODEL_PATH="$WHISPER_DIR/models/ggml-$MODEL_NAME.bin"
|
32
|
+
|
33
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting whisper-server with model: $MODEL_NAME" >> "$STARTUP_LOG"
|
34
|
+
|
35
|
+
# Check if model exists
|
36
|
+
if [ ! -f "$MODEL_PATH" ]; then
|
37
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Error: Model $MODEL_NAME not found at $MODEL_PATH" >> "$STARTUP_LOG"
|
38
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Available models:" >> "$STARTUP_LOG"
|
39
|
+
ls -1 "$WHISPER_DIR/models/" 2>/dev/null | grep "^ggml-.*\.bin$" >> "$STARTUP_LOG"
|
40
|
+
|
41
|
+
# Try to find any available model as fallback
|
42
|
+
FALLBACK_MODEL=$(ls -1 "$WHISPER_DIR/models/" 2>/dev/null | grep "^ggml-.*\.bin$" | head -1)
|
43
|
+
if [ -n "$FALLBACK_MODEL" ]; then
|
44
|
+
MODEL_PATH="$WHISPER_DIR/models/$FALLBACK_MODEL"
|
45
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Using fallback model: $FALLBACK_MODEL" >> "$STARTUP_LOG"
|
46
|
+
else
|
47
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Fatal: No whisper models found" >> "$STARTUP_LOG"
|
48
|
+
exit 1
|
49
|
+
fi
|
50
|
+
fi
|
51
|
+
|
52
|
+
# Port configuration (with environment variable support)
|
53
|
+
WHISPER_PORT="${VOICEMODE_WHISPER_PORT:-2022}"
|
54
|
+
|
55
|
+
# Determine server binary location
|
56
|
+
# Check new CMake build location first, then legacy location
|
57
|
+
if [ -f "$WHISPER_DIR/build/bin/whisper-server" ]; then
|
58
|
+
SERVER_BIN="$WHISPER_DIR/build/bin/whisper-server"
|
59
|
+
elif [ -f "$WHISPER_DIR/server" ]; then
|
60
|
+
SERVER_BIN="$WHISPER_DIR/server"
|
61
|
+
else
|
62
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Error: whisper-server binary not found" >> "$STARTUP_LOG"
|
63
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Checked: $WHISPER_DIR/build/bin/whisper-server" >> "$STARTUP_LOG"
|
64
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Checked: $WHISPER_DIR/server" >> "$STARTUP_LOG"
|
65
|
+
exit 1
|
66
|
+
fi
|
67
|
+
|
68
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Using binary: $SERVER_BIN" >> "$STARTUP_LOG"
|
69
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Model path: $MODEL_PATH" >> "$STARTUP_LOG"
|
70
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Port: $WHISPER_PORT" >> "$STARTUP_LOG"
|
71
|
+
|
72
|
+
# Start whisper-server
|
73
|
+
# Using exec to replace this script process with whisper-server
|
74
|
+
cd "$WHISPER_DIR"
|
75
|
+
exec "$SERVER_BIN" \
|
76
|
+
--host 0.0.0.0 \
|
77
|
+
--port "$WHISPER_PORT" \
|
78
|
+
--model "$MODEL_PATH" \
|
79
|
+
--inference-path /v1/audio/transcriptions \
|
80
|
+
--threads 8
|
@@ -11,6 +11,11 @@ from pathlib import Path
|
|
11
11
|
from typing import Dict, Any, Optional, Union
|
12
12
|
import asyncio
|
13
13
|
import aiohttp
|
14
|
+
try:
|
15
|
+
from importlib.resources import files
|
16
|
+
except ImportError:
|
17
|
+
# Python < 3.9 fallback
|
18
|
+
from importlib_resources import files
|
14
19
|
|
15
20
|
from voice_mode.server import mcp
|
16
21
|
from voice_mode.config import SERVICE_AUTO_ENABLE
|
@@ -28,7 +33,7 @@ logger = logging.getLogger("voice-mode")
|
|
28
33
|
@mcp.tool()
|
29
34
|
async def whisper_install(
|
30
35
|
install_dir: Optional[str] = None,
|
31
|
-
model: str = "
|
36
|
+
model: str = "base",
|
32
37
|
use_gpu: Optional[Union[bool, str]] = None,
|
33
38
|
force_reinstall: Union[bool, str] = False,
|
34
39
|
auto_enable: Optional[Union[bool, str]] = None,
|
@@ -42,7 +47,7 @@ async def whisper_install(
|
|
42
47
|
Args:
|
43
48
|
install_dir: Directory to install whisper.cpp (default: ~/.voicemode/whisper.cpp)
|
44
49
|
model: Whisper model to download (tiny, base, small, medium, large-v2, large-v3, etc.)
|
45
|
-
Default is
|
50
|
+
Default is base for good balance of speed and accuracy (142MB).
|
46
51
|
use_gpu: Enable GPU support if available (default: auto-detect)
|
47
52
|
force_reinstall: Force reinstallation even if already installed
|
48
53
|
auto_enable: Enable service after install. If None, uses VOICEMODE_SERVICE_AUTO_ENABLE config.
|
@@ -214,7 +219,8 @@ async def whisper_install(
|
|
214
219
|
if is_macos:
|
215
220
|
# On macOS, always enable Metal
|
216
221
|
cmake_flags.append("-DGGML_METAL=ON")
|
217
|
-
# On Apple Silicon, also enable Core ML
|
222
|
+
# On Apple Silicon, also enable Core ML support with fallback
|
223
|
+
# This allows using CoreML models if available, but falls back to Metal if not
|
218
224
|
if platform.machine() == "arm64":
|
219
225
|
cmake_flags.append("-DWHISPER_COREML=ON")
|
220
226
|
cmake_flags.append("-DWHISPER_COREML_ALLOW_FALLBACK=ON")
|
@@ -302,59 +308,34 @@ async def whisper_install(
|
|
302
308
|
if 'original_dir' in locals():
|
303
309
|
os.chdir(original_dir)
|
304
310
|
|
305
|
-
#
|
306
|
-
logger.info("
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
# Note: whisper-server is now built as part of the main build target
|
333
|
-
|
334
|
-
# Determine server binary location
|
335
|
-
if [ -f "$WHISPER_DIR/build/bin/whisper-server" ]; then
|
336
|
-
SERVER_BIN="$WHISPER_DIR/build/bin/whisper-server"
|
337
|
-
elif [ -f "$WHISPER_DIR/server" ]; then
|
338
|
-
SERVER_BIN="$WHISPER_DIR/server"
|
339
|
-
else
|
340
|
-
echo "Error: whisper-server binary not found" >> "$LOG_FILE"
|
341
|
-
exit 1
|
342
|
-
fi
|
343
|
-
|
344
|
-
# Start whisper-server
|
345
|
-
cd "$WHISPER_DIR"
|
346
|
-
exec "$SERVER_BIN" \\
|
347
|
-
--model "$MODEL_PATH" \\
|
348
|
-
--host 0.0.0.0 \\
|
349
|
-
--port 2022 \\
|
350
|
-
--inference-path /v1/audio/transcriptions \\
|
351
|
-
--threads 8 \\
|
352
|
-
>> "$LOG_FILE" 2>&1
|
353
|
-
"""
|
354
|
-
|
355
|
-
start_script_path = os.path.join(install_dir, "start-whisper-server.sh")
|
311
|
+
# Copy template start script for whisper-server
|
312
|
+
logger.info("Installing whisper-server start script from template...")
|
313
|
+
|
314
|
+
# Create bin directory
|
315
|
+
bin_dir = os.path.join(install_dir, "bin")
|
316
|
+
os.makedirs(bin_dir, exist_ok=True)
|
317
|
+
|
318
|
+
# Copy template script
|
319
|
+
template_content = None
|
320
|
+
|
321
|
+
# First try to load from source if running in development
|
322
|
+
source_template = Path(__file__).parent.parent.parent / "templates" / "scripts" / "start-whisper-server.sh"
|
323
|
+
if source_template.exists():
|
324
|
+
logger.info(f"Loading template from source: {source_template}")
|
325
|
+
template_content = source_template.read_text()
|
326
|
+
else:
|
327
|
+
# Try loading from package resources
|
328
|
+
try:
|
329
|
+
template_resource = files("voice_mode.templates.scripts").joinpath("start-whisper-server.sh")
|
330
|
+
template_content = template_resource.read_text()
|
331
|
+
logger.info("Loaded template from package resources")
|
332
|
+
except Exception as e:
|
333
|
+
logger.warning(f"Failed to load template script: {e}. Using fallback inline script.")
|
334
|
+
|
335
|
+
# Create the start script (whether template was loaded from file or created inline)
|
336
|
+
start_script_path = os.path.join(bin_dir, "start-whisper-server.sh")
|
356
337
|
with open(start_script_path, 'w') as f:
|
357
|
-
f.write(
|
338
|
+
f.write(template_content)
|
358
339
|
os.chmod(start_script_path, 0o755)
|
359
340
|
|
360
341
|
# Install launchagent on macOS
|
@@ -370,33 +351,22 @@ exec "$SERVER_BIN" \\
|
|
370
351
|
plist_name = "com.voicemode.whisper.plist"
|
371
352
|
plist_path = os.path.join(launchagents_dir, plist_name)
|
372
353
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
<key>StandardOutPath</key>
|
390
|
-
<string>{os.path.join(voicemode_dir, 'logs', 'whisper', 'whisper.out.log')}</string>
|
391
|
-
<key>StandardErrorPath</key>
|
392
|
-
<string>{os.path.join(voicemode_dir, 'logs', 'whisper', 'whisper.err.log')}</string>
|
393
|
-
<key>EnvironmentVariables</key>
|
394
|
-
<dict>
|
395
|
-
<key>PATH</key>
|
396
|
-
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin</string>
|
397
|
-
</dict>
|
398
|
-
</dict>
|
399
|
-
</plist>"""
|
354
|
+
# Load plist template
|
355
|
+
# First try to load from source if running in development
|
356
|
+
source_template = Path(__file__).parent.parent.parent / "templates" / "launchd" / "com.voicemode.whisper.plist"
|
357
|
+
if source_template.exists():
|
358
|
+
logger.info(f"Loading plist template from source: {source_template}")
|
359
|
+
plist_content = source_template.read_text()
|
360
|
+
else:
|
361
|
+
# Load from package resources
|
362
|
+
template_resource = files("voice_mode.templates.launchd").joinpath("com.voicemode.whisper.plist")
|
363
|
+
plist_content = template_resource.read_text()
|
364
|
+
logger.info("Loaded plist template from package resources")
|
365
|
+
|
366
|
+
# Replace placeholders
|
367
|
+
plist_content = plist_content.replace("{START_SCRIPT_PATH}", start_script_path)
|
368
|
+
plist_content = plist_content.replace("{LOG_DIR}", os.path.join(voicemode_dir, 'logs'))
|
369
|
+
plist_content = plist_content.replace("{INSTALL_DIR}", install_dir)
|
400
370
|
|
401
371
|
with open(plist_path, 'w') as f:
|
402
372
|
f.write(plist_content)
|
@@ -444,9 +414,8 @@ exec "$SERVER_BIN" \\
|
|
444
414
|
"start_script": start_script_path,
|
445
415
|
"message": f"Successfully installed whisper.cpp {current_version} with {gpu_type} support and whisper-server on port 2022{enable_message}{' (' + migration_msg + ')' if migration_msg else ''}"
|
446
416
|
}
|
447
|
-
|
448
|
-
# Install systemd service on Linux
|
449
417
|
elif system == "Linux":
|
418
|
+
# Install systemd service on Linux
|
450
419
|
logger.info("Installing systemd user service for whisper-server...")
|
451
420
|
systemd_user_dir = os.path.expanduser("~/.config/systemd/user")
|
452
421
|
os.makedirs(systemd_user_dir, exist_ok=True)
|
@@ -459,23 +428,22 @@ exec "$SERVER_BIN" \\
|
|
459
428
|
service_path = os.path.join(systemd_user_dir, service_name)
|
460
429
|
|
461
430
|
service_content = f"""[Unit]
|
462
|
-
Description=Whisper.cpp Speech Recognition Server
|
463
|
-
After=network.target
|
431
|
+
Description=Whisper.cpp Speech Recognition Server
|
432
|
+
After=network.target
|
464
433
|
|
465
|
-
[Service]
|
466
|
-
Type=simple
|
467
|
-
ExecStart={start_script_path}
|
468
|
-
Restart=on-failure
|
469
|
-
RestartSec=10
|
470
|
-
WorkingDirectory={install_dir}
|
471
|
-
StandardOutput=append:{os.path.join(voicemode_dir, 'logs', 'whisper', 'whisper.out.log')}
|
472
|
-
StandardError=append:{os.path.join(voicemode_dir, 'logs', 'whisper', 'whisper.err.log')}
|
473
|
-
Environment="PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/cuda/bin"
|
474
|
-
Environment="VOICEMODE_WHISPER_MODEL={model}"
|
434
|
+
[Service]
|
435
|
+
Type=simple
|
436
|
+
ExecStart={start_script_path}
|
437
|
+
Restart=on-failure
|
438
|
+
RestartSec=10
|
439
|
+
WorkingDirectory={install_dir}
|
440
|
+
StandardOutput=append:{os.path.join(voicemode_dir, 'logs', 'whisper', 'whisper.out.log')}
|
441
|
+
StandardError=append:{os.path.join(voicemode_dir, 'logs', 'whisper', 'whisper.err.log')}
|
442
|
+
Environment="PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/cuda/bin"
|
475
443
|
|
476
|
-
[Install]
|
477
|
-
WantedBy=default.target
|
478
|
-
"""
|
444
|
+
[Install]
|
445
|
+
WantedBy=default.target
|
446
|
+
"""
|
479
447
|
|
480
448
|
with open(service_path, 'w') as f:
|
481
449
|
f.write(service_content)
|
@@ -510,49 +478,49 @@ WantedBy=default.target
|
|
510
478
|
current_version = get_current_version(Path(install_dir))
|
511
479
|
return {
|
512
480
|
"success": True,
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
481
|
+
"install_path": install_dir,
|
482
|
+
"model_path": model_path,
|
483
|
+
"gpu_enabled": use_gpu,
|
484
|
+
"gpu_type": gpu_type,
|
485
|
+
"version": current_version,
|
486
|
+
"performance_info": {
|
487
|
+
"system": system,
|
488
|
+
"gpu_acceleration": gpu_type,
|
489
|
+
"model": model,
|
490
|
+
"binary_path": main_path if 'main_path' in locals() else os.path.join(install_dir, "main"),
|
491
|
+
"server_port": 2022,
|
492
|
+
"server_url": "http://localhost:2022"
|
493
|
+
},
|
494
|
+
"systemd_service": service_path,
|
495
|
+
"systemd_enabled": systemd_enabled,
|
496
|
+
"start_script": start_script_path,
|
497
|
+
"message": f"Successfully installed whisper.cpp {current_version} with {gpu_type} support. {systemd_message}{enable_message}{' (' + migration_msg + ')' if migration_msg else ''}"
|
530
498
|
}
|
531
|
-
|
499
|
+
|
532
500
|
else:
|
533
501
|
# Handle auto_enable for other systems (if we add Windows support later)
|
534
502
|
enable_message = ""
|
535
503
|
if auto_enable is None:
|
536
|
-
|
504
|
+
auto_enable = SERVICE_AUTO_ENABLE
|
537
505
|
|
538
506
|
if auto_enable:
|
539
|
-
|
507
|
+
logger.info("Auto-enable not supported on this platform")
|
540
508
|
|
541
509
|
current_version = get_current_version(Path(install_dir))
|
542
510
|
return {
|
543
511
|
"success": True,
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
512
|
+
"install_path": install_dir,
|
513
|
+
"model_path": model_path,
|
514
|
+
"gpu_enabled": use_gpu,
|
515
|
+
"gpu_type": gpu_type,
|
516
|
+
"version": current_version,
|
517
|
+
"performance_info": {
|
518
|
+
"system": system,
|
519
|
+
"gpu_acceleration": gpu_type,
|
520
|
+
"model": model,
|
521
|
+
"binary_path": main_path if 'main_path' in locals() else os.path.join(install_dir, "main")
|
522
|
+
},
|
523
|
+
"message": f"Successfully installed whisper.cpp {current_version} with {gpu_type} support{enable_message}{' (' + migration_msg + ')' if migration_msg else ''}"
|
556
524
|
}
|
557
525
|
|
558
526
|
except subprocess.CalledProcessError as e:
|
@@ -569,4 +537,4 @@ WantedBy=default.target
|
|
569
537
|
return {
|
570
538
|
"success": False,
|
571
539
|
"error": str(e)
|
572
|
-
}
|
540
|
+
}
|
@@ -127,7 +127,8 @@ async def whisper_model_install(
|
|
127
127
|
result = await download_whisper_model(
|
128
128
|
model_name,
|
129
129
|
actual_models_dir,
|
130
|
-
force_download=force_download
|
130
|
+
force_download=force_download,
|
131
|
+
skip_core_ml=skip_core_ml
|
131
132
|
)
|
132
133
|
|
133
134
|
# Build comprehensive result entry
|
@@ -242,58 +243,48 @@ async def _handle_coreml_dependencies(
|
|
242
243
|
if skip_core_ml:
|
243
244
|
return {"continue": True}
|
244
245
|
|
245
|
-
# Check if
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
246
|
+
# Check if the CoreML environment already exists
|
247
|
+
whisper_dir = Path.home() / ".voicemode" / "services" / "whisper"
|
248
|
+
venv_coreml = whisper_dir / "venv-coreml" / "bin" / "python"
|
249
|
+
|
250
|
+
if venv_coreml.exists():
|
251
|
+
# Test if it has the required packages
|
252
|
+
try:
|
253
|
+
result = subprocess.run(
|
254
|
+
[str(venv_coreml), "-c", "import torch, coremltools, whisper"],
|
255
|
+
capture_output=True,
|
256
|
+
timeout=5
|
257
|
+
)
|
258
|
+
if result.returncode == 0:
|
259
|
+
logger.info("CoreML environment already exists and is valid")
|
260
|
+
# Return with a flag indicating CoreML is ready
|
261
|
+
return {
|
262
|
+
"continue": True,
|
263
|
+
"coreml_ready": True,
|
264
|
+
"coreml_deps_note": "CoreML environment exists and is valid"
|
265
|
+
}
|
266
|
+
except:
|
267
|
+
pass
|
252
268
|
|
253
|
-
# Check if user wants to
|
269
|
+
# Check if user wants to create CoreML environment
|
254
270
|
if not install_torch and not auto_confirm:
|
255
271
|
return {
|
256
272
|
"continue": False,
|
257
273
|
"success": False,
|
258
274
|
"requires_confirmation": True,
|
259
|
-
"message": "CoreML requires PyTorch
|
260
|
-
"recommendation": "Set install_torch=True for CoreML acceleration (2-3x faster)"
|
275
|
+
"message": "CoreML conversion requires a dedicated Python environment with PyTorch. Setup may download up to 2.5GB if packages aren't cached.",
|
276
|
+
"recommendation": "💡 Set install_torch=True for CoreML acceleration (2-3x faster)"
|
261
277
|
}
|
262
278
|
|
263
|
-
#
|
264
|
-
|
279
|
+
# Note: We don't actually install CoreML dependencies in the voicemode environment anymore
|
280
|
+
# The CoreML conversion uses its own dedicated environment in ~/.voicemode/services/whisper/venv-coreml
|
281
|
+
# This is handled automatically by whisper_helpers.convert_to_coreml()
|
265
282
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
else:
|
275
|
-
# Fallback to pip
|
276
|
-
cmd = [sys.executable, "-m", "pip", "install"] + packages
|
277
|
-
logger.info("Installing via pip...")
|
278
|
-
|
279
|
-
# Run installation
|
280
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
281
|
-
|
282
|
-
if result.returncode == 0:
|
283
|
-
logger.info("CoreML dependencies installed successfully")
|
284
|
-
return {"continue": True, "coreml_deps_installed": True}
|
285
|
-
else:
|
286
|
-
logger.warning(f"Failed to install CoreML dependencies: {result.stderr}")
|
287
|
-
return {
|
288
|
-
"continue": True,
|
289
|
-
"coreml_deps_failed": True,
|
290
|
-
"warning": "CoreML dependencies installation failed. Models will use Metal acceleration."
|
291
|
-
}
|
292
|
-
|
293
|
-
except Exception as e:
|
294
|
-
logger.warning(f"Error installing CoreML dependencies: {e}")
|
295
|
-
return {
|
296
|
-
"continue": True,
|
297
|
-
"coreml_deps_failed": True,
|
298
|
-
"warning": f"CoreML setup error: {str(e)}. Models will use Metal acceleration."
|
299
|
-
}
|
283
|
+
logger.info("CoreML dependencies will be handled by the conversion process")
|
284
|
+
|
285
|
+
# We still return success to continue with the model download
|
286
|
+
# The actual CoreML environment setup happens during conversion
|
287
|
+
return {
|
288
|
+
"continue": True,
|
289
|
+
"coreml_deps_note": "CoreML environment will be created during conversion if needed"
|
290
|
+
}
|