Skip to content

Release 2.10.2a1 - #415

Open
github-actions[bot] wants to merge 61 commits into
masterfrom
release-2.10.2a1
Open

Release 2.10.2a1#415
github-actions[bot] wants to merge 61 commits into
masterfrom
release-2.10.2a1

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Human review requested!

JarbasAl and others added 30 commits January 29, 2026 11:51
* chore: move 3rd party code to own file for proper attribution and to make it easier to keep in sync with upstream

* chore: docstr

* chore: docstr

* Rename AudioData to srAudioData in sr.py
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…355)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* refactor: deprecate solver plugins

remove autotranslation, standardize api contract, simplify implementation, remove redundant methods, improve naming conventions to better convey expectations

* Apply suggestions from code review

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* plugin loaders

* Apply suggestions from code review

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* coderabbit

* coderabbit

* improve deprecation logs

* fix import

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix: explicitly model streaming tokens vs sentences

* Apply suggestion from @coderabbitai[bot]

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…systems (#376)

* fix: docs, tests, typing, fix config

* docs and unittests

* 📝 Add docstrings to `tests_and_docs`

Docstrings generation was requested by @JarbasAl.

The following files were modified:

* `ovos_plugin_manager/installation.py`
* `ovos_plugin_manager/plugin_entry.py`
* `ovos_plugin_manager/stt.py`
* `ovos_plugin_manager/templates/gui.py`
* `ovos_plugin_manager/templates/phal.py`
* `ovos_plugin_manager/templates/stt.py`
* `ovos_plugin_manager/templates/transformers.py`
* `ovos_plugin_manager/templates/tts.py`
* `ovos_plugin_manager/templates/vad.py`
* `ovos_plugin_manager/tts.py`
* `ovos_plugin_manager/utils/config.py`
* `ovos_plugin_manager/wakewords.py`
* `test/unittests/test_installation.py`
* `test/unittests/test_pipeline.py`
* `test/unittests/test_plugin_entry.py`
* `test/unittests/test_solver.py`
* `test/unittests/test_stt.py`
* `test/unittests/test_transformers.py`
* `test/unittests/test_utils.py`
* `test/unittests/test_vad.py`

These files were kept as they were:
* `ovos_plugin_manager/templates/hotwords.py`
* `test/unittests/test_g2p.py`
* `test/unittests/test_language.py`
* `test/unittests/test_phal.py`
* `test/unittests/test_tts.py`
* `test/unittests/test_wakewords.py`

These file types are not supported:
* `docs/api/agents.md`
* `docs/api/language.md`
* `docs/api/microphone.md`
* `docs/api/phal.md`
* `docs/api/pipeline.md`
* `docs/api/stt.md`
* `docs/api/transformers.md`
* `docs/api/tts.md`
* `docs/api/vad.md`
* `docs/api/wake-word.md`
* `docs/configuration.md`
* `docs/index.md`
* `docs/installation.md`
* `docs/plugin-types.md`
* `docs/writing-plugins.md`

* docs and unittests

* ci: modernize GitHub Actions workflows to use shared gh-automations@dev

Replace legacy inline/individual workflow files with standard reusable
callers from OpenVoiceOS/gh-automations@dev:

- Removed: build_tests.yml, pipaudit.yml, license_tests.yml,
  conventional-label.yaml (old naming conventions / inline steps)
- Added: build-tests.yml, pip_audit.yml, license_check.yml,
  conventional-label.yml (reusable callers, standardized naming)
- Added: coverage.yml, lint.yml, release-preview.yml, repo-health.yml
  (previously missing, now added per OVOS workflow standards)

All new workflows pass system_deps: 'swig libssl-dev' where package
installation is needed. build-tests.yml covers Python 3.10–3.14.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* update

* fix: resolve CI failures and address CodeRabbit inline review comments

Test fixes (CI failures):
- test_microphone.py: replace removed assertNotEquals with assertNotEqual (Python 3.12+)
- test_pipeline.py: fix mock patch target to ovos_plugin_manager.pipeline.find_plugins
- test_skills.py: fix mock patch target to ovos_plugin_manager.skills.find_plugins
- test_plugin_entry.py: update assertions to expect list args for pip_install calls
- test_solver.py: add assert_has_calls/call_count to test_auto_translate_decorator

Source fixes (CodeRabbit):
- installation.py: fix recursive budget passing, Union[str,List[str]] signature, guard proc.stdout/stderr
- plugin_entry.py: pass package names and git urls as lists to pip_install
- templates/tts.py: bare except -> except Exception
- templates/vad.py: soften extract_speech docstring (may return None)
- templates/stt.py: fix return type annotation to List[Tuple[Optional[str], float]]
- utils/__init__.py: add GUI_ADAPTER to PluginConfigTypes for symmetry
- utils/config.py: add 'or {}' guard on load_plugin_configs; fix dialect matching logic
- wakewords.py: remove unreachable return after raise; wrap scan_wws in try/except NotImplementedError
- gui.py: add find_gui_adapter_plugins() and load_gui_adapter_plugin() helpers

Docs fixes (CodeRabbit):
- docs/api/pipeline.md: fix example plugins (adapt/padatious, not converse/common-QA)
- docs/api/tts.md: correct OVOSTTSFactory.create() failure modes documentation
- docs/api/vad.md: fix AudioFrame.duration type from int to float
- docs/configuration.md: correct dialect priority semantics
- docs/plugin-types.md: add ovos.plugin.audio2ipa to deprecated entry points table
- docs/writing-plugins.md: fix cls->self in @Property examples for STT and TTS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: use correct constructor param in TestAutoTranslate setUp

AbstractSolver uses `internal_lang` (not `default_lang`) to set the
solver's default language. Passing `default_lang='en'` silently fell
into **kwargs and was ignored, causing the solver to pick up the
system locale (e.g. pt-PT) instead of English. This made
test_auto_translate_decorator fail on any machine whose locale differs
from English.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: correct 8 bugs and typing issues found in full audit

- templates/tts.py: lstrip/rstrip used as substring removers (character-set
  bug) fixed with removeprefix/removesuffix; validate_ssml discarded the
  return value of format_speak_tags making all SSML wrapping a no-op
- templates/vad.py: extract_speech annotated -> bytes but can return None;
  changed to -> Optional[bytes]
- stt.py: bare assert for lang validation replaced with raise ValueError
  (assert is disabled under python -O flag)
- utils/config.py: get_valid_plugin_configs used .append(list) instead of
  .extend(list), corrupting the config list structure; bare except fixed
- tts.py: os.listdir() called without os.path.isdir() guard; crashes on
  first run before voice cache is populated
- wakewords.py: same os.listdir() without isdir guard
- templates/transformers.py: (list, dict) tuple-expression used as return
  annotation; fixed to Tuple[List[str], dict] and dict respectively
- skills.py, ocp.py, installation.py, templates/solvers.py,
  templates/audio.py: bare except: → except Exception:
- agents.py, embeddings.py, intent_transformers.py, triples.py,
  gui_adapter.py: Apache 2.0 headers, module docstrings, type(X) return
  annotations replaced with Optional[Type[X]], bare dict → Dict[str, Type[X]]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: raise coverage from 58% to 86%

Added 24 new test files and expanded 3 existing stubs, covering the
highest-impact uncovered modules:

New files:
- test_embeddings_templates.py: EmbeddingsDB, TextEmbedder, ImageEmbedder,
  VoiceEmbedder, FaceEmbedder base class logic (was 28%)
- test_tts_template_streaming.py: ConcatTTS and StreamingTTS threading paths
- test_phal_template_extended.py: PHAL plugin base class coverage (was 57%)
- test_thirdparty_sr.py: SpeechRecognition wrapper with mocked audio
- test_media_templates.py: MediaBackend abstract base class methods
- test_led_animations.py: all LED animation classes with mocked hardware
- test_embeddings.py, test_embeddings_distance.py: distance metrics, vectors
- test_agents.py, test_gui_module.py, test_gui_plugin_template.py,
  test_intent_transformers_triples_gui_adapter.py: plugin factory functions
- test_audio_backend_template.py, test_g2p_template.py,
  test_coreference_legacy.py, test_tokenization_template.py,
  test_tts_template_extended.py, test_tts_cache.py,
  test_ocp_extended.py, test_sr_audiodata.py,
  test_thirdparty_solvers.py: gap-filling across template modules

Expanded:
- test_installation.py, test_pipeline.py, test_skills.py

All test methods carry -> None annotations and docstrings per standards.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add full audit findings and repo documentation

- AUDIT.md: complete file:line-cited findings — critical bugs (tts.py SSML
  no-op, vad.py None return, config.py list append, assert validation,
  bare excepts), performance issues (entry-point rescans, O(n) sets,
  scipy hot-path import), type annotation gaps, and code smells
- SUGGESTIONS.md: architecture proposals (plugin discovery caching,
  distance() dispatch table, thirdparty/sr.py removal timeline,
  unify find_X/load_X boilerplate via factory)
- FAQ.md: keyword-rich Q&A for common plugin manager questions
- QUICK_FACTS.md: machine-readable package reference
- MAINTENANCE_REPORT.md: AI-transparency log of all changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: skip embeddings tests when numpy is not installed

Use pytest.importorskip("numpy") so the test module is gracefully
skipped in CI environments that do not have numpy, instead of causing
a collection error that blocks all other tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: move GUI plugin changes to feat/gui-plugin branch

Remove AbstractGUIPlugin, gui_adapter.py, GUI_ADAPTER enum and related
tests — these belong in a separate PR (feat/gui-plugin branch).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: remove GUI_ADAPTER reference from test_plugin_types

GUI_ADAPTER was moved to the feat/gui-plugin branch; remove it from
the exclusion list so the test does not fail on AttributeError.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* update

* chore: update documentation and maintenance report

* fix: properly handle print_logs in pip_install

When print_logs=True, output should go directly to console without piping.
When print_logs=False, output should be piped to suppress it silently.

Previously, the code always piped stdout/stderr and manually read and printed
them when print_logs=True, which is inefficient and causes test failures.

This fix:
- Conditionally pipes output based on the print_logs flag
- When print_logs=True, Popen is called without stdout/stderr pipes
- When print_logs=False, Popen is called with PIPE to capture output silently

Fixes: test_print_logs_true_no_pipe failure across all Python versions

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: add tests for uncovered modules and fix SSML dangling tag handling

Add comprehensive unit tests:
- test_intent_transformers.py: 100% coverage for intent_transformers.py
- test_triples.py: 100% coverage for triples.py and templates/triples.py
- Enhanced test_keywords.py: Added factory method tests for OVOSKeywordExtractorFactory
- Enhanced test_tts.py: Added test for dangling closing tag normalization

Bug fix in templates/tts.py:
- Fixed validate_ssml to detect both opening "<speak>" and closing "</speak>" tags
- Previously, dangling closing tags like "hello</speak>" were not normalized
- Now properly calls format_speak_tags for both opening and closing tags

Test results:
- 990 tests passing
- 85% code coverage
- All new tests passing

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: add comprehensive tests for postag, segmentation, tokenization modules

Add unit tests for plugin discovery functions:
- test_postag.py: Complete test coverage for postag.py
- test_segmentation.py: Complete test coverage for segmentation.py
- test_tokenization.py: Complete test coverage for tokenization.py

Tests follow the same pattern as other plugin discovery modules:
- find_plugins tests
- load_plugin tests
- get_configs tests
- get_module_configs tests
- get_lang_configs tests
- get_supported_langs tests

Total tests: 21 new tests covering utility functions and template base classes

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* chore: deprecate dead code modules scheduled for removal in v3.0

Mark the following modules as deprecated with proper warnings and logging:

1. ovos_plugin_manager.installation - deprecated, will be removed in v3.0
   - Added module-level deprecation log
   - Added @deprecated decorator to main functions:
     - search_pip()
     - pip_install()
   - Recommended: use modern package managers instead

2. ovos_plugin_manager.plugin_entry - deprecated, will be removed in v3.0
   - Added module-level deprecation log
   - OpenVoiceOSPlugin class should no longer be used

3. ovos_plugin_manager.hardware - deprecated, will be removed in v3.0
   - Module is being superseded by ovos-hardware-helpers package
   - Added deprecation log at module level
   - Affects:
     - ovos_plugin_manager.hardware.switches
     - ovos_plugin_manager.hardware.fan
     - ovos_plugin_manager.hardware.led

Deprecation warnings include:
- Deprecation version marker (3.0)
- Clear guidance on what to use instead
- Warning messages logged when modules are imported or functions called

All existing tests pass. Deprecation warnings are properly logged via ovos_utils.log.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* refactor: use dynamic version for deprecation notices

Update deprecation messages to dynamically calculate the next major version
instead of hardcoding '3.0'. This ensures deprecation version stays accurate
when the package version is updated.

Changes:
- installation.py: Use VERSION_MAJOR to determine deprecation version (3.0)
- plugin_entry.py: Use VERSION_MAJOR to determine deprecation version (3.0)
- hardware/__init__.py: Use VERSION_MAJOR to determine deprecation version (3.0)

All deprecation notices now display as "removed in v3.0" dynamically calculated
from VERSION_MAJOR (currently 2). When version is bumped to 3.x, deprecation
version will automatically update to 4.0.

Tests: All 990 tests passing

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* refactor: convert hardware modules to backwards-compatibility shims

Convert ovos_plugin_manager.hardware modules to act as backwards-compatibility
shims that re-export classes from ovos-hardware-helpers. This directs users to
the new canonical package while maintaining API compatibility.

Changes:
- fan.py: import AbstractFan from ovos_hardware_helpers.fan with deprecation log
- switches.py: import AbstractSwitches from ovos_hardware_helpers.switches with deprecation log
- led/__init__.py: import AbstractLed from ovos_hardware_helpers.led, maintain Color enum for backwards compatibility
- hardware/__init__.py: add re-exports of all hardware classes for backwards-compatible imports

All modules emit deprecation warnings on import, guiding users to migrate to
ovos_hardware_helpers package. Tests verify backwards-compatibility is maintained.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* refactor: make ovos-hardware-helpers an optional dependency with lazy imports

Use module-level __getattr__ to implement lazy importing of ovos-hardware-helpers
classes. This allows the shim modules to be imported without requiring
ovos-hardware-helpers to be installed, while still providing helpful error
messages if users try to access the hardware classes without the dependency.

Deprecation warnings are still logged on module import to guide users toward
the canonical ovos-hardware-helpers package. Only when a class is actually
used does the import attempt happen.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: add ovos-hardware-helpers as test dependency

Include ovos-hardware-helpers in the test dependencies so that hardware
module tests can properly import the shim classes from ovos_plugin_manager.hardware.

This ensures CI tests pass when running pytest, as the lazy imports need
the dependency available when accessing the hardware classes.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: specify test extras for coverage workflow

Ensure test dependencies (including ovos-hardware-helpers) are installed
when running coverage tests by passing install_extras: '.[test]' to the
gh-automations coverage workflow.

This fixes CI failures where hardware tests couldn't import the required
ovos_hardware_helpers module.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: tool plugins

* Update requirements/requirements.txt

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* discovery

* pydantic validation

* better output validation

* better input validation

* Update ovos_plugin_manager/templates/agent_tools.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix coderabbit mess

* fix: agent_tools bug fixes, Apache header, docs, and tests

- Add Apache 2.0 license header (fixes License CI)
- Fix discover_tools() result not stored in __init__ (tools dict was always empty after init)
- Add LOG.debug on discover failure instead of silent except: pass
- Remove unused `Field` import (pydantic)
- docs: add docs/api/agent-tools.md — ToolBox plugin API reference
- docs: update docs/index.md to link agent-tools.md
- test: add test/unittests/test_agent_tools.py — 21 tests covering init,
  call_tool (dict/model/error paths), lazy refresh, bus handlers, json schema

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: move tool plugins to opm.agents namespace, add AgenticLoopEngine

- PluginTypes: add AGENT_TOOLBOX (opm.agents.toolbox) and AGENT_LOOP (opm.agents.loop)
- PluginTypes: remove PERSONA_TOOL (opm.persona.tool) — wrong namespace for tool plugins
- PluginConfigTypes: add AGENT_TOOLBOX and AGENT_LOOP config entries
- persona.py: find_toolbox_plugins() now uses AGENT_TOOLBOX
- agent_tools.py: update ToolBox docstring to reference correct entry point group
- templates/agents.py: add AgenticLoopEngine(ChatEngine) — opm.agents.loop entry point
  - standard load_toolboxes(toolboxes) interface for persona-injected ToolBoxes
  - presents as ChatEngine to all callers; loop internals are implementation details
- docs/api/agents.md: document AgenticLoopEngine and persona config toolboxes pattern
- docs/api/agent-tools.md: fix entry point to opm.agents.toolbox

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: remove AgenticLoopEngine and AGENT_LOOP from OPM

AgenticLoopEngine is now implemented in the standalone ovos-agentic-loop
repo (Agent Plugins/ovos-agentic-loop) and registers under the existing
opm.agents.chat entry-point group — no new OPM type is needed.

Removed:
- PluginTypes.AGENT_LOOP ("opm.agents.loop")
- PluginConfigTypes.AGENT_LOOP ("opm.agents.loop.config")
- AgenticLoopEngine class from templates/agents.py
- AgenticLoopEngine section from docs/api/agents.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address PR #340 CodeRabbit review comments

Addressed 6 critical issues from CodeRabbit review on tool plugins PR:

**pyproject.toml:**
- Added pydantic~=2.0 to test optional-dependencies
  Reason: GitHub Actions installs with 'test' extras from pyproject.toml;
  pytest was failing with ModuleNotFoundError for pydantic

**test/unittests/test_agent_tools.py:**
- Removed dead placeholder definition (line 48-49)
  Was causing Ruff F821 (Undefined name) warning

**docs/index.md:**
- Fixed entry-point name from opm.persona.tool to opm.agents.toolbox
  Matches actual implementation in ToolBox class definition

**ovos_plugin_manager/templates/agent_tools.py:**
1. handle_discover() (line 123): Call refresh_tools() before broadcasting
   - Prevents stale cache if initial discover_tools() fails
   - Ensures dynamic tool discovery works for bus-only clients

2. handle_call() (line 144): Use model_dump(mode='json') for consistency
   - Matches model_json_schema() advertised in tool_json_list
   - Prevents divergence for datetime, UUID, enum, or aliased fields

3. validate_input() (line 170): Remove raw tool_kwargs from error message
   - Security fix: prevents echoing secrets/user content on bus

4. validate_output() (line 193): Remove raw result from error message
   - Security fix: prevents echoing secrets/user content on bus

**Testing:**
- All 1011 unit tests pass
- All 21 agent_tools tests pass
- Python syntax verified

AI-Generated Change:
- Model: Claude Haiku 4.5
- Intent: Address CodeRabbit PR review comments for tool plugins feature
- Impact: 6 critical fixes for cache staleness, JSON serialization, security, and dependencies
- Verified via: uv run pytest test/unittests/ -v (1011 passed)

* docs: update FAQ and MAINTENANCE_REPORT for PR #340 fixes

- Added 3 new FAQ entries: Agent Tools API, dynamic discovery, JSON serialization
- Added comprehensive MAINTENANCE_REPORT entry documenting all 6 fixes:
  * pyproject.toml dependency fix
  * Test placeholder removal
  * Documentation accuracy fix
  * Cache staleness prevention
  * JSON serialization consistency
  * Security fixes for error messages
- Included AI transparency (model, actions, oversight notes)
- Linked to source file locations for future reference

AI-Generated Change:
- Model: Claude Haiku 4.5
- Intent: Document PR #340 fixes and new agent tools features for future maintainers
- Impact: Improved discoverability and traceability of changes
- Verified via: Manual verification of FAQ clarity and MAINTENANCE_REPORT completeness

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add OptionMatcherEngine plugin type for ask_selection slot resolution

Introduces a dedicated engine class for matching free-form user utterances
to predefined option slots — semantically distinct from ReRankerEngine
(which ranks answers against a semantic query) and modelled after the
existing YesNoEngine pattern.

Changes:
- templates/agents.py: add OptionMatcherEngine with abstract match_option()
- utils/__init__.py: add AGENT_OPTION_MATCHER = "opm.agents.option_matcher"
  to both PluginTypes and PluginConfigTypes
- agents.py: add find_option_matcher_plugins() and load_option_matcher_plugin()
- test_agents.py: 4 new tests covering find, load, abstract enforcement,
  and concrete subclass instantiation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* 📝 Add docstrings to `feat/opt_matcher`

Docstrings generation was requested by @JarbasAl.

The following files were modified:

* `ovos_plugin_manager/agents.py`
* `ovos_plugin_manager/templates/agents.py`
* `test/unittests/test_agents.py`

* Update agents.py

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* refactor: migrate to ovos-spec-tools for language matching

Replace langcodes.tag_distance with ovos_spec_tools.lang_distance and
ovos_utils.lang.standardize_lang_tag with ovos_spec_tools.standardize_lang,
the conformant reference implementation of the OVOS language specs.

- swap langcodes dependency for ovos-spec-tools>=0.0.1a2
- utils/config.py: tag_distance -> lang_distance (same <10 threshold)
- migrate all standardize_lang_tag callers to standardize_lang; the
  macro=True argument is dropped as standardize_lang already normalizes
  to the comparable base/macro form
- ovos_utils.lang.phonemes / ovos_utils.lang.visimes kept (not part of
  ovos-spec-tools)

Wave 2 of the OVOS migration. Refs OpenVoiceOS/architecture#7

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: depend on ovos-spec-tools[langcodes]

`langcodes` is optional for ovos-spec-tools itself, but the OVOS
ecosystem silently degrades without it (region-stripping in
standardize_lang_tag). Dropping the direct langcodes dependency left it
absent in CI — language tags came back bare (`en` instead of `en-US`).
Pull it back in via the ovos-spec-tools langcodes extra.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2.x only removed the bundled hivemind agent protocol + messagebus solver
(ovos-bus-client#207); no API break (#215). Widening keeps 1.x as default
while permitting 2.x, unblocking the HiveMind stack's 2.x adoption.
JarbasAl and others added 30 commits June 6, 2026 19:45
#395)

- Add TestWakeWordVerifier with 5 tests covering find_wake_word_verifier_plugins,
  load_wake_word_verifier_plugin, and HotWordVerifier base class behaviour
- Add AGENT_TOOLBOX / opm.agents.toolbox / ToolBox row to docs/plugin-types.md
* feat: add voice-clone plugin family (opm.vc)

Introduces VoiceClonePlugin — an audio-to-audio ABC for converting source
speech to a reference voice. Text input is explicitly out of scope for
this family; zero-shot TTS-with-speaker-ref belongs in TTS plugins.

- ovos_plugin_manager/templates/vc.py: VoiceClonePlugin ABC with a single
  abstract method clone_voice(audio, reference_voice, out_path) → str
- ovos_plugin_manager/vc.py: find_voice_clone_plugins, load_voice_clone_plugin,
  get_voice_clone_config, OVOSVoiceClonerFactory
- PluginTypes.VOICE_CLONE = "opm.vc", PluginConfigTypes.VOICE_CLONE = "opm.vc.config"
- 21 unit tests covering template, enum, discovery, factory, and ABC enforcement
- docs/plugin-types.md: add VOICE_CLONE row
- docs/voice-clone.md: scope note, contract table, registration guide

First implementation: vconnx (forward-looking mention).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: keep voice-clone docs implementation-agnostic

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ools, was test-only) (#399)

* fix: declare pydantic as a core dependency (runtime import in agent_tools, was test-only)

* chore: drop stale requirements/ (pyproject is the sole dependency source)
DEPRECATED_ENTRYPOINTS mapped the deprecated VAD group to 'opm.vad', but
PluginTypes.VAD (and find_vad_plugins) use 'opm.VAD'. The case mismatch meant a
VAD plugin still declaring 'ovos.plugin.VAD' aliased to a group the finder never
queries, so it was never discovered.
* feat: first-class tool calls in the agent message/chat contract

Make tool turns representable in a conversation so a tool loop can be threaded
through history, persisted by memory plugins, and use providers' native
function-calling:

- MessageRole.TOOL and a ToolCall dataclass (id, name, arguments)
- AgentMessage gains optional tool_calls / tool_call_id / name (trailing,
  default None — backward compatible; MultimodalAgentMessage inherits them)
- ChatEngine.supports_tools class flag (default False) and an optional trailing
  tools= kwarg on continue_chat (existing 4-arg overrides keep satisfying the ABC)
- ToolBox.tools_to_openai_spec staticmethod + openai_tools property: convert
  tool_json_list to the neutral OpenAI tools/function spec

Additive and non-breaking. The orchestration loop lives in ovos-agentic-loop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat: accept ToolBox objects directly in the tools argument

Add ToolBox.normalize_tools (staticmethod) coercing a ToolBox, an OpenAI tool
dict, or a list mixing either (or None) into the OpenAI spec list, and broaden
ChatEngine.continue_chat's tools type so callers can pass toolbox objects
directly rather than pre-serialized dicts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat: add MediaProvider plugin type (opm.media.provider)

Introduce a media catalog/search plugin type to replace OCP search skills.
MediaProvider plugins are loaded and queried in-process by the OCP pipeline
instead of broadcasting ovos.common_play.query over the bus.

- PluginTypes.MEDIA_PROVIDER / PluginConfigTypes.MEDIA_PROVIDER
  ("opm.media.provider" / ".config")
- templates/media_provider.py: MediaProvider base, three-axis routing
  (media/modality/genre_filter) mirroring mediavocab MetadataProvider;
  search() returns list[mediavocab.Release]; search_safe() for pool dispatch
- media_provider.py: find/load discovery + load_media_providers() with
  is_available() + config-disabled filtering
- depend on mediavocab>=0.1.1 (Release is the canonical catalog entry)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: align MediaProvider routing with mediavocab four-axis gate

mediavocab dev replaced PlaybackModality (3-axis) with PlaybackType +
ContentForm (4-axis: media/playback_type/content_form/genre_filter).
Update the MediaProvider base class attrs and tests to match the canonical
provider_matches() contract. Note mediavocab.PlaybackType (audio/video/
paged/interactive) is distinct from ovos_utils.ocp.PlaybackType (the backend
selector) — bridged downstream, not in the template.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: align MediaProvider with mediavocab 1.0 three-axis gate

mediavocab 1.0 dropped content_form from the MetadataProvider routing gate
("no real provider filters on ContentForm"). Match the canonical contract:
MediaProvider routes on (media, playback_type, genre_filter). Require
mediavocab>=1.0.0 (the published 1.0 line; the earlier <1.0.0 cap predated it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs: document MediaProvider plugin type (OVOS-OCP-1)

* fix: make mediavocab an optional dependency

opm only references mediavocab for type hints in the MediaProvider template and
one routing-gate call; the Release/Signals types and routing primitives belong to
the downstream provider plugins. Move the import under TYPE_CHECKING, lazy-import
provider_matches inside matches(), and move mediavocab from hard deps to a [media]
extra (also in [test]). opm now imports the media_provider template without
mediavocab installed; providers that use it declare their own mediavocab dep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* build: require Python >=3.10 (mediavocab -> ovos-spec-tools needs >=3.10)

The new MediaProvider plugin type depends on mediavocab, which requires
ovos-spec-tools>=0.6.0a1; ovos-spec-tools is published only for Python >=3.10,
so the py3.9 build could not resolve dependencies. Python 3.9 is EOL and
ovos-core already targets >=3.10.

* ci: drop Python 3.9 from the build matrix (requires-python is now >=3.10)

* feat: context-aware MediaProvider — QueryContext + serves() gate

Providers gain a QueryContext (device-supported playback types + content policy +
locale/session) so the pipeline can gate them on what the device/user can actually
use — a video provider is skipped on an audio-only device, an adult provider is
skipped when the content filter blocks it — before any search runs. Additive:
matches() (3-axis) unchanged; serves()/search_context() default to permissive so
existing providers keep working.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor!: simplify MediaProvider to a single search(signals, context) method

Collapse the base class to plain boilerplate: drop is_available / matches /
serves / search_context / search_safe / featured_media and the three routing
ClassVars (media/playback_type/genre_filter). A provider now implements ONE
abstract method — search(signals, context) -> list[Release] — and returns [] when
it can't serve the query/context (availability/routing is the provider's own
concern, gated via the QueryContext it receives). QueryContext + its
allows_playback/allows_genres helpers are kept; the loader no longer filters on
is_available.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: drop QueryContext.extras bag

* refactor!: replace QueryContext with plain **context kwargs on search()

search(signals, lang='en-us', **context) is now the entire MediaProvider
contract — no QueryContext dataclass to import/maintain. The pipeline passes
supported_playback_types/blocked_genres/region/session_id as kwargs; providers
read what they need and ignore the rest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: define explicit search() context kwargs (no **kwargs)

search(signals, lang, *, supported_playback_types, blocked_genres, region,
session_id) — the valid context arguments are named in the signature, not a
catch-all **context.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A pipeline whose match terminates or continues an already-active skill's
participation (e.g. a stop) sets suppress_activation=True so the orchestrator
dispatches the match without activating skill_id. Defaults False, so normal
matches are unaffected.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: canonical transformer pipeline runner services

Add ovos_plugin_manager.transformer_services with the six shared
*TransformersService runners (utterance, metadata, intent, dialog,
tts, audio) so downstream projects consume one implementation instead
of maintaining per-repo copies.

- config-gated opt-in loading with explicit config= injection
- accepts full core config or the section mapping itself
- OVOS-TRANSFORM-1 §4 ascending priority by default, legacy
  descending order available via sort_ascending=False
- audio service supports top-level audio_transformers and the
  legacy listener.audio_transformers section
- move TTS transformer helpers to ovos_plugin_manager.tts_transformers
  with backwards-compatible re-exports in dialog_transformers
- fix TTSTransformer reading the dialog_transformers config section

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: patch module-local find_plugins reference

The dialog transformer discovery test patched
ovos_plugin_manager.utils.find_plugins, which only intercepts the call
when the factory module has not been imported yet in the test session.
Patch the module-local reference so the test is import-order independent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: cache sorted plugin order in transformer services

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: OVOS-TRANSFORM §4 explicit order and §8.1 cancellation in transformer services

- explicit deployer order: an 'order' list in the config section wins
  over priorities; loaded plugins absent from the list are not run;
  plugins named only in the list are enabled without a config entry
- cancellation: a valid canceled/cancel_reason pair stops the chain and
  the runner stamps cancel_by from the emitting plugin; an incomplete
  pair is logged, stripped and the chain proceeds
- reserved config keys (order, blacklisted_skills) are never treated as
  plugin names

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: harden transformer services per review

- plugin construction uses signature inspection instead of a TypeError
  retry, so a TypeError raised inside a config-accepting constructor
  propagates instead of triggering a silent no-config fallback
- a plugin-supplied cancel_by outside a valid cancellation signal is
  stripped before merging into context (orchestrator-stamped only)
- shutdown() iterates all loaded plugins, not just the execution chain,
  so plugins excluded by an explicit 'order' list are still shut down
- feed_audio/feed_hotword/feed_speech isolate per-plugin failures so a
  broken feeder does not starve subsequent plugins
- AudioTransformersService.transform overlays caller context onto the
  default context instead of discarding the defaults
- DialogTransformersService.transform skips dialog from blacklisted
  skills (context skill_id checked against blacklisted_skills)
- explicit re-export aliases for the tts transformer helpers kept in
  dialog_transformers for backwards compatibility

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: transformer pipelines guide

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: honor legacy cancellation signals without cancel_reason

ovos-utterance-plugin-cancel (and other pre-OVOS-TRANSFORM plugins)
signal cancellation with 'canceled' alone. The strict §8.1 pair
validation stripped those signals, breaking mid-sentence cancellation
in ovos-core (caught by its ovoscope e2e). Honor 'canceled: true'
without a reason, defaulting cancel_reason to the spec's universal
fallback 'other' and logging a warning nudging plugins to add it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Importing ovos_plugin_manager pulled in plugin_entry and installation
during package assembly, so every process logged deprecation notices
whose caller was the library itself. A module-level deprecation notice
addresses external callers; internal wiring must stay silent.

OpenVoiceOSPlugin is now re-exported lazily from the package, and each
deprecated module skips its notice when the import originates from within
ovos_plugin_manager. Direct imports of a deprecated module by an external
caller still warn exactly once. The solvers loader module no longer
carries a blanket notice of its own; the notice lives on the deprecated
templates.solvers it pulls the classes from.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant