From dd7caaa6c2e51d013fb587ec8c6b58ac7015c491 Mon Sep 17 00:00:00 2001 From: Ayush Nangia Date: Wed, 15 Jul 2026 20:28:32 +0530 Subject: [PATCH] fix(delegation): carry provider request_overrides through the base_url path (#65035) --- tests/tools/test_delegate.py | 52 ++++++++++++++++++++++++++++++++++++ tools/delegate_tool.py | 30 +++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/tests/tools/test_delegate.py b/tests/tools/test_delegate.py index c896af7ada317..f7c07316ee370 100644 --- a/tests/tools/test_delegate.py +++ b/tests/tools/test_delegate.py @@ -827,6 +827,58 @@ def test_direct_endpoint_auto_detects_anthropic_messages_suffix(self): self.assertEqual(creds["api_mode"], "anthropic_messages") + @patch("hermes_cli.runtime_provider.resolve_runtime_provider") + def test_base_url_with_provider_carries_runtime_request_overrides(self, mock_resolve): + """#65035: the base_url short-circuit must not drop the configured + provider's request_overrides / max_output_tokens.""" + mock_resolve.return_value = { + "provider": "custom", + "base_url": "https://provider-default.example/v1", + "api_key": "provider-key", + "api_mode": "chat_completions", + "request_overrides": {"extra_body": {"thinking": {"type": "disabled"}}}, + "max_output_tokens": 8192, + } + parent = _make_mock_parent(depth=0) + cfg = { + "model": "mimo-v2.5-pro", + "provider": "mimo", + "base_url": "https://api.xiaomimimo.com/v1", + "api_key": "cfg-key", + } + creds = _resolve_delegation_credentials(cfg, parent) + # Explicitly configured endpoint + key still win over the runtime's. + self.assertEqual(creds["base_url"], "https://api.xiaomimimo.com/v1") + self.assertEqual(creds["api_key"], "cfg-key") + # The provider's request personality survives the short-circuit. + self.assertEqual( + creds["request_overrides"], + {"extra_body": {"thinking": {"type": "disabled"}}}, + ) + self.assertEqual(creds["max_output_tokens"], 8192) + + def test_bare_base_url_returns_none_overrides(self): + """No provider alongside base_url → no overrides source; keys are + present but None (shape parity with the inherit-everything path).""" + parent = _make_mock_parent(depth=0) + cfg = {"model": "m", "provider": "", "base_url": "http://localhost:1234/v1", "api_key": "k"} + creds = _resolve_delegation_credentials(cfg, parent) + self.assertIsNone(creds["request_overrides"]) + self.assertIsNone(creds["max_output_tokens"]) + + @patch("hermes_cli.runtime_provider.resolve_runtime_provider") + def test_base_url_survives_runtime_resolution_failure(self, mock_resolve): + """Best-effort: the explicit endpoint worked before this change even + when the provider can't resolve — a resolution failure must not + break it, only skip the overrides.""" + mock_resolve.side_effect = RuntimeError("MIMO_API_KEY not set") + parent = _make_mock_parent(depth=0) + cfg = {"model": "m", "provider": "mimo", "base_url": "https://api.xiaomimimo.com/v1", "api_key": "k"} + creds = _resolve_delegation_credentials(cfg, parent) + self.assertEqual(creds["base_url"], "https://api.xiaomimimo.com/v1") + self.assertIsNone(creds["request_overrides"]) + self.assertIsNone(creds["max_output_tokens"]) + @patch("hermes_cli.runtime_provider.resolve_runtime_provider") def test_provider_resolution_failure_raises_valueerror(self, mock_resolve): """When provider resolution fails, ValueError is raised with helpful message.""" diff --git a/tools/delegate_tool.py b/tools/delegate_tool.py index d3c2a2dda8817..2c6765098b6b4 100644 --- a/tools/delegate_tool.py +++ b/tools/delegate_tool.py @@ -4531,12 +4531,42 @@ def _resolve_delegation_credentials(cfg: dict, parent_agent) -> dict: if configured_api_mode in {"chat_completions", "codex_responses", "anthropic_messages"}: api_mode = configured_api_mode + # A provider configured ALONGSIDE base_url means the user wants that + # provider's request personality on an explicit endpoint. This + # short-circuit runs before the resolve_runtime_provider() call below, + # so without this block the runtime-carried request_overrides + # (extra_body / extra_headers, e.g. `thinking: {type: disabled}`) and + # max_output_tokens are silently dropped for subagents (#65035). + # Best-effort: the explicit endpoint worked before this change even + # when the provider can't resolve, so a resolution failure only skips + # the overrides — it must not fail the dispatch. + request_overrides = None + max_output_tokens = None + if configured_provider: + try: + from hermes_cli.runtime_provider import resolve_runtime_provider + + runtime = resolve_runtime_provider( + requested=configured_provider, target_model=configured_model + ) + request_overrides = dict(runtime.get("request_overrides") or {}) or None + max_output_tokens = runtime.get("max_output_tokens") + except Exception as exc: + logger.debug( + "delegation.base_url: runtime resolution for provider '%s' " + "failed; proceeding without request_overrides: %s", + configured_provider, + exc, + ) + return { "model": configured_model, "provider": provider, "base_url": configured_base_url, "api_key": api_key, "api_mode": api_mode, + "request_overrides": request_overrides, + "max_output_tokens": max_output_tokens, } if not configured_provider: