cade-cli 0.15.2__tar.gz → 0.15.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. {cade_cli-0.15.2 → cade_cli-0.15.5}/PKG-INFO +2 -2
  2. {cade_cli-0.15.2 → cade_cli-0.15.5}/README.md +1 -1
  3. {cade_cli-0.15.2 → cade_cli-0.15.5}/pyproject.toml +6 -1
  4. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/voice/stt.py +35 -23
  5. {cade_cli-0.15.2 → cade_cli-0.15.5}/.gitignore +0 -0
  6. {cade_cli-0.15.2 → cade_cli-0.15.5}/LICENSE +0 -0
  7. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/__init__.py +0 -0
  8. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/__main__.py +0 -0
  9. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/_metadata.py +0 -0
  10. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/config.py +0 -0
  11. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/errors.py +0 -0
  12. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/resources.py +0 -0
  13. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/server.py +0 -0
  14. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/__init__.py +0 -0
  15. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/_read_cache.py +0 -0
  16. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/agents.py +0 -0
  17. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/context.py +0 -0
  18. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/filesystem.py +0 -0
  19. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/git.py +0 -0
  20. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/notifications.py +0 -0
  21. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/questions.py +0 -0
  22. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/results.py +0 -0
  23. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/schemas.py +0 -0
  24. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/search.py +0 -0
  25. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/shell.py +0 -0
  26. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/snippets.py +0 -0
  27. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/tasks.py +0 -0
  28. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/web_fetch.py +0 -0
  29. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/tools/web_search.py +0 -0
  30. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/utils.py +0 -0
  31. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/__init__.py +0 -0
  32. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/arcade_client.py +0 -0
  33. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/cache.py +0 -0
  34. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/extract.py +0 -0
  35. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/markdown.py +0 -0
  36. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/preapproved.py +0 -0
  37. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/prompts.py +0 -0
  38. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/scrape.py +0 -0
  39. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/search/__init__.py +0 -0
  40. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/search/arcade.py +0 -0
  41. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/search/base.py +0 -0
  42. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cade_mcp_local/web/search/http.py +0 -0
  43. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/__init__.py +0 -0
  44. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ai/__init__.py +0 -0
  45. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ai/prompts.py +0 -0
  46. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/__init__.py +0 -0
  47. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/app.py +0 -0
  48. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/auth.py +0 -0
  49. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/__init__.py +0 -0
  50. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/account.py +0 -0
  51. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/agents.py +0 -0
  52. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/auth.py +0 -0
  53. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/chat.py +0 -0
  54. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/cron.py +0 -0
  55. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/hooks.py +0 -0
  56. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/mcp.py +0 -0
  57. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/mem.py +0 -0
  58. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/serve.py +0 -0
  59. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/tasks.py +0 -0
  60. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/thread.py +0 -0
  61. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/commands/tools.py +0 -0
  62. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/cli/onboarding.py +0 -0
  63. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/__init__.py +0 -0
  64. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/agent.py +0 -0
  65. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/comms.py +0 -0
  66. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/config.py +0 -0
  67. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/constants.py +0 -0
  68. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/errors.py +0 -0
  69. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/frozen.py +0 -0
  70. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/git.py +0 -0
  71. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/inbox.py +0 -0
  72. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/logging.py +0 -0
  73. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/memory.py +0 -0
  74. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/model_specs.py +0 -0
  75. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/names.py +0 -0
  76. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/paths.py +0 -0
  77. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/runtime.py +0 -0
  78. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/secrets.py +0 -0
  79. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/spawn.py +0 -0
  80. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/core/types.py +0 -0
  81. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/execution/__init__.py +0 -0
  82. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/execution/context_window.py +0 -0
  83. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/execution/mcp_schema_index.py +0 -0
  84. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/execution/orchestrator.py +0 -0
  85. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/execution/parallel.py +0 -0
  86. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/execution/tool_result_store.py +0 -0
  87. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/execution/tool_schema_cache.py +0 -0
  88. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/__init__.py +0 -0
  89. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/config.py +0 -0
  90. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/engine.py +0 -0
  91. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/executors/__init__.py +0 -0
  92. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/executors/callback.py +0 -0
  93. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/executors/command.py +0 -0
  94. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/executors/http.py +0 -0
  95. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/executors/prompt.py +0 -0
  96. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
  97. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/matchers.py +0 -0
  98. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/registry.py +0 -0
  99. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/hooks/types.py +0 -0
  100. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/providers/__init__.py +0 -0
  101. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/providers/anthropic.py +0 -0
  102. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/providers/base.py +0 -0
  103. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/providers/ollama.py +0 -0
  104. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/providers/openai.py +0 -0
  105. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/__init__.py +0 -0
  106. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/adapters/__init__.py +0 -0
  107. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/adapters/base.py +0 -0
  108. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/adapters/desktop.py +0 -0
  109. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/adapters/registry.py +0 -0
  110. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/adapters/stub.py +0 -0
  111. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/adapters/telegram.py +0 -0
  112. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/allowlist.py +0 -0
  113. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/chunker.py +0 -0
  114. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/commands.py +0 -0
  115. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/config.py +0 -0
  116. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/cron.py +0 -0
  117. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/daemon.py +0 -0
  118. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/elicitation.py +0 -0
  119. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/format.py +0 -0
  120. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/install.py +0 -0
  121. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/lock.py +0 -0
  122. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/observability.py +0 -0
  123. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/owner.py +0 -0
  124. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/progress_sink.py +0 -0
  125. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/router.py +0 -0
  126. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/secrets.py +0 -0
  127. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/services.py +0 -0
  128. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/sinks.py +0 -0
  129. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/serve/worker.py +0 -0
  130. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/storage/__init__.py +0 -0
  131. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/storage/threads.py +0 -0
  132. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tasks/__init__.py +0 -0
  133. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tasks/lock.py +0 -0
  134. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tasks/scheduler.py +0 -0
  135. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tasks/store.py +0 -0
  136. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tasks/types.py +0 -0
  137. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/templates/login_failed.html +0 -0
  138. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/templates/login_success.html +0 -0
  139. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/templates/serve/launchd.plist.tmpl +0 -0
  140. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/templates/serve/serve.toml.example +0 -0
  141. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/templates/serve/systemd.service.tmpl +0 -0
  142. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/templates/styles.css +0 -0
  143. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/__init__.py +0 -0
  144. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/manager/__init__.py +0 -0
  145. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
  146. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/manager/base.py +0 -0
  147. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/manager/composite.py +0 -0
  148. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/manager/config.py +0 -0
  149. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/manager/mcp.py +0 -0
  150. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/search/__init__.py +0 -0
  151. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/search/discovered.py +0 -0
  152. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/search/scoring.py +0 -0
  153. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/tools/search/service.py +0 -0
  154. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ui/__init__.py +0 -0
  155. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ui/completion.py +0 -0
  156. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ui/display.py +0 -0
  157. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ui/elicitation.py +0 -0
  158. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ui/input.py +0 -0
  159. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ui/session.py +0 -0
  160. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/ui/slash.py +0 -0
  161. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/voice/__init__.py +0 -0
  162. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/voice/audio.py +0 -0
  163. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/voice/cleanup.py +0 -0
  164. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/voice/session.py +0 -0
  165. {cade_cli-0.15.2 → cade_cli-0.15.5}/src/cadecoder/voice/tts.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cade-cli
