Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions tests/tools/test_lazy_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,16 +316,24 @@ def test_finds_features_with_at_least_one_package_installed(self, monkeypatch):
assert "memory.hindsight" not in active
assert "platform.slack" not in active

def test_multi_package_feature_active_if_any_present(self, monkeypatch):
# platform.slack has 3 packages; only one needs to be present
# for the feature to count as active (user activated it before,
# one transitive may have been uninstalled separately).
def test_multi_package_feature_active_if_primary_present(self, monkeypatch):
# The first spec is the feature's primary package. Secondary packages
# may be shared by unrelated backends.
monkeypatch.setattr(
ld, "_is_present",
lambda spec: ld._pkg_name_from_spec(spec) == "slack-bolt",
)
assert "platform.slack" in ld.active_features()

def test_shared_secondary_package_does_not_activate_feature(self, monkeypatch):
# aiohttp is a core/shared dependency and must not make an otherwise
# unused Matrix backend look active during `hermes update`.
monkeypatch.setattr(
ld, "_is_present",
lambda spec: ld._pkg_name_from_spec(spec) == "aiohttp",
)
assert "platform.matrix" not in ld.active_features()


class TestRefreshActiveFeatures:
def test_no_active_features_returns_empty(self, monkeypatch):
Expand Down
12 changes: 7 additions & 5 deletions tools/lazy_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@
"starlette==1.0.1", # CVE-2026-48710 — keep in sync with pyproject [computer-use]
),
# HF Agent Trace Viewer upload (hermes trace upload / /upload-trace).
"tool.trace_upload": ("huggingface-hub==1.2.3",),
"tool.trace_upload": ("huggingface-hub==1.23.0",),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split this trace-upload dependency pin into a separate change. It is independent of lazy-feature activation, as the existing member comment also notes.

}


Expand Down Expand Up @@ -858,16 +858,18 @@ def feature_install_command(feature: str) -> Optional[str]:
def active_features() -> list[str]:
"""Return the list of features the user has ever lazy-installed.

A feature counts as "active" if at least one of its declared packages
is currently installed in the venv (presence check, ignoring version).
Features the user has never enabled stay quiet.
A feature counts as "active" when its primary package (the first spec in
:data:`LAZY_DEPS`) is currently installed in the venv, ignoring version.
Secondary specs are often shared core dependencies (for example aiohttp),
so treating any declared package as proof of activation causes unrelated
backends to be installed during every update.

Used by ``hermes update`` to figure out which lazy backends need a
refresh pass when pins move in :data:`LAZY_DEPS`.
"""
active = []

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes shared-dependency false positives, but it also removes the existing partial-install recovery behavior documented by the prior test: if slack-bolt is missing while another Slack-specific dependency remains, update will no longer detect and repair Slack. Please use activation state (or preserve that case) rather than making tuple position the activation record.

for feature, specs in LAZY_DEPS.items():
if any(_is_present(s) for s in specs):
if specs and _is_present(specs[0]):
active.append(feature)
return active

Expand Down