Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/python_a2ui_agent_build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ jobs:
python -m pip install --upgrade pip
pip install uv

- name: Check Formatting
working-directory: a2a_agents/python/a2ui_agent
run: uv run pyink --check .

- name: Build the python SDK
working-directory: a2a_agents/python/a2ui_agent
run: uv build .
Expand Down
1 change: 1 addition & 0 deletions a2a_agents/python/a2ui_agent/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ pyink-annotation-pragmas = [
dev = [
"pytest>=9.0.2",
"pytest-asyncio>=1.3.0",
"pyink>=24.10.0",
]
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@


def test_a2ui_part_serialization():
a2ui_data = {
"beginRendering": {"surfaceId": "test-surface", "root": "root-column"}
}
a2ui_data = {"beginRendering": {"surfaceId": "test-surface", "root": "root-column"}}

part = a2ui_extension.create_a2ui_part(a2ui_data)

Expand All @@ -41,9 +39,7 @@ def test_non_a2ui_data_part():
metadata={"mimeType": "application/json"}, # Not A2UI
)
)
assert not a2ui_extension.is_a2ui_part(
part
), "Should not be identified as A2UI part"
assert not a2ui_extension.is_a2ui_part(part), "Should not be identified as A2UI part"
assert (
a2ui_extension.get_a2ui_datapart(part) is None
), "Should not return A2UI DataPart"
Expand All @@ -53,9 +49,7 @@ def test_non_a2ui_part():
text_part = TextPart(text="this is some text")
part = Part(root=text_part)

assert not a2ui_extension.is_a2ui_part(
part
), "Should not be identified as A2UI part"
assert not a2ui_extension.is_a2ui_part(part), "Should not be identified as A2UI part"
assert (
a2ui_extension.get_a2ui_datapart(part) is None
), "Should not return A2UI DataPart"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@

@pytest.mark.asyncio
async def test_toolset_init_bool():
toolset = SendA2uiToClientToolset(
a2ui_enabled=True, a2ui_schema=TEST_A2UI_SCHEMA
)
toolset = SendA2uiToClientToolset(a2ui_enabled=True, a2ui_schema=TEST_A2UI_SCHEMA)
ctx = MagicMock(spec=ReadonlyContext)
assert await toolset._resolve_a2ui_enabled(ctx) == True

Expand All @@ -54,9 +52,7 @@ async def test_toolset_init_bool():
async def test_toolset_init_callable():
enabled_mock = MagicMock(return_value=True)
schema_mock = MagicMock(return_value=TEST_A2UI_SCHEMA)
toolset = SendA2uiToClientToolset(
a2ui_enabled=enabled_mock, a2ui_schema=schema_mock
)
toolset = SendA2uiToClientToolset(a2ui_enabled=enabled_mock, a2ui_schema=schema_mock)
ctx = MagicMock(spec=ReadonlyContext)
assert await toolset._resolve_a2ui_enabled(ctx) == True

Expand Down Expand Up @@ -88,19 +84,15 @@ async def async_schema(_ctx):

@pytest.mark.asyncio
async def test_toolset_get_tools_enabled():
toolset = SendA2uiToClientToolset(
a2ui_enabled=True, a2ui_schema=TEST_A2UI_SCHEMA
)
toolset = SendA2uiToClientToolset(a2ui_enabled=True, a2ui_schema=TEST_A2UI_SCHEMA)
tools = await toolset.get_tools(MagicMock(spec=ReadonlyContext))
assert len(tools) == 1
assert isinstance(tools[0], SendA2uiToClientToolset._SendA2uiJsonToClientTool)


@pytest.mark.asyncio
async def test_toolset_get_tools_disabled():
toolset = SendA2uiToClientToolset(
a2ui_enabled=False, a2ui_schema=TEST_A2UI_SCHEMA
)
toolset = SendA2uiToClientToolset(a2ui_enabled=False, a2ui_schema=TEST_A2UI_SCHEMA)
tools = await toolset.get_tools(MagicMock(spec=ReadonlyContext))
assert len(tools) == 0

Expand All @@ -113,20 +105,15 @@ async def test_toolset_get_tools_disabled():