3
- Version: 0.15.2
3
+ Version: 0.15.5
4
4
  Summary: Cade - The CLI Agent from Arcade.dev
5
5
  Project-URL: Homepage, https://arcade.dev
6
6
  Project-URL: Documentation, https://docs.arcade.dev
@@ -123,7 +123,7 @@ The install script detects your OS/arch, downloads the matching prebuilt binary
123
123
  `~/.local/bin` — no GitHub token required. Pin a version or install location:
124
124
 
125
125
  ```bash
126
- CADE_VERSION=0.15.2 CADE_INSTALL_DIR=/usr/local/bin \
126
+ CADE_VERSION=0.15.5 CADE_INSTALL_DIR=/usr/local/bin \
127
127
  sh -c "$(curl -fsSL https://arcadeagent.dev/install)"
128
128
  ```
129
129
 
@@ -35,7 +35,7 @@ The install script detects your OS/arch, downloads the matching prebuilt binary
35
35
  `~/.local/bin` — no GitHub token required. Pin a version or install location:
36
36
 
37
37
  ```bash
38
- CADE_VERSION=0.15.2 CADE_INSTALL_DIR=/usr/local/bin \
38
+ CADE_VERSION=0.15.5 CADE_INSTALL_DIR=/usr/local/bin \
39
39
  sh -c "$(curl -fsSL https://arcadeagent.dev/install)"
40
40
  ```
41
41
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cade-cli"
3
- version = "0.15.2"
3
+ version = "0.15.5"
4
4
  description = "Cade - The CLI Agent from Arcade.dev"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -75,6 +75,11 @@ voice = [
75
75
  "sounddevice>=0.5.5,<1.0.0",
76
76
  "numpy>=1.24.0",
77
77
  "webrtcvad>=2.0.10",
78
+ # NOTE: Kokoro TTS also needs spaCy's en_core_web_sm model, but it is NOT
79
+ # listed here — a direct-URL dependency in package metadata is rejected by
80
+ # PyPI on upload. The macOS release build installs it explicitly before
81
+ # PyInstaller (see .github/workflows/release.yml), and cade.spec bundles it
82
+ # so misaki never downloads it at runtime in the frozen app.
78
83
  ]
