Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions hermes_cli/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -2108,24 +2108,30 @@ def _build_service_path_dirs(project_root: Path | None = None) -> list[str]:
if project_root is None:
project_root = PROJECT_ROOT

def _is_dir(path: Path) -> bool:
try:
return path.is_dir()
except OSError:
return False

candidates = []

venv_bin = project_root / "venv" / "bin"
if venv_bin.is_dir():
if _is_dir(venv_bin):
candidates.append(str(venv_bin))
elif sys.prefix != sys.base_prefix:
candidates.append(str(Path(sys.prefix) / "bin"))

node_bin = project_root / "node_modules" / ".bin"
if node_bin.is_dir():
if _is_dir(node_bin):
candidates.append(str(node_bin))

hermes_home = get_hermes_home()
hermes_node = hermes_home / "node" / "bin"
if hermes_node.is_dir():
if _is_dir(hermes_node):
candidates.append(str(hermes_node))
hermes_nm = hermes_home / "node_modules" / ".bin"
if hermes_nm.is_dir():
if _is_dir(hermes_nm):
candidates.append(str(hermes_nm))

return candidates
Expand Down
22 changes: 18 additions & 4 deletions tests/run_agent/test_provider_parity.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,12 @@ def test_original_messages_not_mutated(self, monkeypatch):
assert messages[0]["role"] == "system"

def test_developer_role_via_nous_portal(self, monkeypatch):
agent = _make_agent(monkeypatch, "nous", base_url="https://inference-api.nousresearch.com/v1")
agent.model = "gpt-5"
agent = _make_agent(
monkeypatch,
"nous",
base_url="https://inference-api.nousresearch.com/v1",
model="gpt-5",
)
messages = [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "hi"},
Expand Down Expand Up @@ -346,14 +350,24 @@ def test_includes_tools(self, monkeypatch):
class TestBuildApiKwargsNousPortal:
def test_includes_nous_product_tags(self, monkeypatch):
from agent.portal_tags import nous_portal_tags
agent = _make_agent(monkeypatch, "nous", base_url="https://inference-api.nousresearch.com/v1")
agent = _make_agent(
monkeypatch,
"nous",
base_url="https://inference-api.nousresearch.com/v1",
model="gpt-5",
)
messages = [{"role": "user", "content": "hi"}]
kwargs = agent._build_api_kwargs(messages)
extra = kwargs.get("extra_body", {})
assert extra.get("tags") == nous_portal_tags()

def test_uses_chat_completions_format(self, monkeypatch):
agent = _make_agent(monkeypatch, "nous", base_url="https://inference-api.nousresearch.com/v1")
agent = _make_agent(
monkeypatch,
"nous",
base_url="https://inference-api.nousresearch.com/v1",
model="gpt-5",
)
messages = [{"role": "user", "content": "hi"}]
kwargs = agent._build_api_kwargs(messages)
assert "messages" in kwargs
Expand Down
27 changes: 27 additions & 0 deletions tests/tools/test_transcription_dotenv_fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,33 @@ def test_import_after_config_env_patch_uses_restored_dotenv_loader(self):
finally:
importlib.reload(tt)

def test_xai_resolver_import_after_config_env_patch_uses_restored_dotenv_loader(self):
"""xAI HTTP auth must not cache a temporarily patched env helper."""
import importlib
import hermes_cli.config as config_mod
from tools import xai_http

with pytest.MonkeyPatch.context() as mp:
mp.setattr(config_mod, "get_env_value", lambda name, default=None: "")
xai_http = importlib.reload(xai_http)

try:
with patch(
"hermes_cli.runtime_provider.resolve_runtime_provider",
side_effect=RuntimeError("no oauth"),
), patch(
"hermes_cli.auth.resolve_xai_oauth_runtime_credentials",
return_value={},
), patch(
"hermes_cli.config.load_env",
return_value={"XAI_API_KEY": "dotenv-secret"},
):
creds = xai_http.resolve_xai_http_credentials()
finally:
importlib.reload(xai_http)

assert creds["api_key"] == "dotenv-secret"

def test_explicit_groq_sees_dotenv(self):
from tools import transcription_tools as tt

Expand Down
12 changes: 5 additions & 7 deletions tools/xai_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,21 @@
import os
from typing import Dict

try:
from hermes_cli.config import get_env_value as _hermes_get_env_value
except Exception:
_hermes_get_env_value = None


def get_env_value(name: str, default=None):
"""Read ``name`` from ``~/.hermes/.env`` first, then ``os.environ``.

Wraps :func:`hermes_cli.config.get_env_value` so tests can patch
``tools.xai_http.get_env_value`` to inject dotenv-only secrets into the
xAI credential resolver.
"""
if _hermes_get_env_value is not None:
try:
from hermes_cli.config import get_env_value as _hermes_get_env_value

value = _hermes_get_env_value(name)
if value is not None:
return value
except Exception:
pass
return os.environ.get(name, default)


Expand Down
Loading