From e79f16cab608bb6fa6000834f08ee947ccf1eafd Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 5 Aug 2026 13:52:05 -0700 Subject: [PATCH] feat(providers): env-var metadata, config-driven local no-auth, reasoning-effort clamp for Actual - config_defaults: ACTUAL_API_KEY / ACTUAL_BASE_URL entries (setup wizard + hermes tools) - codex transport: clamp xhigh->high, ultra->max for provider=actual (SGLang/vLLM backends reject the wider values with a wrapped HTTP 400) - chat_completion_helpers: thread provider into Responses build_kwargs - tests: transport clamp + config-driven local no-auth regression --- agent/chat_completion_helpers.py | 1 + agent/transports/codex.py | 6 +++ hermes_cli/config_defaults.py | 16 ++++++++ tests/hermes_cli/test_actual_provider.py | 51 ++++++++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/agent/chat_completion_helpers.py b/agent/chat_completion_helpers.py index 313e7cd865..0fd556f840 100644 --- a/agent/chat_completion_helpers.py +++ b/agent/chat_completion_helpers.py @@ -1260,6 +1260,7 @@ def build_api_kwargs(agent, api_messages: list, tools_for_api: list | None = Non max_tokens=agent.max_tokens, timeout=agent._resolved_api_call_timeout(), request_overrides=agent.request_overrides, + provider=getattr(agent, "provider", None), is_github_responses=is_github_responses, is_codex_backend=is_codex_backend, is_xai_responses=is_xai_responses, diff --git a/agent/transports/codex.py b/agent/transports/codex.py index 499238b513..a60e5291b5 100644 --- a/agent/transports/codex.py +++ b/agent/transports/codex.py @@ -297,6 +297,12 @@ class ResponsesApiTransport(ProviderTransport): if params.get("is_xai_responses", False): # xAI Responses tops out at high; keep generic stronger values usable. _effort_clamp.update({"xhigh": "high", "max": "high", "ultra": "high"}) + if (params.get("provider") or "").strip().lower() == "actual": + # Actual Computer relays to SGLang/vLLM backends that accept only + # none/low/medium/high/max for reasoning effort — a forwarded + # xhigh/ultra fails with a wrapped HTTP 400 ("Expecting value: + # line 1 column 1"). Clamp Hermes' wider set to the supported one. + _effort_clamp.update({"xhigh": "high", "ultra": "max"}) reasoning_effort = _effort_clamp.get(reasoning_effort, reasoning_effort) response_tools = _responses_tools(tools) diff --git a/hermes_cli/config_defaults.py b/hermes_cli/config_defaults.py index 24659cc03b..41c493383b 100644 --- a/hermes_cli/config_defaults.py +++ b/hermes_cli/config_defaults.py @@ -3334,6 +3334,22 @@ OPTIONAL_ENV_VARS = { "category": "provider", "advanced": True, }, + "ACTUAL_API_KEY": { + "description": "Actual Computer inference key (ac_...)", + "prompt": "Actual Computer inference key", + "url": "https://actual.inc/user/keys", + "password": True, + "category": "provider", + "advanced": True, + }, + "ACTUAL_BASE_URL": { + "description": "Actual Computer base URL override (set to http://127.0.0.1:8080 for the local offline daemon)", + "prompt": "Actual Computer base URL (leave empty for hosted relay)", + "url": None, + "password": False, + "category": "provider", + "advanced": True, + }, "FIREWORKS_API_KEY": { "description": "Fireworks AI API key", "prompt": "Fireworks AI API key", diff --git a/tests/hermes_cli/test_actual_provider.py b/tests/hermes_cli/test_actual_provider.py index 37d50ad8ac..2e9116c08e 100644 --- a/tests/hermes_cli/test_actual_provider.py +++ b/tests/hermes_cli/test_actual_provider.py @@ -199,3 +199,54 @@ def test_actual_provider_model_ids_use_local_profile_catalog(monkeypatch): api_key=ACTUAL_LOCAL_NOAUTH_PLACEHOLDER, base_url=DEFAULT_ACTUAL_LOCAL_BASE_URL, ) + + +def test_actual_codex_transport_clamps_reasoning_effort(): + """Actual's SGLang/vLLM backends only accept none/low/medium/high/max. + + A global xhigh/ultra reasoning_effort must be clamped before the wire, + otherwise every request fails with a wrapped HTTP 400. Regression for + the field-reported reasoning_effort trap. + """ + from agent.transports.codex import ResponsesApiTransport + + t = ResponsesApiTransport() + for requested, expected in (("xhigh", "high"), ("ultra", "max"), ("high", "high")): + kwargs = t.build_kwargs( + model="glm-5.2-nvfp4", + messages=[{"role": "user", "content": "hi"}], + tools=None, + provider="actual", + reasoning_config={"effort": requested}, + base_url=DEFAULT_ACTUAL_BASE_URL, + ) + assert kwargs["reasoning"]["effort"] == expected, requested + + # Other providers keep the wider values untouched on the generic path. + kwargs = t.build_kwargs( + model="some-model", + messages=[{"role": "user", "content": "hi"}], + tools=None, + provider="openai-codex", + reasoning_config={"effort": "xhigh"}, + ) + assert kwargs["reasoning"]["effort"] == "xhigh" + + +def test_actual_runtime_config_local_base_url_without_key(monkeypatch): + """Config-driven loopback base_url (not just env) reaches the no-auth path.""" + _clear_actual_env(monkeypatch) + monkeypatch.setattr( + rp, + "_get_model_config", + lambda: { + "provider": "actual", + "base_url": "http://localhost:8080", + "default": "actual/local-model", + }, + ) + + resolved = rp.resolve_runtime_provider(requested="actual") + + assert resolved["api_key"] == ACTUAL_LOCAL_NOAUTH_PLACEHOLDER + assert resolved["api_mode"] == "codex_responses"