voice-mode 2.23.0__py3-none-any.whl → 2.25.0__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.
Files changed (64) hide show
  1. voice_mode/__version__.py +1 -1
  2. voice_mode/cli.py +49 -25
  3. voice_mode/config.py +15 -5
  4. voice_mode/frontend/.next/BUILD_ID +1 -1
  5. voice_mode/frontend/.next/app-build-manifest.json +5 -5
  6. voice_mode/frontend/.next/build-manifest.json +3 -3
  7. voice_mode/frontend/.next/next-minimal-server.js.nft.json +1 -1
  8. voice_mode/frontend/.next/next-server.js.nft.json +1 -1
  9. voice_mode/frontend/.next/prerender-manifest.json +1 -1
  10. voice_mode/frontend/.next/required-server-files.json +1 -1
  11. voice_mode/frontend/.next/server/app/_not-found/page.js +1 -1
  12. voice_mode/frontend/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. voice_mode/frontend/.next/server/app/_not-found.html +1 -1
  14. voice_mode/frontend/.next/server/app/_not-found.rsc +1 -1
  15. voice_mode/frontend/.next/server/app/api/connection-details/route.js +2 -2
  16. voice_mode/frontend/.next/server/app/favicon.ico/route.js +2 -2
  17. voice_mode/frontend/.next/server/app/index.html +1 -1
  18. voice_mode/frontend/.next/server/app/index.rsc +2 -2
  19. voice_mode/frontend/.next/server/app/page.js +2 -2
  20. voice_mode/frontend/.next/server/app/page_client-reference-manifest.js +1 -1
  21. voice_mode/frontend/.next/server/chunks/994.js +1 -1
  22. voice_mode/frontend/.next/server/middleware-build-manifest.js +1 -1
  23. voice_mode/frontend/.next/server/next-font-manifest.js +1 -1
  24. voice_mode/frontend/.next/server/next-font-manifest.json +1 -1
  25. voice_mode/frontend/.next/server/pages/404.html +1 -1
  26. voice_mode/frontend/.next/server/pages/500.html +1 -1
  27. voice_mode/frontend/.next/server/server-reference-manifest.json +1 -1
  28. voice_mode/frontend/.next/standalone/.next/BUILD_ID +1 -1
  29. voice_mode/frontend/.next/standalone/.next/app-build-manifest.json +5 -5
  30. voice_mode/frontend/.next/standalone/.next/build-manifest.json +3 -3
  31. voice_mode/frontend/.next/standalone/.next/prerender-manifest.json +1 -1
  32. voice_mode/frontend/.next/standalone/.next/required-server-files.json +1 -1
  33. voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page.js +1 -1
  34. voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  35. voice_mode/frontend/.next/standalone/.next/server/app/_not-found.html +1 -1
  36. voice_mode/frontend/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  37. voice_mode/frontend/.next/standalone/.next/server/app/api/connection-details/route.js +2 -2
  38. voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico/route.js +2 -2
  39. voice_mode/frontend/.next/standalone/.next/server/app/index.html +1 -1
  40. voice_mode/frontend/.next/standalone/.next/server/app/index.rsc +2 -2
  41. voice_mode/frontend/.next/standalone/.next/server/app/page.js +2 -2
  42. voice_mode/frontend/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  43. voice_mode/frontend/.next/standalone/.next/server/chunks/994.js +1 -1
  44. voice_mode/frontend/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
  45. voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.js +1 -1
  46. voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.json +1 -1
  47. voice_mode/frontend/.next/standalone/.next/server/pages/404.html +1 -1
  48. voice_mode/frontend/.next/standalone/.next/server/pages/500.html +1 -1
  49. voice_mode/frontend/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  50. voice_mode/frontend/.next/standalone/server.js +1 -1
  51. voice_mode/frontend/.next/static/chunks/app/{layout-b625329e9ada0473.js → layout-5675f418c42e0ae3.js} +1 -1
  52. voice_mode/frontend/.next/static/chunks/app/{page-983279b94d1fda4b.js → page-dbb3615cf25cdc16.js} +1 -1
  53. voice_mode/frontend/.next/static/chunks/{main-app-55293f39223cd65f.js → main-app-d25858d98b1d322b.js} +1 -1
  54. voice_mode/frontend/.next/trace +43 -43
  55. voice_mode/frontend/.next/types/app/api/connection-details/route.ts +1 -1
  56. voice_mode/frontend/.next/types/app/layout.ts +1 -1
  57. voice_mode/frontend/.next/types/app/page.ts +1 -1
  58. voice_mode/tools/converse.py +97 -40
  59. {voice_mode-2.23.0.dist-info → voice_mode-2.25.0.dist-info}/METADATA +1 -1
  60. {voice_mode-2.23.0.dist-info → voice_mode-2.25.0.dist-info}/RECORD +64 -64
  61. /voice_mode/frontend/.next/static/{Wzk1z2qG5teKcU-SpnqS6 → SsR9zBdw0hd2LN3Vo9tP_}/_buildManifest.js +0 -0
  62. /voice_mode/frontend/.next/static/{Wzk1z2qG5teKcU-SpnqS6 → SsR9zBdw0hd2LN3Vo9tP_}/_ssgManifest.js +0 -0
  63. {voice_mode-2.23.0.dist-info → voice_mode-2.25.0.dist-info}/WHEEL +0 -0
  64. {voice_mode-2.23.0.dist-info → voice_mode-2.25.0.dist-info}/entry_points.txt +0 -0
voice_mode/__version__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # This file is automatically updated by 'make release'
2
2
  # Do not edit manually
3
- __version__ = "2.23.0"
3
+ __version__ = "2.25.0"
voice_mode/cli.py CHANGED
@@ -6,7 +6,6 @@ import sys
6
6
  import os
7
7
  import warnings
8
8
  import click
9
- from .server import main as voice_mode_main
10
9
 
11
10
  # Suppress known deprecation warnings for better user experience
12
11
  # These apply to both CLI commands and MCP server operation
@@ -47,6 +46,7 @@ def voice_mode_main_cli(ctx, debug):
47
46
  if ctx.invoked_subcommand is None:
48
47
  # No subcommand - run MCP server
49
48
  # Note: warnings are already suppressed at module level unless debug is enabled
49
+ from .server import main as voice_mode_main
50
50
  voice_mode_main()
51
51
 
52
52
 
@@ -74,36 +74,14 @@ def livekit():
74
74
  pass
75
75
 
76
76
 
77
- # Import service functions
78
- from voice_mode.tools.service import (
79
- status_service, start_service, stop_service, restart_service,
80
- enable_service, disable_service, view_logs, update_service_files
81
- )
82
-
83
- # Import install/uninstall functions
84
- from voice_mode.tools.services.kokoro.install import kokoro_install
85
- from voice_mode.tools.services.kokoro.uninstall import kokoro_uninstall
86
- from voice_mode.tools.services.whisper.install import whisper_install
87
- from voice_mode.tools.services.whisper.uninstall import whisper_uninstall
88
- from voice_mode.tools.services.whisper.download_model import download_model
89
- from voice_mode.tools.services.livekit.install import livekit_install
90
- from voice_mode.tools.services.livekit.uninstall import livekit_uninstall
91
- from voice_mode.tools.services.livekit.frontend import livekit_frontend_start, livekit_frontend_stop, livekit_frontend_status, livekit_frontend_open, livekit_frontend_logs, livekit_frontend_install
92
-
93
- # Import configuration management functions
94
- from voice_mode.tools.configuration_management import update_config, list_config_keys
95
-
96
- # Import diagnostic functions - extract the actual async functions from the tools
97
- from voice_mode.tools.diagnostics import voice_mode_info
98
- from voice_mode.tools.devices import check_audio_devices
99
- from voice_mode.tools.voice_registry import voice_registry
100
- from voice_mode.tools.dependencies import check_audio_dependencies
77
+ # Service functions are imported lazily in their respective command handlers to improve startup time
101
78
 
102
79
 
103
80
  # Kokoro service commands
104
81
  @kokoro.command()
105
82
  def status():
106
83
  """Show Kokoro service status."""
84
+ from voice_mode.tools.service import status_service
107
85
  result = asyncio.run(status_service("kokoro"))
108
86
  click.echo(result)
109
87
 
@@ -111,6 +89,7 @@ def status():
111
89
  @kokoro.command()
112
90
  def start():
113
91
  """Start Kokoro service."""
92
+ from voice_mode.tools.service import start_service
114
93
  result = asyncio.run(start_service("kokoro"))
115
94
  click.echo(result)
116
95
 
@@ -118,6 +97,7 @@ def start():
118
97
  @kokoro.command()
119
98
  def stop():
120
99
  """Stop Kokoro service."""
100
+ from voice_mode.tools.service import stop_service
121
101
  result = asyncio.run(stop_service("kokoro"))
122
102
  click.echo(result)
123
103
 
@@ -125,6 +105,7 @@ def stop():
125
105
  @kokoro.command()
126
106
  def restart():
127
107
  """Restart Kokoro service."""
108
+ from voice_mode.tools.service import restart_service
128
109
  result = asyncio.run(restart_service("kokoro"))
129
110
  click.echo(result)
130
111
 
@@ -132,6 +113,7 @@ def restart():
132
113
  @kokoro.command()
133
114
  def enable():
134
115
  """Enable Kokoro service to start at boot/login."""
116
+ from voice_mode.tools.service import enable_service
135
117
  result = asyncio.run(enable_service("kokoro"))
136
118
  click.echo(result)
137
119
 
@@ -139,6 +121,7 @@ def enable():
139
121
  @kokoro.command()
140
122
  def disable():
141
123
  """Disable Kokoro service from starting at boot/login."""
124
+ from voice_mode.tools.service import disable_service
142
125
  result = asyncio.run(disable_service("kokoro"))
143
126
  click.echo(result)
144
127
 
@@ -147,6 +130,7 @@ def disable():
147
130
  @click.option('--lines', '-n', default=50, help='Number of log lines to show')
148
131
  def logs(lines):
149
132
  """View Kokoro service logs."""
133
+ from voice_mode.tools.service import view_logs
150
134
  result = asyncio.run(view_logs("kokoro", lines))
151
135
  click.echo(result)
152
136
 
@@ -154,6 +138,7 @@ def logs(lines):
154
138
  @kokoro.command("update-service-files")
155
139
  def kokoro_update_service_files():
156
140
  """Update Kokoro service files to latest version."""
141
+ from voice_mode.tools.service import update_service_files
157
142
  result = asyncio.run(update_service_files("kokoro"))
