OpenAPI parser doesn't let an operation parameter override the path-level one: the model is asked for the same path value twice, and the wrong one is sent

Aperta Adatta ai principianti
#7,205 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
86/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
openapi, python
Ambito
api

Direzione di ricerca

Inizia da OpenApiSpecParser._collect_operations() ed esegui la riproduzione fornita con httpx.MockTransport per osservare gli argomenti duplicati e l’URL della richiesta. Esamina la gestione dei parametri di OperationParser e il test della PR segnalata, quindi conferma che un parametro a livello di operazione venga usato una sola volta, mentre i parametri con lo stesso nome in altre posizioni continuino a funzionare.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

🔴 Required Information

Describe the Bug:

OpenApiSpecParser._collect_operations() appends path-level parameters to every operation's own parameters without checking for overrides:

operation_dict["parameters"] = operation_dict.get("parameters", []) + path_item.get("parameters", [])

The OpenAPI 3 Path Item Object says an operation-level parameter with the same name and in overrides the path-level one. Declaring the shared path parameter once at the path level and refining it on an operation (a more specific description, pattern or enum) is common. ADK keeps both, OperationParser de-duplicates the names, and the tool then asks the model for the same URL segment twice as two required arguments: account_id and account_id_0. When the model fills both, both map to the same {accountId} placeholder and the path-level value wins, so the operation's own, more specific parameter is silently dropped from the request.

Steps to Reproduce:

  1. pip install google-adk (reproduced on main @ 3f4bb8fa).
  2. Run the script below: an OpenAPIToolset whose /accounts/{accountId} path declares accountId and whose GET overrides it. HTTP goes to an httpx.MockTransport.

Expected Behavior:
One required argument, account_id, described by the operation-level parameter; {"account_id": "ACC-123"} requests /accounts/ACC-123.

Observed Behavior:

model is asked for: ['account_id', 'account_id_0']
args: {'account_id': 'ACC-123', 'account_id_0': 'ACC-999'} -> request: https://crm.example.com/accounts/ACC-999

Environment Details:

  • ADK Library Version (pip show google-adk): main @ 3f4bb8fa
  • Desktop OS: Windows 11
  • Python Version (python -V): 3.12.10

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: N/A (tool declaration and request building)

🟡 Optional Information

Minimal Reproduction Code:

import asyncio

import httpx
from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_toolset import OpenAPIToolset

spec = {
    "openapi": "3.0.0",
    "info": {"title": "CRM", "version": "1"},
    "servers": [{"url": "https://crm.example.com"}],
    "paths": {"/accounts/{accountId}": {
        "parameters": [{"name": "accountId", "in": "path", "required": True,
                        "schema": {"type": "string"}, "description": "Shared account id"}],
        "get": {
            "operationId": "getAccount",
            "parameters": [{"name": "accountId", "in": "path", "required": True,
                            "schema": {"type": "string", "pattern": "^ACC-[0-9]+$"},
                            "description": "Account id, e.g. ACC-123"}],
            "responses": {"200": {"description": "ok"}},
        },
    }},
}
sent = []
toolset = OpenAPIToolset(
    spec_dict=spec,
    httpx_client_factory=lambda: httpx.AsyncClient(transport=httpx.MockTransport(
        lambda r: sent.append(str(r.url)) or httpx.Response(200, json={}))),
)


async def main():
  tool = (await toolset.get_tools())[0]
  schema = tool._get_declaration().parameters_json_schema
  print("model is asked for:", schema["required"])
  args = {"account_id": "ACC-123"}
  if "account_id_0" in schema["properties"]:
    args["account_id_0"] = "ACC-999"  # a model filling both required fields
  await tool.run_async(args=args, tool_context=None)
  print("args:", args, "-> request:", sent[0])


asyncio.run(main())

Suggested fix: when merging, skip path-level parameters whose (name, in) the operation already declares. Parameters with the same name but a different location are still merged. I have a PR ready with a test.

How often has this issue occurred?:

  • Always (100%): any spec that overrides a path-level parameter on an operation.
Lingua principale
Python
Stelle
21.6k
Fork
4k
Merge medio
13h 49m
PR unite (30g)
10

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di google/adk-python

Tutte le issue di google/adk-python

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.