Release 1.0.2a3#50
Open
github-actions[bot] wants to merge 22 commits intomasterfrom
Open
Conversation
Updates the requirements on [ovos-plugin-manager](https://github.com/OpenVoiceOS/OVOS-plugin-manager) to permit the latest version. - [Release notes](https://github.com/OpenVoiceOS/OVOS-plugin-manager/releases) - [Changelog](https://github.com/OpenVoiceOS/ovos-plugin-manager/blob/dev/CHANGELOG.md) - [Commits](OpenVoiceOS/ovos-plugin-manager@0.5.0...1.0.3) --- updated-dependencies: - dependency-name: ovos-plugin-manager dependency-version: 1.0.3 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* fix: move to sequential instead of parallel intent calculation * feat: python gitignore * fix(unit-tests): drop EOL Python, add newer ones * fix: mark cache as dirty on all methods * Update .gitignore Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Updates the requirements on [ovos-plugin-manager](https://github.com/OpenVoiceOS/OVOS-plugin-manager) to permit the latest version. - [Release notes](https://github.com/OpenVoiceOS/OVOS-plugin-manager/releases) - [Changelog](https://github.com/OpenVoiceOS/ovos-plugin-manager/blob/dev/CHANGELOG.md) - [Commits](OpenVoiceOS/ovos-plugin-manager@0.5.0...2.1.0) --- updated-dependencies: - dependency-name: ovos-plugin-manager dependency-version: 2.1.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…ence queries (#44) * feat: normalize whitespace and apostrophes in training data and queries - Add normalize_whitespace() to collapse runs of whitespace to a single space - Add normalize_apostrophes() to map curly/fancy quote variants to ASCII ' - Add normalize_utterance() combining both for use on plain text - Update normalize_example() to apply both normalizations at registration time - Apply normalize_utterance() to every query at the top of calc_intents() - Add 11 new tests: 3 utility unit tests + 8 integration tests covering double whitespace, apostrophe variants, entity interactions, and mixed cases Prevents STT output with curly apostrophes or extra spaces from failing to match intents that were trained with canonical punctuation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: drop apostrophes + modernize CI workflows Normalization: - Switch from apostrophe normalization to dropping — "what's" and "whats" both reduce to "whats", covering all STT apostrophe variants without maintaining an exhaustive list of what to map to - Rename normalize_apostrophes() to drop_apostrophes() accordingly - Applied at both training time (normalize_example) and inference time (normalize_utterance called in calc_intents) - Update tests to reflect drop semantics; use unicode escapes for curly quote test data to keep source files ASCII-safe CI/packaging: - Replace legacy build_tests.yml/install_tests.yml/unit_tests.yml with modern shared workflow callers (build-tests, coverage, lint, opm-check, pip_audit, license_check, release-preview, repo-health) - Remove setup.py and requirements.txt — pyproject.toml covers all deps, entry points, and build config - Modernize release_workflow.yml and publish_stable.yml to thin callers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci: remove unnecessary secrets from non-release workflows PYPI_TOKEN and MATRIX_TOKEN are only needed by release_workflow.yml and publish_stable.yml. All other workflow callers (build-tests, lint, coverage, opm-check, pip_audit, license_check, release-preview, repo-health) had them passed unnecessarily. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: add test extra and opm install_extras to resolve CI failures - Add [test] optional-dependencies group to pyproject.toml with ovos-utils, ovos-bus-client, ovos-plugin-manager so build-tests CI can install the deps required by test/test_pipeline.py - Add install_extras: 'extras' to opm-check.yml so ovos-plugin-manager is present when OPM scans for the entry point Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: replace literal apostrophe glyphs with unicode escapes (RUF001) apostrophe_variants list in drop_apostrophes() used literal non-ASCII characters that trigger ruff RUF001. Replaced each with its \uXXXX escape sequence; runtime behavior is identical. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci: install test extras in coverage workflow test/test_pipeline.py imports ovos_utils which is not a base dependency. Add install_extras: test so the coverage run has the same environment as the build-tests workflow. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: replace apostrophes with space, add entity suffix spacing - drop_apostrophes() now replaces with ' ' instead of '' so "it's" -> "it s" preserving word boundaries rather than merging tokens - _space_entities() inserts spaces around {placeholder} after parenthesis expansion so agglutinative suffixes like {keyword}ren become {keyword} ren and the capture group is not contaminated by the suffix - Add test_entity_suffix_spacing covering Basque-style suffix patterns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: replace hand-rolled tree parser with regex+itertools expansion The old SentenceTreeParser/TreeFragment class hierarchy is replaced with a clean regex-based approach (ported from ovos-utils bracket_expansion): - [optional] expanded via re.sub before alternatives pass - (a|b) alternatives split via re.split and combined with itertools.product - Fixed-point loop handles nested expansions Same public API (expand_parentheses), 190 fewer lines, no external dep. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add TestExpandParentheses suite + fix double-space in optional expansion - Add 24 direct unit tests for expand_parentheses() covering: plain strings, empty input, entity placeholders, two/three alternatives, multiple independent groups, empty-alternative optional form, [optional] syntax, combined alternatives+optional, entity placeholders with alternatives and optional, whitespace handling, and deduplication - Fix double-space bug: when the empty branch of [optional] is taken the join left "word next"; add re.sub(r' +', ' ', ...) inside _fully_expand so internal runs of spaces are collapsed at expansion time Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * ci: fix coverage install_extras format to '.[test]' The coverage workflow does 'pip install <install_extras>' verbatim, so the value must be a valid pip install target. 'test' was interpreted as a package name; '.[test]' correctly installs the local package with the test extra. (build-tests appends extras to the wheel path differently and is unaffected.) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update dependency ovos-bus-client to v1 * Update ovos-bus-client version in pyproject.toml --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: JarbasAI <33701864+JarbasAl@users.noreply.github.com>
* perf: store entity samples as set for O(1) membership lookup
add_entity() stored samples as list; _match() used `str(v) not in list`
which is O(V) per entity check. Changed to set so lookup is O(1).
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: eliminate linear scan in hot matching path
- Impact: entity value validation now O(1) instead of O(V)
- Verified via: python -m pytest test/test_padacioso.py -p no:ovoscope -q
* fix: clamp confidence to [0.0, 1.0] in _match()
Stacked penalties (wildcard + multiple unregistered entities) could push
1 - penalty below zero, producing negative confidence values. Added
max(0.0, ...) at both return sites in the cased and uncased match paths.
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: prevent negative confidence values from reaching callers
- Impact: conf is now guaranteed to be in [0.0, 1.0]
- Verified via: python -m pytest test/test_padacioso.py -p no:ovoscope -q
* perf: proportional wildcard penalty based on open-token ratio
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: Replace flat 0.15 wildcard penalty with a proportional penalty
that scales with the fraction of `*` tokens in the pattern. Range [0.05, 0.25].
Entity placeholders keep their own separate penalty path.
- Impact: Stored in _regex_penalty dict; confidence rounded to 4dp to avoid
float accumulation; test expectations updated to match new formula.
- Verified via: uv run pytest test/test_padacioso.py -q -p no:ovoscope
* fix: word-boundary keyword exclusion in _filter
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: Substring match `s in query` incorrectly excluded intents when a
keyword like "play" appeared inside "display" or "replay". Now uses a word-set
lookup for single-word keywords and regex \b boundary for multi-word phrases.
- Impact: _filter() no longer fires on partial substring hits.
- Verified via: uv run pytest test/test_padacioso.py -q -p no:ovoscope
* fix: non-greedy entity capture for multi-entity patterns
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: simplematch compiles {entity} to (?P<entity>.*) (greedy), causing the
first entity to consume tokens that belong to later ones in patterns with ≥2
placeholders and no literal separator. Patching .* -> .*? via the Matcher.regex
property setter (which triggers recompile) fixes the capture order.
- Impact: _patch_nongreedy() applied at add_intent time and in lazy-init fallbacks.
- Verified via: uv run pytest test/test_padacioso.py -q -p no:ovoscope
* perf: skip cased matcher pass for all-lowercase queries
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: STT output is almost always lowercase; skipping the cased pass for
those queries avoids a redundant regex evaluation per intent. Case-mismatch
penalty (0.05) is only applied when query_has_upper and uncased match fires.
Unregistered-entity penalty stays 0.04 (cased semantics) for pure-lowercase
queries and 0.05 (case-mismatch semantics) when query contains uppercase.
- Impact: _match() checks query_has_upper once per call; cased matchers skipped
for the common lowercase case.
- Verified via: uv run pytest test/test_padacioso.py -q -p no:ovoscope
* perf: early exit at 0.95 confidence + deterministic tie-breaking
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: Stop evaluating intents once a 0.95-confidence match is found (good-
enough threshold). Break ties deterministically by ascending wildcard penalty
(more specific pattern wins), then by intent name as final tiebreaker.
_match() now returns _matched_regex key (stripped from public output) to
support penalty-based tie sorting.
- Impact: calc_intent avoids evaluating all intents for clear-winner queries;
ties are stable across Python dict ordering.
- Verified via: uv run pytest test/test_padacioso.py -q -p no:ovoscope
* perf: increase _calc_padacioso_intent LRU cache to 128
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: maxsize=3 evicts immediately during a burst of ASR hypotheses;
128 keeps recent queries warm across a full recognition cycle.
- Verified via: uv run pytest test/test_padacioso.py -q -p no:ovoscope
* test: add accuracy-improvement regression tests
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: Cover the 9 accuracy/speed improvements with targeted tests:
word-boundary keyword exclusion, confidence clamping, proportional wildcard
penalty values, multi-entity non-greedy split, and deterministic tie-breaking.
- Verified via: uv run pytest test/test_padacioso.py -q -p no:ovoscope
(54 passed)
* Delete status.md
* fix: strip _matched_regex before constructing PadaciosoIntent in opm.py
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: _match() now includes a private _matched_regex key for tie-breaking
in calc_intent(), but opm.py calls calc_intents() directly and passes the
raw dict to PadaciosoIntent(**intent), causing an unexpected keyword argument
error. Strip the key before construction.
- Verified via: uv run pytest test/ -q -p no:ovoscope (56 passed)
* docs: add README with usage guide and speed benchmarks
* docs: add 10k-intent benchmark results to README
* docs: add fuzzy vs non-fuzzy benchmark table to README
* perf: pre-compute fuzz variants + word-length and token-overlap gates
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: Fuzzy matching was re-generating all single-word substitution variants
on every query. Pre-compute them at add_intent() time. Add two O(1) pre-filters
before the expensive simplematch+fuzzy_match pair: skip if word-count distance
is too large, skip if the pattern shares no literal words with the query.
- Impact: entity-match 12x faster, no-match 24x faster, near-miss 2.5x faster.
- Verified via: uv run pytest test/test_padacioso.py -q -p no:ovoscope (54 passed)
* docs: add accuracy benchmark dataset and runner (97.8% on 269 utterances)
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: Provide a reproducible accuracy benchmark. 22 intents, 244 labelled
match utterances, 25 no-match utterances. fuzz=False: 97.8% accuracy, 100%
precision, 0 false positives. fuzz=True: 97.0% accuracy, 4 false positives.
Results summarised in README.
- Verified via: uv run python benchmark/accuracy.py
* fix: literal patterns take priority over entity/wildcard matches
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: A 0.95-confidence entity match (e.g. add_shopping's "i need {item}"
matching "i need help") was triggering early exit before a later intent's
literal exact match (conf=1.0) could be evaluated. Fix on two levels:
1. Sort each intent's regexes literal-first so they short-circuit before
entity patterns within the same intent.
2. Gate the _GOOD_ENOUGH early exit on best_is_literal — an entity match
at 0.96 no longer blocks literal matches in later intents.
3. Tie-breaking prefers literal matches over entity/wildcard matches.
- Impact: "i need help"→help, "play the next song"→next_track now correct.
fuzz=False accuracy 97.8%→98.5%, F1 0.988→0.992.
- Verified via: uv run pytest test/test_padacioso.py -q && python benchmark/accuracy.py
* docs: replace template-fill test utterances with genuine human phrasing
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: The previous benchmark used utterances that were obvious template
fills ("set a timer for five minutes"). Real STT output uses contractions,
idioms, indirect requests, and colloquialisms. The new dataset exposes
padacioso's actual recall on natural speech: ~30% fuzz=False, ~51% fuzz=True.
Precision stays 100%/97% respectively — it never misclassifies, it just
doesn't cover phrasing not in the templates. README updated to present both
datasets and explain the pattern-matcher tradeoff honestly.
* docs: add engine comparison table to README
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: surface comparative benchmark results (padaos/padacioso/padatious/rapidfuzz)
- Impact: README now includes natural-language accuracy and latency comparison table
- Verified via: uv run python benchmark/compare.py
* docs: fix mycroft.conf snippet — JSON not YAML
AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: correct config format
- Impact: README config block now uses valid JSON syntax
* fix: remove f-string with no placeholders (Ruff F541)
Drop the erroneous f prefix from the LOG.debug call in __init__ so
the lint / lint CI step no longer fails.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve Ruff E402 and F841 in benchmark/accuracy.py
Sort stdlib imports alphabetically, add noqa comments for the
intentional sys.path and logging.disable calls that must precede
local imports, and drop the unused true_neg variable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: use lowercased query for multi-token excluded-keyword matching
The multi-token branch in _filter was running re.search against the
raw (mixed-case) query while the keyword was lowercased, so a phrase
like "Stop It now" would not be caught by an excluded keyword "stop it".
Now both branches operate on q_lower for consistent case handling.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: collect all tied candidates before early-exit in calc_intent
The previous early-exit broke immediately on the first literal match at
>= 0.95 confidence, meaning a second intent with the exact same
confidence would never be seen and the _tie_key sort had no effect.
Now the loop continues collecting candidates that share the winning
confidence and only stops when the next candidate arrives with a
strictly lower confidence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: ensure tie-breaker sees all tied candidates at conf=1.0
The premature early-exit in calc_intents (conf == 1.0) would stop
yielding after the first perfect match, preventing tied intents from
reaching calc_intent's _tie_key sort. Remove the early-exit from
calc_intents so that calc_intent's own smarter logic (which continues
collecting ties at the top confidence level) remains in full control.
Also strengthen test_tie_breaking_deterministic to assert that the
alphabetically-first name always wins regardless of registration order,
rather than only checking idempotence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: rename ambiguous loop variable l → line (Ruff E741)
Two for-loops in add_intent and add_entity used the ambiguous
single-letter variable l, which Ruff flags as E741. Rename to line
for clarity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Human review requested!