From 8257259a59ce63e47cfefdb892328690d24836ac Mon Sep 17 00:00:00 2001 From: JarbasAl <33701864+JarbasAl@users.noreply.github.com> Date: Wed, 12 Aug 2026 14:32:10 +0000 Subject: [PATCH 1/7] Increment Version to 0.10.1 --- nebulento/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nebulento/version.py b/nebulento/version.py index 33aa69a..18c3c24 100644 --- a/nebulento/version.py +++ b/nebulento/version.py @@ -2,7 +2,7 @@ VERSION_MAJOR = 0 VERSION_MINOR = 10 VERSION_BUILD = 1 -VERSION_ALPHA = 1 +VERSION_ALPHA = 0 # END_VERSION_BLOCK __version__ = f"{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_BUILD}" + (f"a{VERSION_ALPHA}" if VERSION_ALPHA else "") From c60edfdff2509df09029961423fddc8c4152bbb0 Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+JarbasAl@users.noreply.github.com> Date: Wed, 12 Aug 2026 15:49:43 +0100 Subject: [PATCH 2/7] fix: read package version dynamically from version.py instead of a hardcoded pyproject value (#46) pyproject.toml pinned `version = "0.1.1a1"` under the hatchling backend, so every build shipped that exact string no matter how far version.py had been bumped by the release automation. Every dev-merge alpha publish since April has failed on PyPI with 400 File already exists because the wheel/sdist always claimed 0.1.1a1. Switch to the setuptools backend with dynamic version sourced from nebulento.version.__version__, matching the working palavreado/jurebes/linha-fina setup, so alpha publishes track the real bumped version again. --- pyproject.toml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ae99644..1512d01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" [project] name = "nebulento" -version = "0.1.1a1" +dynamic = ["version"] description = "dead simple fuzzy matching intent parser" readme = "README.md" license = { text = "apache-2.0" } @@ -56,5 +56,8 @@ Homepage = "https://github.com/OpenJarbas/nebulento" "ovos-nebulento-pipeline-plugin" = "nebulento.opm:NebulentoPipeline" "ovos-nebulento-hierarchical-pipeline-plugin" = "nebulento.opm:HierarchicalNebulentoPipeline" -[tool.hatch.build.targets.wheel] -packages = ["nebulento"] +[tool.setuptools.dynamic] +version = {attr = "nebulento.version.__version__"} + +[tool.setuptools.packages.find] +where = ["."] From 6d039a7b99a0ede09f87be6316034d71cd59fb6f Mon Sep 17 00:00:00 2001 From: JarbasAl <33701864+JarbasAl@users.noreply.github.com> Date: Wed, 12 Aug 2026 14:50:08 +0000 Subject: [PATCH 3/7] Increment Version to 0.10.2a1 --- nebulento/version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nebulento/version.py b/nebulento/version.py index 18c3c24..2718479 100644 --- a/nebulento/version.py +++ b/nebulento/version.py @@ -1,8 +1,8 @@ # START_VERSION_BLOCK VERSION_MAJOR = 0 VERSION_MINOR = 10 -VERSION_BUILD = 1 -VERSION_ALPHA = 0 +VERSION_BUILD = 2 +VERSION_ALPHA = 1 # END_VERSION_BLOCK __version__ = f"{VERSION_MAJOR}.{VERSION_MINOR}.{VERSION_BUILD}" + (f"a{VERSION_ALPHA}" if VERSION_ALPHA else "") From 2ac637598482debed518d1e8b02080014a16f2cf Mon Sep 17 00:00:00 2001 From: JarbasAl <33701864+JarbasAl@users.noreply.github.com> Date: Wed, 12 Aug 2026 14:50:38 +0000 Subject: [PATCH 4/7] Update Changelog --- CHANGELOG.md | 124 ++------------------------------------------------- 1 file changed, 3 insertions(+), 121 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 812cd2e..ea82c8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,130 +1,12 @@ # Changelog -## [0.10.1a1](https://github.com/OpenVoiceOS/nebulento/tree/0.10.1a1) (2026-08-11) +## [0.10.2a1](https://github.com/OpenVoiceOS/nebulento/tree/0.10.2a1) (2026-08-12) -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.10.0a3...0.10.1a1) +[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.10.1...0.10.2a1) **Merged pull requests:** -- fix: tolerate malformed templates during intent registration [\#43](https://github.com/OpenVoiceOS/nebulento/pull/43) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.10.0a3](https://github.com/OpenVoiceOS/nebulento/tree/0.10.0a3) (2026-08-11) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.10.0a2...0.10.0a3) - -**Merged pull requests:** - -- docs: clarify that the OVOS pipeline plugin needs the \[ovos\] extra [\#41](https://github.com/OpenVoiceOS/nebulento/pull/41) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.10.0a2](https://github.com/OpenVoiceOS/nebulento/tree/0.10.0a2) (2026-08-11) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.10.0a1...0.10.0a2) - -**Merged pull requests:** - -- docs: rewrite README in Simplified Technical English [\#40](https://github.com/OpenVoiceOS/nebulento/pull/40) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.10.0a1](https://github.com/OpenVoiceOS/nebulento/tree/0.10.0a1) (2026-07-03) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.9.0a1...0.10.0a1) - -**Merged pull requests:** - -- feat: OVOS-CONTEXT-1 ยง7 context slot fill + OVOS-INTENT-2 ยง4.3 blacklist [\#38](https://github.com/OpenVoiceOS/nebulento/pull/38) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.9.0a1](https://github.com/OpenVoiceOS/nebulento/tree/0.9.0a1) (2026-07-02) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.8.0a1...0.9.0a1) - -**Merged pull requests:** - -- feat: enforce OVOS-CONTEXT-1 requires/excludes\_context gating [\#36](https://github.com/OpenVoiceOS/nebulento/pull/36) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.8.0a1](https://github.com/OpenVoiceOS/nebulento/tree/0.8.0a1) (2026-06-27) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.7.0a1...0.8.0a1) - -**Merged pull requests:** - -- feat: consume OVOS-INTENT-4 template registration \(alongside legacy\) [\#33](https://github.com/OpenVoiceOS/nebulento/pull/33) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.7.0a1](https://github.com/OpenVoiceOS/nebulento/tree/0.7.0a1) (2026-06-25) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.6.0a2...0.7.0a1) - -**Merged pull requests:** - -- feat: massive-templates benchmark, ovos-spec-tools migration, benchmark cleanup [\#28](https://github.com/OpenVoiceOS/nebulento/pull/28) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.6.0a2](https://github.com/OpenVoiceOS/nebulento/tree/0.6.0a2) (2026-06-13) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.6.0a1...0.6.0a2) - -**Merged pull requests:** - -- docs: standardize NGI0 Commons Fund attribution [\#30](https://github.com/OpenVoiceOS/nebulento/pull/30) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.6.0a1](https://github.com/OpenVoiceOS/nebulento/tree/0.6.0a1) (2026-05-23) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.5.0a1...0.6.0a1) - -**Merged pull requests:** - -- feat: migrate to ovos-spec-tools [\#27](https://github.com/OpenVoiceOS/nebulento/pull/27) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.5.0a1](https://github.com/OpenVoiceOS/nebulento/tree/0.5.0a1) (2026-05-22) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.4.0a1...0.5.0a1) - -**Merged pull requests:** - -- feat: add HierarchicalNebulentoPipeline for two-stage intent matching [\#25](https://github.com/OpenVoiceOS/nebulento/pull/25) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.4.0a1](https://github.com/OpenVoiceOS/nebulento/tree/0.4.0a1) (2026-05-14) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.3.0a4...0.4.0a1) - -**Merged pull requests:** - -- feat: ovoscope end-to-end tests for NebulentoPipeline [\#23](https://github.com/OpenVoiceOS/nebulento/pull/23) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.3.0a4](https://github.com/OpenVoiceOS/nebulento/tree/0.3.0a4) (2026-05-13) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.3.0a3...0.3.0a4) - -**Merged pull requests:** - -- Feat/ovoscope e2e tests [\#21](https://github.com/OpenVoiceOS/nebulento/pull/21) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.3.0a3](https://github.com/OpenVoiceOS/nebulento/tree/0.3.0a3) (2026-05-13) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.3.0a2...0.3.0a3) - -## [0.3.0a2](https://github.com/OpenVoiceOS/nebulento/tree/0.3.0a2) (2026-05-13) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.3.0a1...0.3.0a2) - -**Merged pull requests:** - -- Update marocchino/sticky-pull-request-comment action to v3 [\#17](https://github.com/OpenVoiceOS/nebulento/pull/17) ([renovate[bot]](https://github.com/apps/renovate)) -- Update actions/setup-python action to v6 [\#16](https://github.com/OpenVoiceOS/nebulento/pull/16) ([renovate[bot]](https://github.com/apps/renovate)) -- Update actions/checkout action to v6 [\#15](https://github.com/OpenVoiceOS/nebulento/pull/15) ([renovate[bot]](https://github.com/apps/renovate)) - -## [0.3.0a1](https://github.com/OpenVoiceOS/nebulento/tree/0.3.0a1) (2026-05-12) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.2.1a1...0.3.0a1) - -**Merged pull requests:** - -- feat: ovoscope end-to-end tests for NebulentoPipeline [\#14](https://github.com/OpenVoiceOS/nebulento/pull/14) ([JarbasAl](https://github.com/JarbasAl)) - -## [0.2.1a1](https://github.com/OpenVoiceOS/nebulento/tree/0.2.1a1) (2026-05-12) - -[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.2.0...0.2.1a1) - -**Merged pull requests:** - -- docs: NLNet credit and pipeline plugin tests [\#10](https://github.com/OpenVoiceOS/nebulento/pull/10) ([JarbasAl](https://github.com/JarbasAl)) +- fix: dynamic pyproject version so alpha publishes actually track version.py [\#46](https://github.com/OpenVoiceOS/nebulento/pull/46) ([JarbasAl](https://github.com/JarbasAl)) From 8456ae677487e8778c6600c7c32748650f0af7ad Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+JarbasAl@users.noreply.github.com> Date: Thu, 13 Aug 2026 19:48:35 +0100 Subject: [PATCH 5/7] fix: dealias munged workshop entity names before container registration (#48) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ovos-workshop's register_entity_file() emits entity registration names as :_. Nebulento's own slot-tagging contract (IntentContainer.match_fuzzy) requires the registered entity name to be the bare token that appears literally as {token} in the skill's own intent templates, so a skill_id-prefixed and/or hash-suffixed name can never satisfy that check - the entity value is found in the sentence but never tagged into match_data, and detach_entity/deregister can never find the stored key to remove it either. Same wildcard/orphaned-slot bug class as ovos-padatious-pipeline-plugin#95, ported and adapted to nebulento's own bare-token lookup contract (which also strips the skill_id prefix, not just the hash, since templates never carry it). > ๐Ÿค– Auto-generated by Claude Fable 5 (claude-fable-5) via Claude Code โ€” NOT human-reviewed. Verify before acting. --- nebulento/opm.py | 42 ++++++++++++++-- test/test_entity_dealias_regression.py | 70 ++++++++++++++++++++++++++ test/test_opm.py | 5 +- 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 test/test_entity_dealias_regression.py diff --git a/nebulento/opm.py b/nebulento/opm.py index 2dcc53f..8cd8aa2 100644 --- a/nebulento/opm.py +++ b/nebulento/opm.py @@ -18,6 +18,38 @@ from nebulento import HierarchicalIntentContainer, IntentContainer, MatchStrategy +# ovos-workshop's ``register_entity_file`` builds the entity name as +# ``:_``. Nebulento's own slot-tagging +# contract (see IntentContainer.add_entity / match_fuzzy) requires the +# registered entity name to be the *bare* token that appears literally as +# ``{token}`` inside the skill's own intent templates โ€” it never carries a +# skill_id prefix, since templates are written as e.g. ``"drive to {place}"``, +# not ``"drive to {skill_id:place}"``. A munged (skill_id-prefixed and/or +# hash-suffixed) registration name can therefore never satisfy the +# ``"{" + ent + "}" in sample`` check in ``match_fuzzy``, so the entity is +# found in the sentence but never tagged into the returned match_data โ€” +# the same wildcard/orphaned-slot bug class fixed for padatious in +# ovos-padatious-pipeline-plugin#95, just with an additional (pre-existing) +# skill_id-prefix mismatch on top of the hash suffix. +_ENTITY_HASH_SUFFIX = re.compile(r"_[0-9a-f]{32}$") + + +def _dealias_entity_name(name: Optional[str]) -> Optional[str]: + """Fold a (possibly skill_id-prefixed, hash-suffixed) entity + registration name onto the bare token nebulento's own container expects. + + Collapsing here โ€” at the registration/removal boundary this plugin owns โ€” + repairs every emitter vintage, including deployed ovos-workshop releases + that will keep emitting the munged name. + """ + if not name: + return name + if ":" in name: + name = name.rsplit(":", 1)[1] + if name.endswith(".entity"): + name = name[:-len(".entity")] + return _ENTITY_HASH_SUFFIX.sub("", name) + class NebulentoIntent: """ @@ -254,7 +286,7 @@ def _add_intent(self, container: IntentContainer, name: str, def _add_entity(self, container: IntentContainer, name: str, samples: List[str]) -> None: """Register entity *name* with *samples* on *container*.""" - container.add_entity(name, samples) + container.add_entity(_dealias_entity_name(name), samples) def _remove_intent(self, container: IntentContainer, name: str) -> None: """Remove intent *name* from *container*.""" @@ -262,7 +294,7 @@ def _remove_intent(self, container: IntentContainer, name: str) -> None: def _remove_entity(self, container: IntentContainer, name: str) -> None: """Remove entity *name* from *container*.""" - container.remove_entity(name) + container.remove_entity(_dealias_entity_name(name)) def _remove_skill(self, container: IntentContainer, skill_id: str) -> None: """Remove anything left for *skill_id* after per-intent detach. @@ -600,7 +632,8 @@ def _add_intent(self, container: HierarchicalIntentContainer, name: str, def _add_entity(self, container: HierarchicalIntentContainer, name: str, samples: List[str]) -> None: - container.register_domain_entity(self._domain_of(name), name, samples) + container.register_domain_entity(self._domain_of(name), + _dealias_entity_name(name), samples) def _remove_intent(self, container: HierarchicalIntentContainer, name: str) -> None: @@ -608,7 +641,8 @@ def _remove_intent(self, container: HierarchicalIntentContainer, def _remove_entity(self, container: HierarchicalIntentContainer, name: str) -> None: - container.remove_domain_entity(self._domain_of(name), name) + container.remove_domain_entity(self._domain_of(name), + _dealias_entity_name(name)) def _remove_skill(self, container: HierarchicalIntentContainer, skill_id: str) -> None: diff --git a/test/test_entity_dealias_regression.py b/test/test_entity_dealias_regression.py new file mode 100644 index 0000000..cabf802 --- /dev/null +++ b/test/test_entity_dealias_regression.py @@ -0,0 +1,70 @@ +"""Regression test for the entity-name munging bug (mirrors +ovos-padatious-pipeline-plugin#95). + +ovos-workshop's register_entity_file() emits entity registration messages +with name = ``:_``. Nebulento's slot-tagging +logic (IntentContainer.match_fuzzy) only tags a matched entity value into +the result when the literal token ``{}`` appears in +one of the intent's own templates. Since skill authors write bare templates +like ``"drive to {place}"``, a munged registration name can never satisfy +that check, so the entity slot is silently dropped from match_data. +""" +from ovos_bus_client.message import Message +from ovos_utils.fakebus import FakeBus + +from nebulento.opm import NebulentoPipeline + + +def _make_pipeline(): + return NebulentoPipeline(bus=FakeBus(), config={}) + + +def test_entity_registered_via_real_workshop_munged_name_is_matched(): + p = _make_pipeline() + lang = p.lang + skill_id = "skill-driver.tigregotico" + entity_bare = "place" + # exact shape emitted by ovos_workshop.skills.base.register_entity_file + munged_name = f"{skill_id}:{entity_bare}_1234567890abcdef1234567890abcdef" + + p.bus.emit(Message("padatious:register_intent", { + "name": f"{skill_id}:drive.intent", + "samples": ["drive to {place}", "navigate to {place}"], + "lang": lang, + })) + p.bus.emit(Message("padatious:register_entity", { + "name": munged_name, + "samples": ["kitchen", "office", "garden"], + "lang": lang, + })) + + match = p.match_low(["drive to kitchen"], lang, Message("recognizer_loop:utterance")) + assert match is not None, "intent failed to match at all" + assert "kitchen" in (match.match_data.get("place") or []), \ + f"expected bare 'place' slot to contain 'kitchen', got {match.match_data!r}" + p.shutdown() + + +def test_detach_entity_via_munged_name_actually_removes_it(): + p = _make_pipeline() + lang = p.lang + skill_id = "skill-driver.tigregotico" + munged_name = f"{skill_id}:place_1234567890abcdef1234567890abcdef" + + p.bus.emit(Message("padatious:register_intent", { + "name": f"{skill_id}:drive.intent", + "samples": ["drive to {place}"], + "lang": lang, + })) + p.bus.emit(Message("padatious:register_entity", { + "name": munged_name, + "samples": ["kitchen"], + "lang": lang, + })) + container = p.containers[lang] + assert "place" in container.registered_entities + + p.bus.emit(Message("detach_entity", {"name": munged_name, "lang": lang})) + assert "place" not in container.registered_entities, \ + "remove path did not dealias the munged name, entity was never removed" + p.shutdown() diff --git a/test/test_opm.py b/test/test_opm.py index bad576b..7c22e8b 100644 --- a/test/test_opm.py +++ b/test/test_opm.py @@ -188,8 +188,11 @@ def test_handle_detach_entity_via_bus(self): def test_handle_detach_entity_missing_name_is_noop(self): """detach_entity without a name must not raise or clear the container.""" self.pipeline.handle_detach_entity(Message("detach_entity", {})) + # entity names are dealiased to their bare token at the registration + # boundary (see _dealias_entity_name) โ€” "skill:item" is stored as + # "item", matching the ``{item}`` token in the registered template. self.assertIn( - "skill:item", + "item", self.pipeline.containers["en-US"].registered_entities, ) From b7ee2c264c5d6383f21ac94c6adedfa0518946e7 Mon Sep 17 00:00:00 2001 From: JarbasAl <33701864+JarbasAl@users.noreply.github.com> Date: Thu, 13 Aug 2026 18:49:09 +0000 Subject: [PATCH 6/7] Increment Version to 0.10.3a1 --- nebulento/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nebulento/version.py b/nebulento/version.py index 2718479..1fc3460 100644 --- a/nebulento/version.py +++ b/nebulento/version.py @@ -1,7 +1,7 @@ # START_VERSION_BLOCK VERSION_MAJOR = 0 VERSION_MINOR = 10 -VERSION_BUILD = 2 +VERSION_BUILD = 3 VERSION_ALPHA = 1 # END_VERSION_BLOCK From afc4bf1a5be122f6f6ed7f4ea72993c59cbbb102 Mon Sep 17 00:00:00 2001 From: JarbasAl <33701864+JarbasAl@users.noreply.github.com> Date: Thu, 13 Aug 2026 18:49:45 +0000 Subject: [PATCH 7/7] Update Changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea82c8b..ebb0b17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.10.3a1](https://github.com/OpenVoiceOS/nebulento/tree/0.10.3a1) (2026-08-13) + +[Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.10.2a1...0.10.3a1) + +**Merged pull requests:** + +- fix: dealias munged workshop entity names before container registration [\#48](https://github.com/OpenVoiceOS/nebulento/pull/48) ([JarbasAl](https://github.com/JarbasAl)) + ## [0.10.2a1](https://github.com/OpenVoiceOS/nebulento/tree/0.10.2a1) (2026-08-12) [Full Changelog](https://github.com/OpenVoiceOS/nebulento/compare/0.10.1...0.10.2a1)