Skip to content
2 changes: 2 additions & 0 deletions google/genai/_extra_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,8 @@ def should_disable_afc(
return True

# Default to enable AFC if not specified.
if not config_model.tools or len(config_model.tools) == 0:
return True
if (
not config_model.automatic_function_calling
or config_model.automatic_function_calling.disable is None
Expand Down
15 changes: 12 additions & 3 deletions google/genai/tests/afc/test_should_disable_afc.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_config_is_none():


def test_afc_config_unset():
assert should_disable_afc(types.GenerateContentConfig()) is False
assert should_disable_afc(types.GenerateContentConfig()) is True


def test_afc_enable_unset_max_0():
Expand Down Expand Up @@ -71,6 +71,9 @@ def test_afc_enable_unset_max_1():
automatic_function_calling=types.AutomaticFunctionCallingConfig(
maximum_remote_calls=1,
),
tools=[types.Tool(function_declarations=[
types.FunctionDeclaration(name='tool_name')
])],
)
)
is False
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change the test cases as much as possible instead of changing the assertion. By changing only the assertion, the tests are not validating the actual test case. E.g. in this test case, pass in a tool and keep the assertion.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! I've updated the test cases as suggested.

Instead of changing the assertions, I've added tools parameter to the three failing tests:

test_afc_enable_unset_max_1 , test_afc_enable_unset_max_1_0 , test_afc_enable_true_max_unset

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! In addition to that would you mind adding an example code that reproduce the bug? AFC logic may seem simple in the first glance but there are corner cases. Would be idea if you could add an example to regenerate the issue or refer me to an existing one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My pleasure , Here's the minimal that can reproduce the error mentioned by @npkanaka :

from google import genai
from google.genai import types

client = genai.Client(api_key="#####")
MODEL = "gemini-2.5-pro"

tools = [
    types.Tool(
        function_declarations=[
            types.FunctionDeclaration(
                name="noop", description="noop", parameters={"type": "object", "properties": {}}
            )
        ]
    )
]

# Call 1: with tools, force function_call
resp1 = client.models.generate_content(
    model=MODEL,
    contents="Call the noop tool. Do not answer in text. Only return a function call to noop with empty args.",
    config=types.GenerateContentConfig(tools=tools),
)
print("Call 1:", resp1)

# Call 2: tools=None, fresh
resp2 = client.models.generate_content(
    model=MODEL,
    contents="hello from call without tools",
    config=types.GenerateContentConfig(tools=None),
)
print("Call 2:", resp2)

# Call 3: history includes prior function_call, tools=None
history = [
    types.Content(
        role="user",
        parts=[types.Part(text="Call the noop tool. Do not answer in text. Only return a function call to noop with empty args.")],
    ),
    resp1.candidates[0].content,  # contains function_call
    types.Content(
        role="user",
        parts=[types.Part(text="Now answer normally (no tools). Just say hi in plain text.")],
    ),
]
resp3 = client.models.generate_content(
    model=MODEL,
    contents=history,
    config=types.GenerateContentConfig(tools=None),
)
print("Call 3:", resp3)

Although, as you said there can be corner cases, so kindly let me know what is up to you. I'd be happy to implement that under your mentorship.

Expand All @@ -80,7 +83,10 @@ def test_afc_enable_unset_max_1():
def test_afc_enable_unset_max_1_0():
assert (
should_disable_afc(
{'automatic_function_calling': {'maximum_remote_calls': 1.0}}
{
'automatic_function_calling': {'maximum_remote_calls': 1.0},
'tools': [{'function_declarations': [{'name': 'tool_name'}]}]
}
)
is False
)
Expand Down Expand Up @@ -157,6 +163,9 @@ def test_afc_enable_true_max_unset():
automatic_function_calling=types.AutomaticFunctionCallingConfig(
disable=False,
),
tools=[types.Tool(function_declarations=[
types.FunctionDeclaration(name='tool_name')
])],
)
)
is False
Expand Down Expand Up @@ -210,5 +219,5 @@ def test_afc_enable_true_max_1():
),
)
)
is False
is True
)