From c57dc4e7a857cbffdd612655990e41064d9a99d8 Mon Sep 17 00:00:00 2001 From: chcavignx Date: Wed, 24 Jun 2026 17:43:35 +0200 Subject: [PATCH 01/11] docs: update doc following refactory LiveReview Pre-Commit Check: ran (iter:1, coverage:0%) --- docs/STS_VAD_models.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/STS_VAD_models.md b/docs/STS_VAD_models.md index ba203a4..8cdcb26 100644 --- a/docs/STS_VAD_models.md +++ b/docs/STS_VAD_models.md @@ -171,12 +171,17 @@ The library exposes three main components that map directly to the recommended a ### Wake Word Detection -`WakeWordDetector` uses **openWakeWord**: +`WakeWordDetector` is implemented by directly running pre-trained openWakeWord ONNX models using `onnxruntime`, bypassing the standard `openwakeword` Python package (which has compatibility issues with newer Python versions). -- Runs fully offline -- Captures microphone input with PyAudio -- Expects 16 kHz audio internally, resamples device audio when necessary -- Uses a cooldown to avoid repeated triggers +Key Details: + +- Model designs and weights are derived from [openWakeWord](https://github.com/dscripka/openWakeWord.git). +- Audio feature extraction (log-mel spectrogram and Google speech embeddings) is handled directly via `ONNXAudioFeatures` to reproduce openWakeWord's preprocessing pipeline. +- Performs inference using three chained ONNX sessions: `melspectrogram.onnx`, `embedding_model.onnx`, and the wake word classifier `.onnx` model. +- Runs fully offline. +- Captures microphone input with PyAudio. +- Expects 16 kHz audio internally, resampling device audio when necessary. +- Uses a cooldown to avoid repeated triggers. ### Speech Recognition From 28f4ab2ed4608749132b0f233e77d8f11e6a79ae Mon Sep 17 00:00:00 2001 From: chcavignx Date: Mon, 20 Jul 2026 07:25:07 +0200 Subject: [PATCH 02/11] chore: Directories improvments, Update Build Configuration and Linting Tools LiveReview Pre-Commit Check: ran (iter:2, coverage:0%) --- .pre-commit-config.yaml | 2 +- .python-version | 2 +- examples/{ => audio}/AUDIO_TESTS_README.md | 0 .../{ => audio}/INTEGRATION_TEST_SUMMARY.py | 0 .../{ => audio}/QUICK_START_AUDIO_TESTS.sh | 0 examples/{ => audio}/run_all_audio_tests.py | 0 .../test_asr_recording_validation.py | 0 examples/{ => audio}/test_asr_with_tts.py | 0 .../{ => audio}/test_hardware_detection.py | 0 examples/{ => audio}/test_playback.py | 0 .../{ => audio}/test_recorder_standalone.py | 0 examples/{ => audio}/test_recording.py | 0 .../{ => audio}/test_stream_open_close.py | 0 .../test_tts_lifecycle_and_utils.py | 0 examples/{ => audio}/test_vad_standalone.py | 0 .../{ => audio}/test_wake_word_standalone.py | 0 pyproject.toml | 115 +++++++++++++++++- 17 files changed, 112 insertions(+), 7 deletions(-) rename examples/{ => audio}/AUDIO_TESTS_README.md (100%) rename examples/{ => audio}/INTEGRATION_TEST_SUMMARY.py (100%) rename examples/{ => audio}/QUICK_START_AUDIO_TESTS.sh (100%) rename examples/{ => audio}/run_all_audio_tests.py (100%) rename examples/{ => audio}/test_asr_recording_validation.py (100%) rename examples/{ => audio}/test_asr_with_tts.py (100%) rename examples/{ => audio}/test_hardware_detection.py (100%) rename examples/{ => audio}/test_playback.py (100%) rename examples/{ => audio}/test_recorder_standalone.py (100%) rename examples/{ => audio}/test_recording.py (100%) rename examples/{ => audio}/test_stream_open_close.py (100%) rename examples/{ => audio}/test_tts_lifecycle_and_utils.py (100%) rename examples/{ => audio}/test_vad_standalone.py (100%) rename examples/{ => audio}/test_wake_word_standalone.py (100%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 04901d0..165d55f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.14 + rev: v0.15.22 hooks: - id: ruff name: ruff check --fix (commit) diff --git a/.python-version b/.python-version index 2c07333..24ee5b1 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.11 +3.13 diff --git a/examples/AUDIO_TESTS_README.md b/examples/audio/AUDIO_TESTS_README.md similarity index 100% rename from examples/AUDIO_TESTS_README.md rename to examples/audio/AUDIO_TESTS_README.md diff --git a/examples/INTEGRATION_TEST_SUMMARY.py b/examples/audio/INTEGRATION_TEST_SUMMARY.py similarity index 100% rename from examples/INTEGRATION_TEST_SUMMARY.py rename to examples/audio/INTEGRATION_TEST_SUMMARY.py diff --git a/examples/QUICK_START_AUDIO_TESTS.sh b/examples/audio/QUICK_START_AUDIO_TESTS.sh similarity index 100% rename from examples/QUICK_START_AUDIO_TESTS.sh rename to examples/audio/QUICK_START_AUDIO_TESTS.sh diff --git a/examples/run_all_audio_tests.py b/examples/audio/run_all_audio_tests.py similarity index 100% rename from examples/run_all_audio_tests.py rename to examples/audio/run_all_audio_tests.py diff --git a/examples/test_asr_recording_validation.py b/examples/audio/test_asr_recording_validation.py similarity index 100% rename from examples/test_asr_recording_validation.py rename to examples/audio/test_asr_recording_validation.py diff --git a/examples/test_asr_with_tts.py b/examples/audio/test_asr_with_tts.py similarity index 100% rename from examples/test_asr_with_tts.py rename to examples/audio/test_asr_with_tts.py diff --git a/examples/test_hardware_detection.py b/examples/audio/test_hardware_detection.py similarity index 100% rename from examples/test_hardware_detection.py rename to examples/audio/test_hardware_detection.py diff --git a/examples/test_playback.py b/examples/audio/test_playback.py similarity index 100% rename from examples/test_playback.py rename to examples/audio/test_playback.py diff --git a/examples/test_recorder_standalone.py b/examples/audio/test_recorder_standalone.py similarity index 100% rename from examples/test_recorder_standalone.py rename to examples/audio/test_recorder_standalone.py diff --git a/examples/test_recording.py b/examples/audio/test_recording.py similarity index 100% rename from examples/test_recording.py rename to examples/audio/test_recording.py diff --git a/examples/test_stream_open_close.py b/examples/audio/test_stream_open_close.py similarity index 100% rename from examples/test_stream_open_close.py rename to examples/audio/test_stream_open_close.py diff --git a/examples/test_tts_lifecycle_and_utils.py b/examples/audio/test_tts_lifecycle_and_utils.py similarity index 100% rename from examples/test_tts_lifecycle_and_utils.py rename to examples/audio/test_tts_lifecycle_and_utils.py diff --git a/examples/test_vad_standalone.py b/examples/audio/test_vad_standalone.py similarity index 100% rename from examples/test_vad_standalone.py rename to examples/audio/test_vad_standalone.py diff --git a/examples/test_wake_word_standalone.py b/examples/audio/test_wake_word_standalone.py similarity index 100% rename from examples/test_wake_word_standalone.py rename to examples/audio/test_wake_word_standalone.py diff --git a/pyproject.toml b/pyproject.toml index d02bfb9..687dad4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,6 @@ dependencies = [ "numpy>=1.24.0", "onnxruntime>=1.18.0", "openai-whisper==20250625", - "openwakeword>=0.1.0", "piper-tts>=1.4.1", "psutil>=7.2.2", "pydantic>=2.0.0", @@ -81,6 +80,13 @@ dependencies = [ raspberry-pi = [ "rpi-ws281x>=0.1.0", ] +vision = [ + "insightface>=1.0.1", + "opencv-python>=4.13.0.92", + "picamera2>=0.3.36", + "ultralytics>=8.4.78", +] + [tool.uv.sources] openai-whisper = { git = "https://github.com/openai/whisper.git" } @@ -137,8 +143,8 @@ extend-exclude = [ "scripts/", "src/ai_autonomous_assistant.egg-info/", "src/audio/", - "src/vision/", ".agents/", + ".source/", ] [tool.ruff.lint] @@ -152,7 +158,21 @@ ignore = [ "ISC001", # implicit string concat - conflicts with ruff format "TD003", # missing link in TODO "FIX002", # line contains TODO (I have TODOs and I'm not apologizing) - "D100", "D101", "D102", "D103", "D104", "DOC201" # docstrings - I don't care about docstrings, Ruff. Relax. + "D100", "D101", "D102", "D103", "D104", "DOC201", "DOC402", "DOC501", # docstrings - I don't care about docstrings, Ruff. Relax. + "FBT", # Boolean positional args + "PLW0717", # Try clause contains too many statements + "PLC0415", # Import should be at the top-level + "BLE001", # Do not catch blind exception + "PLR", # Pylint refactor (complexity, magic values, etc.) + "C901", # Too complex + "ANN401", # Dynamically typed expressions (Any) + "S110", # try-except-pass + "S404", "S607", # subprocess insecure/partial path + "TRY401", # Redundant exception object in logger + "RUF069", # Unreliable floating point equality comparison + "E501", # Line too long (let formatting handle it) + "ARG", # Unused args + "RUF001", "RUF002" # Ambiguous characters ] [tool.ruff.lint.per-file-ignores] @@ -187,7 +207,6 @@ python_functions = ["test_*"] addopts = [ "--strict-markers", "--strict-config", - "-W error", # STRICT: Treat warnings as errors (catch deprecations early) "-ra", "--tb=short", "--cov=src", @@ -198,6 +217,10 @@ addopts = [ ] filterwarnings = [ "error", # Treat all warnings as errors by default + "ignore:.*estimate.*:FutureWarning", + "ignore:.*estimate.*:FutureWarning:skimage", + "ignore::FutureWarning", + "ignore::FutureWarning:skimage", # Uncomment to selectively ignore specific third-party warnings if needed: # "ignore::DeprecationWarning:some_noisy_library", # "ignore::UserWarning:another_library", @@ -226,6 +249,7 @@ norecursedirs = [ "logs", "outputs", "*.egg-info", +".source" ] [tool.coverage.run] @@ -278,8 +302,8 @@ exclude = [ "dist", "*.egg-info", "*.egg", - "src/vision/", ".agents/", + ".source", ] # ============================================================ @@ -424,3 +448,84 @@ reportUninitializedInstanceVariable = false reportUntypedFunctionDecorator = false reportUntypedClassDecorator = false reportUntypedBaseClass = false + +[[tool.basedpyright.executionEnvironments]] +root = "src/vision" +reportUnknownParameterType = false +reportMissingParameterType = false +reportUnknownLambdaType = false +reportUnknownArgumentType = false +reportUnknownMemberType = false +reportUnknownVariableType = false +reportPrivateUsage = false +reportPrivateLocalImportUsage = false +reportAny = false +reportExplicitAny = false +reportUnusedParameter = false +reportAttributeAccessIssue = false +reportUnusedCallResult = false +reportImplicitOverride = false +reportUnannotatedClassAttribute = false +reportMissingTypeArgument = false +reportOptionalMemberAccess = false +reportCallIssue = false +reportArgumentType = false +reportMissingSuperCall = false +reportUninitializedInstanceVariable = false +reportUntypedFunctionDecorator = false +reportUntypedClassDecorator = false +reportUntypedBaseClass = false + +[[tool.basedpyright.executionEnvironments]] +root = "src/audio" +reportUnknownParameterType = false +reportMissingParameterType = false +reportUnknownLambdaType = false +reportUnknownArgumentType = false +reportUnknownMemberType = false +reportUnknownVariableType = false +reportPrivateUsage = false +reportPrivateLocalImportUsage = false +reportAny = false +reportExplicitAny = false +reportUnusedParameter = false +reportAttributeAccessIssue = false +reportUnusedCallResult = false +reportImplicitOverride = false +reportUnannotatedClassAttribute = false +reportMissingTypeArgument = false +reportOptionalMemberAccess = false +reportCallIssue = false +reportArgumentType = false +reportMissingSuperCall = false +reportUninitializedInstanceVariable = false +reportUntypedFunctionDecorator = false +reportUntypedClassDecorator = false +reportUntypedBaseClass = false + +[[tool.basedpyright.executionEnvironments]] +root = "src/utils" +reportUnknownParameterType = false +reportMissingParameterType = false +reportUnknownLambdaType = false +reportUnknownArgumentType = false +reportUnknownMemberType = false +reportUnknownVariableType = false +reportPrivateUsage = false +reportPrivateLocalImportUsage = false +reportAny = false +reportExplicitAny = false +reportUnusedParameter = false +reportAttributeAccessIssue = false +reportUnusedCallResult = false +reportImplicitOverride = false +reportUnannotatedClassAttribute = false +reportMissingTypeArgument = false +reportOptionalMemberAccess = false +reportCallIssue = false +reportArgumentType = false +reportMissingSuperCall = false +reportUninitializedInstanceVariable = false +reportUntypedFunctionDecorator = false +reportUntypedClassDecorator = false +reportUntypedBaseClass = false From 50cb70bddc4b0df14d31224262b342b56d1d1402 Mon Sep 17 00:00:00 2001 From: chcavignx Date: Mon, 20 Jul 2026 07:59:51 +0200 Subject: [PATCH 03/11] feat: Implement Multilingual Voice Agent with LLM Integration and Enhanced Audio Testing LiveReview Pre-Commit Check: ran (iter:2, coverage:90%) --- .gitmessage | 11 + data/responses_en.yaml | 33 +++ data/responses_fr.yaml | 33 +++ examples/VAD/voice_agent_offline.py | 230 ++++++++++++--- examples/VAD/voice_agent_offline.yaml | 11 + examples/audio/AUDIO_TESTS_README.md | 86 +++--- examples/audio/INTEGRATION_TEST_SUMMARY.py | 117 ++++---- examples/audio/QUICK_START_AUDIO_TESTS.sh | 40 +-- examples/audio/run_all_audio_tests.py | 24 +- .../audio/test_asr_recording_validation.py | 4 +- examples/audio/test_asr_with_tts.py | 4 +- examples/audio/test_hardware_detection.py | 4 +- examples/audio/test_playback.py | 6 +- examples/audio/test_recorder_standalone.py | 2 +- examples/audio/test_recording.py | 8 +- examples/audio/test_stream_open_close.py | 23 +- .../audio/test_tts_lifecycle_and_utils.py | 2 +- examples/audio/test_vad_standalone.py | 2 +- examples/audio/test_wake_word_standalone.py | 2 +- examples/llm/llm_example.py | 98 +++++++ scripts/install/dependencies.sh | 12 +- scripts/models/audio/wakeword_model.py | 21 +- scripts/test_ollama.sh | 113 ++++++++ src/audio/__init__.py | 4 +- src/audio/asr.py | 2 +- src/audio/audio_utils.py | 34 +-- src/audio/wake_word.py | 24 +- src/llm/__init__.py | 5 + src/llm/llm_client.py | 65 +++++ src/utils/log_filters.py | 2 +- src/utils/sysutils.py | 42 ++- tests/audio/test_audio_engine_units.py | 3 +- tests/audio/test_voice_agent_responses.py | 264 ++++++++++++++++++ tests/conftest.py | 15 +- tests/llm/__init__.py | 0 tests/llm/test_llm_client.py | 99 +++++++ tests/utils/test_sysutils.py | 28 ++ 37 files changed, 1232 insertions(+), 241 deletions(-) create mode 100644 .gitmessage create mode 100644 data/responses_en.yaml create mode 100644 data/responses_fr.yaml create mode 100644 examples/VAD/voice_agent_offline.yaml create mode 100644 examples/llm/llm_example.py create mode 100755 scripts/test_ollama.sh create mode 100644 src/llm/__init__.py create mode 100755 src/llm/llm_client.py create mode 100755 tests/audio/test_voice_agent_responses.py create mode 100644 tests/llm/__init__.py create mode 100644 tests/llm/test_llm_client.py diff --git a/.gitmessage b/.gitmessage new file mode 100644 index 0000000..c6ec716 --- /dev/null +++ b/.gitmessage @@ -0,0 +1,11 @@ +# (): +# +# +# +#