Skip to content

fix(openapi): disambiguate colliding truncated tool names - #7284

Open
chelsealong wants to merge 1 commit into
google:mainfrom
chelsealong:fix-openapi-tool-name-collision-7281
Open

chelsealong wants to merge 1 commit into
google:mainfrom
chelsealong:fix-openapi-tool-name-collision-7281

Conversation

@chelsealong

Copy link
Copy Markdown
Contributor

Problem

Fixes #7281.

Gemini limits function names to less than 64 characters, so
OperationParser.get_function_name() and RestApiTool.__init__ both hard
truncate the generated tool name to 60 characters. Codegen-style OpenAPI
specs routinely produce long operationIds that share a long common
prefix, so distinct operations can truncate to the exact same name.

When that happens in OpenAPIToolset._parse, every colliding operation
produces a RestApiTool with the same .name. LlmRequest.append_tools
still sends a FunctionDeclaration for each tool, but tools_dict keeps
only the last one registered, so the model gets duplicate declarations with
one name while every call is routed to the last-registered operation's
endpoint — the earlier endpoint(s) become silently unreachable.

Fix

OpenAPIToolset._parse builds every RestApiTool for a given spec in one
place and therefore has full visibility into the whole toolset. After a
tool's name is generated, check it against the names already produced for
this toolset; on a collision, append a short numeric suffix (_2, _3,
...) and truncate so the final name still fits in 60 characters.

This is a minimal, additive change scoped to OpenAPIToolset._parse — it
does not change get_function_name() or RestApiTool.__init__, which have
no visibility into sibling operations and can't dedupe on their own.

Test plan

Added test_openapi_toolset_disambiguates_colliding_truncated_names in
tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py,
using two operations whose operationIds share a 63-character prefix
(mirroring the issue's repro) and differ only after the 60-char truncation
point. It asserts the toolset produces two distinct tool names, each ≤60
chars, and that both original endpoints remain reachable via get_tool().

Confirmed the test fails without the fix (reverted just the source file
with git checkout HEAD~1 -- src/.../openapi_toolset.py and reran):

$ python -m pytest tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py::test_openapi_toolset_disambiguates_colliding_truncated_names -q
F
AssertionError: Tool names collided: ['list_all_company_organization_department_users_by_filter_cri', 'list_all_company_organization_department_users_by_filter_cri']
assert 2 == 1
1 failed in 1.00s

With the fix restored:

$ python -m pytest tests/unittests/tools/openapi_tool/ -q
342 passed, 38 warnings in 3.96s

pre-commit (ruff, isort, pyink, addlicense, codespell, etc.) was run
against the changed files; the only failing hook
(check-new-py-prefix, demanding a unit guide for
src/google/adk/memory/_sqlite_memory_service.py) is pre-existing and
unrelated to this change — that file was added in an earlier commit
(9625b06c) not touched here.

AI assistance disclosure

This change was authored with the assistance of Claude Code (Anthropic).

🤖 Generated with Claude Code

Operation IDs that share a >60-char prefix all truncate to the same
name in OpenAPIToolset._parse, so distinct operations collapse to one
RestApiTool and the model only sees the last-registered one; earlier
endpoints become unreachable. Append a stable numeric suffix when a
truncated name collides with one already generated for the toolset.

Fixes google#7281
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenAPI operationId truncated to 60 chars without uniqueness → shadowed RestApiTool names

2 participants