mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
fix(lazy-deps): never downgrade a shared dependency; track a range for huggingface-hub
An exact ==1.2.3 pin on huggingface-hub (feature tool.trace_upload) force-downgraded the shared venv on every lazy refresh whenever the core embedding stack (transformers/sentence-transformers, used by local/local_embedded Hindsight) had installed a newer version — transformers 5.x requires huggingface-hub>=1.5,<2.0, so the downgrade made sentence_transformers unimportable and the embedded Hindsight daemon abort at startup (silent memory loss until noticed). Two layers: - Track the compatibility range the trace-upload client actually needs (>=1.5,<2.0) instead of an exact pin, so an already-healthy shared version satisfies the spec and is left alone. - Add a general no-downgrade guard in _is_satisfied: a lazy, opt-in backend must never move an already-installed package backwards; treat 'installed newer than the pin allows' as satisfied and warn to widen the pin. Legitimate upgrades (installed below the spec) are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MN8RMDLwxCfFxwtADoEJJf
This commit is contained in:
parent
21695a10bf
commit
3ab819dc87
1 changed files with 45 additions and 3 deletions
|
|
@ -246,8 +246,10 @@ LAZY_DEPS: dict[str, tuple[str, ...]] = {
|
|||
# import (#60783) — active_features() flags this feature "active" from
|
||||
# mere package presence, so the downgrade fires even for users who never
|
||||
# ran a trace upload. The HfApi surface used here (whoami / create_repo /
|
||||
# upload_file) is stable across the whole 1.x line.
|
||||
"tool.trace_upload": ("huggingface-hub>=1.2.3,<2.0",),
|
||||
# upload_file) is stable across the whole 1.x line. (See also the
|
||||
# no-downgrade guard in _is_satisfied, which backstops this for any
|
||||
# shared-dep pin.)
|
||||
"tool.trace_upload": ("huggingface-hub>=1.5.0,<2.0",),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -552,12 +554,52 @@ def _is_satisfied(spec: str) -> bool:
|
|||
return True
|
||||
|
||||
try:
|
||||
return Version(installed) in SpecifierSet(spec_tail)
|
||||
iv = Version(installed)
|
||||
ss = SpecifierSet(spec_tail)
|
||||
if iv in ss:
|
||||
return True
|
||||
# No-downgrade guard. The installed version is outside the spec — but if
|
||||
# installing this spec would move the package BACKWARDS, refuse. A lazy,
|
||||
# opt-in backend must never downgrade a package that the core (or another
|
||||
# backend) already installed at a higher version: that is exactly how an
|
||||
# exact "==" pin on a SHARED transitive dependency (e.g. huggingface-hub,
|
||||
# pulled by transformers) silently bricks an unrelated core feature on
|
||||
# `hermes update`. Treat "already newer than this pin allows" as
|
||||
# satisfied, leave the higher version in place, and warn the maintainer
|
||||
# to widen the pin instead of churning a shared dependency.
|
||||
if _would_downgrade(iv, ss):
|
||||
logger.warning(
|
||||
"Lazy spec %r would DOWNGRADE already-installed %s==%s; leaving the "
|
||||
"installed version in place. Widen this pin to a range that includes "
|
||||
"the installed version if the downgrade is not intended.",
|
||||
spec, pkg, installed,
|
||||
)
|
||||
return True
|
||||
return False
|
||||
except (InvalidSpecifier, InvalidVersion, Exception):
|
||||
# Malformed spec or installed version we can't parse — don't churn.
|
||||
return True
|
||||
|
||||
|
||||
def _would_downgrade(installed, spec_set) -> bool:
|
||||
"""True if ``spec_set`` permits no version >= ``installed``.
|
||||
|
||||
``installed`` is already known to fall outside ``spec_set``. If every
|
||||
upper-bounding operator (``==``, ``<``, ``<=``, ``~=``) in the spec sits
|
||||
below the installed version, the only way to satisfy the spec is to install
|
||||
something older — a downgrade. Purely local version arithmetic; no network.
|
||||
"""
|
||||
try:
|
||||
from packaging.version import Version
|
||||
for s in spec_set:
|
||||
if s.operator in ("==", "<", "<=", "~="):
|
||||
if installed > Version(s.version):
|
||||
return True
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _is_present(spec: str) -> bool:
|
||||
"""Cheap presence-only check (package name installed at any version).
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue