Skip to content

feat: per-clip WakeWordProbe for benchmark/test harnesses#97

Draft
JarbasAl wants to merge 2 commits into
devfrom
feat/wakeword-probe
Draft

feat: per-clip WakeWordProbe for benchmark/test harnesses#97
JarbasAl wants to merge 2 commits into
devfrom
feat/wakeword-probe

Conversation

@JarbasAl

Copy link
Copy Markdown
Member

What

Adds ovoscope/wakeword_probe.py — a lightweight, self-contained probe that drives a real OVOS HotWordEngine over a single audio clip and returns a per-clip detection decision, latency, and frames-to-detection.

from ovoscope.wakeword_probe import WakeWordProbe

probe = WakeWordProbe.from_plugin("ovos-ww-plugin-openwakeword", key_phrase="hey_mycroft")
result = probe.detect(clip)          # mono float32 @ 16 kHz
assert result.detected

Why

Streaming detectors (openWakeWord, microWakeWord, …) only emit a prediction once their rolling mel/embedding window is full (~2.5 s of frames). A clip fed with too little leading silence never fills that window — the activation is missed (a false reject), and on the shortest clips the half-full buffer raises a shape mismatch that drops the sample entirely. The probe primes every clip with a few seconds of leading silence (PRIME_SECONDS = 3.0) so the window is warm before the keyword arrives, exactly as a live mic keeps the loop warm, then streams the clip frame by frame (1280-sample chunks).

Unlike MiniVoiceLoop (which runs the full DinkumVoiceLoop and needs the [listener] extra), this is bus-free, VAD-free, and needs only the new [bench] extra (numpy) — ideal for plugin test suites and benchmarks that score detection on labelled fixtures.

It also tolerates the HotWordEngine(key_phrase, config, lang) signature change and the vestigial found_wake_word(frame_data) argument some plugins keep.

Notes

  • Engine-agnostic; the engine itself is environmental (not a dependency).
  • import ovoscope stays light — numpy is imported lazily inside the probe.
  • Audio contract: mono float32 in [-1, 1] at the engine rate (16 kHz). Resample upstream.
  • Tests use a fake engine (no heavy deps): priming, frame streaming, reset, both found_wake_word signatures, no-detection path.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cce4fb05-12c7-42b2-b4fe-c8a08c71f0f2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/wakeword-probe

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown

Greetings! The CI pipeline has delivered its findings. 🏗️

I've aggregated the results of the automated checks for this PR below.

🔍 Lint

The results are fresh out of the pipeline. 🏗️

ruff: issues found — see job log

📋 Repo Health

I've checked the repo's memory (aka git history). 🧠

✅ All required files present.

Latest Version: 1.5.0

ovoscope/version.py — Version file
README.md — README
LICENSE — License file
pyproject.toml — pyproject.toml
⚠️ setup.py — setup.py
CHANGELOG.md — Changelog
ovoscope/version.py has valid version block markers

🏷️ Release Preview

Evaluating the overall quality of the next release. ✨

Current: 1.5.0Next: 1.6.0a1

Signal Value
Label feature
PR title feat: per-clip WakeWordProbe for benchmark/test harnesses
Bump minor

✅ PR title follows conventional commit format.


🚀 Release Channel Compatibility

Predicted next version: 1.6.0a1

Channel Status Note Current Constraint
Stable Not in channel -
Testing Too new (must be <1.0.0) ovoscope>=0.7.2,<1.0.0
Alpha Compatible ovoscope>=1.5.0

📊 Coverage

A bird's eye view of your test coverage landscape. 🦅

53.7% total coverage

Files below 80% coverage (18 files)
File Coverage Missing lines
ovoscope/simple_listener.py 0.0% 55
ovoscope/tts_intelligibility.py 0.0% 190
ovoscope/version.py 0.0% 5
ovoscope/intent_cases.py 14.0% 141
ovoscope/classic_listener.py 18.2% 117
ovoscope/pipeline.py 31.0% 78
ovoscope/ocp.py 31.8% 58
ovoscope/cli.py 32.8% 162
ovoscope/e2e.py 34.9% 99
ovoscope/pytest_plugin.py 41.4% 222
ovoscope/listener.py 56.2% 123
ovoscope/media.py 57.2% 101
ovoscope/voice_loop.py 58.3% 115
ovoscope/__init__.py 59.0% 317
ovoscope/coverage.py 63.3% 69
ovoscope/audio.py 64.2% 112
ovoscope/media_provider.py 64.9% 20
ovoscope/wakeword_probe.py 75.0% 20

Full report: download the coverage-report artifact.

🔒 Security (pip-audit)

Ensuring our security headers are properly set. 🎩

✅ No known vulnerabilities found (78 packages scanned).

⚖️ License Check

I've audited the copyright holders list. 👥

✅ No license violations found.

Policy: Apache 2.0 (universal donor). StrongCopyleft / NetworkCopyleft / WeakCopyleft / Other / Error categories fail. MPL allowed.

🔨 Build Tests

Checking if the architectural integrity holds up. 🏛️

✅ All versions pass

Python Build Install Tests
3.10
3.11
3.12
3.13
3.14

Generated with ❤️ by OVOS Automations

@JarbasAl JarbasAl marked this pull request as draft June 27, 2026 15:16
JarbasAl and others added 2 commits July 4, 2026 02:42
Add a self-contained probe that drives a real HotWordEngine over a single clip
the way the listening loop does: a few seconds of leading silence to warm the
engine's rolling feature window (openWakeWord et al. only emit once it is full,
~2.5 s — too little lead silently drops short positives and biases false
rejects), then the clip streamed frame by frame, returning a detection decision
plus latency and frames-to-detection. Unlike MiniVoiceLoop it needs no bus or
[listener] extra — just the [bench] extra (numpy). Tolerates the
HotWordEngine(lang) signature and the vestigial found_wake_word(frame) arg.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JarbasAl JarbasAl force-pushed the feat/wakeword-probe branch from 04fc0a5 to 679b3e4 Compare July 4, 2026 01:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant