Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
4146ab7
fix: preserve URL fragment as query params in _prepare_request_params
saiprasanth-git Mar 12, 2026
b899c49
test: add test for fragment key=value extraction in _prepare_request_…
saiprasanth-git Mar 12, 2026
e8eca1b
fix: parse URL fragment key=value pairs into query params
saiprasanth-git Mar 12, 2026
9ba41af
test: add coverage for fragment params being extracted as query params
saiprasanth-git Mar 12, 2026
71e49d5
fix: remove redundant outer if guard causing mypy syntax error
saiprasanth-git Mar 12, 2026
36edc8f
feat(tools): add tuple type support in function parameter schema parsing
saiprasanth-git Mar 13, 2026
b27c685
fix: correct indentation of tuple type handler block
saiprasanth-git Mar 13, 2026
7f8c3f1
fix: correct tuple block indentation to 4-space level per Gemini review
saiprasanth-git Mar 13, 2026
209ecbb
Merge branch 'main' into feat/tuple-type-support
saiprasanth-git Mar 13, 2026
64f0363
Merge branch 'main' into feat/tuple-type-support
rohityan Mar 13, 2026
a12430e
fix: dedent default-value check in tuple block to match list block level
saiprasanth-git Mar 13, 2026
cf16360
style: fix pyink formatting in test_rest_api_tool.py
saiprasanth-git Mar 13, 2026
29a2343
style: fix pyink formatting in rest_api_tool.py
saiprasanth-git Mar 13, 2026
565598e
Merge branch 'main' into feat/tuple-type-support
saiprasanth-git Mar 13, 2026
c9a5d5e
fix: correct tuple block indentation and pyink formatting
saiprasanth-git Mar 13, 2026
e9dd5d5
Merge branch 'main' into feat/tuple-type-support
saiprasanth-git Mar 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/google/adk/tools/_function_parameter_parse_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
float: types.Type.NUMBER,
bool: types.Type.BOOLEAN,
list: types.Type.ARRAY,
dict: types.Type.OBJECT,
dict: types.Type.OBJECT,
tuple: types.Type.ARRAY,
None: types.Type.NULL,
# TODO requested google GenAI SDK to add a Type.ANY and do the mapping on
# their side, once new enum is added, replace the below one with
Expand Down Expand Up @@ -330,6 +331,29 @@ def _parse_schema_from_parameter(
schema.default = param.default
_raise_if_schema_unsupported(variant, schema)
return schema
if origin is tuple:
schema.type = types.Type.ARRAY
if args:
if len(args) == 2 and args[1] is Ellipsis:
# variable-length tuple, e.g. tuple[int, ...]
schema.items = _parse_schema_from_parameter(
variant,
inspect.Parameter(
'item',
inspect.Parameter.POSITIONAL_OR_KEYWORD,
annotation=args[0],
),
func_name,
)

if param.default is not inspect.Parameter.empty:
if not _is_default_value_compatible(
param.default, param.annotation
):
raise ValueError(default_value_error_msg)
schema.default = param.default
_raise_if_schema_unsupported(variant, schema)
return schema
if origin is Union:
schema.any_of = []
schema.type = types.Type.OBJECT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,10 +381,14 @@ def _prepare_request_params(
# Move query params embedded in the path into query_params, since httpx
# replaces (rather than merges) the URL query string when `params` is set.
parsed_url = urlparse(url)
if parsed_url.query or parsed_url.fragment:
for key, values in parse_qs(parsed_url.query).items():
query_params.setdefault(key, values[0] if len(values) == 1 else values)
url = urlunparse(parsed_url._replace(query="", fragment=""))
for part in (parsed_url.query, parsed_url.fragment):
if part:
for key, values in parse_qs(part).items():
query_params.setdefault(
key, values[0] if len(values) == 1 else values
)
# URL without query and fragment
url = urlunparse(parsed_url._replace(query="", fragment=""))

# Construct body
body_kwargs: Dict[str, Any] = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1423,6 +1423,47 @@ def test_prepare_request_params_plain_url_unchanged(
request_params = tool._prepare_request_params([], {})

assert request_params["url"] == "https://example.com/test"
def test_prepare_request_params_fragment_params_become_query_params(
self, sample_auth_credential, sample_auth_scheme
):
# When the ApplicationIntegrationToolset builds an endpoint URL, it sometimes
# puts params in the fragment (e.g. #triggerId=my_trigger). Without this fix
# those params were silently dropped and the API returned a 400 error.
# See: https://github.com/google/adk-python/issues/4598
integration_endpoint = OperationEndpoint(
base_url="https://integrations.googleapis.com",
path=(
"/v2/projects/demo/locations/us-central1"
"/integrations/MyFlow:execute"
"?triggerId=api_trigger/MyFlow"
"#httpMethod=POST"
),
method="POST",
)
op = Operation(operationId="run_integration")
tool = RestApiTool(
name="run_integration",
description="Runs a Google Cloud integration flow",
endpoint=integration_endpoint,
operation=op,
auth_credential=sample_auth_credential,
auth_scheme=sample_auth_scheme,
)

result = tool._prepare_request_params([], {})

# Both the query string and fragment params should land in query params
assert result["params"]["triggerId"] == "api_trigger/MyFlow"
assert result["params"]["httpMethod"] == "POST"

# The final URL should be clean — no leftover ? or #
assert "?" not in result["url"]
assert "#" not in result["url"]
assert result["url"] == (
"https://integrations.googleapis.com"
"/v2/projects/demo/locations/us-central1"
"/integrations/MyFlow:execute"
)


def test_snake_to_lower_camel():
Expand Down