diff --git a/google/genai/_extra_utils.py b/google/genai/_extra_utils.py index 98a75748e..f124a9435 100644 --- a/google/genai/_extra_utils.py +++ b/google/genai/_extra_utils.py @@ -472,10 +472,13 @@ def should_disable_afc( not config_model.automatic_function_calling or config_model.automatic_function_calling.disable is None ): - return False + afc_disabled = False + else: + afc_disabled = bool(config_model.automatic_function_calling.disable) if ( - config_model.automatic_function_calling.disable + afc_disabled + and config_model.automatic_function_calling and config_model.automatic_function_calling.maximum_remote_calls is not None # exclude the case where max_remote_calls is set to 10 by default. @@ -496,7 +499,23 @@ def should_disable_afc( ' `automatic_function_calling.maximum_remote_calls` unset.' ) - return config_model.automatic_function_calling.disable + if ( + not afc_disabled + and config_model.tool_config + and config_model.tool_config.function_calling_config + and config_model.tool_config.function_calling_config.mode + in (types.FunctionCallingConfigMode.ANY, 'ANY', 'any') + ): + logger.warning( + '`tool_config.function_calling_config.mode` is set to `ANY` with' + ' automatic function calling enabled. The model is forced to call a' + ' function on every turn and cannot return a text response, which will' + ' exhaust `maximum_remote_calls` and return empty text. If you want the' + ' model to answer with text, use mode=`AUTO`, or disable automatic' + ' function calling to manually orchestrate tool calls.' + ) + + return afc_disabled def get_max_remote_calls_afc( diff --git a/google/genai/tests/afc/test_should_disable_afc.py b/google/genai/tests/afc/test_should_disable_afc.py index bdd78cd42..aec808dff 100644 --- a/google/genai/tests/afc/test_should_disable_afc.py +++ b/google/genai/tests/afc/test_should_disable_afc.py @@ -16,6 +16,7 @@ """Tests for should_disable_afc.""" +import logging import pytest from .. import pytest_helper from ... import types @@ -213,3 +214,57 @@ def test_afc_enable_true_max_1(): ) is False ) + + +def test_afc_mode_any_warns(caplog): + with caplog.at_level(logging.WARNING, logger='google_genai.models'): + config = types.GenerateContentConfig( + tool_config=types.ToolConfig( + function_calling_config=types.FunctionCallingConfig(mode='ANY') + ) + ) + result = should_disable_afc(config) + assert result is False + assert len(caplog.records) == 1 + assert '`tool_config.function_calling_config.mode` is set to `ANY`' in caplog.records[0].message + + +def test_afc_mode_any_dict_warns(caplog): + with caplog.at_level(logging.WARNING, logger='google_genai.models'): + config = { + 'tool_config': { + 'function_calling_config': {'mode': 'ANY'} + } + } + result = should_disable_afc(config) + assert result is False + assert len(caplog.records) == 1 + assert '`tool_config.function_calling_config.mode` is set to `ANY`' in caplog.records[0].message + + +def test_afc_mode_any_disabled_no_warn(caplog): + with caplog.at_level(logging.WARNING, logger='google_genai.models'): + config = types.GenerateContentConfig( + tool_config=types.ToolConfig( + function_calling_config=types.FunctionCallingConfig(mode='ANY') + ), + automatic_function_calling=types.AutomaticFunctionCallingConfig( + disable=True + ), + ) + result = should_disable_afc(config) + assert result is True + assert len(caplog.records) == 0 + + +def test_afc_mode_auto_no_warn(caplog): + with caplog.at_level(logging.WARNING, logger='google_genai.models'): + config = types.GenerateContentConfig( + tool_config=types.ToolConfig( + function_calling_config=types.FunctionCallingConfig(mode='AUTO') + ) + ) + result = should_disable_afc(config) + assert result is False + assert len(caplog.records) == 0 +