def test_send_tool_init():
tool = SendA2uiToClientToolset._SendA2uiJsonToClientTool(TEST_A2UI_SCHEMA)
assert (
tool.name == SendA2uiToClientToolset._SendA2uiJsonToClientTool.TOOL_NAME
)
assert tool.name == SendA2uiToClientToolset._SendA2uiJsonToClientTool.TOOL_NAME
assert tool._a2ui_schema == TEST_A2UI_SCHEMA


def test_send_tool_get_declaration():
tool = SendA2uiToClientToolset._SendA2uiJsonToClientTool(TEST_A2UI_SCHEMA)
declaration = tool._get_declaration()
assert declaration is not None
assert (
declaration.name
== SendA2uiToClientToolset._SendA2uiJsonToClientTool.TOOL_NAME
)
assert declaration.name == SendA2uiToClientToolset._SendA2uiJsonToClientTool.TOOL_NAME
assert (
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME
in declaration.parameters.properties
Expand Down Expand Up @@ -182,8 +169,8 @@ async def test_send_tool_run_async_valid():

valid_a2ui = [{"type": "Text", "text": "Hello"}]
args = {
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: (
json.dumps(valid_a2ui)
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: json.dumps(
valid_a2ui
)
}

Expand All @@ -205,8 +192,8 @@ async def test_send_tool_run_async_valid_list():

valid_a2ui = [{"type": "Text", "text": "Hello"}]
args = {
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: (
json.dumps(valid_a2ui)
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: json.dumps(
valid_a2ui
)
}

Expand Down Expand Up @@ -234,9 +221,7 @@ async def test_send_tool_run_async_missing_arg():
async def test_send_tool_run_async_invalid_json():
tool = SendA2uiToClientToolset._SendA2uiJsonToClientTool(TEST_A2UI_SCHEMA)
args = {
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: (
"{invalid"
)
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: "{invalid"
}
result = await tool.run_async(args=args, tool_context=MagicMock())
assert "error" in result
Expand All @@ -248,8 +233,8 @@ async def test_send_tool_run_async_schema_validation_fail():
tool = SendA2uiToClientToolset._SendA2uiJsonToClientTool(TEST_A2UI_SCHEMA)
invalid_a2ui = [{"type": "Text"}] # Missing 'text'
args = {
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: (
json.dumps(invalid_a2ui)
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: json.dumps(
invalid_a2ui
)
}
result = await tool.run_async(args=args, tool_context=MagicMock())
Expand Down Expand Up @@ -307,9 +292,7 @@ def test_converter_convert_function_call_returns_empty():
function_call = genai_types.FunctionCall(
name=SendA2uiToClientToolset._SendA2uiJsonToClientTool.TOOL_NAME,
args={
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: (
"..."
)
SendA2uiToClientToolset._SendA2uiJsonToClientTool.A2UI_JSON_ARG_NAME: "..."
},
)
part = genai_types.Part(function_call=function_call)
Expand Down Expand Up @@ -337,9 +320,7 @@ def test_converter_convert_empty_result_response():
assert len(a2a_parts) == 0


@patch(
"google.adk.a2a.converters.part_converter.convert_genai_part_to_a2a_part"
)
@patch("google.adk.a2a.converters.part_converter.convert_genai_part_to_a2a_part")
def test_converter_convert_non_a2ui_function_call(mock_convert):
function_call = genai_types.FunctionCall(name="other_tool", args={})
part = genai_types.Part(function_call=function_call)
Expand All @@ -352,9 +333,7 @@ def test_converter_convert_non_a2ui_function_call(mock_convert):
mock_convert.assert_called_once_with(part)


@patch(
"google.adk.a2a.converters.part_converter.convert_genai_part_to_a2a_part"
)
@patch("google.adk.a2a.converters.part_converter.convert_genai_part_to_a2a_part")
def test_converter_convert_other_part(mock_convert):
part = genai_types.Part(text="Hello")
mock_a2a_part = a2a_types.Part(root=a2a_types.TextPart(text="Hello"))
Expand Down
Loading
Loading