Skip to content

Commit 2b72e5f

Browse files
committed
fix: connect anthropic_llm with top_k, top_p, etc
1 parent 684a6e7 commit 2b72e5f

2 files changed

Lines changed: 194 additions & 2 deletions

File tree

src/google/adk/models/anthropic_llm.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,15 +493,20 @@ async def generate_content_async(
493493
)
494494
thinking = _build_anthropic_thinking_param(llm_request.config)
495495

496+
config = llm_request.config
496497
if not stream:
497498
message = await self._anthropic_client.messages.create(
498499
model=model_to_use,
499-
system=llm_request.config.system_instruction,
500+
system=config.system_instruction,
500501
messages=messages,
501502
tools=tools,
502503
tool_choice=tool_choice,
503504
max_tokens=self.max_tokens,
504505
thinking=thinking,
506+
temperature=config.temperature if config.temperature is not None else NOT_GIVEN,
507+
top_p=config.top_p if config.top_p is not None else NOT_GIVEN,
508+
top_k=config.top_k if config.top_k is not None else NOT_GIVEN,
509+
stop_sequences=config.stop_sequences if config.stop_sequences is not None else NOT_GIVEN,
505510
)
506511
yield message_to_generate_content_response(message)
507512
else:
@@ -528,13 +533,18 @@ async def _generate_content_streaming(
528533
a final aggregated LlmResponse with all content.
529534
"""
530535
model_to_use = self._resolve_model_name(llm_request.model)
536+
config = llm_request.config
531537
raw_stream = await self._anthropic_client.messages.create(
532538
model=model_to_use,
533-
system=llm_request.config.system_instruction,
539+
system=config.system_instruction,
534540
messages=messages,
535541
tools=tools,
536542
tool_choice=tool_choice,
537543
max_tokens=self.max_tokens,
544+
temperature=config.temperature if config.temperature is not None else NOT_GIVEN,
545+
top_p=config.top_p if config.top_p is not None else NOT_GIVEN,
546+
top_k=config.top_k if config.top_k is not None else NOT_GIVEN,
547+
stop_sequences=config.stop_sequences if config.stop_sequences is not None else NOT_GIVEN,
538548
stream=True,
539549
thinking=thinking,
540550
)

tests/unittests/models/test_anthropic_llm.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,3 +1905,185 @@ async def test_streaming_redacted_thinking_block_preserved_in_final():
19051905

19061906
text_part = final.content.parts[1]
19071907
assert text_part.text == "Done."
1908+
1909+
1910+
1911+
1912+
# --- Tests for generation config parameter forwarding ---
1913+
1914+
1915+
def _make_minimal_stream(text="Hi"):
1916+
"""Minimal streaming event sequence for param-forwarding tests."""
1917+
return [
1918+
MagicMock(
1919+
type="message_start",
1920+
message=MagicMock(usage=MagicMock(input_tokens=5, output_tokens=0)),
1921+
),
1922+
MagicMock(
1923+
type="content_block_start",
1924+
index=0,
1925+
content_block=anthropic_types.TextBlock(text="", type="text"),
1926+
),
1927+
MagicMock(
1928+
type="content_block_delta",
1929+
index=0,
1930+
delta=anthropic_types.TextDelta(text=text, type="text_delta"),
1931+
),
1932+
MagicMock(type="content_block_stop", index=0),
1933+
MagicMock(
1934+
type="message_delta",
1935+
delta=MagicMock(stop_reason="end_turn"),
1936+
usage=MagicMock(output_tokens=1),
1937+
),
1938+
MagicMock(type="message_stop"),
1939+
]
1940+
1941+
1942+
@pytest.mark.asyncio
1943+
async def test_non_streaming_forwards_generation_params():
1944+
"""temperature, top_p, top_k, stop_sequences forwarded to messages.create."""
1945+
from anthropic import NOT_GIVEN
1946+
1947+
llm = AnthropicLlm(model="claude-sonnet-4-20250514")
1948+
mock_message = anthropic_types.Message(
1949+
id="msg_test",
1950+
content=[anthropic_types.TextBlock(text="Hi", type="text", citations=None)],
1951+
model="claude-sonnet-4-20250514",
1952+
role="assistant",
1953+
stop_reason="end_turn",
1954+
stop_sequence=None,
1955+
type="message",
1956+
usage=anthropic_types.Usage(
1957+
input_tokens=5,
1958+
output_tokens=2,
1959+
cache_creation_input_tokens=0,
1960+
cache_read_input_tokens=0,
1961+
server_tool_use=None,
1962+
service_tier=None,
1963+
),
1964+
)
1965+
mock_client = MagicMock()
1966+
mock_client.messages.create = AsyncMock(return_value=mock_message)
1967+
1968+
llm_request = LlmRequest(
1969+
model="claude-sonnet-4-20250514",
1970+
contents=[Content(role="user", parts=[Part.from_text(text="Hi")])],
1971+
config=types.GenerateContentConfig(
1972+
system_instruction="Test",
1973+
temperature=0.7,
1974+
top_p=0.9,
1975+
top_k=40,
1976+
stop_sequences=["STOP", "END"],
1977+
),
1978+
)
1979+
1980+
with mock.patch.object(llm, "_anthropic_client", mock_client):
1981+
_ = [r async for r in llm.generate_content_async(llm_request, stream=False)]
1982+
1983+
_, kwargs = mock_client.messages.create.call_args
1984+
assert kwargs["temperature"] == 0.7
1985+
assert kwargs["top_p"] == 0.9
1986+
assert kwargs["top_k"] == 40
1987+
assert kwargs["stop_sequences"] == ["STOP", "END"]
1988+
1989+
1990+
@pytest.mark.asyncio
1991+
async def test_non_streaming_omits_unset_generation_params():
1992+
"""Unset generation params should be NOT_GIVEN, not None."""
1993+
from anthropic import NOT_GIVEN
1994+
1995+
llm = AnthropicLlm(model="claude-sonnet-4-20250514")
1996+
mock_message = anthropic_types.Message(
1997+
id="msg_test",
1998+
content=[anthropic_types.TextBlock(text="Hi", type="text", citations=None)],
1999+
model="claude-sonnet-4-20250514",
2000+
role="assistant",
2001+
stop_reason="end_turn",
2002+
stop_sequence=None,
2003+
type="message",
2004+
usage=anthropic_types.Usage(
2005+
input_tokens=5,
2006+
output_tokens=2,
2007+
cache_creation_input_tokens=0,
2008+
cache_read_input_tokens=0,
2009+
server_tool_use=None,
2010+
service_tier=None,
2011+
),
2012+
)
2013+
mock_client = MagicMock()
2014+
mock_client.messages.create = AsyncMock(return_value=mock_message)
2015+
2016+
llm_request = LlmRequest(
2017+
model="claude-sonnet-4-20250514",
2018+
contents=[Content(role="user", parts=[Part.from_text(text="Hi")])],
2019+
config=types.GenerateContentConfig(system_instruction="Test"),
2020+
)
2021+
2022+
with mock.patch.object(llm, "_anthropic_client", mock_client):
2023+
_ = [r async for r in llm.generate_content_async(llm_request, stream=False)]
2024+
2025+
_, kwargs = mock_client.messages.create.call_args
2026+
assert kwargs["temperature"] is NOT_GIVEN
2027+
assert kwargs["top_p"] is NOT_GIVEN
2028+
assert kwargs["top_k"] is NOT_GIVEN
2029+
assert kwargs["stop_sequences"] is NOT_GIVEN
2030+
2031+
2032+
@pytest.mark.asyncio
2033+
async def test_streaming_forwards_generation_params():
2034+
"""temperature, top_p, top_k, stop_sequences forwarded in streaming path."""
2035+
from anthropic import NOT_GIVEN
2036+
2037+
llm = AnthropicLlm(model="claude-sonnet-4-20250514")
2038+
mock_client = MagicMock()
2039+
mock_client.messages.create = AsyncMock(
2040+
return_value=_make_mock_stream_events(_make_minimal_stream())
2041+
)
2042+
2043+
llm_request = LlmRequest(
2044+
model="claude-sonnet-4-20250514",
2045+
contents=[Content(role="user", parts=[Part.from_text(text="Hi")])],
2046+
config=types.GenerateContentConfig(
2047+
system_instruction="Test",
2048+
temperature=0.5,
2049+
top_p=0.8,
2050+
top_k=20,
2051+
stop_sequences=["DONE"],
2052+
),
2053+
)
2054+
2055+
with mock.patch.object(llm, "_anthropic_client", mock_client):
2056+
_ = [r async for r in llm.generate_content_async(llm_request, stream=True)]
2057+
2058+
_, kwargs = mock_client.messages.create.call_args
2059+
assert kwargs["temperature"] == 0.5
2060+
assert kwargs["top_p"] == 0.8
2061+
assert kwargs["top_k"] == 20
2062+
assert kwargs["stop_sequences"] == ["DONE"]
2063+
2064+
2065+
@pytest.mark.asyncio
2066+
async def test_streaming_omits_unset_generation_params():
2067+
"""Unset generation params should be NOT_GIVEN in streaming path."""
2068+
from anthropic import NOT_GIVEN
2069+
2070+
llm = AnthropicLlm(model="claude-sonnet-4-20250514")
2071+
mock_client = MagicMock()
2072+
mock_client.messages.create = AsyncMock(
2073+
return_value=_make_mock_stream_events(_make_minimal_stream())
2074+
)
2075+
2076+
llm_request = LlmRequest(
2077+
model="claude-sonnet-4-20250514",
2078+
contents=[Content(role="user", parts=[Part.from_text(text="Hi")])],
2079+
config=types.GenerateContentConfig(system_instruction="Test"),
2080+
)
2081+
2082+
with mock.patch.object(llm, "_anthropic_client", mock_client):
2083+
_ = [r async for r in llm.generate_content_async(llm_request, stream=True)]
2084+
2085+
_, kwargs = mock_client.messages.create.call_args
2086+
assert kwargs["temperature"] is NOT_GIVEN
2087+
assert kwargs["top_p"] is NOT_GIVEN
2088+
assert kwargs["top_k"] is NOT_GIVEN
2089+
assert kwargs["stop_sequences"] is NOT_GIVEN

0 commit comments

Comments
 (0)