79
84
  training = [
80
85
  "torch>=2.1.0",
@@ -7,8 +7,6 @@ Models are downloaded from HuggingFace on first use and cached automatically.
7
7
  import asyncio
8
8
  import logging
9
9
  import os
10
- import tempfile
11
- import wave
12
10
 
13
11
  import numpy as np
14
12
 
@@ -17,6 +15,10 @@ from cadecoder.core.errors import VoiceError
17
15
  from cadecoder.core.logging import log
18
16
  from cadecoder.voice.audio import DEFAULT_SAMPLE_RATE
19
17
 
18
+ # Whisper always operates at 16 kHz. When given a numpy array (rather than a
19
+ # file path) mlx-whisper does NOT resample, so the samples must already be 16 kHz.
20
+ _WHISPER_SAMPLE_RATE = 16_000
21
+
20
22
 
21
23
  def _suppress_hf_logging() -> None:
22
24
  """Suppress noisy HuggingFace Hub download/progress logging.
@@ -109,17 +111,21 @@ class WhisperSTT:
109
111
  raise VoiceError("STT model not loaded — call _ensure_loaded() first")
110
112
 
111
113
  try:
112
- # mlx_whisper.transcribe accepts a file path or numpy array.
113
- # Write to a temp WAV file for maximum compatibility across
114
- # mlx-whisper versions.
115
- with tempfile.NamedTemporaryFile(suffix=".wav", delete=True) as tmp:
116
- self._write_wav(tmp.name, audio, sample_rate)
117
- result = self._mlx_whisper.transcribe( # type: ignore[union-attr]
118
- tmp.name,
119
- path_or_hf_repo=self.model,
120
- language=self.language,
121
- fp16=False,
122
- )
114
+ # Pass the raw float32 samples directly. Writing a temp WAV and
115
+ # passing the *path* makes mlx-whisper shell out to `ffmpeg` to
116
+ # decode it (not bundled in the frozen binary, and often absent on
117
+ # user machines). Whisper accepts a 16 kHz mono float32 array with
118
+ # no external tools — the recorder already produces exactly that.
119
+ samples = np.asarray(audio, dtype=np.float32).flatten()
120
+ if sample_rate != _WHISPER_SAMPLE_RATE:
121
+ samples = _resample(samples, sample_rate, _WHISPER_SAMPLE_RATE)
122
+
123
+ result = self._mlx_whisper.transcribe( # type: ignore[union-attr]
124
+ samples,
125
+ path_or_hf_repo=self.model,
126
+ language=self.language,
127
+ fp16=False,
128
+ )
123
129
 
124
130
  text = result.get("text", "").strip()
125
131
  log.debug(f"Transcribed {len(audio) / sample_rate:.1f}s -> '{text[:80]}'")
@@ -128,13 +134,19 @@ class WhisperSTT:
128
134
  except Exception as exc:
129
135
  raise VoiceError(f"Transcription failed: {exc}") from exc
130
136
 
131
- @staticmethod
132
- def _write_wav(path: str, audio: np.ndarray, sample_rate: int) -> None:
133
- """Write a float32 numpy array to a 16-bit PCM WAV file."""
134
- # Convert float32 [-1.0, 1.0] to int16
135
- pcm = (audio * 32767).clip(-32768, 32767).astype(np.int16)
136
- with wave.open(path, "wb") as wf:
137
- wf.setnchannels(1)
138
- wf.setsampwidth(2) # 16-bit
139
- wf.setframerate(sample_rate)
140
- wf.writeframes(pcm.tobytes())
137
+
138
+ def _resample(samples: np.ndarray, src_rate: int, dst_rate: int) -> np.ndarray:
139
+ """Linearly resample mono float32 audio to ``dst_rate`` (no external deps).
140
+
141
+ Adequate for speech fed to Whisper. Only used on the rare path where the
142
+ recorder's rate differs from Whisper's 16 kHz.
143
+ """
144
+ if src_rate == dst_rate or samples.size == 0:
145
+ return samples
146
+ duration = samples.shape[0] / float(src_rate)
147
+ n_out = int(round(duration * dst_rate))
148
+ if n_out <= 0:
149
+ return np.array([], dtype=np.float32)
150
+ x_old = np.linspace(0.0, duration, num=samples.shape[0], endpoint=False)
151
+ x_new = np.linspace(0.0, duration, num=n_out, endpoint=False)
152
+ return np.interp(x_new, x_old, samples).astype(np.float32)
File without changes
File without changes