googleapis / googleapis/python-genai
tool_config mode="ANY" with automatic function calling exhausts maximum_remote_calls and returns empty text, with no warning
- Dominant language
- Python
- Stars
- 4k
- Forks
- 1k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 40
Description
### Summary
`FunctionCallingConfig(mode="ANY")` combined with automatic function
calling produces a request that consumes the whole remote-call budget and
returns **empty text**, with no error and no warning. `mode="ANY"`
obliges the model to emit a function call on every turn; AFC dutifully
executes each one and asks again; the loop only ends when
`maximum_remote_calls` runs out, at which point there is no natural
language answer to return because the model was never permitted to give
one.
The combination is detectable at config time, and the library already
warns about two neighbouring misconfigurations — just not this one.
### Where
`google/genai/_extra_utils.py`, at `ccbc6c5`. Both existing guards live
in the same function:
```python
if (... maximum_remote_calls is not None
and int(...maximum_remote_calls) <= 0):
logger.warning(
'max_remote_calls in automatic_function_calling_config'
f' {...} is less than or equal to 0. Disabling automatic function'
' calling. Please set max_remote_calls to a positive integer.'
)
return True
if (config_model.automatic_function_calling.disable
and ...maximum_remote_calls is not None
and 'maximum_remote_calls' in ...model_fields_set
and int(...maximum_remote_calls) > 0):
logger.warning(
'`automatic_function_calling.disable` is set to `True`. And'
' `automatic_function_calling.maximum_remote_calls` is a positive'
' number ...'
)
```
There is no equivalent check for `tool_config.function_calling_config.mode
== ANY` while AFC is enabled.
### Why it matters
Every symptom points away from the cause. You get a successful response
object, `finish_reason` looks ordinary, and the text is empty — so the
natural conclusion is that the model failed to answer, or the prompt is
bad, or the tools are wrong. The actual cause is two settings that are
each individually reasonable and only interact badly together. The
`logger.info('Reached max remote calls...')` that does fire reads as
routine, because it is the same message a legitimately long tool chain
produces.
### Reproduce
```python
from google import genai
from google.genai import types
def get_weather(city: str) -> str:
return f"sunny in {city}"
client = genai.Client()
resp = client.models.generate_content(
model="gemini-2.5-flash",
contents="What is the weather in Paris?",
config=types.GenerateContentConfig(
tools=[get_weather],
tool_config=types.ToolConfig(
function_calling_config=types.FunctionCallingConfig(mode="ANY")
),
),
)
print(repr(resp.text)) # empty
```
### Suggested fix
A warning at config time, in the same place as the two existing ones —
something to the effect that `mode="ANY"` with automatic function calling
enabled will consume `maximum_remote_calls` and return no text, and that
`mode="AUTO"` is wanted if the model should be allowed to answer.
I am raising this as an issue rather than sending the patch because
whether you want a warning here is your call: `mode="ANY"` is a
legitimate setting, and someone driving the loop manually with AFC
disabled has a perfectly good reason to use it. If you would like the
warning, I am happy to send a PR.
### Environment
- google-genai: read from source at `ccbc6c5` (current `main`); latest
release at time of writing is v2.17.0
- Python: 3.13
Contributor guide
Assessment
This issue has not been assessed yet.