From 01fb46e2863d8b6f417c051ec03025796bd0a9b6 Mon Sep 17 00:00:00 2001 From: mkzung <103102868+mkzung@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:52:53 +0500 Subject: [PATCH] fix(a2a): don't claim sampling params the protocol has no field for get_supported_openai_params listed temperature, max_tokens and top_p. map_openai_params only ever reads stream, and transform_request builds a JSON-RPC body of just {message}, so the three values went nowhere. A2A has no sampling controls by design: SendMessageConfiguration is acceptedOutputModes, taskPushNotificationConfig, historyLength and returnImmediately, because the remote agent owns its own model config. Advertising them suppressed the UnsupportedParamsError and dropped them in silence. stream stays, it selects message/stream over message/send. --- litellm/llms/a2a/chat/transformation.py | 12 ++++++--- .../a2a/chat/test_a2a_chat_transformation.py | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/litellm/llms/a2a/chat/transformation.py b/litellm/llms/a2a/chat/transformation.py index be3b5fea601..9b81de9883f 100644 --- a/litellm/llms/a2a/chat/transformation.py +++ b/litellm/llms/a2a/chat/transformation.py @@ -133,12 +133,16 @@ class A2AConfig(BaseConfig): return api_base, api_key, headers def get_supported_openai_params(self, model: str) -> list[str]: - """Return list of supported OpenAI parameters""" + """Return list of supported OpenAI parameters. + + A2A has no sampling controls. SendMessageConfiguration carries + acceptedOutputModes, taskPushNotificationConfig, historyLength and + returnImmediately, nothing else, because the remote agent owns its own + model config. Only stream is real here: it picks message/stream over + message/send. + """ return [ "stream", - "temperature", - "max_tokens", - "top_p", ] def map_openai_params( diff --git a/tests/unit/llms/a2a/chat/test_a2a_chat_transformation.py b/tests/unit/llms/a2a/chat/test_a2a_chat_transformation.py index 76bce26e2e0..cb6fb8baae7 100644 --- a/tests/unit/llms/a2a/chat/test_a2a_chat_transformation.py +++ b/tests/unit/llms/a2a/chat/test_a2a_chat_transformation.py @@ -7,10 +7,12 @@ import httpx import pytest from pydantic import ValidationError +import litellm from litellm.llms.a2a.chat.streaming_iterator import A2AModelResponseIterator from litellm.llms.a2a.chat.transformation import A2AConfig from litellm.llms.a2a.common_utils import A2AError from litellm.types.utils import ModelResponse +from litellm.utils import get_optional_params def _raw_response(text: str) -> MagicMock: @@ -226,3 +228,28 @@ def test_transform_response_reports_an_undecodable_body_as_an_agent_error(): assert exc_info.value.status_code == 502 assert exc_info.value.message.startswith("Failed to parse A2A response: ") + + +def test_a2a_only_claims_stream(): + """A2A has no sampling params, so it must not advertise any. + + Claiming one suppresses the UnsupportedParamsError and the value is + dropped without telling the caller. + """ + supported = A2AConfig().get_supported_openai_params("test-agent") + assert "stream" in supported + for param in ("temperature", "max_tokens", "top_p"): + assert param not in supported + + +@pytest.mark.parametrize("param,value", [("temperature", 0.5), ("max_tokens", 100), ("top_p", 0.9)]) +def test_a2a_sampling_params_raise(param, value): + """Sampling params should surface an error rather than vanish.""" + with pytest.raises(litellm.UnsupportedParamsError): + get_optional_params(model="test-agent", custom_llm_provider="a2a", **{param: value}) + + +def test_a2a_stream_still_maps(): + """stream is the one real param, it must survive.""" + params = get_optional_params(model="test-agent", custom_llm_provider="a2a", stream=True) + assert params["stream"] is True