158
143
  click.echo(result)
159
144
 
@@ -193,6 +178,7 @@ def health():
193
178
  @click.option('--auto-enable/--no-auto-enable', default=None, help='Enable service at boot/login')
194
179
  def install(install_dir, port, force, version, auto_enable):
195
180
  """Install kokoro-fastapi TTS service."""
181
+ from voice_mode.tools.services.kokoro.install import kokoro_install
196
182
  result = asyncio.run(kokoro_install.fn(
197
183
  install_dir=install_dir,
198
184
  port=port,
@@ -227,6 +213,7 @@ def install(install_dir, port, force, version, auto_enable):
227
213
  @click.confirmation_option(prompt='Are you sure you want to uninstall Kokoro?')
228
214
  def uninstall(remove_models, remove_all_data):
229
215
  """Uninstall kokoro-fastapi service and optionally remove data."""
216
+ from voice_mode.tools.services.kokoro.uninstall import kokoro_uninstall
230
217
  result = asyncio.run(kokoro_uninstall.fn(
231
218
  remove_models=remove_models,
232
219
  remove_all_data=remove_all_data
@@ -260,6 +247,7 @@ def uninstall(remove_models, remove_all_data):
260
247
  @whisper.command()
261
248
  def status():
262
249
  """Show Whisper service status."""
250
+ from voice_mode.tools.service import status_service
263
251
  result = asyncio.run(status_service("whisper"))
264
252
  click.echo(result)
265
253
 
@@ -267,6 +255,7 @@ def status():
267
255
  @whisper.command()
268
256
  def start():
269
257
  """Start Whisper service."""
258
+ from voice_mode.tools.service import start_service
270
259
  result = asyncio.run(start_service("whisper"))
271
260
  click.echo(result)
272
261
 
@@ -274,6 +263,7 @@ def start():
274
263
  @whisper.command()
275
264
  def stop():
276
265
  """Stop Whisper service."""
266
+ from voice_mode.tools.service import stop_service
277
267
  result = asyncio.run(stop_service("whisper"))
278
268
  click.echo(result)
279
269
 
@@ -281,6 +271,7 @@ def stop():
281
271
  @whisper.command()
282
272
  def restart():
283
273
  """Restart Whisper service."""
274
+ from voice_mode.tools.service import restart_service
284
275
  result = asyncio.run(restart_service("whisper"))
285
276
  click.echo(result)
286
277
 
@@ -288,6 +279,7 @@ def restart():
288
279
  @whisper.command()
289
280
  def enable():
290
281
  """Enable Whisper service to start at boot/login."""
282
+ from voice_mode.tools.service import enable_service
291
283
  result = asyncio.run(enable_service("whisper"))
292
284
  click.echo(result)
293
285
 
@@ -295,6 +287,7 @@ def enable():
295
287
  @whisper.command()
296
288
  def disable():
297
289
  """Disable Whisper service from starting at boot/login."""
290
+ from voice_mode.tools.service import disable_service
298
291
  result = asyncio.run(disable_service("whisper"))
299
292
  click.echo(result)
300
293
 
@@ -303,6 +296,7 @@ def disable():
303
296
  @click.option('--lines', '-n', default=50, help='Number of log lines to show')
304
297
  def logs(lines):
305
298
  """View Whisper service logs."""
299
+ from voice_mode.tools.service import view_logs
306
300
  result = asyncio.run(view_logs("whisper", lines))
307
301
  click.echo(result)
308
302
 
@@ -310,6 +304,7 @@ def logs(lines):
310
304
  @whisper.command("update-service-files")
311
305
  def whisper_update_service_files():
312
306
  """Update Whisper service files to latest version."""
307
+ from voice_mode.tools.service import update_service_files
313
308
  result = asyncio.run(update_service_files("whisper"))
314
309
  click.echo(result)
315
310
 
@@ -350,6 +345,7 @@ def health():
350
345
  @click.option('--auto-enable/--no-auto-enable', default=None, help='Enable service at boot/login')
351
346
  def install(install_dir, model, use_gpu, force, version, auto_enable):
352
347
  """Install whisper.cpp STT service with automatic system detection."""
348
+ from voice_mode.tools.services.whisper.install import whisper_install
353
349
  result = asyncio.run(whisper_install.fn(
354
350
  install_dir=install_dir,
355
351
  model=model,
@@ -394,6 +390,7 @@ def install(install_dir, model, use_gpu, force, version, auto_enable):
394
390
  @click.confirmation_option(prompt='Are you sure you want to uninstall Whisper?')
395
391
  def uninstall(remove_models, remove_all_data):
396
392
  """Uninstall whisper.cpp and optionally remove models and data."""
393
+ from voice_mode.tools.services.whisper.uninstall import whisper_uninstall
397
394
  result = asyncio.run(whisper_uninstall.fn(
398
395
  remove_models=remove_models,
399
396
  remove_all_data=remove_all_data
@@ -437,6 +434,7 @@ def download_model_cmd(model, force, skip_core_ml):
437
434
  medium, medium.en, large-v1, large-v2, large-v3, large-v3-turbo
438
435
  """
439
436
  import json
437
+ from voice_mode.tools.services.whisper.download_model import download_model
440
438
  result = asyncio.run(download_model.fn(
441
439
  model=model,
442
440
  force_download=force,
@@ -478,6 +476,7 @@ def download_model_cmd(model, force, skip_core_ml):
478
476
  @livekit.command()
479
477
  def status():
480
478
  """Show LiveKit service status."""
479
+ from voice_mode.tools.service import status_service
481
480
  result = asyncio.run(status_service("livekit"))
482
481
  click.echo(result)
483
482
 
@@ -485,6 +484,7 @@ def status():
485
484
  @livekit.command()
486
485
  def start():
487
486
  """Start LiveKit service."""
487
+ from voice_mode.tools.service import start_service
488
488
  result = asyncio.run(start_service("livekit"))
489
489
  click.echo(result)
490
490
 
@@ -492,6 +492,7 @@ def start():
492
492
  @livekit.command()
493
493
  def stop():
494
494
  """Stop LiveKit service."""
495
+ from voice_mode.tools.service import stop_service
495
496
  result = asyncio.run(stop_service("livekit"))
496
497
  click.echo(result)
497
498
 
@@ -499,6 +500,7 @@ def stop():
499
500
  @livekit.command()
500
501
  def restart():
501
502
  """Restart LiveKit service."""
503
+ from voice_mode.tools.service import restart_service
502
504
  result = asyncio.run(restart_service("livekit"))
503
505
  click.echo(result)
504
506
 
@@ -506,6 +508,7 @@ def restart():
506
508
  @livekit.command()
507
509
  def enable():
508
510
  """Enable LiveKit service to start at boot/login."""
511
+ from voice_mode.tools.service import enable_service
509
512
  result = asyncio.run(enable_service("livekit"))
510
513
  click.echo(result)
511
514
 
@@ -513,6 +516,7 @@ def enable():
513
516
  @livekit.command()
514
517
  def disable():
515
518
  """Disable LiveKit service from starting at boot/login."""
519
+ from voice_mode.tools.service import disable_service
516
520
  result = asyncio.run(disable_service("livekit"))
517
521
  click.echo(result)
518
522
 
@@ -521,6 +525,7 @@ def disable():
521
525
  @click.option('--lines', '-n', default=50, help='Number of log lines to show')
522
526
  def logs(lines):
523
527
  """View LiveKit service logs."""
528
+ from voice_mode.tools.service import view_logs
524
529
  result = asyncio.run(view_logs("livekit", lines))
525
530
  click.echo(result)
526
531
 
@@ -528,6 +533,7 @@ def logs(lines):
528
533
  @livekit.command()
529
534
  def update():
530
535
  """Update LiveKit service files to the latest version."""
536
+ from voice_mode.tools.service import update_service_files
531
537
  result = asyncio.run(update_service_files("livekit"))
532
538
 
533
539
  if result.get("success"):
@@ -546,6 +552,7 @@ def update():
546
552
  @click.option('--auto-enable/--no-auto-enable', default=None, help='Enable service at boot/login')
547
553
  def install(install_dir, port, force, version, auto_enable):
548
554
  """Install LiveKit server with development configuration."""
555
+ from voice_mode.tools.services.livekit.install import livekit_install
549
556
  result = asyncio.run(livekit_install.fn(
550
557
  install_dir=install_dir,
551
558
  port=port,
@@ -583,6 +590,7 @@ def install(install_dir, port, force, version, auto_enable):
583
590
  @click.confirmation_option(prompt='Are you sure you want to uninstall LiveKit?')
584
591
  def uninstall(remove_config, remove_all_data):
585
592
  """Uninstall LiveKit server and optionally remove configuration and data."""
593
+ from voice_mode.tools.services.livekit.uninstall import livekit_uninstall
586
594
  result = asyncio.run(livekit_uninstall.fn(
587
595
  remove_config=remove_config,
588
596
  remove_all_data=remove_all_data
@@ -615,6 +623,7 @@ def frontend():
615
623
  @click.option('--auto-enable/--no-auto-enable', default=None, help='Enable service after installation (default: from config)')
616
624
  def frontend_install(auto_enable):
617
625
  """Install and setup LiveKit Voice Assistant Frontend."""
626
+ from voice_mode.tools.services.livekit.frontend import livekit_frontend_install
618
627
  result = asyncio.run(livekit_frontend_install.fn(auto_enable=auto_enable))
619
628
 
620
629
  if result.get('success'):
@@ -642,6 +651,7 @@ def frontend_install(auto_enable):
642
651
  @click.option('--host', default='127.0.0.1', help='Host to bind to (default: 127.0.0.1)')
643
652
  def frontend_start(port, host):
644
653
  """Start the LiveKit Voice Assistant Frontend."""
654
+ from voice_mode.tools.services.livekit.frontend import livekit_frontend_start
645
655
  result = asyncio.run(livekit_frontend_start.fn(port=port, host=host))
646
656
 
647
657
  if result.get('success'):
@@ -663,6 +673,7 @@ def frontend_start(port, host):
663
673
  @frontend.command("stop")
664
674
  def frontend_stop():
665
675
  """Stop the LiveKit Voice Assistant Frontend."""
676
+ from voice_mode.tools.services.livekit.frontend import livekit_frontend_stop
666
677
  result = asyncio.run(livekit_frontend_stop.fn())
667
678
 
668
679
  if result.get('success'):
@@ -674,6 +685,7 @@ def frontend_stop():
674
685
  @frontend.command("status")
675
686
  def frontend_status():
676
687
  """Check status of the LiveKit Voice Assistant Frontend."""
688
+ from voice_mode.tools.services.livekit.frontend import livekit_frontend_status
677
689
  result = asyncio.run(livekit_frontend_status.fn())
678
690
 
679
691
  if 'error' in result:
@@ -701,6 +713,7 @@ def frontend_open():
701
713
 
702
714
  Starts the frontend if not already running, then opens it in the default browser.
703
715
  """
716
+ from voice_mode.tools.services.livekit.frontend import livekit_frontend_open
704
717
  result = asyncio.run(livekit_frontend_open.fn())
705
718
 
706
719
  if result.get('success'):
@@ -723,11 +736,13 @@ def frontend_logs(lines, follow):
723
736
  """
724
737
  if follow:
725
738
  # For following, run tail -f directly
739
+ from voice_mode.tools.services.livekit.frontend import livekit_frontend_logs
726
740
  result = asyncio.run(livekit_frontend_logs.fn(follow=True))
727
741
  if result.get('success'):
728
742
  click.echo(f"📂 Log file: {result['log_file']}")
729
743
  click.echo("🔄 Following logs (press Ctrl+C to stop)...")
730
744
  try:
745
+ import subprocess
731
746
  subprocess.run(["tail", "-f", result['log_file']])
732
747
  except KeyboardInterrupt:
733
748
  click.echo("\n✅ Stopped following logs")
@@ -735,6 +750,7 @@ def frontend_logs(lines, follow):
735
750
  click.echo(f"❌ Error: {result.get('error', 'Unknown error')}")
736
751
  else:
737
752
  # Show last N lines
753
+ from voice_mode.tools.services.livekit.frontend import livekit_frontend_logs
738
754
  result = asyncio.run(livekit_frontend_logs.fn(lines=lines, follow=False))
739
755
  if result.get('success'):
740
756
  click.echo(f"📂 Log file: {result['log_file']}")
@@ -748,6 +764,7 @@ def frontend_logs(lines, follow):
748
764
  @frontend.command("enable")
749
765
  def frontend_enable():
750
766
  """Enable frontend service to start automatically at boot/login."""
767
+ from voice_mode.tools.service import enable_service
751
768
  result = asyncio.run(enable_service("frontend"))
752
769
  # enable_service returns a string, not a dict
753
770
  click.echo(result)
@@ -756,6 +773,7 @@ def frontend_enable():
756
773
  @frontend.command("disable")
757
774
  def frontend_disable():
758
775
  """Disable frontend service from starting automatically."""
776
+ from voice_mode.tools.service import disable_service
759
777
  result = asyncio.run(disable_service("frontend"))
760
778
  # disable_service returns a string, not a dict
761
779
  click.echo(result)
@@ -827,6 +845,7 @@ def config():
827
845
  @config.command("list")
828
846
  def config_list():
829
847
  """List all configuration keys with their descriptions."""
848
+ from voice_mode.tools.configuration_management import list_config_keys
830
849
  result = asyncio.run(list_config_keys.fn())
831
850
  click.echo(result)
832
851
 
@@ -873,6 +892,7 @@ def config_get(key):
873
892
  @click.argument('value')
874
893
  def config_set(key, value):
875
894
  """Set a configuration value."""
895
+ from voice_mode.tools.configuration_management import update_config
876
896
  result = asyncio.run(update_config.fn(key, value))
877
897
  click.echo(result)
878
898
 
@@ -887,6 +907,7 @@ def diag():
887
907
  @diag.command()
888
908
  def info():
889
909
  """Show voice-mode installation information."""
910
+ from voice_mode.tools.diagnostics import voice_mode_info
890
911
  result = asyncio.run(voice_mode_info.fn())
891
912
  click.echo(result)
892
913
 
@@ -894,6 +915,7 @@ def info():
894
915
  @diag.command()
895
916
  def devices():
896
917
  """List available audio input and output devices."""
918
+ from voice_mode.tools.devices import check_audio_devices
897
919
  result = asyncio.run(check_audio_devices.fn())
898
920
  click.echo(result)
899
921
 
@@ -901,6 +923,7 @@ def devices():
901
923
  @diag.command()
902
924
  def registry():
903
925
  """Show voice provider registry with all discovered endpoints."""
926
+ from voice_mode.tools.voice_registry import voice_registry
904
927
  result = asyncio.run(voice_registry.fn())
905
928
  click.echo(result)
906
929
 
@@ -909,6 +932,7 @@ def registry():
909
932
  def dependencies():
910
933
  """Check system audio dependencies and provide installation guidance."""
911
934
  import json
935
+ from voice_mode.tools.dependencies import check_audio_dependencies
912
936
  result = asyncio.run(check_audio_dependencies.fn())
913
937
 
914
938
  if isinstance(result, dict):
voice_mode/config.py CHANGED
@@ -149,19 +149,29 @@ def env_bool(env_var: str, default: bool = False) -> bool:
149
149
  value = os.getenv(env_var, "").lower()
150
150
  return value in ("true", "1", "yes", "on") if value else default
151
151
 
152
+ # Helper function to expand paths with tilde
153
+ def expand_path(path_str: str) -> Path:
154
+ """Expand tilde and environment variables in path strings."""
155
+ # First expand any environment variables
156
+ expanded = os.path.expandvars(path_str)
157
+ # Then expand tilde
158
+ expanded = os.path.expanduser(expanded)
159
+ return Path(expanded)
160
+
152
161
  # Base directory for all voicemode data
153
- BASE_DIR = Path(os.getenv("VOICEMODE_BASE_DIR", str(Path.home() / ".voicemode")))
162
+ BASE_DIR = expand_path(os.getenv("VOICEMODE_BASE_DIR", str(Path.home() / ".voicemode")))
154
163
 
155
164
  # Unified directory structure
156
165
  AUDIO_DIR = BASE_DIR / "audio"
157
166
  TRANSCRIPTIONS_DIR = BASE_DIR / "transcriptions"
158
167
  LOGS_DIR = BASE_DIR / "logs"
159
168
  # CONFIG_DIR = BASE_DIR / "config" # Removed - config stored in .voicemode.env file instead
160
- MODELS_DIR = Path(os.getenv("VOICEMODE_MODELS_DIR", str(BASE_DIR / "models")))
169
+ MODELS_DIR = expand_path(os.getenv("VOICEMODE_MODELS_DIR", str(BASE_DIR / "models")))
161
170
 
162
171
  # Debug configuration
163
172
  DEBUG = os.getenv("VOICEMODE_DEBUG", "").lower() in ("true", "1", "yes", "on")
164
173
  TRACE_DEBUG = os.getenv("VOICEMODE_DEBUG", "").lower() == "trace"
174
+ VAD_DEBUG = os.getenv("VOICEMODE_VAD_DEBUG", "").lower() in ("true", "1", "yes", "on")
165
175
  DEBUG_DIR = LOGS_DIR / "debug" # Debug files now go under logs
166
176
 
167
177
  # Master save-all configuration
@@ -224,14 +234,14 @@ LIVEKIT_API_SECRET = os.getenv("LIVEKIT_API_SECRET", "secret")
224
234
  WHISPER_MODEL = os.getenv("VOICEMODE_WHISPER_MODEL", "large-v2")
225
235
  WHISPER_PORT = int(os.getenv("VOICEMODE_WHISPER_PORT", "2022"))
226
236
  WHISPER_LANGUAGE = os.getenv("VOICEMODE_WHISPER_LANGUAGE", "auto")
227
- WHISPER_MODEL_PATH = os.getenv("VOICEMODE_WHISPER_MODEL_PATH", str(BASE_DIR / "models" / "whisper"))
237
+ WHISPER_MODEL_PATH = expand_path(os.getenv("VOICEMODE_WHISPER_MODEL_PATH", str(BASE_DIR / "models" / "whisper")))
228
238
 
229
239
  # ==================== KOKORO CONFIGURATION ====================
230
240
 
231
241
  # Kokoro-specific configuration
232
242
  KOKORO_PORT = int(os.getenv("VOICEMODE_KOKORO_PORT", "8880"))
233
- KOKORO_MODELS_DIR = os.getenv("VOICEMODE_KOKORO_MODELS_DIR", str(BASE_DIR / "models" / "kokoro"))
234
- KOKORO_CACHE_DIR = os.getenv("VOICEMODE_KOKORO_CACHE_DIR", str(BASE_DIR / "cache" / "kokoro"))
243
+ KOKORO_MODELS_DIR = expand_path(os.getenv("VOICEMODE_KOKORO_MODELS_DIR", str(BASE_DIR / "models" / "kokoro")))
244
+ KOKORO_CACHE_DIR = expand_path(os.getenv("VOICEMODE_KOKORO_CACHE_DIR", str(BASE_DIR / "cache" / "kokoro")))
235
245
  KOKORO_DEFAULT_VOICE = os.getenv("VOICEMODE_KOKORO_DEFAULT_VOICE", "af_sky")
236
246
 
237
247
  # ==================== LIVEKIT CONFIGURATION ====================
@@ -1 +1 @@
1
- Wzk1z2qG5teKcU-SpnqS6
1
+ SsR9zBdw0hd2LN3Vo9tP_
@@ -4,25 +4,25 @@
4
4
  "static/chunks/webpack-0ea9b80f19935b70.js",
5
5
  "static/chunks/fd9d1056-af324d327b243cf1.js",
6
6
  "static/chunks/117-40bc79a2b97edb21.js",
7
- "static/chunks/main-app-55293f39223cd65f.js",
7
+ "static/chunks/main-app-d25858d98b1d322b.js",
8
8
  "static/chunks/app/_not-found/page-5011050e402ab9c8.js"
9
9
  ],
10
10
  "/layout": [
11
11
  "static/chunks/webpack-0ea9b80f19935b70.js",
12
12
  "static/chunks/fd9d1056-af324d327b243cf1.js",
13
13
  "static/chunks/117-40bc79a2b97edb21.js",
14
- "static/chunks/main-app-55293f39223cd65f.js",
14
+ "static/chunks/main-app-d25858d98b1d322b.js",
15
15
  "static/css/a2f49a47752b5010.css",
16
- "static/chunks/app/layout-b625329e9ada0473.js"
16
+ "static/chunks/app/layout-5675f418c42e0ae3.js"
17
17
  ],
18
18
  "/page": [
19
19
  "static/chunks/webpack-0ea9b80f19935b70.js",
20
20
  "static/chunks/fd9d1056-af324d327b243cf1.js",
21
21
  "static/chunks/117-40bc79a2b97edb21.js",
22
- "static/chunks/main-app-55293f39223cd65f.js",
22
+ "static/chunks/main-app-d25858d98b1d322b.js",
23
23
  "static/chunks/144d3bae-2d5f122b82426d88.js",
24
24
  "static/chunks/471-bd4b96a33883dfa2.js",
25
- "static/chunks/app/page-983279b94d1fda4b.js"
25
+ "static/chunks/app/page-dbb3615cf25cdc16.js"
26
26
  ]
27
27
  }
28
28
  }
@@ -5,14 +5,14 @@
5
5
  "devFiles": [],
6
6
  "ampDevFiles": [],
7
7
  "lowPriorityFiles": [
8
- "static/Wzk1z2qG5teKcU-SpnqS6/_buildManifest.js",
9
- "static/Wzk1z2qG5teKcU-SpnqS6/_ssgManifest.js"
8
+ "static/SsR9zBdw0hd2LN3Vo9tP_/_buildManifest.js",
9
+ "static/SsR9zBdw0hd2LN3Vo9tP_/_ssgManifest.js"
10
10
  ],
11
11
  "rootMainFiles": [
12
12
  "static/chunks/webpack-0ea9b80f19935b70.js",
13
13
  "static/chunks/fd9d1056-af324d327b243cf1.js",
14
14
  "static/chunks/117-40bc79a2b97edb21.js",
15
- "static/chunks/main-app-55293f39223cd65f.js"
15
+ "static/chunks/main-app-d25858d98b1d322b.js"
16
16
  ],
17
17
  "pages": {
18
18
  "/_app": [
@@ -1 +1 @@
1
- {"version":1,"files":["../node_modules/styled-jsx/index.js","../node_modules/styled-jsx/package.json","../node_modules/styled-jsx/dist/index/index.js","../node_modules/react/package.json","../node_modules/react/index.js","../node_modules/client-only/package.json","../node_modules/react/cjs/react.production.min.js","../node_modules/client-only/index.js","../node_modules/styled-jsx/style.js","../node_modules/next/dist/compiled/next-server/server.runtime.prod.js","../node_modules/next/package.json","../node_modules/next/dist/lib/constants.js","../node_modules/next/dist/server/body-streams.js","../node_modules/next/dist/lib/picocolors.js","../node_modules/next/dist/shared/lib/constants.js","../node_modules/next/dist/server/web/utils.js","../node_modules/next/dist/client/components/app-router-headers.js","../node_modules/next/dist/server/lib/trace/tracer.js","../node_modules/next/dist/server/lib/trace/constants.js","../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../node_modules/next/dist/shared/lib/error-source.js","../node_modules/next/dist/shared/lib/modern-browserslist-target.js","../node_modules/next/dist/compiled/debug/package.json","../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../node_modules/next/dist/shared/lib/runtime-config.external.js","../node_modules/next/dist/compiled/debug/index.js","../node_modules/next/dist/compiled/ws/package.json","../node_modules/next/dist/compiled/node-html-parser/package.json","../node_modules/next/dist/compiled/lru-cache/package.json","../node_modules/@swc/helpers/_/_interop_require_default/package.json","../node_modules/next/dist/compiled/ws/index.js","../node_modules/next/dist/compiled/node-html-parser/index.js","../node_modules/next/dist/compiled/lru-cache/index.js","../node_modules/next/dist/client/components/async-local-storage.js","../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../node_modules/@swc/helpers/package.json","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/parseStack.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/nodeStackFrames.js","../node_modules/next/dist/compiled/jsonwebtoken/package.json","../node_modules/next/dist/client/components/react-dev-overlay/server/middleware.js","../node_modules/@swc/helpers/cjs/_interop_require_default.cjs","../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../node_modules/next/dist/compiled/jsonwebtoken/index.js","../node_modules/next/dist/compiled/browserslist/package.json","../node_modules/next/dist/compiled/browserslist/index.js","../node_modules/next/dist/client/components/react-dev-overlay/server/shared.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/launchEditor.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/getRawSourceMap.js","../node_modules/next/dist/compiled/babel/code-frame.js","../node_modules/next/dist/compiled/json5/package.json","../node_modules/next/dist/compiled/semver/package.json","../node_modules/next/dist/compiled/babel/package.json","../node_modules/next/dist/lib/semver-noop.js","../node_modules/next/dist/compiled/json5/index.js","../node_modules/next/dist/compiled/semver/index.js","../node_modules/next/dist/compiled/stacktrace-parser/package.json","../node_modules/next/dist/compiled/source-map08/package.json","../node_modules/caniuse-lite/dist/unpacker/agents.js","../node_modules/caniuse-lite/dist/unpacker/region.js","../node_modules/caniuse-lite/dist/unpacker/feature.js","../node_modules/next/dist/compiled/babel/bundle.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/getSourceMapUrl.js","../node_modules/next/dist/compiled/stacktrace-parser/stack-trace-parser.cjs.js","../node_modules/next/dist/compiled/source-map08/source-map.js","../node_modules/caniuse-lite/package.json","../node_modules/next/dist/compiled/babel/core.js","../node_modules/caniuse-lite/data/agents.js","../node_modules/caniuse-lite/dist/unpacker/browsers.js","../node_modules/caniuse-lite/dist/unpacker/browserVersions.js","../node_modules/caniuse-lite/dist/lib/statuses.js","../node_modules/caniuse-lite/dist/lib/supported.js","../node_modules/next/dist/compiled/data-uri-to-buffer/package.json","../node_modules/next/dist/compiled/shell-quote/package.json","../node_modules/next/dist/compiled/data-uri-to-buffer/index.js","../node_modules/next/dist/compiled/shell-quote/index.js","../node_modules/caniuse-lite/data/browsers.js","../node_modules/caniuse-lite/data/browserVersions.js","../node_modules/next/dist/compiled/babel-packages/package.json","../node_modules/next/dist/compiled/babel-packages/packages-bundle.js","../node_modules/next/dist/compiled/babel/traverse.js","../node_modules/next/dist/compiled/babel/parser.js","../node_modules/next/dist/compiled/babel/types.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/app-page/module.compiled.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/pages/module.compiled.js"]}
1
+ {"version":1,"files":["../node_modules/styled-jsx/index.js","../node_modules/styled-jsx/package.json","../node_modules/styled-jsx/dist/index/index.js","../node_modules/react/package.json","../node_modules/react/index.js","../node_modules/client-only/package.json","../node_modules/react/cjs/react.production.min.js","../node_modules/client-only/index.js","../node_modules/styled-jsx/style.js","../node_modules/next/dist/compiled/next-server/server.runtime.prod.js","../node_modules/next/package.json","../node_modules/next/dist/lib/constants.js","../node_modules/next/dist/server/body-streams.js","../node_modules/next/dist/lib/picocolors.js","../node_modules/next/dist/shared/lib/constants.js","../node_modules/next/dist/server/web/utils.js","../node_modules/next/dist/client/components/app-router-headers.js","../node_modules/next/dist/server/lib/trace/tracer.js","../node_modules/next/dist/server/lib/trace/constants.js","../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../node_modules/next/dist/shared/lib/error-source.js","../node_modules/next/dist/shared/lib/modern-browserslist-target.js","../node_modules/next/dist/compiled/debug/package.json","../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../node_modules/next/dist/shared/lib/runtime-config.external.js","../node_modules/next/dist/compiled/debug/index.js","../node_modules/next/dist/compiled/ws/package.json","../node_modules/next/dist/compiled/lru-cache/package.json","../node_modules/next/dist/compiled/node-html-parser/package.json","../node_modules/@swc/helpers/_/_interop_require_default/package.json","../node_modules/next/dist/compiled/ws/index.js","../node_modules/next/dist/compiled/lru-cache/index.js","../node_modules/next/dist/compiled/node-html-parser/index.js","../node_modules/next/dist/client/components/async-local-storage.js","../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../node_modules/@swc/helpers/package.json","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/parseStack.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/nodeStackFrames.js","../node_modules/next/dist/compiled/jsonwebtoken/package.json","../node_modules/next/dist/client/components/react-dev-overlay/server/middleware.js","../node_modules/@swc/helpers/cjs/_interop_require_default.cjs","../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../node_modules/next/dist/compiled/jsonwebtoken/index.js","../node_modules/next/dist/compiled/browserslist/package.json","../node_modules/next/dist/compiled/browserslist/index.js","../node_modules/next/dist/client/components/react-dev-overlay/server/shared.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/getRawSourceMap.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/launchEditor.js","../node_modules/next/dist/compiled/babel/code-frame.js","../node_modules/next/dist/compiled/json5/package.json","../node_modules/next/dist/compiled/semver/package.json","../node_modules/next/dist/compiled/babel/package.json","../node_modules/next/dist/lib/semver-noop.js","../node_modules/next/dist/compiled/json5/index.js","../node_modules/next/dist/compiled/semver/index.js","../node_modules/next/dist/compiled/stacktrace-parser/package.json","../node_modules/next/dist/compiled/source-map08/package.json","../node_modules/caniuse-lite/dist/unpacker/agents.js","../node_modules/caniuse-lite/dist/unpacker/feature.js","../node_modules/caniuse-lite/dist/unpacker/region.js","../node_modules/next/dist/compiled/babel/bundle.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/getSourceMapUrl.js","../node_modules/next/dist/compiled/stacktrace-parser/stack-trace-parser.cjs.js","../node_modules/next/dist/compiled/source-map08/source-map.js","../node_modules/caniuse-lite/package.json","../node_modules/next/dist/compiled/babel/core.js","../node_modules/caniuse-lite/data/agents.js","../node_modules/caniuse-lite/dist/lib/statuses.js","../node_modules/caniuse-lite/dist/lib/supported.js","../node_modules/caniuse-lite/dist/unpacker/browsers.js","../node_modules/caniuse-lite/dist/unpacker/browserVersions.js","../node_modules/next/dist/compiled/data-uri-to-buffer/package.json","../node_modules/next/dist/compiled/shell-quote/package.json","../node_modules/next/dist/compiled/data-uri-to-buffer/index.js","../node_modules/next/dist/compiled/shell-quote/index.js","../node_modules/caniuse-lite/data/browsers.js","../node_modules/caniuse-lite/data/browserVersions.js","../node_modules/next/dist/compiled/babel-packages/package.json","../node_modules/next/dist/compiled/babel-packages/packages-bundle.js","../node_modules/next/dist/compiled/babel/traverse.js","../node_modules/next/dist/compiled/babel/types.js","../node_modules/next/dist/compiled/babel/parser.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/app-page/module.compiled.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/pages/module.compiled.js"]}