diff --git a/src/google/adk/a2a/utils/agent_card_builder.py b/src/google/adk/a2a/utils/agent_card_builder.py index c007870931..2855077704 100644 --- a/src/google/adk/a2a/utils/agent_card_builder.py +++ b/src/google/adk/a2a/utils/agent_card_builder.py @@ -114,7 +114,7 @@ async def _build_llm_agent_skills(agent: LlmAgent) -> List[AgentSkill]: id=agent.name, name='model', description=agent_description, - examples=agent_examples, + examples=_extract_inputs_from_examples(agent_examples), input_modes=_get_input_modes(agent), output_modes=_get_output_modes(agent), tags=['llm'], @@ -239,7 +239,7 @@ async def _build_non_llm_agent_skills(agent: BaseAgent) -> List[AgentSkill]: id=agent.name, name=agent_name, description=agent_description, - examples=agent_examples, + examples=_extract_inputs_from_examples(agent_examples), input_modes=_get_input_modes(agent), output_modes=_get_output_modes(agent), tags=[agent_type], @@ -350,6 +350,7 @@ def _build_llm_agent_description_with_instructions(agent: LlmAgent) -> str: def _replace_pronouns(text: str) -> str: """Replace pronouns and conjugate common verbs for agent description. + (e.g., "You are" -> "I am", "your" -> "my"). """ pronoun_map = { @@ -460,6 +461,33 @@ def _get_default_description(agent: BaseAgent) -> str: return 'A custom agent' +def _extract_inputs_from_examples(examples: Optional[list[dict]]) -> list[str]: + """Extracts only the input strings so they can be added to an AgentSkill.""" + if examples is None: + return [] + + extracted_inputs = [] + for example in examples: + example_input = example.get('input') + if not example_input: + continue + + parts = example_input.get('parts') + if parts is not None: + part_texts = [] + for part in parts: + text = part.get('text') + if text is not None: + part_texts.append(text) + extracted_inputs.append('\n'.join(part_texts)) + else: + text = example_input.get('text') + if text is not None: + extracted_inputs.append(text) + + return extracted_inputs + + async def _extract_examples_from_agent( agent: BaseAgent, ) -> Optional[List[Dict]]: diff --git a/tests/unittests/a2a/utils/test_agent_card_builder.py b/tests/unittests/a2a/utils/test_agent_card_builder.py index 3bf3202897..d8fbf1e9f9 100644 --- a/tests/unittests/a2a/utils/test_agent_card_builder.py +++ b/tests/unittests/a2a/utils/test_agent_card_builder.py @@ -28,6 +28,7 @@ from google.adk.a2a.utils.agent_card_builder import _build_sequential_description from google.adk.a2a.utils.agent_card_builder import _convert_example_tool_examples from google.adk.a2a.utils.agent_card_builder import _extract_examples_from_instruction +from google.adk.a2a.utils.agent_card_builder import _extract_inputs_from_examples from google.adk.a2a.utils.agent_card_builder import _get_agent_skill_name from google.adk.a2a.utils.agent_card_builder import _get_agent_type from google.adk.a2a.utils.agent_card_builder import _get_default_description @@ -41,6 +42,7 @@ from google.adk.agents.loop_agent import LoopAgent from google.adk.agents.parallel_agent import ParallelAgent from google.adk.agents.sequential_agent import SequentialAgent +from google.adk.examples import Example from google.adk.tools.example_tool import ExampleTool import pytest @@ -1100,3 +1102,73 @@ def test_extract_examples_from_instruction_odd_number_of_matches(self): assert len(result) == 1 # Only complete pairs should be included assert result[0]["input"] == {"text": "What is the weather?"} assert result[0]["output"] == [{"text": "What time is it?"}] + + def test_extract_inputs_from_examples_from_plain_text_input(self): + """Test _extract_inputs_from_examples on plain text as input.""" + # Arrange + examples = [ + { + "input": {"text": "What is the weather?"}, + "output": [{"text": "What time is it?"}], + }, + { + "input": {"text": "The weather is sunny."}, + "output": [{"text": "It is 3 PM."}], + }, + ] + + # Act + result = _extract_inputs_from_examples(examples) + + # Assert + assert len(result) == 2 + assert result[0] == "What is the weather?" + assert result[1] == "The weather is sunny." + + def test_extract_inputs_from_examples_from_example_tool(self): + """Test _extract_inputs_from_examples as extracted from ExampleTool.""" + + # Arrange + # This is what would be extracted from an ExampleTool + examples = [ + { + "input": { + "role": "user", + "parts": [{"text": "What is the weather?"}], + }, + "output": [ + { + "role": "model", + "parts": [{"text": "What time is it?"}], + }, + ], + }, + { + "input": { + "role": "user", + "parts": [{"text": "The weather is sunny."}], + }, + "output": [ + { + "role": "model", + "parts": [{"text": "It is 3 PM."}], + }, + ], + }, + ] + + # Act + result = _extract_inputs_from_examples(examples) + + # Assert + assert len(result) == 2 + assert result[0] == "What is the weather?" + assert result[1] == "The weather is sunny." + + def test_extract_inputs_from_examples_none_input(self): + """Test _extract_inputs_from_examples on None as input.""" + # Act + result = _extract_inputs_from_examples(None) + + # Assert + assert len(result) == 0