mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-19 15:18:03 +00:00
fix(gateway): scope context refs to runtime profile
This commit is contained in:
parent
4df6e6280d
commit
37a942650f
3 changed files with 126 additions and 1 deletions
|
|
@ -10613,6 +10613,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
|||
_msg_cwd = os.environ.get("TERMINAL_CWD", os.path.expanduser("~"))
|
||||
_msg_config_ctx = None
|
||||
_msg_cfg = None
|
||||
_msg_custom_providers = []
|
||||
try:
|
||||
_msg_cfg = _load_gateway_config()
|
||||
_msg_model_cfg = _msg_cfg.get("model", {})
|
||||
|
|
@ -10620,6 +10621,12 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
|||
_msg_raw_ctx = _msg_model_cfg.get("context_length")
|
||||
if _msg_raw_ctx is not None:
|
||||
_msg_config_ctx = int(_msg_raw_ctx)
|
||||
try:
|
||||
from hermes_cli.config import get_compatible_custom_providers
|
||||
|
||||
_msg_custom_providers = get_compatible_custom_providers(_msg_cfg)
|
||||
except Exception:
|
||||
_msg_custom_providers = _msg_cfg.get("custom_providers") or []
|
||||
except Exception:
|
||||
pass
|
||||
# Resolve the session's actual model/provider/base_url the
|
||||
|
|
@ -10640,6 +10647,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
|||
api_key=_msg_runtime.get("api_key") or "",
|
||||
config_context_length=_msg_config_ctx,
|
||||
provider=_msg_runtime.get("provider") or "",
|
||||
custom_providers=_msg_custom_providers,
|
||||
)
|
||||
_ctx_result = await preprocess_context_references_async(
|
||||
message_text,
|
||||
|
|
@ -10663,6 +10671,30 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
|||
|
||||
return message_text
|
||||
|
||||
async def _prepare_profile_scoped_inbound_message_text(
|
||||
self,
|
||||
*,
|
||||
event: MessageEvent,
|
||||
source: SessionSource,
|
||||
history: List[Dict[str, Any]],
|
||||
session_key: Optional[str] = None,
|
||||
) -> Optional[str]:
|
||||
"""Run inbound preprocessing under the routed profile when multiplexed."""
|
||||
if getattr(getattr(self, "config", None), "multiplex_profiles", False):
|
||||
with _profile_runtime_scope(self._resolve_profile_home_for_source(source)):
|
||||
return await self._prepare_inbound_message_text(
|
||||
event=event,
|
||||
source=source,
|
||||
history=history,
|
||||
session_key=session_key,
|
||||
)
|
||||
return await self._prepare_inbound_message_text(
|
||||
event=event,
|
||||
source=source,
|
||||
history=history,
|
||||
session_key=session_key,
|
||||
)
|
||||
|
||||
def _consume_pending_native_image_paths(self, session_key: str) -> List[str]:
|
||||
pending_native = getattr(self, "_pending_native_image_paths_by_session", None)
|
||||
if not pending_native:
|
||||
|
|
@ -11505,7 +11537,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
|||
# attachments (documents, audio, etc.) are not sent to the vision
|
||||
# tool even when they appear in the same message.
|
||||
# -----------------------------------------------------------------
|
||||
message_text = await self._prepare_inbound_message_text(
|
||||
message_text = await self._prepare_profile_scoped_inbound_message_text(
|
||||
event=event,
|
||||
source=source,
|
||||
history=history,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
|
|||
# Auto-extracted from noreply emails + manual overrides
|
||||
AUTHOR_MAP = {
|
||||
"iganapolsky@gmail.com": "IgorGanapolsky", # PR #62125 salvage (compaction anti-thrash threshold verification)
|
||||
"tturney1@gmail.com": "TheTom", # PR #62696 salvage (gateway: expand @ context references under runtime/session model resolution)
|
||||
"wilsonkinyuam@gmail.com": "WilsonKinyua", # PR #62052 (tui: persist unflushed conversations on disconnect/restart)
|
||||
"humphreysun98@gmail.com": "HumphreySun98", # PR #61142 salvage (web: null web/backend config value guards)
|
||||
"sonxi@nous.local": "17324393074", # PR #53196 salvage (tools_config: known_plugin_toolsets null guard; commit under unlinked local identity)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ the hygiene-compression block already uses) and must actually reach
|
|||
"""
|
||||
import logging
|
||||
import threading
|
||||
from contextlib import contextmanager
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -185,3 +186,94 @@ async def test_at_reference_resolves_model_via_session_runtime(monkeypatch):
|
|||
assert captured_runtime_call.get("model") == "openai/gpt-4.1-mini"
|
||||
assert captured_runtime_call.get("base_url") == "https://api.openai.com/v1"
|
||||
assert captured_runtime_call.get("provider") == "openai"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_at_reference_uses_routed_profile_scope_when_multiplexed(monkeypatch, tmp_path):
|
||||
"""Secondary-profile preprocessing must resolve inside that profile scope."""
|
||||
runner = _make_runner()
|
||||
runner.config.multiplex_profiles = True
|
||||
source = _source()
|
||||
source.profile = "secondary"
|
||||
profile_home = tmp_path / "profiles" / "secondary"
|
||||
seen = []
|
||||
|
||||
@contextmanager
|
||||
def _scope(home):
|
||||
seen.append(("enter", home))
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
seen.append(("exit", home))
|
||||
|
||||
async def _prepared(**kwargs):
|
||||
seen.append(("prepared", kwargs["source"].profile))
|
||||
return "expanded"
|
||||
|
||||
monkeypatch.setattr(gateway_run, "_profile_runtime_scope", _scope)
|
||||
monkeypatch.setattr(runner, "_resolve_profile_home_for_source", lambda _source: profile_home)
|
||||
monkeypatch.setattr(runner, "_prepare_inbound_message_text", _prepared)
|
||||
|
||||
result = await runner._prepare_profile_scoped_inbound_message_text(
|
||||
event=MessageEvent(text="@file:note", source=source),
|
||||
source=source,
|
||||
history=[],
|
||||
session_key="agent:secondary:telegram:dm:123",
|
||||
)
|
||||
|
||||
assert result == "expanded"
|
||||
assert seen == [
|
||||
("enter", profile_home),
|
||||
("prepared", "secondary"),
|
||||
("exit", profile_home),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_at_reference_passes_compatible_custom_provider_context(monkeypatch):
|
||||
"""Per-model custom-provider limits must bound context-reference injection."""
|
||||
runner = _make_runner()
|
||||
source = _source()
|
||||
captured = {}
|
||||
custom_providers = [{
|
||||
"name": "private",
|
||||
"base_url": "https://private.example/v1",
|
||||
"models": {"private/model": {"context_length": 32768}},
|
||||
}]
|
||||
|
||||
monkeypatch.setattr(
|
||||
gateway_run,
|
||||
"_load_gateway_config",
|
||||
lambda: {"model": {"default": "private/model"}, "custom_providers": custom_providers},
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
gateway_run,
|
||||
"_resolve_gateway_model",
|
||||
lambda _cfg=None: "private/model",
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
gateway_run,
|
||||
"_resolve_runtime_agent_kwargs",
|
||||
lambda: {"provider": "custom:private", "api_key": "test", "base_url": "https://private.example/v1"},
|
||||
)
|
||||
|
||||
import hermes_cli.config as config_mod
|
||||
import agent.model_metadata as model_meta_mod
|
||||
import agent.context_references as ctx_mod
|
||||
|
||||
monkeypatch.setattr(config_mod, "get_compatible_custom_providers", lambda _cfg: custom_providers)
|
||||
|
||||
async def _fake_get_context(_model, **kwargs):
|
||||
captured["custom_providers"] = kwargs["custom_providers"]
|
||||
return 32768
|
||||
|
||||
async def _passthrough(message, **_kwargs):
|
||||
return ContextReferenceResult(message=message, original_message=message)
|
||||
|
||||
monkeypatch.setattr(model_meta_mod, "get_model_context_length_async", _fake_get_context)
|
||||
monkeypatch.setattr(ctx_mod, "preprocess_context_references_async", _passthrough)
|
||||
|
||||
await runner._prepare_inbound_message_text(
|
||||
event=MessageEvent(text="@file:note", source=source), source=source, history=[]
|
||||
)
|
||||
assert captured["custom_providers"] == custom_providers
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue