mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-01 07:01:41 +00:00
refactor(honcho): accept pinUserPeer as backwards-compatible alias for pinPeerName
The original key 'pinPeerName' from #14984 is ambiguous: a fresh reader can't tell whether it pins the user peer or the AI peer from the name alone. The resolver only ever pins the user-side (_resolve_user_peer_id short-circuits when pin_peer_name is true; the AI peer is already pinned by construction via aiPeer). Add 'pinUserPeer' as the canonical alias. Both keys land on the same internal pin_peer_name field; precedence is host pinUserPeer → host pinPeerName → root pinUserPeer → root pinPeerName → default. Host-level always beats root-level regardless of alias, so a host block can still explicitly disable a root-level pin even via the new key. Make _resolve_bool variadic so it can express the four-value precedence chain. All existing callers pass two positional args + default keyword, which the new signature accepts unchanged. Internal var name (pin_peer_name) stays the same to keep the cherry-picked #27371 commits clean and avoid a noisy rename diff.
This commit is contained in:
parent
0bac880991
commit
3cf5e8225d
2 changed files with 86 additions and 6 deletions
|
|
@ -91,12 +91,17 @@ def _normalize_recall_mode(val: str) -> str:
|
|||
return val if val in _VALID_RECALL_MODES else "hybrid"
|
||||
|
||||
|
||||
def _resolve_bool(host_val, root_val, *, default: bool) -> bool:
|
||||
"""Resolve a bool config field: host wins, then root, then default."""
|
||||
if host_val is not None:
|
||||
return bool(host_val)
|
||||
if root_val is not None:
|
||||
return bool(root_val)
|
||||
def _resolve_bool(*vals, default: bool) -> bool:
|
||||
"""Resolve a bool config field: first non-None wins, else default.
|
||||
|
||||
Variadic to support aliased keys (e.g. ``pinUserPeer`` shadowing
|
||||
``pinPeerName`` for backwards compatibility). Pass values in
|
||||
precedence order: caller's preferred alias first, then fallback
|
||||
aliases, in (host, root) interleaving as needed.
|
||||
"""
|
||||
for val in vals:
|
||||
if val is not None:
|
||||
return bool(val)
|
||||
return default
|
||||
|
||||
|
||||
|
|
@ -488,7 +493,15 @@ class HonchoClientConfig:
|
|||
peer_name=host_block.get("peerName") or raw.get("peerName"),
|
||||
ai_peer=ai_peer,
|
||||
pin_peer_name=_resolve_bool(
|
||||
# ``pinUserPeer`` is the clearer name (the resolver pins
|
||||
# the user-side peer to ``peerName``, ignoring runtime
|
||||
# identity). ``pinPeerName`` is the original key from
|
||||
# #14984 and stays accepted for backward compatibility.
|
||||
# Host-level keys win over root-level; among same-level
|
||||
# keys, ``pinUserPeer`` wins over ``pinPeerName``.
|
||||
host_block.get("pinUserPeer"),
|
||||
host_block.get("pinPeerName"),
|
||||
raw.get("pinUserPeer"),
|
||||
raw.get("pinPeerName"),
|
||||
default=False,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -614,3 +614,70 @@ class TestCrossPlatformMemoryUnification:
|
|||
"multi-user default MUST keep users separate — a regression "
|
||||
"here would silently merge unrelated users' memory"
|
||||
)
|
||||
|
||||
|
||||
class TestPinUserPeerAlias:
|
||||
"""``pinUserPeer`` is the canonical name; ``pinPeerName`` is the
|
||||
backwards-compatible alias.
|
||||
|
||||
Both keys land on the same internal ``pin_peer_name`` field. When
|
||||
both appear, the precedence is: host pinUserPeer → host pinPeerName
|
||||
→ root pinUserPeer → root pinPeerName → default. This matches the
|
||||
rule for every other host/root override in the plugin and lets a
|
||||
host block explicitly disable a root-level pin even via the legacy
|
||||
key.
|
||||
"""
|
||||
|
||||
def test_root_pinUserPeer_true_pins(self, tmp_path):
|
||||
from plugins.memory.honcho.client import HonchoClientConfig
|
||||
import json
|
||||
config_file = tmp_path / "honcho.json"
|
||||
config_file.write_text(json.dumps({
|
||||
"apiKey": "***",
|
||||
"peerName": "eri",
|
||||
"pinUserPeer": True,
|
||||
}))
|
||||
config = HonchoClientConfig.from_global_config(config_path=config_file)
|
||||
assert config.pin_peer_name is True
|
||||
|
||||
def test_host_pinUserPeer_wins_over_root_pinPeerName(self, tmp_path):
|
||||
from plugins.memory.honcho.client import HonchoClientConfig
|
||||
import json
|
||||
config_file = tmp_path / "honcho.json"
|
||||
config_file.write_text(json.dumps({
|
||||
"apiKey": "***",
|
||||
"peerName": "eri",
|
||||
"pinPeerName": False,
|
||||
"hosts": {"hermes": {"pinUserPeer": True}},
|
||||
}))
|
||||
config = HonchoClientConfig.from_global_config(config_path=config_file)
|
||||
assert config.pin_peer_name is True
|
||||
|
||||
def test_host_pinUserPeer_false_disables_root_pinPeerName(self, tmp_path):
|
||||
from plugins.memory.honcho.client import HonchoClientConfig
|
||||
import json
|
||||
config_file = tmp_path / "honcho.json"
|
||||
config_file.write_text(json.dumps({
|
||||
"apiKey": "***",
|
||||
"peerName": "eri",
|
||||
"pinPeerName": True,
|
||||
"hosts": {"hermes": {"pinUserPeer": False}},
|
||||
}))
|
||||
config = HonchoClientConfig.from_global_config(config_path=config_file)
|
||||
assert config.pin_peer_name is False, (
|
||||
"Host-level pinUserPeer=false must override the legacy "
|
||||
"root-level pinPeerName=true, otherwise a host can never "
|
||||
"unpin a globally-pinned profile via the new alias."
|
||||
)
|
||||
|
||||
def test_pinPeerName_still_works_unchanged(self, tmp_path):
|
||||
from plugins.memory.honcho.client import HonchoClientConfig
|
||||
import json
|
||||
config_file = tmp_path / "honcho.json"
|
||||
config_file.write_text(json.dumps({
|
||||
"apiKey": "***",
|
||||
"peerName": "eri",
|
||||
"hosts": {"hermes": {"pinPeerName": True}},
|
||||
}))
|
||||
config = HonchoClientConfig.from_global_config(config_path=config_file)
|
||||
assert config.pin_peer_name is True
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue