diff --git a/src/google/adk/agents/llm_agent.py b/src/google/adk/agents/llm_agent.py index cd14cefb3b..c8133f8ba0 100644 --- a/src/google/adk/agents/llm_agent.py +++ b/src/google/adk/agents/llm_agent.py @@ -943,6 +943,24 @@ def __maybe_save_output_to_state(self, event: Event): @model_validator(mode='after') def __model_validator_after(self) -> LlmAgent: + generate_content_config = self.generate_content_config + if ( + self.tools + and generate_content_config + and generate_content_config.response_mime_type == 'application/json' + ): + tool_config = generate_content_config.tool_config + function_calling_config = ( + tool_config.function_calling_config if tool_config else None + ) + mode = function_calling_config.mode if function_calling_config else None + mode_value = getattr(mode, 'value', mode) + if mode_value is None or str(mode_value).upper() != 'NONE': + raise ValueError( + '`response_mime_type="application/json"` is incompatible with ' + 'function calling. Set `function_calling_config.mode` to "NONE" ' + 'or remove tools from the agent.' + ) return self @field_validator('generate_content_config', mode='after') diff --git a/tests/unittests/agents/test_llm_agent_fields.py b/tests/unittests/agents/test_llm_agent_fields.py index 92f3c34e49..9fcb2b5be7 100644 --- a/tests/unittests/agents/test_llm_agent_fields.py +++ b/tests/unittests/agents/test_llm_agent_fields.py @@ -250,6 +250,51 @@ def _a_tool(): ) +@pytest.mark.parametrize('mode', ['AUTO', 'ANY', None]) +def test_generate_content_config_json_response_with_function_calling_throws( + mode, +): + def _a_tool(): + pass + + function_calling_config = ( + types.FunctionCallingConfig(mode=mode) if mode else None + ) + tool_config = ( + types.ToolConfig(function_calling_config=function_calling_config) + if function_calling_config + else None + ) + + with pytest.raises(ValueError, match='response_mime_type="application/json"'): + LlmAgent( + name='test_agent', + tools=[_a_tool], + generate_content_config=types.GenerateContentConfig( + response_mime_type='application/json', + tool_config=tool_config, + ), + ) + + +def test_generate_content_config_json_response_with_function_calling_none(): + def _a_tool(): + pass + + agent = LlmAgent( + name='test_agent', + tools=[_a_tool], + generate_content_config=types.GenerateContentConfig( + response_mime_type='application/json', + tool_config=types.ToolConfig( + function_calling_config=types.FunctionCallingConfig(mode='NONE') + ), + ), + ) + + assert agent.generate_content_config.response_mime_type == 'application/json' + + def test_before_model_callback(): def _before_model_callback( callback_context: CallbackContext,