[A2A] RemoteA2aAgent(use_legacy=False): extension header written to state['http_kwargs'], ignored by a2a-sdk 1.x transports
Maintainer thường phản hồi trong vòng 5 ngày
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
- 84/100
Hướng nghiên cứu
Bắt đầu tại google/adk/a2a/agent/interceptors/new_integration_extension.py:43-51 và so sánh việc cập nhật context của nó với hành vi transport của a2a-sdk 1.x được mô tả trong issue. Giữ nguyên các tham số service hiện có, đồng thời đặt header A2A-Extensions vào nơi các transport HTTP và gRPC đọc nó, sau đó chạy bản tái hiện in-process được cung cấp và xác nhận rằng server đã ghi nhận header.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Describe the Bug
With RemoteA2aAgent(use_legacy=False), ADK installs a request interceptor that is supposed to negotiate the new A2A-ADK integration extension (the one documented as the a2a-extension / v2 A2aAgentExecutor). The interceptor (google/adk/a2a/agent/interceptors/new_integration_extension.py:43-51, same code on main @ eb1a002 and on 2.7.1–2.10.0) writes the A2A-Extensions header into:
params.client_call_context.state['http_kwargs']
But on a2a-sdk 1.x, HTTP transports build outbound headers exclusively from ClientCallContext.service_parameters (a2a/client/transports/http_helpers.py:46-47 in 1.1.2):
if context and context.service_parameters:
http_kwargs['headers'] = context.service_parameters.copy()
Nothing in a2a-sdk 1.x reads client_call_context.state['http_kwargs'], so the extension header never reaches the server. The server therefore keeps running the legacy executor even though the client asked for the new integration. (The gRPC transport likewise reads service_parameters, a2a/client/transports/grpc.py:304-305.)
For reference, a2a-sdk 1.x's own auth interceptor writes Authorization into context.service_parameters (a2a/client/auth/interceptor.py:47-84), which is the convention downstream users follow too.
Steps to Reproduce
Self-contained, in-process, no network or credentials:
import asyncio
import httpx
from google.adk.a2a.utils.agent_to_a2a import to_a2a
from google.adk.agents import LlmAgent
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
from google.adk.models.base_llm import BaseLlm
from google.adk.models.llm_response import LlmResponse
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
CAPTURED: dict[str, str] = {}
class CaptureMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
if scope["type"] == "http":
headers = {k.decode().lower(): v.decode() for k, v in scope["headers"]}
if "a2a-extensions" in headers:
CAPTURED[headers["a2a-extensions"]] = headers["a2a-extensions"]
await self.app(scope, receive, send)
class Scripted(BaseLlm):
async def generate_content_async(self, req, stream=False):
yield LlmResponse(content=types.Content(role="model", parts=[types.Part(text="ok")]))
async def main():
server = to_a2a(LlmAgent(name="remote", model=Scripted(model="x"), instruction="x"))
async with server.router.lifespan_context(server):
remote = RemoteA2aAgent(
name="remote",
agent_card="http://localhost:8000/.well-known/agent-card.json",
use_legacy=False,
httpx_client=httpx.AsyncClient(
transport=httpx.ASGITransport(app=CaptureMiddleware(server))
),
)
runner = Runner(
app_name="caller",
agent=remote,
session_service=InMemorySessionService(),
auto_create_session=True,
)
async for _ in runner.run_async(
user_id="u", session_id="s",
new_message=types.Content(role="user", parts=[types.Part(text="hi")]),
):
pass
print("A2A-Extensions seen by server:", CAPTURED or "NOT RECEIVED")
asyncio.run(main())
Observed Behavior
A2A-Extensions seen by server: NOT RECEIVED
Adding a custom request_interceptors entry that puts the header into params.client_call_context.service_parameters instead makes the server receive it, which confirms the interceptor pipeline runs and the failure is specifically the state['http_kwargs'] write target.
Expected Behavior
RemoteA2aAgent(use_legacy=False) causes the A2A-Extensions: https://google.github.io/adk-docs/a2a/a2a-extension/ header to actually reach the remote server on a2a-sdk 1.x.
Environment Details
- google-adk: 2.7.1 (pinned in my project); the interceptor source hasn't been updated since
- a2a-sdk: 1.1.2, 1.1.5 also affected
- Python: 3.14
- OS: macOS 15
Impact / Suggested direction
Impact is silent: no error, the server just keeps legacy behavior. This specifically blocks use_legacy=False from doing anything on the 1.x client stack, and it masks verification of the v2-executor bugs tracked in #7247 / #6343 (their suggested workaround of activating the v2 path via use_legacy=False is a no-op on a2a-sdk 1.x).
Suggested fix: in new_integration_extension.py, write the header into params.client_call_context.service_parameters (initializing ClientCallContext if absent) and merge with any existing mapping.
- Ngôn ngữ chính
- Python
- Star
- 21.6k
- Fork
- 4k
- Merge trung bình
- 12 giờ 6 phút
- Pull request đã merge (30 ngày)
- 4
Chuẩn bị môi trường
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của google/adk-python
-
Redis append_event stamps last_update_time with the wall clock, not the event timestampCó thể đã có người làm @sanketpatil06 đã nhận 1 ngày trước. Đang mởservices
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
google/adk-python#7292 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 5 ngày
-
GoogleOidcVerifier treats string "false" as a verified email claimCó thể đã có người làm @surajksharma07 đã nhận 1 ngày trước. Đang mởcore
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
google/adk-python#7289 · 5 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 5 ngày
-
RestApiTool raises uncaught KeyError when a required path param is omittedCó thể đã có người làm @llalitkumarrr đã nhận 1 ngày trước. Đang mởtools
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
google/adk-python#7282 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 5 ngày
-
CredentialsManager should also extract scopes when populating auth schemesCó thể đã có người làm @sanketpatil06 đã nhận 4 ngày trước. Đang mởcore needs review
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
google/adk-python#7266 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 5 ngày
-
OAuth2 Discovery method fails because FastMCP with GoogleProvider (OAuth) returns issuerUrl with trailing slashCó thể đã có người làm @sanketpatil06 đã nhận 4 ngày trước. Đang mởmcp
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
google/adk-python#7265 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 5 ngày
Tất cả issue của google/adk-python
Issue tương tự
-
[Bug] @deck.gl/arcgis dist import resolves to unpublished @deck.gl/core source path (9.3.11, 9.4.0)Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
workflow: a tick's dispatch counts as 'only this step', and no review self-grants a round unattendedĐang mởworkflow
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
kristofdegrave/homeassistant-smart-charging#1505 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
New Submission: TropWATERĐang mởmetadata submission
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
-
Wrongly named dashboard variableĐang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
canonical/content-cache-operator#163 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[submission]Đang mởsubmission
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 65/100
leanprover/lean-eval-submissions#1852 ·
Maintainer thường phản hồi trong vòng 1 ngày