fix(code): route Anthropic effort through output config (#4446)

Fixes dcode `/effort` for Anthropic models by writing effort to
`output_config` instead of the unsupported top-level kwarg.

Made by [Open
SWE](https://openswe.vercel.app/agents/019f2412-222a-7370-9e96-fcc97867720c)

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
This commit is contained in:
Johannes du Plessis
2026-07-02 12:00:59 -07:00
committed by GitHub
parent ef591e7a86
commit 1e8ed81940
2 changed files with 63 additions and 18 deletions
+36 -13
View File
@@ -113,7 +113,14 @@ See https://docs.fireworks.ai/guides/reasoning.
"""
_REASONING_KEYS: frozenset[str] = frozenset(
{"effort", "reasoning", "reasoning_effort", "thinking", "thinking_level"}
{
"effort",
"output_config",
"reasoning",
"reasoning_effort",
"thinking",
"thinking_level",
}
)
"""Runtime config keys that may already carry provider reasoning settings."""
@@ -227,7 +234,7 @@ def _anthropic_model_params(effort: str) -> dict[str, Any]:
"""Return Anthropic reasoning params for an effort label."""
return {
"thinking": {"type": "adaptive", "display": "summarized"},
"effort": effort,
"output_config": {"effort": effort},
}
@@ -237,12 +244,21 @@ def _anthropic_current_effort(model_params: dict[str, Any]) -> str | None:
Returns:
The configured effort label, or `None` when unset.
"""
value = model_params.get("effort")
if value is not None and not isinstance(value, str):
output_config = model_params.get("output_config")
if isinstance(output_config, dict):
value = output_config.get("effort")
if value is not None and not isinstance(value, str):
logger.warning(
"Ignoring non-str Anthropic output_config.effort of type %s",
type(value).__name__,
)
return value if isinstance(value, str) else None
if output_config is not None:
logger.warning(
"Ignoring non-str Anthropic effort of type %s", type(value).__name__
"Ignoring Anthropic output_config params of unexpected type %s",
type(output_config).__name__,
)
return value if isinstance(value, str) else None
return None
def _google_supported_efforts(_model: str) -> tuple[EffortLabel, ...]:
@@ -506,15 +522,15 @@ def merge_effort_model_params(
effort_params: Params returned by `model_params_for_effort`.
Returns:
A new merged dictionary preserving unrelated nested `model_kwargs`.
A new merged dictionary preserving unrelated nested config objects.
"""
merged = dict(existing) if existing else {}
for key, value in effort_params.items():
if key == "model_kwargs" and isinstance(value, dict):
current = merged.get("model_kwargs")
if key in {"model_kwargs", "output_config"} and isinstance(value, dict):
current = merged.get(key)
base = dict(current) if isinstance(current, dict) else {}
base.update(value)
merged["model_kwargs"] = base
merged[key] = base
else:
merged[key] = value
return merged
@@ -533,14 +549,14 @@ def without_effort_model_params(
"""
if not existing:
return None
# Exclude `model_kwargs` from the comprehension and rebuild it below.
# Leaving it here would retain a stale `reasoning_effort` when the cleaned
# Exclude nested config objects from the comprehension and rebuild them below.
# Leaving them here would retain stale nested effort keys when the cleaned
# nested dict ends up empty — the empty-check would then skip the overwrite
# and the original (still-populated) copy would survive.
cleaned = {
key: (dict(value) if isinstance(value, dict) else value)
for key, value in existing.items()
if key not in _REASONING_KEYS and key != "model_kwargs"
if key not in _REASONING_KEYS and key not in {"model_kwargs", "output_config"}
}
kwargs = existing.get("model_kwargs")
if isinstance(kwargs, dict):
@@ -549,4 +565,11 @@ def without_effort_model_params(
cleaned["model_kwargs"] = model_kwargs
elif kwargs is not None:
cleaned["model_kwargs"] = kwargs
output_config = existing.get("output_config")
if isinstance(output_config, dict):
output_config_params = {k: v for k, v in output_config.items() if k != "effort"}
if output_config_params:
cleaned["output_config"] = output_config_params
elif output_config is not None:
cleaned["output_config"] = output_config
return cleaned or None
@@ -120,7 +120,7 @@ def test_model_params_for_effort_maps_provider_kwargs() -> None:
}
assert model_params_for_effort("anthropic:claude-opus-4-8", "xhigh") == {
"thinking": {"type": "adaptive", "display": "summarized"},
"effort": "xhigh",
"output_config": {"effort": "xhigh"},
}
assert model_params_for_effort("google_genai:gemini-3.5-flash", "low") == {
"thinking_level": "low"
@@ -130,6 +130,15 @@ def test_model_params_for_effort_maps_provider_kwargs() -> None:
) == {"model_kwargs": {"reasoning_effort": "max"}}
def test_current_effort_reads_anthropic_output_config() -> None:
assert (
current_effort_from_model_params(
"anthropic:claude-opus-4-8", {"output_config": {"effort": "low"}}
)
== "low"
)
def test_model_params_for_effort_rejects_unsupported_effort() -> None:
assert (
model_params_for_effort(
@@ -169,7 +178,8 @@ def test_merge_and_clear_effort_model_params_preserves_unrelated_params() -> Non
("openai:gpt-5.5", {"reasoning": "high"}),
# `reasoning.effort` present but not a str.
("openai:gpt-5.5", {"reasoning": {"effort": 5}}),
("anthropic:claude-opus-4-8", {"effort": 5}),
("anthropic:claude-opus-4-8", {"output_config": {"effort": 5}}),
("anthropic:claude-opus-4-8", {"output_config": "high"}),
("google_genai:gemini-3.5-flash", {"thinking_level": 5}),
(
"fireworks:accounts/fireworks/models/deepseek-v4-pro",
@@ -488,10 +498,22 @@ async def test_effort_selector_apply_failure_reports_error(
def test_without_effort_clears_anthropic_thinking_and_effort() -> None:
effort_params = model_params_for_effort("anthropic:claude-opus-4-8", "xhigh")
assert effort_params is not None
params = merge_effort_model_params({"temperature": 0.3}, effort_params)
assert params["effort"] == "xhigh"
format_config = {"type": "json_schema", "schema": {"type": "object"}}
params = merge_effort_model_params(
{"temperature": 0.3, "output_config": {"format": format_config}}, effort_params
)
assert params["output_config"] == {"format": format_config, "effort": "xhigh"}
assert "thinking" in params
assert without_effort_model_params(params) == {"temperature": 0.3}
assert without_effort_model_params(params) == {
"temperature": 0.3,
"output_config": {"format": format_config},
}
def test_without_effort_clears_legacy_anthropic_top_level_effort() -> None:
assert without_effort_model_params({"temperature": 0.3, "effort": "xhigh"}) == {
"temperature": 0.3
}
def test_without_effort_clears_google_thinking_level() -> None: