Skip to content

Commit 3c7e09b

Browse files
authored
Merge branch 'main' into feat/app-name-override
2 parents b61e6e8 + 472e463 commit 3c7e09b

3 files changed

Lines changed: 153 additions & 0 deletions

File tree

src/google/adk/agents/llm_agent.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,18 @@ def validate_generate_content_config(
10891089
raise ValueError(
10901090
'Response schema must be set via LlmAgent.output_schema.'
10911091
)
1092+
if generate_content_config.http_options:
1093+
if generate_content_config.http_options.base_url:
1094+
raise ValueError(
1095+
'Base URL is a transport setting and must be set on the model or'
1096+
' its client, not via LlmAgent.generate_content_config.'
1097+
)
1098+
if generate_content_config.http_options.extra_body:
1099+
raise ValueError(
1100+
'Extra body is merged into the request body and can overwrite the'
1101+
' tools, system instruction and response schema rejected above.'
1102+
' Set it on the model or its client.'
1103+
)
10921104
return generate_content_config
10931105

10941106
@override

tests/unittests/agents/test_llm_agent_fields.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,42 @@ class Schema(BaseModel):
329329
)
330330

331331

332+
def test_validate_generate_content_config_http_options_base_url_throw():
333+
"""Tests that a transport base URL cannot be set directly in config."""
334+
with pytest.raises(ValueError):
335+
_ = LlmAgent(
336+
name='test_agent',
337+
generate_content_config=types.GenerateContentConfig(
338+
http_options=types.HttpOptions(base_url='http://example.invalid')
339+
),
340+
)
341+
342+
343+
def test_validate_generate_content_config_http_options_extra_body_throw():
344+
"""Tests that an extra request body cannot be set directly in config."""
345+
with pytest.raises(ValueError):
346+
_ = LlmAgent(
347+
name='test_agent',
348+
generate_content_config=types.GenerateContentConfig(
349+
http_options=types.HttpOptions(
350+
extra_body={'systemInstruction': {'parts': [{'text': 'hi'}]}}
351+
)
352+
),
353+
)
354+
355+
356+
def test_validate_generate_content_config_http_options_allowed():
357+
"""Tests that request-time http options remain settable in config."""
358+
agent = LlmAgent(
359+
name='test_agent',
360+
generate_content_config=types.GenerateContentConfig(
361+
http_options=types.HttpOptions(timeout=1000)
362+
),
363+
)
364+
365+
assert agent.generate_content_config.http_options.timeout == 1000
366+
367+
332368
def test_allow_transfer_by_default():
333369
sub_agent = LlmAgent(name='sub_agent')
334370
agent = LlmAgent(name='test_agent', sub_agents=[sub_agent])

tests/unittests/flows/llm_flows/test_request_confirmation.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,3 +587,108 @@ async def test_request_confirmation_processor_dynamic_success():
587587
assert (
588588
args[4][MOCK_FUNCTION_CALL_ID] == user_confirmation
589589
) # tool_confirmation_dict
590+
591+
592+
@pytest.mark.parametrize(
593+
"tools, original_args, confirmation_args, expected_exception_match",
594+
[
595+
(
596+
[],
597+
{"param1": "test"},
598+
{"param1": "test"},
599+
"is not registered",
600+
),
601+
(
602+
[FunctionTool(mock_tool, require_confirmation=False)],
603+
{"param1": "test"},
604+
{"param1": "test"},
605+
"does not require confirmation",
606+
),
607+
(
608+
[FunctionTool(mock_tool, require_confirmation=True)],
609+
{"param1": "test"},
610+
{"param1": "tampered"},
611+
"arguments mismatch",
612+
),
613+
],
614+
)
615+
@pytest.mark.asyncio
616+
async def test_request_confirmation_processor_rejections(
617+
tools, original_args, confirmation_args, expected_exception_match
618+
):
619+
"""Test various validation rejections in request confirmation processor."""
620+
agent = LlmAgent(name="test_agent", tools=tools)
621+
invocation_context = await testing_utils.create_invocation_context(
622+
agent=agent
623+
)
624+
llm_request = LlmRequest()
625+
626+
original_function_call = types.FunctionCall(
627+
name=MOCK_TOOL_NAME, args=original_args, id=MOCK_FUNCTION_CALL_ID
628+
)
629+
630+
# 1. Event with the original tool call
631+
invocation_context.session.events.append(
632+
Event(
633+
author=agent.name,
634+
content=types.Content(
635+
parts=[types.Part(function_call=original_function_call)]
636+
),
637+
)
638+
)
639+
640+
# 2. Confirmation request event from the agent to the client.
641+
confirmation_function_call = types.FunctionCall(
642+
name=MOCK_TOOL_NAME, args=confirmation_args, id=MOCK_FUNCTION_CALL_ID
643+
)
644+
tool_confirmation = ToolConfirmation(confirmed=False, hint="test hint")
645+
tool_confirmation_args = {
646+
"originalFunctionCall": confirmation_function_call.model_dump(
647+
exclude_none=True, by_alias=True
648+
),
649+
"toolConfirmation": tool_confirmation.model_dump(
650+
by_alias=True, exclude_none=True
651+
),
652+
}
653+
654+
invocation_context.session.events.append(
655+
Event(
656+
author=agent.name,
657+
content=types.Content(
658+
parts=[
659+
types.Part(
660+
function_call=types.FunctionCall(
661+
name=functions.REQUEST_CONFIRMATION_FUNCTION_CALL_NAME,
662+
args=tool_confirmation_args,
663+
id=MOCK_CONFIRMATION_FUNCTION_CALL_ID,
664+
)
665+
)
666+
]
667+
),
668+
)
669+
)
670+
671+
# 3. Event with the user's confirmation response.
672+
user_confirmation = ToolConfirmation(confirmed=True)
673+
invocation_context.session.events.append(
674+
Event(
675+
author="user",
676+
content=types.Content(
677+
parts=[
678+
types.Part(
679+
function_response=types.FunctionResponse(
680+
name=functions.REQUEST_CONFIRMATION_FUNCTION_CALL_NAME,
681+
id=MOCK_CONFIRMATION_FUNCTION_CALL_ID,
682+
response={
683+
"response": user_confirmation.model_dump_json()
684+
},
685+
)
686+
)
687+
]
688+
),
689+
)
690+
)
691+
692+
with pytest.raises(ValueError, match=expected_exception_match):
693+
async for _ in request_processor.run_async(invocation_context, llm_request):
694+
pass

0 commit comments

Comments
 (0)