4848if TYPE_CHECKING :
4949 from .llm_request import LlmRequest
5050
51- __all__ = ["AnthropicLlm" , "Claude" ]
51+ __all__ = ["AnthropicGenerateContentConfig" , " AnthropicLlm" , "Claude" ]
5252
5353logger = logging .getLogger ("google_adk." + __name__ )
5454
55+ _THINKING_LEVEL_TO_EFFORT : dict [types .ThinkingLevel , str ] = {
56+ types .ThinkingLevel .MINIMAL : "low" ,
57+ types .ThinkingLevel .LOW : "medium" ,
58+ types .ThinkingLevel .MEDIUM : "high" ,
59+ types .ThinkingLevel .HIGH : "xhigh" ,
60+ }
61+
62+
63+ # google-genai ships no py.typed marker / complete stubs, so mypy resolves
64+ # GenerateContentConfig as Any and flags the subclass. Composition would lose
65+ # isinstance() checks and Pydantic field inheritance; a local stub file would
66+ # need maintenance on every google-genai release. Suppressing narrowly is the
67+ # least-bad option.
68+ class AnthropicGenerateContentConfig (types .GenerateContentConfig ): # type: ignore[misc]
69+ """GenerateContentConfig extended with Anthropic-specific parameters."""
70+
71+ effort : Optional [Literal ["low" , "medium" , "high" , "xhigh" , "max" ]] = None
72+
73+
74+ def _build_effort_param (
75+ config : types .GenerateContentConfig ,
76+ ) -> Union [anthropic_types .OutputConfigParam , NotGiven ]:
77+ """Maps ADK config to Anthropic output_config.effort, or NOT_GIVEN."""
78+ if isinstance (config , AnthropicGenerateContentConfig ) and config .effort :
79+ return anthropic_types .OutputConfigParam (effort = config .effort )
80+ if (
81+ config .thinking_config
82+ and config .thinking_config .thinking_level
83+ and config .thinking_config .thinking_level in _THINKING_LEVEL_TO_EFFORT
84+ ):
85+ effort = _THINKING_LEVEL_TO_EFFORT [config .thinking_config .thinking_level ]
86+ return anthropic_types .OutputConfigParam (effort = effort )
87+ return NOT_GIVEN
88+
5589
5690@dataclasses .dataclass
5791class _ToolUseAccumulator :
@@ -96,11 +130,9 @@ def _build_anthropic_thinking_param(
96130 thinking_budget = config .thinking_config .thinking_budget
97131
98132 if thinking_budget is None :
99- raise ValueError (
100- "thinking_budget must be set explicitly when ThinkingConfig is"
101- " provided for Anthropic models. Use 0 to disable thinking, or a"
102- " positive integer (>= 1024) for the token budget."
103- )
133+ # thinking_level (effort) is being used instead of budget; thinking param
134+ # is not needed — output_config handles it.
135+ return NOT_GIVEN
104136
105137 if thinking_budget == 0 :
106138 return anthropic_types .ThinkingConfigDisabledParam (type = "disabled" )
@@ -494,6 +526,8 @@ async def generate_content_async(
494526 thinking = _build_anthropic_thinking_param (llm_request .config )
495527
496528 config = llm_request .config
529+ effort = _build_effort_param (config )
530+ use_sampling = effort is NOT_GIVEN
497531 if not stream :
498532 message = await self ._anthropic_client .messages .create (
499533 model = model_to_use ,
@@ -503,9 +537,10 @@ async def generate_content_async(
503537 tool_choice = tool_choice ,
504538 max_tokens = self .max_tokens ,
505539 thinking = thinking ,
506- temperature = config .temperature if config .temperature is not None else NOT_GIVEN ,
507- top_p = config .top_p if config .top_p is not None else NOT_GIVEN ,
508- top_k = config .top_k if config .top_k is not None else NOT_GIVEN ,
540+ output_config = effort ,
541+ temperature = config .temperature if use_sampling and config .temperature is not None else NOT_GIVEN ,
542+ top_p = config .top_p if use_sampling and config .top_p is not None else NOT_GIVEN ,
543+ top_k = config .top_k if use_sampling and config .top_k is not None else NOT_GIVEN ,
509544 stop_sequences = config .stop_sequences if config .stop_sequences is not None else NOT_GIVEN ,
510545 )
511546 yield message_to_generate_content_response (message )
@@ -534,16 +569,19 @@ async def _generate_content_streaming(
534569 """
535570 model_to_use = self ._resolve_model_name (llm_request .model )
536571 config = llm_request .config
572+ effort = _build_effort_param (config )
573+ use_sampling = effort is NOT_GIVEN
537574 raw_stream = await self ._anthropic_client .messages .create (
538575 model = model_to_use ,
539576 system = config .system_instruction ,
540577 messages = messages ,
541578 tools = tools ,
542579 tool_choice = tool_choice ,
543580 max_tokens = self .max_tokens ,
544- temperature = config .temperature if config .temperature is not None else NOT_GIVEN ,
545- top_p = config .top_p if config .top_p is not None else NOT_GIVEN ,
546- top_k = config .top_k if config .top_k is not None else NOT_GIVEN ,
581+ output_config = effort ,
582+ temperature = config .temperature if use_sampling and config .temperature is not None else NOT_GIVEN ,
583+ top_p = config .top_p if use_sampling and config .top_p is not None else NOT_GIVEN ,
584+ top_k = config .top_k if use_sampling and config .top_k is not None else NOT_GIVEN ,
547585 stop_sequences = config .stop_sequences if config .stop_sequences is not None else NOT_GIVEN ,
548586 stream = True ,
549587 thinking = thinking ,
0 commit comments