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
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user