agent-cli 0.105.0__tar.gz → 0.105.2__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.
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/pytest.yml +1 -1
- {agent_cli-0.105.0 → agent_cli-0.105.2}/PKG-INFO +1 -1
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/audio_format.py +17 -13
- agent_cli-0.105.2/agent_cli/server/whisper/backends/audio.py +44 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/base.py +1 -1
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/nemo.py +6 -24
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/transformers.py +49 -3
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/server/whisper.md +1 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/appcast.xml +34 -34
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_audio_format.py +55 -7
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_faster_whisper_backend.py +59 -1
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_nemo_backend.py +39 -27
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_whisper.py +57 -1
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_transformers_backend.py +203 -2
- agent_cli-0.105.2/tests/test_whisper_backend_audio.py +171 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude/skills/agent-cli-dev/SKILL.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude/skills/agent-cli-dev/examples.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/README.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/marketplace.json +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/plugin.json +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/skills/agent-cli-dev/SKILL.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.claude-plugin/skills/agent-cli-dev/examples.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.cursorrules +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.dockerignore +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.env.example +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/logo.svg +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/release-drafter.yml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/renovate.json +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/check_extras_sync.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/check_plugin_skill_sync.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/normalize_appcast.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/sync_extras.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/scripts/sync_requirements.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/automerge.yml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/docker.yml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/docs.yml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/markdown-code-runner.yml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/release-drafter.yml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/release.yml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.github/workflows/toc.yaml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.gitignore +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.jscpd.json +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.pre-commit-config.yaml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.prompts/docs-review.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/.prompts/pr-review.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/CLAUDE.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/LICENSE +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/LLAMA_SERVER_USAGE.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/README.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/__main__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_extras.json +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_overrides/nemo-whisper.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/.gitkeep +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/audio.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/diarization.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/faster-whisper.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/kokoro.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/llm.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/memory.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/mlx-whisper.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/nemo-whisper.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/piper.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/rag.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/server.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/speed.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/vad.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/vectordb.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/whisper-transformers.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_requirements/wyoming.txt +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/_tools.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/_voice_agent_common.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/assistant.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/autocorrect.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/chat.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/diarize_live_session.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/memory/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/memory/add.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/memory/proxy.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/rag_proxy.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/speak.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/speakers.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/transcribe.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/transcribe_live.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/agents/voice_edit.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/api.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/cli.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/config.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/config_cmd.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/constants.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/alignment.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/audio.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/chroma.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/deps.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/diarization.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/openai_proxy.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/process.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/reranker.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/speaker_identity.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/sse.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/transcription_logger.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/utils.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/vad.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/core/watch.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/daemon/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/daemon/cli.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/_branch_name.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/_config.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/_output.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/cleanup.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/cli.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/aider.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/base.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/claude.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/codex.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/continue_dev.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/copilot.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/cursor_agent.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/gemini.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/opencode.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/coding_agents/registry.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/base.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/cursor.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/emacs.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/jetbrains.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/nano.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/neovim.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/registry.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/sublime.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/vim.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/vscode.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/editors/zed.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/hooks.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/launch.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/project.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/registry.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/skill/SKILL.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/skill/examples.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/apple_terminal.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/base.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/cmux.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/gnome.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/iterm2.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/kitty.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/registry.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/tmux.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/warp.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/terminals/zellij.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/dev/worktree.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/docs_gen.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/example-config.toml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/common.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/extras.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/hotkeys.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/launchd.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/service_config.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/services.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/install/systemd.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_files.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_filters.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_git.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_indexer.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_ingest.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_persistence.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_prompt.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_retrieval.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_store.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_streaming.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/_tasks.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/api.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/client.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/engine.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/entities.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/memory/models.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/opts.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/py.typed +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_indexer.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_indexing.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_prompt.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_retriever.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_store.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/_utils.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/api.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/client.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/engine.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/rag/models.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/.runtime/.gitkeep +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/linux-hotkeys/README.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/linux-hotkeys/toggle-autocorrect.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/linux-hotkeys/toggle-transcription.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/linux-hotkeys/toggle-voice-edit.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/README.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/skhd-config-example +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/toggle-autocorrect.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/toggle-transcription.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/macos-hotkeys/toggle-voice-edit.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/README.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/pyproject.toml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/server.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/shell.nix +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/nvidia-asr-server/uv.lock +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/run-openwakeword.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-linux-hotkeys.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-linux.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-macos-hotkeys.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-macos.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/setup-windows.ps1 +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/start-all-services-windows.ps1 +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/scripts/start-all-services.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/cli.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/common.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/model_manager.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/model_registry.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/proxy/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/proxy/api.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/proxy/diarization.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/streaming.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/api.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/backends/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/backends/base.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/backends/kokoro.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/backends/piper.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/model_manager.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/model_registry.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/tts/wyoming_handler.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/api.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/faster_whisper.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/backends/mlx.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/languages.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/model_manager.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/model_registry.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/server/whisper/wyoming_handler.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/_wyoming_utils.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/asr.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/llm.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/tts.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/agent_cli/services/wake_word.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/diarization.Dockerfile +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/docker-compose.yml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/memory-proxy.Dockerfile +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/rag-proxy.Dockerfile +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/transcribe-proxy.Dockerfile +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/tts.Dockerfile +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docker/whisper.Dockerfile +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/CNAME +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/architecture/index.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/architecture/memory.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/architecture/rag.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/assistant.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/autocorrect.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/chat.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/config.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/daemon.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/dev.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/diarize-live-session.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/index.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/install-extras.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/install-hotkeys.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/install-services.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/memory.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/rag-proxy.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/server/index.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/server/transcribe-proxy.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/server/tts.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/speak.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/speakers.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/start-services.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/transcribe-live.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/transcribe.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/commands/voice-edit.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/configuration.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/getting-started.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/iOS_Shortcut_Guide.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/index.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/docker.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/index.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/linux.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/macos-app.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/macos.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/nixos.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/installation/windows.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/logo-avatar.svg +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/logo-clean.svg +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/overrides/partials/integrations/analytics/custom.html +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/run_markdown_code_runner.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/docs/system-integration.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/example.agent-cli-config.toml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/justfile +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Package.resolved +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Package.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/README.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Resources/AgentCLI.entitlements +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Resources/Info.plist +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Resources/dmg-background.svg +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AgentCLIApp.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AgentCommand.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AgentCommandRunner.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AgentRuntime.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AppDelegate.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AppMetadata.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/AppUpdater.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/BootstrapState.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/CommandResult.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/ConfigurableHotkeyController.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/FocusedTextTarget.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/LiveTranscriptionPreview.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/LoginItemController.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/MenuActivityStatus.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/MenuActivityStatusRow.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/MenuActivityTracker.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/MenuBarIcon.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/RecentTranscriptionReader.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/RecordingIndicatorController.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/RecordingSoundSettings.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/RuntimeSettings.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/SettingsWindowController.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/Shortcuts.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/StatusMenuController.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/TranscriptPasteController.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/TranscriptionSettings.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Sources/AgentCLI/VoiceLevelOverlay.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/AgentCommandTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/ConfigurableHotkeyControllerTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/LivePreviewSwiftTestingTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/LiveTranscriptionPreviewTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/LoginItemControllerTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/RecentTranscriptionReaderTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/RecordingIndicatorControllerTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/ShortcutDefaultsTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/AgentCLI/Tests/AgentCLITests/VoiceLevelMeterTests.swift +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/build-macos-app.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/macos/test-macos-app-e2e.sh +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/pyproject.toml +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/reddit.md +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/shell.nix +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_fix_my_text.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_interactive.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_interactive_extra.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_memory_add.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_speak.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_speak_e2e.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_speakers.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe_agent.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe_e2e.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe_live.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_transcribe_recovery.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_tts_common.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_tts_common_extra.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_voice_agent_common.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_voice_edit.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_voice_edit_e2e.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/agents/test_wake_word_assistant.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/conftest.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_audio.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_audio_levels.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_chroma.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_sse.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_vad.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/core/test_watch.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_cleanup.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_cli.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_coding_agents.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_editors.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_hooks.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_launch.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_project.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_terminals.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_verification.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/dev/test_worktree.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/install/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/install/test_extras.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/install/test_launchd.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_api_health.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_api_integration_liveish.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_client.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_engine.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_files.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_filters.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_git_integration.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_indexer.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_memory_integration.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_proxy_passthrough.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_store.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/memory/test_utils.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/mocks/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/mocks/audio.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/mocks/llm.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/mocks/wyoming.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/__init__.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_api.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_engine.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_history.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_indexer.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_indexing.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_rag_client.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_rag_integration_liveish.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_rag_proxy_passthrough.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_retriever.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_store.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/rag/test_utils.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_alignment.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_api.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_api_integration.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_asr.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_asr_recovery.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_audio_e2e.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_cli.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_config.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_config_cmd.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_daemon.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_diarization.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_diarize_live_session.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_docs_gen.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_env_vars.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_json_output.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_llm.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_llm_gemini.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_macos_app.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_memory_tools.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_mlx_backend.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_normalize_appcast.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_process_manager.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_proxy_diarization.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_requires_extras.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_streaming.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_tts.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_tts_wyoming.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_server_whisper_wyoming.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_services.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_speaker_identity.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_tools.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_tts.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_utils.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_wake_word.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/tests/test_wyoming_utils.py +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/uv.lock +0 -0
- {agent_cli-0.105.0 → agent_cli-0.105.2}/zensical.toml +0 -0
|
@@ -43,6 +43,6 @@ jobs:
|
|
|
43
43
|
run: uv run --extra audio --extra diarization --extra llm --extra rag --extra memory --extra vad --extra faster-whisper --extra piper --extra kokoro --extra server --extra speed --extra test pytest -vvv
|
|
44
44
|
- name: Upload coverage reports to Codecov
|
|
45
45
|
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13'
|
|
46
|
-
uses: codecov/codecov-action@v7.
|
|
46
|
+
uses: codecov/codecov-action@v7.1.0
|
|
47
47
|
env:
|
|
48
48
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-cli
|
|
3
|
-
Version: 0.105.
|
|
3
|
+
Version: 0.105.2
|
|
4
4
|
Summary: A suite of AI-powered command-line tools for text correction, audio transcription, and voice assistance.
|
|
5
5
|
Project-URL: Homepage, https://github.com/basnijholt/agent-cli
|
|
6
6
|
Author-email: Bas Nijholt <bas@nijho.lt>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import asyncio
|
|
5
6
|
import io
|
|
6
7
|
import logging
|
|
7
8
|
import shutil
|
|
@@ -152,13 +153,13 @@ def convert_audio_to_wyoming_format(
|
|
|
152
153
|
output_path.unlink(missing_ok=True)
|
|
153
154
|
|
|
154
155
|
|
|
155
|
-
def convert_audio_to_wav_format(
|
|
156
|
+
async def convert_audio_to_wav_format(
|
|
156
157
|
audio_data: bytes,
|
|
157
158
|
source_filename: str,
|
|
158
159
|
*,
|
|
159
|
-
timeout: int | None = 60,
|
|
160
|
+
timeout: int | None = 60, # noqa: ASYNC109 - bounds and cleans up the child process
|
|
160
161
|
) -> bytes:
|
|
161
|
-
"""Convert
|
|
162
|
+
"""Convert to 16kHz mono 16-bit WAV, stopping FFmpeg on cancellation or timeout."""
|
|
162
163
|
if not shutil.which("ffmpeg"):
|
|
163
164
|
msg = "FFmpeg not found in PATH. Please install FFmpeg to convert audio formats."
|
|
164
165
|
raise RuntimeError(msg)
|
|
@@ -185,20 +186,23 @@ def convert_audio_to_wav_format(
|
|
|
185
186
|
]
|
|
186
187
|
|
|
187
188
|
logger.debug("Running FFmpeg WAV conversion: %s", " ".join(cmd))
|
|
189
|
+
process = await asyncio.create_subprocess_exec(
|
|
190
|
+
*cmd,
|
|
191
|
+
stdout=asyncio.subprocess.DEVNULL,
|
|
192
|
+
stderr=asyncio.subprocess.PIPE,
|
|
193
|
+
)
|
|
188
194
|
try:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
capture_output=True,
|
|
192
|
-
text=False,
|
|
193
|
-
check=False,
|
|
194
|
-
timeout=timeout,
|
|
195
|
-
)
|
|
196
|
-
except subprocess.TimeoutExpired as e:
|
|
195
|
+
_, stderr = await asyncio.wait_for(process.communicate(), timeout=timeout)
|
|
196
|
+
except TimeoutError as e:
|
|
197
197
|
msg = f"FFmpeg conversion timed out after {timeout} seconds"
|
|
198
198
|
raise RuntimeError(msg) from e
|
|
199
|
+
finally:
|
|
200
|
+
if process.returncode is None:
|
|
201
|
+
process.kill()
|
|
202
|
+
await process.communicate()
|
|
199
203
|
|
|
200
|
-
if
|
|
201
|
-
stderr_text =
|
|
204
|
+
if process.returncode != 0:
|
|
205
|
+
stderr_text = (stderr or b"").decode("utf-8", errors="replace")
|
|
202
206
|
logger.error("FFmpeg WAV conversion failed: %s", stderr_text)
|
|
203
207
|
msg = f"FFmpeg WAV conversion failed: {stderr_text}"
|
|
204
208
|
raise RuntimeError(msg)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Audio preparation for ASR backends that require PCM WAV input."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import io
|
|
6
|
+
import wave
|
|
7
|
+
|
|
8
|
+
from agent_cli import constants
|
|
9
|
+
from agent_cli.core.audio_format import convert_audio_to_wav_format
|
|
10
|
+
from agent_cli.server.whisper.backends.base import InvalidAudioError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def prepare_wav_audio(
|
|
14
|
+
audio: bytes,
|
|
15
|
+
source_filename: str | None,
|
|
16
|
+
*,
|
|
17
|
+
backend_label: str,
|
|
18
|
+
) -> bytes:
|
|
19
|
+
"""Return 16kHz mono 16-bit PCM WAV bytes, converting only when needed.
|
|
20
|
+
|
|
21
|
+
Container validity alone is insufficient: Transformers reads samples as
|
|
22
|
+
int16 mono. Normalize other sample formats before model-worker dispatch.
|
|
23
|
+
Backends with their own decoders, such as faster-whisper, do not need this.
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
with wave.open(io.BytesIO(audio), "rb") as wav_file:
|
|
27
|
+
if (
|
|
28
|
+
wav_file.getsampwidth() == constants.AUDIO_FORMAT_WIDTH
|
|
29
|
+
and wav_file.getnchannels() == constants.AUDIO_CHANNELS
|
|
30
|
+
and wav_file.getframerate() == constants.AUDIO_RATE
|
|
31
|
+
):
|
|
32
|
+
return audio
|
|
33
|
+
except (wave.Error, EOFError, RuntimeError):
|
|
34
|
+
# wave can raise RuntimeError when a malformed RIFF chunk seeks past EOF.
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
return await convert_audio_to_wav_format(audio, source_filename or "audio")
|
|
39
|
+
except RuntimeError as exc:
|
|
40
|
+
msg = (
|
|
41
|
+
f"Unsupported audio format for {backend_label}. "
|
|
42
|
+
"Provide a valid WAV file or install ffmpeg to convert uploads."
|
|
43
|
+
)
|
|
44
|
+
raise InvalidAudioError(msg) from exc
|
|
@@ -89,7 +89,7 @@ class WhisperBackend(Protocol):
|
|
|
89
89
|
"""Transcribe audio data.
|
|
90
90
|
|
|
91
91
|
Args:
|
|
92
|
-
audio:
|
|
92
|
+
audio: Encoded audio bytes (16kHz mono 16-bit PCM WAV preferred).
|
|
93
93
|
source_filename: Optional filename to help detect audio format.
|
|
94
94
|
language: Language code or None for auto-detection
|
|
95
95
|
task: "transcribe" or "translate" (to English)
|
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import inspect
|
|
7
|
-
import io
|
|
8
7
|
import logging
|
|
9
8
|
import os
|
|
10
9
|
import sys
|
|
@@ -18,11 +17,10 @@ from multiprocessing import get_context
|
|
|
18
17
|
from pathlib import Path
|
|
19
18
|
from typing import Any, Literal
|
|
20
19
|
|
|
21
|
-
from agent_cli.core.audio_format import convert_audio_to_wav_format
|
|
22
20
|
from agent_cli.core.process import set_process_title
|
|
21
|
+
from agent_cli.server.whisper.backends.audio import prepare_wav_audio
|
|
23
22
|
from agent_cli.server.whisper.backends.base import (
|
|
24
23
|
BackendConfig,
|
|
25
|
-
InvalidAudioError,
|
|
26
24
|
TranscriptionResult,
|
|
27
25
|
)
|
|
28
26
|
|
|
@@ -182,26 +180,6 @@ def _audio_duration_seconds(wav_path: str) -> float:
|
|
|
182
180
|
return 0.0
|
|
183
181
|
|
|
184
182
|
|
|
185
|
-
def _prepare_audio_for_nemo(audio_bytes: bytes, source_filename: str | None) -> bytes:
|
|
186
|
-
"""Return a WAV container suitable for NeMo file-path transcription."""
|
|
187
|
-
try:
|
|
188
|
-
with wave.open(io.BytesIO(audio_bytes), "rb"):
|
|
189
|
-
return audio_bytes
|
|
190
|
-
except (wave.Error, EOFError):
|
|
191
|
-
pass
|
|
192
|
-
|
|
193
|
-
filename = source_filename or "audio"
|
|
194
|
-
try:
|
|
195
|
-
return convert_audio_to_wav_format(audio_bytes, filename)
|
|
196
|
-
except RuntimeError as exc:
|
|
197
|
-
logger.warning("FFmpeg conversion failed for NeMo Whisper: %s", exc)
|
|
198
|
-
msg = (
|
|
199
|
-
"Unsupported audio format for NeMo Whisper. "
|
|
200
|
-
"Provide a WAV file or install ffmpeg to convert uploads."
|
|
201
|
-
)
|
|
202
|
-
raise InvalidAudioError(msg) from exc
|
|
203
|
-
|
|
204
|
-
|
|
205
183
|
def _build_transcribe_kwargs(
|
|
206
184
|
transcribe_func: Any,
|
|
207
185
|
*,
|
|
@@ -433,7 +411,11 @@ class NemoWhisperBackend:
|
|
|
433
411
|
)
|
|
434
412
|
self._warned_initial_prompt_ignored = True
|
|
435
413
|
|
|
436
|
-
audio = await
|
|
414
|
+
audio = await prepare_wav_audio(
|
|
415
|
+
audio,
|
|
416
|
+
source_filename,
|
|
417
|
+
backend_label="NeMo Whisper",
|
|
418
|
+
)
|
|
437
419
|
|
|
438
420
|
kwargs: dict[str, Any] = {
|
|
439
421
|
"language": language,
|
|
@@ -6,6 +6,7 @@ import asyncio
|
|
|
6
6
|
import logging
|
|
7
7
|
import tempfile
|
|
8
8
|
import time
|
|
9
|
+
import traceback
|
|
9
10
|
import wave
|
|
10
11
|
from concurrent.futures import ProcessPoolExecutor
|
|
11
12
|
from dataclasses import dataclass
|
|
@@ -14,6 +15,7 @@ from pathlib import Path
|
|
|
14
15
|
from typing import Any, Literal
|
|
15
16
|
|
|
16
17
|
from agent_cli.core.process import set_process_title
|
|
18
|
+
from agent_cli.server.whisper.backends.audio import prepare_wav_audio
|
|
17
19
|
from agent_cli.server.whisper.backends.base import (
|
|
18
20
|
BackendConfig,
|
|
19
21
|
TranscriptionResult,
|
|
@@ -428,8 +430,8 @@ def _transcribe_with_generate(
|
|
|
428
430
|
)
|
|
429
431
|
|
|
430
432
|
|
|
431
|
-
def
|
|
432
|
-
"""
|
|
433
|
+
def _transcribe_with_loaded_model(kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
434
|
+
"""Dispatch transcription using the model retained in this worker."""
|
|
433
435
|
if _state.model is None or _state.processor is None:
|
|
434
436
|
msg = "Model not loaded in subprocess. Call _load_model_in_subprocess first."
|
|
435
437
|
raise RuntimeError(msg)
|
|
@@ -479,6 +481,44 @@ def _transcribe_in_subprocess(kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
|
479
481
|
)
|
|
480
482
|
|
|
481
483
|
|
|
484
|
+
def _clear_exception_frames(exc: BaseException) -> None:
|
|
485
|
+
"""Release helper locals retained by an exception and its chained failures."""
|
|
486
|
+
pending = [exc]
|
|
487
|
+
seen: set[int] = set()
|
|
488
|
+
while pending:
|
|
489
|
+
current = pending.pop()
|
|
490
|
+
if id(current) in seen:
|
|
491
|
+
continue
|
|
492
|
+
seen.add(id(current))
|
|
493
|
+
traceback.clear_frames(current.__traceback__)
|
|
494
|
+
pending.extend(
|
|
495
|
+
error for error in (current.__cause__, current.__context__) if error is not None
|
|
496
|
+
)
|
|
497
|
+
if isinstance(current, BaseExceptionGroup):
|
|
498
|
+
pending.extend(current.exceptions)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def _transcribe_in_subprocess(kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
502
|
+
"""Transcribe and return unused CUDA memory while keeping model weights loaded."""
|
|
503
|
+
if not _state.device or not _state.device.startswith("cuda"):
|
|
504
|
+
return _transcribe_with_loaded_model(kwargs)
|
|
505
|
+
|
|
506
|
+
import torch # noqa: PLC0415
|
|
507
|
+
|
|
508
|
+
try:
|
|
509
|
+
return _transcribe_with_loaded_model(kwargs)
|
|
510
|
+
except BaseException as exc:
|
|
511
|
+
# Failed helper frames otherwise keep temporary tensors live until the
|
|
512
|
+
# executor serializes the exception, which happens after our finally.
|
|
513
|
+
# Clear locals without losing the traceback's function names and lines.
|
|
514
|
+
_clear_exception_frames(exc)
|
|
515
|
+
raise
|
|
516
|
+
finally:
|
|
517
|
+
# Helper frames have returned, so their temporary tensors are now free.
|
|
518
|
+
# Only unused allocator blocks are released; model weights stay resident.
|
|
519
|
+
torch.cuda.empty_cache()
|
|
520
|
+
|
|
521
|
+
|
|
482
522
|
class TransformersWhisperBackend:
|
|
483
523
|
"""ASR backend using HuggingFace transformers.
|
|
484
524
|
|
|
@@ -554,7 +594,7 @@ class TransformersWhisperBackend:
|
|
|
554
594
|
self,
|
|
555
595
|
audio: bytes,
|
|
556
596
|
*,
|
|
557
|
-
source_filename: str | None = None,
|
|
597
|
+
source_filename: str | None = None,
|
|
558
598
|
language: str | None = None,
|
|
559
599
|
task: Literal["transcribe", "translate"] = "transcribe",
|
|
560
600
|
initial_prompt: str | None = None,
|
|
@@ -567,6 +607,12 @@ class TransformersWhisperBackend:
|
|
|
567
607
|
msg = "Model not loaded. Call load() first."
|
|
568
608
|
raise RuntimeError(msg)
|
|
569
609
|
|
|
610
|
+
audio = await prepare_wav_audio(
|
|
611
|
+
audio,
|
|
612
|
+
source_filename,
|
|
613
|
+
backend_label="transformers ASR",
|
|
614
|
+
)
|
|
615
|
+
|
|
570
616
|
# Write audio to temp file for wave parsing in subprocess
|
|
571
617
|
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
|
|
572
618
|
tmp.write(audio)
|
|
@@ -55,6 +55,7 @@ Run a local ASR server with automatic backend selection based on your platform:
|
|
|
55
55
|
- **OpenAI-compatible API** at `/v1/audio/transcriptions` - drop-in replacement for OpenAI's Whisper API
|
|
56
56
|
- **Wyoming protocol** for [Home Assistant](https://www.home-assistant.io/) voice integration (Wyoming is the standard protocol for local voice services)
|
|
57
57
|
- **TTL-based memory management** - models unload after idle period, freeing RAM/VRAM
|
|
58
|
+
- **CUDA cache cleanup** - the transformers backend releases unused GPU cache after each transcription while keeping model weights loaded
|
|
58
59
|
- **Multiple models** - run different model sizes with independent TTLs
|
|
59
60
|
- **Background preloading** - downloads start at startup without blocking; use `--preload` to wait
|
|
60
61
|
- **Multi-platform support** - automatically uses the optimal backend for your hardware (`auto` switches to `nemo` for Parakeet models)
|
|
@@ -4,6 +4,40 @@
|
|
|
4
4
|
<title>Agent CLI Updates</title>
|
|
5
5
|
<link>https://github.com/basnijholt/agent-cli</link>
|
|
6
6
|
<description>Signed Agent CLI macOS app updates.</description>
|
|
7
|
+
<item>
|
|
8
|
+
<title>0.105.1</title>
|
|
9
|
+
<pubDate>Tue, 08 Sep 2026 13:10:30 +0000</pubDate>
|
|
10
|
+
<link>https://github.com/basnijholt/agent-cli</link>
|
|
11
|
+
<sparkle:version>205</sparkle:version>
|
|
12
|
+
<sparkle:shortVersionString>0.105.1</sparkle:shortVersionString>
|
|
13
|
+
<sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
|
|
14
|
+
<sparkle:hardwareRequirements>arm64</sparkle:hardwareRequirements>
|
|
15
|
+
<description sparkle:format="markdown"><![CDATA[## Bug fixes
|
|
16
|
+
|
|
17
|
+
- Release unused CUDA cache after each transcription in the transformers ASR backend, keeping model weights loaded for the next request. Long recordings no longer leave their peak temporary GPU allocation reserved throughout the idle timeout. In a Qwen3-ASR test, idle process VRAM dropped from 10.89 GiB to 4.13 GiB. (#642)
|
|
18
|
+
- Clear temporary tensor references held by failed-request tracebacks, including chained and grouped exceptions, before releasing CUDA cache. CPU and MPS behavior is unchanged. (#642)
|
|
19
|
+
|
|
20
|
+
**Full changelog:** https://github.com/basnijholt/agent-cli/compare/v0.105.0...v0.105.1
|
|
21
|
+
|
|
22
|
+
]]></description>
|
|
23
|
+
<enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.105.1/AgentCLI.zip" length="18455354" type="application/octet-stream" sparkle:edSignature="vZqacIut3YOxE/I4mgUeHiI9jviHLgzymE3jK8R7aLEooAPZhNqkzxTFpB95iWShRRWfR8lf9T4ztVFs0EHUDw=="/>
|
|
24
|
+
</item>
|
|
25
|
+
<item>
|
|
26
|
+
<title>0.105.0</title>
|
|
27
|
+
<pubDate>Tue, 08 Sep 2026 10:29:52 +0000</pubDate>
|
|
28
|
+
<link>https://github.com/basnijholt/agent-cli</link>
|
|
29
|
+
<sparkle:version>204</sparkle:version>
|
|
30
|
+
<sparkle:shortVersionString>0.105.0</sparkle:shortVersionString>
|
|
31
|
+
<sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
|
|
32
|
+
<sparkle:hardwareRequirements>arm64</sparkle:hardwareRequirements>
|
|
33
|
+
<description sparkle:format="markdown"><![CDATA[## What’s Changed
|
|
34
|
+
|
|
35
|
+
* Prevent Qwen long-audio truncation (#641) @basnijholt
|
|
36
|
+
* Publish diarization Docker image (#640) @basnijholt
|
|
37
|
+
|
|
38
|
+
]]></description>
|
|
39
|
+
<enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.105.0/AgentCLI.zip" length="18454913" type="application/octet-stream" sparkle:edSignature="Z3253GuNAV6GC2V4Qib0Bm7m8bSdlWHPiXeHIZNa06qAJaY+qRXzrSPRyT8iZW5GnkZOJHciIbqvkkoJ9/DoCw=="/>
|
|
40
|
+
</item>
|
|
7
41
|
<item>
|
|
8
42
|
<title>0.104.0</title>
|
|
9
43
|
<pubDate>Mon, 07 Sep 2026 21:38:19 +0000</pubDate>
|
|
@@ -174,39 +208,5 @@
|
|
|
174
208
|
]]></description>
|
|
175
209
|
<enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.101.0/AgentCLI.zip" length="22912290" type="application/octet-stream" sparkle:edSignature="9re7kqubAoQgSpsmDvAT/LCWy8/tI1RzR2uGkb+BCwGJbtfvuzDU2ETm/JD+lpQt3Ayik6ULAOv+/5S6JSmdBA=="/>
|
|
176
210
|
</item>
|
|
177
|
-
<item>
|
|
178
|
-
<title>0.100.0</title>
|
|
179
|
-
<pubDate>Wed, 10 Jun 2026 17:59:16 +0000</pubDate>
|
|
180
|
-
<link>https://github.com/basnijholt/agent-cli</link>
|
|
181
|
-
<sparkle:version>195</sparkle:version>
|
|
182
|
-
<sparkle:shortVersionString>0.100.0</sparkle:shortVersionString>
|
|
183
|
-
<sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
|
|
184
|
-
<sparkle:hardwareRequirements>arm64</sparkle:hardwareRequirements>
|
|
185
|
-
<description sparkle:format="markdown"><![CDATA[## What’s Changed
|
|
186
|
-
|
|
187
|
-
* Add cmux support to agent-cli dev (#589) @basnijholt
|
|
188
|
-
* ⬆️ Update sparkle-project/Sparkle to v2.9.3 (#587) @[renovate[bot]](https://github.com/apps/renovate)
|
|
189
|
-
* [pre-commit.ci] pre-commit autoupdate (#588) @[pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci)
|
|
190
|
-
* ⬆️ Update codecov/codecov-action action to v7 (#586) @[renovate[bot]](https://github.com/apps/renovate)
|
|
191
|
-
* ⬆️ Update codecov/codecov-action action to v6.0.2 (#585) @[renovate[bot]](https://github.com/apps/renovate)
|
|
192
|
-
|
|
193
|
-
]]></description>
|
|
194
|
-
<enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.100.0/AgentCLI.zip" length="22908645" type="application/octet-stream" sparkle:edSignature="2xn/3yQOqyqwCQ5871EAgFra/D/z6UrDl1B+ZxUh7PnOxJpL4Oe44UzQgjFB+LJXzqrY6Kmqvu9GBmk+3aZ1Ag=="/>
|
|
195
|
-
</item>
|
|
196
|
-
<item>
|
|
197
|
-
<title>0.99.0</title>
|
|
198
|
-
<pubDate>Fri, 05 Jun 2026 22:57:28 +0000</pubDate>
|
|
199
|
-
<link>https://github.com/basnijholt/agent-cli</link>
|
|
200
|
-
<sparkle:version>194</sparkle:version>
|
|
201
|
-
<sparkle:shortVersionString>0.99.0</sparkle:shortVersionString>
|
|
202
|
-
<sparkle:minimumSystemVersion>13.0</sparkle:minimumSystemVersion>
|
|
203
|
-
<sparkle:hardwareRequirements>arm64</sparkle:hardwareRequirements>
|
|
204
|
-
<description sparkle:format="markdown"><![CDATA[## What’s Changed
|
|
205
|
-
|
|
206
|
-
* Add live transcription preview (#571) @basnijholt
|
|
207
|
-
|
|
208
|
-
]]></description>
|
|
209
|
-
<enclosure url="https://github.com/basnijholt/agent-cli/releases/download/v0.99.0/AgentCLI.zip" length="22924494" type="application/octet-stream" sparkle:edSignature="COI8nqDd0cBtOO9jw/QRmsucsxE0SPiSgNF/o+s/nv3FCjSXy0C+gZX/bIFtCYyz/DYkff+IaHaLzhw8Z5aEBQ=="/>
|
|
210
|
-
</item>
|
|
211
211
|
</channel>
|
|
212
212
|
</rss>
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import asyncio
|
|
5
6
|
import shutil
|
|
6
7
|
import struct
|
|
7
|
-
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
8
11
|
|
|
9
12
|
import pytest
|
|
10
13
|
|
|
@@ -78,28 +81,73 @@ def test_convert_audio_arguments() -> None:
|
|
|
78
81
|
assert "s16le" in cmd
|
|
79
82
|
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
@pytest.mark.asyncio
|
|
85
|
+
async def test_convert_audio_to_wav_arguments() -> None:
|
|
82
86
|
"""Regression test: WAV conversion should produce a PCM WAV container."""
|
|
83
87
|
with (
|
|
84
88
|
patch("shutil.which", return_value="/usr/bin/ffmpeg"),
|
|
85
|
-
patch("
|
|
89
|
+
patch("asyncio.create_subprocess_exec") as mock_run,
|
|
86
90
|
patch("pathlib.Path.read_bytes", return_value=b"wav_data"),
|
|
87
91
|
patch("shutil.rmtree"),
|
|
88
92
|
):
|
|
89
|
-
mock_run.return_value = MagicMock(
|
|
93
|
+
mock_run.return_value = MagicMock(
|
|
94
|
+
returncode=0, communicate=AsyncMock(return_value=(None, b""))
|
|
95
|
+
)
|
|
90
96
|
|
|
91
|
-
converted = audio_format.convert_audio_to_wav_format(b"input_data", "test.mp3")
|
|
97
|
+
converted = await audio_format.convert_audio_to_wav_format(b"input_data", "test.mp3")
|
|
92
98
|
|
|
93
99
|
assert converted == b"wav_data"
|
|
94
100
|
args, kwargs = mock_run.call_args
|
|
95
|
-
assert kwargs
|
|
96
|
-
cmd = args
|
|
101
|
+
assert kwargs["stderr"] == asyncio.subprocess.PIPE
|
|
102
|
+
cmd = args
|
|
97
103
|
assert cmd[0] == "ffmpeg"
|
|
98
104
|
assert "-acodec" in cmd
|
|
99
105
|
assert "pcm_s16le" in cmd
|
|
100
106
|
assert "-f" not in cmd
|
|
101
107
|
|
|
102
108
|
|
|
109
|
+
@pytest.mark.parametrize("cancel", [False, True], ids=["timeout", "cancellation"])
|
|
110
|
+
@pytest.mark.asyncio
|
|
111
|
+
async def test_wav_conversion_stops_process_and_removes_files(
|
|
112
|
+
monkeypatch: pytest.MonkeyPatch, cancel: bool
|
|
113
|
+
) -> None:
|
|
114
|
+
"""Timed-out or cancelled requests must reap FFmpeg before removing its files."""
|
|
115
|
+
spawn = asyncio.create_subprocess_exec
|
|
116
|
+
started = asyncio.Event()
|
|
117
|
+
processes: list[asyncio.subprocess.Process] = []
|
|
118
|
+
directories: list[Path] = []
|
|
119
|
+
|
|
120
|
+
async def start_process(*args: str, stdout: int, stderr: int) -> asyncio.subprocess.Process:
|
|
121
|
+
directories.append(Path(args[-1]).parent)
|
|
122
|
+
process = await spawn(
|
|
123
|
+
sys.executable, "-c", "import time; time.sleep(60)", stdout=stdout, stderr=stderr
|
|
124
|
+
)
|
|
125
|
+
processes.append(process)
|
|
126
|
+
started.set()
|
|
127
|
+
return process
|
|
128
|
+
|
|
129
|
+
monkeypatch.setattr("shutil.which", lambda _name: "ffmpeg")
|
|
130
|
+
monkeypatch.setattr(asyncio, "create_subprocess_exec", start_process)
|
|
131
|
+
task = asyncio.create_task(
|
|
132
|
+
audio_format.convert_audio_to_wav_format(b"input", "voice.m4a", timeout=1),
|
|
133
|
+
)
|
|
134
|
+
try:
|
|
135
|
+
await asyncio.wait_for(started.wait(), timeout=1)
|
|
136
|
+
assert directories[0].exists()
|
|
137
|
+
if cancel:
|
|
138
|
+
task.cancel()
|
|
139
|
+
error = asyncio.CancelledError if cancel else RuntimeError
|
|
140
|
+
with pytest.raises(error):
|
|
141
|
+
await task
|
|
142
|
+
assert processes[0].returncode is not None
|
|
143
|
+
assert not directories[0].exists()
|
|
144
|
+
finally:
|
|
145
|
+
for process in processes:
|
|
146
|
+
if process.returncode is None:
|
|
147
|
+
process.kill()
|
|
148
|
+
await process.communicate()
|
|
149
|
+
|
|
150
|
+
|
|
103
151
|
def test_convert_audio_integration(sample_wav_data: bytes) -> None:
|
|
104
152
|
"""Integration test using actual ffmpeg if available."""
|
|
105
153
|
if not shutil.which("ffmpeg"):
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import io
|
|
6
|
+
import wave
|
|
5
7
|
from concurrent.futures.process import BrokenProcessPool
|
|
6
8
|
from typing import TYPE_CHECKING, cast
|
|
7
9
|
from unittest.mock import AsyncMock, patch
|
|
@@ -58,9 +60,65 @@ async def test_faster_whisper_transcribe_recovers_from_broken_process_pool() ->
|
|
|
58
60
|
patch.object(backend, "load", new=AsyncMock(side_effect=fake_load)) as load_mock,
|
|
59
61
|
):
|
|
60
62
|
mock_loop.return_value.run_in_executor = mock_run_in_executor
|
|
61
|
-
result = await backend.transcribe(
|
|
63
|
+
result = await backend.transcribe(_create_test_wav())
|
|
62
64
|
|
|
63
65
|
assert result.text == "hello world"
|
|
64
66
|
unload_mock.assert_awaited_once()
|
|
65
67
|
load_mock.assert_awaited_once()
|
|
66
68
|
assert executors_seen == [initial_executor, recovered_executor]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _create_test_wav() -> bytes:
|
|
72
|
+
"""Create a tiny valid 16kHz mono 16-bit WAV file."""
|
|
73
|
+
buffer = io.BytesIO()
|
|
74
|
+
with wave.open(buffer, "wb") as wav_file:
|
|
75
|
+
wav_file.setnchannels(1)
|
|
76
|
+
wav_file.setsampwidth(2)
|
|
77
|
+
wav_file.setframerate(16000)
|
|
78
|
+
wav_file.writeframes(b"\x00\x00" * 160)
|
|
79
|
+
return buffer.getvalue()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def _transcribe_capturing_audio(audio: bytes, source_filename: str | None) -> bytes:
|
|
83
|
+
"""Run the backend against a stub executor and return the bytes it dispatched."""
|
|
84
|
+
backend = FasterWhisperBackend(BackendConfig(model_name="tiny"))
|
|
85
|
+
backend._executor = cast("ProcessPoolExecutor", object())
|
|
86
|
+
dispatched: dict[str, bytes] = {}
|
|
87
|
+
|
|
88
|
+
async def mock_run_in_executor(
|
|
89
|
+
_executor: object,
|
|
90
|
+
_func: object,
|
|
91
|
+
audio_bytes: bytes,
|
|
92
|
+
_kwargs: dict[str, object],
|
|
93
|
+
) -> dict[str, object]:
|
|
94
|
+
dispatched["audio"] = audio_bytes
|
|
95
|
+
return {
|
|
96
|
+
"text": "hello",
|
|
97
|
+
"language": "en",
|
|
98
|
+
"language_probability": 1.0,
|
|
99
|
+
"duration": 0.01,
|
|
100
|
+
"segments": [],
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
with patch("asyncio.get_running_loop") as mock_loop:
|
|
104
|
+
mock_loop.return_value.run_in_executor = mock_run_in_executor
|
|
105
|
+
result = await backend.transcribe(audio, source_filename=source_filename)
|
|
106
|
+
|
|
107
|
+
assert result.text == "hello"
|
|
108
|
+
return dispatched["audio"]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@pytest.mark.parametrize(
|
|
112
|
+
("audio", "filename"),
|
|
113
|
+
[(b"OggS\x00\x02", "voice.ogg"), (b"\x00\x00\x00 ftypM4A ", "voice.m4a")],
|
|
114
|
+
)
|
|
115
|
+
@pytest.mark.asyncio
|
|
116
|
+
async def test_transcribe_preserves_native_decoder_input_without_ffmpeg(
|
|
117
|
+
monkeypatch: pytest.MonkeyPatch, audio: bytes, filename: str
|
|
118
|
+
) -> None:
|
|
119
|
+
"""PyAV owns decoding; pre-conversion would add an unnecessary system dependency.
|
|
120
|
+
|
|
121
|
+
https://github.com/SYSTRAN/faster-whisper/blob/master/faster_whisper/audio.py
|
|
122
|
+
"""
|
|
123
|
+
monkeypatch.setattr("shutil.which", lambda _name: None)
|
|
124
|
+
assert await _transcribe_capturing_audio(audio, filename) == audio
|
|
@@ -9,15 +9,18 @@ import wave
|
|
|
9
9
|
from concurrent.futures import ThreadPoolExecutor
|
|
10
10
|
from concurrent.futures.process import BrokenProcessPool
|
|
11
11
|
from types import ModuleType, SimpleNamespace
|
|
12
|
-
from typing import TYPE_CHECKING, Any
|
|
12
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
13
|
+
from unittest.mock import patch
|
|
13
14
|
|
|
14
15
|
import pytest
|
|
15
16
|
|
|
16
17
|
from agent_cli.server.cli import _is_parakeet_model
|
|
18
|
+
from agent_cli.server.whisper.backends import audio as audio_preparation
|
|
17
19
|
from agent_cli.server.whisper.backends import nemo as backend
|
|
18
20
|
from agent_cli.server.whisper.backends.base import BackendConfig, InvalidAudioError
|
|
19
21
|
|
|
20
22
|
if TYPE_CHECKING:
|
|
23
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
21
24
|
from pathlib import Path
|
|
22
25
|
|
|
23
26
|
|
|
@@ -308,51 +311,60 @@ def test_audio_duration_seconds_returns_zero_for_non_wav(tmp_path: Path) -> None
|
|
|
308
311
|
assert backend._audio_duration_seconds(str(audio_path)) == 0.0
|
|
309
312
|
|
|
310
313
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
) -> None:
|
|
314
|
-
"""Valid WAV uploads should not require FFmpeg conversion."""
|
|
315
|
-
audio = _create_test_wav()
|
|
316
|
-
monkeypatch.setattr(
|
|
317
|
-
backend,
|
|
318
|
-
"convert_audio_to_wav_format",
|
|
319
|
-
lambda *_args, **_kwargs: pytest.fail("unexpected conversion"),
|
|
320
|
-
)
|
|
321
|
-
|
|
322
|
-
assert backend._prepare_audio_for_nemo(audio, "sample.wav") == audio
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
def test_prepare_audio_for_nemo_converts_non_wav(
|
|
326
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
327
|
-
) -> None:
|
|
314
|
+
@pytest.mark.asyncio
|
|
315
|
+
async def test_transcribe_converts_non_wav_upload(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
328
316
|
"""Non-WAV uploads should be converted to a WAV container before NeMo sees them."""
|
|
329
317
|
converted = _create_test_wav()
|
|
330
318
|
calls: dict[str, object] = {}
|
|
331
319
|
|
|
332
|
-
def fake_convert(audio: bytes, source_filename: str) -> bytes:
|
|
320
|
+
async def fake_convert(audio: bytes, source_filename: str) -> bytes:
|
|
333
321
|
calls["audio"] = audio
|
|
334
322
|
calls["source_filename"] = source_filename
|
|
335
323
|
return converted
|
|
336
324
|
|
|
337
|
-
monkeypatch.setattr(
|
|
325
|
+
monkeypatch.setattr(audio_preparation, "convert_audio_to_wav_format", fake_convert)
|
|
326
|
+
nemo_backend = backend.NemoWhisperBackend(BackendConfig(model_name="parakeet-tdt-0.6b-v2"))
|
|
327
|
+
nemo_backend._executor = cast("ProcessPoolExecutor", object())
|
|
328
|
+
dispatched: dict[str, bytes] = {}
|
|
329
|
+
|
|
330
|
+
async def mock_run_in_executor(
|
|
331
|
+
_executor: object,
|
|
332
|
+
_func: object,
|
|
333
|
+
audio_bytes: bytes,
|
|
334
|
+
_kwargs: dict[str, object],
|
|
335
|
+
) -> dict[str, Any]:
|
|
336
|
+
dispatched["audio"] = audio_bytes
|
|
337
|
+
return {
|
|
338
|
+
"text": "hello",
|
|
339
|
+
"language": "en",
|
|
340
|
+
"language_probability": 1.0,
|
|
341
|
+
"duration": 0.01,
|
|
342
|
+
"segments": [],
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
with patch("asyncio.get_running_loop") as mock_loop:
|
|
346
|
+
mock_loop.return_value.run_in_executor = mock_run_in_executor
|
|
347
|
+
result = await nemo_backend.transcribe(b"ID3", source_filename="sample.mp3")
|
|
338
348
|
|
|
339
|
-
assert
|
|
349
|
+
assert result.text == "hello"
|
|
350
|
+
assert dispatched["audio"] == converted
|
|
340
351
|
assert calls == {"audio": b"ID3", "source_filename": "sample.mp3"}
|
|
341
352
|
|
|
342
353
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
) -> None:
|
|
354
|
+
@pytest.mark.asyncio
|
|
355
|
+
async def test_transcribe_raises_invalid_audio_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
346
356
|
"""Conversion failures should become API-visible invalid-audio errors."""
|
|
347
357
|
|
|
348
|
-
def fake_convert(audio: bytes, source_filename: str) -> bytes: # noqa: ARG001
|
|
358
|
+
async def fake_convert(audio: bytes, source_filename: str) -> bytes: # noqa: ARG001
|
|
349
359
|
msg = "ffmpeg failed"
|
|
350
360
|
raise RuntimeError(msg)
|
|
351
361
|
|
|
352
|
-
monkeypatch.setattr(
|
|
362
|
+
monkeypatch.setattr(audio_preparation, "convert_audio_to_wav_format", fake_convert)
|
|
363
|
+
nemo_backend = backend.NemoWhisperBackend(BackendConfig(model_name="parakeet-tdt-0.6b-v2"))
|
|
364
|
+
nemo_backend._executor = cast("ProcessPoolExecutor", object())
|
|
353
365
|
|
|
354
366
|
with pytest.raises(InvalidAudioError, match="Unsupported audio format for NeMo"):
|
|
355
|
-
|
|
367
|
+
await nemo_backend.transcribe(b"not audio", source_filename="sample.mp3")
|
|
356
368
|
|
|
357
369
|
|
|
358
370
|
def test_build_transcribe_kwargs_omits_language_for_plain_asr_models() -> None:
|