mirror of
https://github.com/langchain-ai/deepagents.git
synced 2026-07-21 17:25:26 -04:00
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:
committed by
GitHub
parent
ef591e7a86
commit
1e8ed81940
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user