mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-03 02:11:48 +00:00
Gate tool-gateway behind an env var, so it's not in users' faces until we're ready. Even if users enable it, it'll be blocked server-side for now, until we unlock for non-admin users on tool-gateway.
This commit is contained in:
parent
e95965d76a
commit
1cbb1b99cc
35 changed files with 426 additions and 147 deletions
|
|
@ -45,6 +45,11 @@ def _restore_tool_and_agent_modules():
|
|||
sys.modules.update(original_modules)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _enable_managed_nous_tools(monkeypatch):
|
||||
monkeypatch.setenv("HERMES_ENABLE_NOUS_MANAGED_TOOLS", "1")
|
||||
|
||||
|
||||
def _install_fake_tools_package():
|
||||
_reset_modules(("tools", "agent"))
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,11 @@ def _restore_tool_and_agent_modules():
|
|||
sys.modules.update(original_modules)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _enable_managed_nous_tools(monkeypatch):
|
||||
monkeypatch.setenv("HERMES_ENABLE_NOUS_MANAGED_TOOLS", "1")
|
||||
|
||||
|
||||
def _install_fake_tools_package():
|
||||
tools_package = types.ModuleType("tools")
|
||||
tools_package.__path__ = [str(TOOLS_DIR)] # type: ignore[attr-defined]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,14 @@ resolve_managed_tool_gateway = managed_tool_gateway.resolve_managed_tool_gateway
|
|||
|
||||
|
||||
def test_resolve_managed_tool_gateway_derives_vendor_origin_from_shared_domain():
|
||||
with patch.dict(os.environ, {"TOOL_GATEWAY_DOMAIN": "nousresearch.com"}, clear=False):
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS": "1",
|
||||
"TOOL_GATEWAY_DOMAIN": "nousresearch.com",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
result = resolve_managed_tool_gateway(
|
||||
"firecrawl",
|
||||
token_reader=lambda: "nous-token",
|
||||
|
|
@ -29,7 +36,14 @@ def test_resolve_managed_tool_gateway_derives_vendor_origin_from_shared_domain()
|
|||
|
||||
|
||||
def test_resolve_managed_tool_gateway_uses_vendor_specific_override():
|
||||
with patch.dict(os.environ, {"BROWSERBASE_GATEWAY_URL": "http://browserbase-gateway.localhost:3009/"}, clear=False):
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS": "1",
|
||||
"BROWSERBASE_GATEWAY_URL": "http://browserbase-gateway.localhost:3009/",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
result = resolve_managed_tool_gateway(
|
||||
"browserbase",
|
||||
token_reader=lambda: "nous-token",
|
||||
|
|
@ -40,7 +54,14 @@ def test_resolve_managed_tool_gateway_uses_vendor_specific_override():
|
|||
|
||||
|
||||
def test_resolve_managed_tool_gateway_is_inactive_without_nous_token():
|
||||
with patch.dict(os.environ, {"TOOL_GATEWAY_DOMAIN": "nousresearch.com"}, clear=False):
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS": "1",
|
||||
"TOOL_GATEWAY_DOMAIN": "nousresearch.com",
|
||||
},
|
||||
clear=False,
|
||||
):
|
||||
result = resolve_managed_tool_gateway(
|
||||
"firecrawl",
|
||||
token_reader=lambda: None,
|
||||
|
|
@ -49,6 +70,16 @@ def test_resolve_managed_tool_gateway_is_inactive_without_nous_token():
|
|||
assert result is None
|
||||
|
||||
|
||||
def test_resolve_managed_tool_gateway_is_disabled_without_feature_flag():
|
||||
with patch.dict(os.environ, {"TOOL_GATEWAY_DOMAIN": "nousresearch.com"}, clear=False):
|
||||
result = resolve_managed_tool_gateway(
|
||||
"firecrawl",
|
||||
token_reader=lambda: "nous-token",
|
||||
)
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_read_nous_access_token_refreshes_expiring_cached_token(tmp_path, monkeypatch):
|
||||
monkeypatch.delenv("TOOL_GATEWAY_USER_TOKEN", raising=False)
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ terminal_tool_module = importlib.import_module("tools.terminal_tool")
|
|||
def _clear_terminal_env(monkeypatch):
|
||||
"""Remove terminal env vars that could affect requirements checks."""
|
||||
keys = [
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS",
|
||||
"TERMINAL_ENV",
|
||||
"TERMINAL_MODAL_MODE",
|
||||
"TERMINAL_SSH_HOST",
|
||||
|
|
@ -73,13 +74,14 @@ def test_modal_backend_without_token_or_config_logs_specific_error(monkeypatch,
|
|||
|
||||
assert ok is False
|
||||
assert any(
|
||||
"Modal backend selected but no direct Modal credentials/config or managed tool gateway was found" in record.getMessage()
|
||||
"Modal backend selected but no direct Modal credentials/config was found" in record.getMessage()
|
||||
for record in caplog.records
|
||||
)
|
||||
|
||||
|
||||
def test_modal_backend_with_managed_gateway_does_not_require_direct_creds_or_minisweagent(monkeypatch, tmp_path):
|
||||
_clear_terminal_env(monkeypatch)
|
||||
monkeypatch.setenv("HERMES_ENABLE_NOUS_MANAGED_TOOLS", "1")
|
||||
monkeypatch.setenv("TERMINAL_ENV", "modal")
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
monkeypatch.setenv("USERPROFILE", str(tmp_path))
|
||||
|
|
@ -115,3 +117,21 @@ def test_modal_backend_direct_mode_does_not_fall_back_to_managed(monkeypatch, ca
|
|||
"TERMINAL_MODAL_MODE=direct" in record.getMessage()
|
||||
for record in caplog.records
|
||||
)
|
||||
|
||||
|
||||
def test_modal_backend_managed_mode_without_feature_flag_logs_clear_error(monkeypatch, caplog, tmp_path):
|
||||
_clear_terminal_env(monkeypatch)
|
||||
monkeypatch.setenv("TERMINAL_ENV", "modal")
|
||||
monkeypatch.setenv("TERMINAL_MODAL_MODE", "managed")
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
monkeypatch.setenv("USERPROFILE", str(tmp_path))
|
||||
monkeypatch.setattr(terminal_tool_module, "is_managed_tool_gateway_ready", lambda _vendor: False)
|
||||
|
||||
with caplog.at_level(logging.ERROR):
|
||||
ok = terminal_tool_module.check_terminal_requirements()
|
||||
|
||||
assert ok is False
|
||||
assert any(
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS is not enabled" in record.getMessage()
|
||||
for record in caplog.records
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class TestTerminalRequirements:
|
|||
assert {"read_file", "write_file", "patch", "search_files"}.issubset(names)
|
||||
|
||||
def test_terminal_and_execute_code_tools_resolve_for_managed_modal(self, monkeypatch, tmp_path):
|
||||
monkeypatch.setenv("HERMES_ENABLE_NOUS_MANAGED_TOOLS", "1")
|
||||
monkeypatch.setenv("HOME", str(tmp_path))
|
||||
monkeypatch.setenv("USERPROFILE", str(tmp_path))
|
||||
monkeypatch.delenv("MODAL_TOKEN_ID", raising=False)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ Coverage:
|
|||
import importlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock, AsyncMock
|
||||
|
||||
|
|
@ -24,6 +26,7 @@ class TestFirecrawlClientConfig:
|
|||
tools.web_tools._firecrawl_client = None
|
||||
tools.web_tools._firecrawl_client_config = None
|
||||
for key in (
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS",
|
||||
"FIRECRAWL_API_KEY",
|
||||
"FIRECRAWL_API_URL",
|
||||
"FIRECRAWL_GATEWAY_URL",
|
||||
|
|
@ -32,6 +35,7 @@ class TestFirecrawlClientConfig:
|
|||
"TOOL_GATEWAY_USER_TOKEN",
|
||||
):
|
||||
os.environ.pop(key, None)
|
||||
os.environ["HERMES_ENABLE_NOUS_MANAGED_TOOLS"] = "1"
|
||||
|
||||
def teardown_method(self):
|
||||
"""Reset client after each test."""
|
||||
|
|
@ -39,6 +43,7 @@ class TestFirecrawlClientConfig:
|
|||
tools.web_tools._firecrawl_client = None
|
||||
tools.web_tools._firecrawl_client_config = None
|
||||
for key in (
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS",
|
||||
"FIRECRAWL_API_KEY",
|
||||
"FIRECRAWL_API_URL",
|
||||
"FIRECRAWL_GATEWAY_URL",
|
||||
|
|
@ -293,6 +298,7 @@ class TestBackendSelection:
|
|||
"""
|
||||
|
||||
_ENV_KEYS = (
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS",
|
||||
"PARALLEL_API_KEY",
|
||||
"FIRECRAWL_API_KEY",
|
||||
"FIRECRAWL_API_URL",
|
||||
|
|
@ -304,8 +310,10 @@ class TestBackendSelection:
|
|||
)
|
||||
|
||||
def setup_method(self):
|
||||
os.environ["HERMES_ENABLE_NOUS_MANAGED_TOOLS"] = "1"
|
||||
for key in self._ENV_KEYS:
|
||||
os.environ.pop(key, None)
|
||||
if key != "HERMES_ENABLE_NOUS_MANAGED_TOOLS":
|
||||
os.environ.pop(key, None)
|
||||
|
||||
def teardown_method(self):
|
||||
for key in self._ENV_KEYS:
|
||||
|
|
@ -417,11 +425,25 @@ class TestParallelClientConfig:
|
|||
import tools.web_tools
|
||||
tools.web_tools._parallel_client = None
|
||||
os.environ.pop("PARALLEL_API_KEY", None)
|
||||
fake_parallel = types.ModuleType("parallel")
|
||||
|
||||
class Parallel:
|
||||
def __init__(self, api_key):
|
||||
self.api_key = api_key
|
||||
|
||||
class AsyncParallel:
|
||||
def __init__(self, api_key):
|
||||
self.api_key = api_key
|
||||
|
||||
fake_parallel.Parallel = Parallel
|
||||
fake_parallel.AsyncParallel = AsyncParallel
|
||||
sys.modules["parallel"] = fake_parallel
|
||||
|
||||
def teardown_method(self):
|
||||
import tools.web_tools
|
||||
tools.web_tools._parallel_client = None
|
||||
os.environ.pop("PARALLEL_API_KEY", None)
|
||||
sys.modules.pop("parallel", None)
|
||||
|
||||
def test_creates_client_with_key(self):
|
||||
"""PARALLEL_API_KEY set → creates Parallel client."""
|
||||
|
|
@ -479,6 +501,7 @@ class TestCheckWebApiKey:
|
|||
"""Test suite for check_web_api_key() unified availability check."""
|
||||
|
||||
_ENV_KEYS = (
|
||||
"HERMES_ENABLE_NOUS_MANAGED_TOOLS",
|
||||
"PARALLEL_API_KEY",
|
||||
"FIRECRAWL_API_KEY",
|
||||
"FIRECRAWL_API_URL",
|
||||
|
|
@ -490,8 +513,10 @@ class TestCheckWebApiKey:
|
|||
)
|
||||
|
||||
def setup_method(self):
|
||||
os.environ["HERMES_ENABLE_NOUS_MANAGED_TOOLS"] = "1"
|
||||
for key in self._ENV_KEYS:
|
||||
os.environ.pop(key, None)
|
||||
if key != "HERMES_ENABLE_NOUS_MANAGED_TOOLS":
|
||||
os.environ.pop(key, None)
|
||||
|
||||
def teardown_method(self):
|
||||
for key in self._ENV_KEYS:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue