Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

[A2A] RemoteA2aAgent(use_legacy=False): extension header written to state['http_kwargs'], ignored by a2a-sdk 1.x transports

未关闭 适合新手
#7,334 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 5 天内回复

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
84/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
python
领域
api

调研方向

从 google/adk/a2a/agent/interceptors/new_integration_extension.py:43-51 开始,并将其上下文更新与 issue 中描述的 a2a-sdk 1.x 传输行为进行比较。保留现有的服务参数,同时将 A2A-Extensions 标头放置在 HTTP 和 gRPC 传输读取它的位置,然后运行提供的进程内复现,并确认服务器捕获了该标头。

由索引模型根据 Issue 内容生成。

描述

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.

主要语言
Python
星标
21.6k
派生
4k
平均合并
12 小时 6 分钟
30 天内合并 PR
4

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

google/adk-python 的其他 Issue

查看 google/adk-python 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。