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

Đang mở Phù hợp với người mới
#7,205 1 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
86/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
openapi, python
Lĩnh vực
api

Hướng nghiên cứu

Bắt đầu từ OpenApiSpecParser._collect_operations() và chạy bản tái hiện được cung cấp bằng httpx.MockTransport để quan sát các đối số bị trùng lặp và URL của yêu cầu. Kiểm tra cách OperationParser xử lý tham số và bài kiểm thử PR được báo cáo, sau đó xác nhận rằng một tham số ở cấp operation được sử dụng một lần, trong khi các tham số cùng tên ở những vị trí khác vẫn hoạt động.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

🔴 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.
Ngôn ngữ chính
Python
Star
21.6k
Fork
4k
Merge trung bình
13 giờ 49 phút
Pull request đã merge (30 ngày)
10

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của google/adk-python

Tất cả issue của google/adk-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.