mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-25 11:23:51 +08:00
Compare commits
1 Commits
bb/project
...
salvage/em
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1059f68bca |
@@ -1087,6 +1087,7 @@ class APIServerAdapter(BasePlatformAdapter):
|
||||
"""
|
||||
from run_agent import AIAgent
|
||||
from gateway.run import (
|
||||
_consume_runtime_model,
|
||||
_current_max_iterations,
|
||||
_resolve_runtime_agent_kwargs,
|
||||
_resolve_gateway_model,
|
||||
@@ -1098,6 +1099,7 @@ class APIServerAdapter(BasePlatformAdapter):
|
||||
runtime_kwargs = _resolve_runtime_agent_kwargs()
|
||||
reasoning_config = GatewayRunner._load_reasoning_config()
|
||||
model = _resolve_gateway_model()
|
||||
model, runtime_kwargs = _consume_runtime_model(model, runtime_kwargs)
|
||||
|
||||
user_config = _load_gateway_config()
|
||||
enabled_toolsets = sorted(_get_platform_tools(user_config, "api_server"))
|
||||
|
||||
@@ -1790,9 +1790,29 @@ def _resolve_runtime_agent_kwargs() -> dict:
|
||||
"args": list(runtime.get("args") or []),
|
||||
"credential_pool": runtime.get("credential_pool"),
|
||||
"max_tokens": max_tokens,
|
||||
"model": runtime.get("model"),
|
||||
}
|
||||
|
||||
|
||||
def _consume_runtime_model(model: str, runtime_kwargs: dict) -> tuple[str, dict]:
|
||||
"""Apply and remove a runtime-provider model from agent kwargs.
|
||||
|
||||
Runtime provider resolution may supply an explicit model in addition to
|
||||
credentials. AIAgent still receives ``model`` as a separate constructor
|
||||
argument, so callers must consume this handoff key instead of forwarding it
|
||||
through ``**runtime_kwargs`` where it would collide with ``model=...``.
|
||||
"""
|
||||
runtime_model = runtime_kwargs.pop("model", None)
|
||||
if runtime_model:
|
||||
logger.info(
|
||||
"Runtime provider supplied explicit model override: %s -> %s",
|
||||
model,
|
||||
runtime_model,
|
||||
)
|
||||
model = runtime_model
|
||||
return model, runtime_kwargs
|
||||
|
||||
|
||||
def _try_resolve_fallback_provider() -> dict | None:
|
||||
"""Attempt to resolve credentials from the fallback_model/fallback_providers config."""
|
||||
from hermes_cli.runtime_provider import resolve_runtime_provider
|
||||
@@ -3358,14 +3378,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
||||
)
|
||||
|
||||
runtime_kwargs = _resolve_runtime_agent_kwargs()
|
||||
runtime_model = runtime_kwargs.pop("model", None)
|
||||
if runtime_model:
|
||||
logger.info(
|
||||
"Runtime provider supplied explicit model override: %s -> %s",
|
||||
model,
|
||||
runtime_model,
|
||||
)
|
||||
model = runtime_model
|
||||
model, runtime_kwargs = _consume_runtime_model(model, runtime_kwargs)
|
||||
if override and resolved_session_key:
|
||||
model, runtime_kwargs = self._apply_session_model_override(
|
||||
resolved_session_key, model, runtime_kwargs
|
||||
@@ -8753,6 +8766,10 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
|
||||
from agent.model_metadata import get_model_context_length
|
||||
|
||||
_msg_cwd = os.environ.get("TERMINAL_CWD", os.path.expanduser("~"))
|
||||
# Probe-only use: we read base_url/provider/api_key fields for
|
||||
# context-length/credential resolution and never splat this dict
|
||||
# into AIAgent, so the "model" key it now carries is ignored here
|
||||
# (no _consume_runtime_model needed). See _consume_runtime_model.
|
||||
_msg_runtime = _resolve_runtime_agent_kwargs()
|
||||
_msg_config_ctx = None
|
||||
try:
|
||||
|
||||
@@ -979,8 +979,9 @@ def _resolve_model_and_runtime() -> Tuple[str, dict]:
|
||||
user_config = _load_gateway_config()
|
||||
model = _resolve_gateway_model(user_config)
|
||||
|
||||
from gateway.run import _resolve_runtime_agent_kwargs
|
||||
from gateway.run import _consume_runtime_model, _resolve_runtime_agent_kwargs
|
||||
runtime_kwargs = _resolve_runtime_agent_kwargs()
|
||||
model, runtime_kwargs = _consume_runtime_model(model, runtime_kwargs)
|
||||
|
||||
# Fall back to provider's default model if none configured
|
||||
if not model and runtime_kwargs.get("provider"):
|
||||
|
||||
@@ -337,6 +337,41 @@ class TestAdapterInit:
|
||||
assert isinstance(agent, FakeAgent)
|
||||
assert captured["reasoning_config"] == {"enabled": True, "effort": "xhigh"}
|
||||
|
||||
def test_create_agent_consumes_runtime_provider_model(self, monkeypatch):
|
||||
captured = {}
|
||||
|
||||
class FakeAgent:
|
||||
def __init__(self, **kwargs):
|
||||
captured.update(kwargs)
|
||||
|
||||
monkeypatch.setattr("run_agent.AIAgent", FakeAgent)
|
||||
monkeypatch.setattr(
|
||||
"gateway.run._resolve_runtime_agent_kwargs",
|
||||
lambda: {
|
||||
"provider": "custom-runtime",
|
||||
"base_url": "https://example.test/v1",
|
||||
"api_mode": "openai",
|
||||
"model": "runtime/model-from-provider",
|
||||
},
|
||||
)
|
||||
monkeypatch.setattr("gateway.run._resolve_gateway_model", lambda: "")
|
||||
monkeypatch.setattr("gateway.run._load_gateway_config", lambda: {})
|
||||
monkeypatch.setattr(
|
||||
"gateway.run.GatewayRunner._load_reasoning_config",
|
||||
staticmethod(lambda: {}),
|
||||
)
|
||||
monkeypatch.setattr("gateway.run.GatewayRunner._load_fallback_model", staticmethod(lambda: None))
|
||||
monkeypatch.setattr("hermes_cli.tools_config._get_platform_tools", lambda *_: set())
|
||||
|
||||
adapter = APIServerAdapter(PlatformConfig(enabled=True))
|
||||
monkeypatch.setattr(adapter, "_ensure_session_db", lambda: None)
|
||||
|
||||
agent = adapter._create_agent(session_id="api-session")
|
||||
|
||||
assert isinstance(agent, FakeAgent)
|
||||
assert captured["model"] == "runtime/model-from-provider"
|
||||
assert captured["provider"] == "custom-runtime"
|
||||
|
||||
def test_create_agent_refreshes_max_iterations_from_runtime_config(self, monkeypatch):
|
||||
captured = {}
|
||||
|
||||
|
||||
102
tests/gateway/test_runtime_provider_model_handoff.py
Normal file
102
tests/gateway/test_runtime_provider_model_handoff.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from gateway import run as gateway_run
|
||||
|
||||
|
||||
def _runner():
|
||||
runner = gateway_run.GatewayRunner.__new__(gateway_run.GatewayRunner)
|
||||
runner._session_model_overrides = {}
|
||||
runner._last_resolved_model = {}
|
||||
return runner
|
||||
|
||||
|
||||
def test_runtime_agent_kwargs_preserves_explicit_provider_model(monkeypatch):
|
||||
def fake_resolve_runtime_provider():
|
||||
return {
|
||||
"api_key": "sk-test",
|
||||
"base_url": "https://example.test/v1",
|
||||
"provider": "custom-runtime",
|
||||
"api_mode": "openai",
|
||||
"model": "runtime/model-from-provider",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider.resolve_runtime_provider",
|
||||
fake_resolve_runtime_provider,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider._get_model_config",
|
||||
lambda: {"default": ""},
|
||||
)
|
||||
|
||||
kwargs = gateway_run._resolve_runtime_agent_kwargs()
|
||||
|
||||
assert kwargs["provider"] == "custom-runtime"
|
||||
assert kwargs["model"] == "runtime/model-from-provider"
|
||||
|
||||
|
||||
def test_session_runtime_uses_provider_model_when_config_model_empty(monkeypatch):
|
||||
def fake_resolve_runtime_provider():
|
||||
return {
|
||||
"api_key": "sk-test",
|
||||
"base_url": "https://example.test/v1",
|
||||
"provider": "custom-runtime",
|
||||
"api_mode": "openai",
|
||||
"model": "runtime/model-from-provider",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider.resolve_runtime_provider",
|
||||
fake_resolve_runtime_provider,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider._get_model_config",
|
||||
lambda: {"default": ""},
|
||||
)
|
||||
monkeypatch.setattr(gateway_run, "_resolve_gateway_model", lambda _cfg=None: "")
|
||||
|
||||
model, runtime_kwargs = _runner()._resolve_session_agent_runtime(session_key="sid")
|
||||
|
||||
assert model == "runtime/model-from-provider"
|
||||
assert "model" not in runtime_kwargs
|
||||
assert runtime_kwargs["provider"] == "custom-runtime"
|
||||
|
||||
|
||||
def test_consume_runtime_model_leaves_kwargs_safe_for_agent_constructor():
|
||||
model, runtime_kwargs = gateway_run._consume_runtime_model(
|
||||
"",
|
||||
{
|
||||
"provider": "custom-runtime",
|
||||
"model": "runtime/model-from-provider",
|
||||
},
|
||||
)
|
||||
|
||||
assert model == "runtime/model-from-provider"
|
||||
assert runtime_kwargs == {"provider": "custom-runtime"}
|
||||
|
||||
|
||||
def test_session_runtime_keeps_provider_default_fallback_without_runtime_model(monkeypatch):
|
||||
def fake_resolve_runtime_provider():
|
||||
return {
|
||||
"api_key": "sk-test",
|
||||
"base_url": "https://example.test/v1",
|
||||
"provider": "custom-runtime",
|
||||
"api_mode": "openai",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider.resolve_runtime_provider",
|
||||
fake_resolve_runtime_provider,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider._get_model_config",
|
||||
lambda: {"default": ""},
|
||||
)
|
||||
monkeypatch.setattr(gateway_run, "_resolve_gateway_model", lambda _cfg=None: "")
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.models.get_default_model_for_provider",
|
||||
lambda provider: "catalog/default" if provider == "custom-runtime" else "",
|
||||
)
|
||||
|
||||
model, runtime_kwargs = _runner()._resolve_session_agent_runtime(session_key="sid")
|
||||
|
||||
assert model == "catalog/default"
|
||||
assert runtime_kwargs["provider"] == "custom-runtime"
|
||||
Reference in New Issue
Block a user