Skip to content

Commit 0504cba

Browse files
committed
Update contact_multiple_surfaces sample
It updates the sample to use the A2uiSchemaManager from the a2ui-agent python SDK. Tested: - [x] The `contact` lit client successfully connected to the `contact_multiple_surfaces` agent and rendered the response correctly.
1 parent 0ffe2a0 commit 0504cba

File tree

9 files changed

+228
-1071
lines changed

9 files changed

+228
-1071
lines changed

a2a_agents/python/a2ui_agent/src/a2ui/inference/schema/manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
)
3838
from .catalog import CustomCatalogConfig, A2uiCatalog
3939
from ...extension.a2ui_extension import INLINE_CATALOGS_KEY, SUPPORTED_CATALOG_IDS_KEY, get_a2ui_agent_extension
40+
from a2a.types import AgentExtension
4041

4142

4243
def _load_basic_component(version: str, spec_name: str) -> Dict:

samples/agent/adk/contact_lookup/agent.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ class ContactAgent:
4242

4343
SUPPORTED_CONTENT_TYPES = ["text", "text/plain"]
4444

45+
def __init__(self, base_url: str, use_ui: bool = False):
46+
self.base_url = base_url
47+
self.use_ui = use_ui
48+
self._schema_manager = (
49+
A2uiSchemaManager("0.8", basic_examples_path="examples") if use_ui else None
50+
)
51+
self._agent = self._build_agent(use_ui)
52+
self._user_id = "remote_agent"
53+
self._runner = Runner(
54+
app_name=self._agent.name,
55+
agent=self._agent,
56+
artifact_service=InMemoryArtifactService(),
57+
session_service=InMemorySessionService(),
58+
memory_service=InMemoryMemoryService(),
59+
)
4560
def __init__(self, base_url: str, use_ui: bool = False):
4661
self.base_url = base_url
4762
self.use_ui = use_ui
@@ -71,10 +86,7 @@ def get_agent_card(self) -> AgentCard:
7186
" team)."
7287
),
7388
tags=["contact", "directory", "people", "finder"],
74-
examples=[
75-
"Who is David Chen in marketing?",
76-
"Find Sarah Lee from engineering",
77-
],
89+
examples=["Who is David Chen in marketing?", "Find Sarah Lee from engineering"],
7890
)
7991

8092
return AgentCard(
@@ -89,6 +101,18 @@ def get_agent_card(self) -> AgentCard:
89101
capabilities=capabilities,
90102
skills=[skill],
91103
)
104+
return AgentCard(
105+
name="Contact Lookup Agent",
106+
description=(
107+
"This agent helps find contact info for people in your organization."
108+
),
109+
url=self.base_url,
110+
version="1.0.0",
111+
default_input_modes=ContactAgent.SUPPORTED_CONTENT_TYPES,
112+
default_output_modes=ContactAgent.SUPPORTED_CONTENT_TYPES,
113+
capabilities=capabilities,
114+
skills=[skill],
115+
)
92116

93117
def get_processing_message(self) -> str:
94118
return "Looking up contact information..."

samples/agent/adk/contact_multiple_surfaces/__main__.py

Lines changed: 47 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
from a2a.server.apps import A2AStarletteApplication
2020
from a2a.server.request_handlers import DefaultRequestHandler
2121
from a2a.server.tasks import InMemoryTaskStore
22-
from a2a.types import AgentCapabilities, AgentCard, AgentSkill
23-
from a2ui.extension.a2ui_extension import get_a2ui_agent_extension
2422
from agent import ContactAgent
2523
from agent_executor import ContactAgentExecutor
2624
from dotenv import load_dotenv
@@ -36,80 +34,53 @@
3634
class MissingAPIKeyError(Exception):
3735
"""Exception for missing API key."""
3836

39-
40-
@click.command()
41-
@click.option("--host", default="localhost")
42-
@click.option("--port", default=10004)
43-
def main(host, port):
44-
try:
45-
# Check for API key only if Vertex AI is not configured
46-
if not os.getenv("GOOGLE_GENAI_USE_VERTEXAI") == "TRUE":
47-
if not os.getenv("GEMINI_API_KEY"):
48-
raise MissingAPIKeyError(
49-
"GEMINI_API_KEY environment variable not set and GOOGLE_GENAI_USE_VERTEXAI"
50-
" is not TRUE."
51-
)
52-
53-
capabilities = AgentCapabilities(
54-
streaming=True,
55-
extensions=[get_a2ui_agent_extension()],
56-
)
57-
skill = AgentSkill(
58-
id="find_contact",
59-
name="Find Contact Tool",
60-
description=(
61-
"Helps find contact information for colleagues (e.g., email, location,"
62-
" team)."
63-
),
64-
tags=["contact", "directory", "people", "finder"],
65-
examples=["Who is David Chen in marketing?", "Find Sarah Lee from engineering"],
66-
)
67-
68-
base_url = f"http://{host}:{port}"
69-
70-
agent_card = AgentCard(
71-
name="Contact Lookup Agent",
72-
description=(
73-
"This agent helps find contact info for people in your organization."
74-
),
75-
url=base_url, # <-- Use base_url here
76-
version="1.0.0",
77-
default_input_modes=ContactAgent.SUPPORTED_CONTENT_TYPES,
78-
default_output_modes=ContactAgent.SUPPORTED_CONTENT_TYPES,
79-
capabilities=capabilities,
80-
skills=[skill],
81-
)
82-
83-
agent_executor = ContactAgentExecutor(base_url=base_url)
84-
85-
request_handler = DefaultRequestHandler(
86-
agent_executor=agent_executor,
87-
task_store=InMemoryTaskStore(),
88-
)
89-
server = A2AStarletteApplication(
90-
agent_card=agent_card, http_handler=request_handler
91-
)
92-
import uvicorn
93-
94-
app = server.build()
95-
96-
app.add_middleware(
97-
CORSMiddleware,
98-
allow_origins=["http://localhost:5173"],
99-
allow_credentials=True,
100-
allow_methods=["*"],
101-
allow_headers=["*"],
102-
)
103-
104-
app.mount("/static", StaticFiles(directory="images"), name="static")
105-
106-
uvicorn.run(app, host=host, port=port)
107-
except MissingAPIKeyError as e:
108-
logger.error(f"Error: {e}")
109-
exit(1)
110-
except Exception as e:
111-
logger.error(f"An error occurred during server startup: {e}")
112-
exit(1)
37+
@click.command()
38+
@click.option("--host", default="localhost")
39+
@click.option("--port", default=10004)
40+
def main(host, port):
41+
try:
42+
# Check for API key only if Vertex AI is not configured
43+
if not os.getenv("GOOGLE_GENAI_USE_VERTEXAI") == "TRUE":
44+
if not os.getenv("GEMINI_API_KEY"):
45+
raise MissingAPIKeyError(
46+
"GEMINI_API_KEY environment variable not set and"
47+
" GOOGLE_GENAI_USE_VERTEXAI is not TRUE."
48+
)
49+
50+
base_url = f"http://{host}:{port}"
51+
52+
agent = ContactAgent(base_url=base_url, use_ui=True)
53+
54+
agent_executor = ContactAgentExecutor(agent=agent)
55+
56+
request_handler = DefaultRequestHandler(
57+
agent_executor=agent_executor,
58+
task_store=InMemoryTaskStore(),
59+
)
60+
server = A2AStarletteApplication(
61+
agent_card=agent.get_agent_card(), http_handler=request_handler
62+
)
63+
import uvicorn
64+
65+
app = server.build()
66+
67+
app.add_middleware(
68+
CORSMiddleware,
69+
allow_origins=["http://localhost:5173"],
70+
allow_credentials=True,
71+
allow_methods=["*"],
72+
allow_headers=["*"],
73+
)
74+
75+
app.mount("/static", StaticFiles(directory="images"), name="static")
76+
77+
uvicorn.run(app, host=host, port=port)
78+
except MissingAPIKeyError as e:
79+
logger.error(f"Error: {e}")
80+
exit(1)
81+
except Exception as e:
82+
logger.error(f"An error occurred during server startup: {e}")
83+
exit(1)
11384

11485

11586
if __name__ == "__main__":

samples/agent/adk/contact_multiple_surfaces/a2ui_examples.py

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from pathlib import Path
1919

2020
import jsonschema
21-
from a2ui_schema import A2UI_SCHEMA
2221

2322
logger = logging.getLogger(__name__)
2423

@@ -35,77 +34,6 @@
3534
FLOOR_PLAN_FILE = "floor_plan.json"
3635

3736

38-
def load_examples(base_url: str = "http://localhost:10004") -> str:
39-
"""
40-
Loads, validates, and formats the UI examples from JSON files.
41-
42-
Args:
43-
base_url: The base URL to replace placeholder URLs with.
44-
(Currently examples have http://localhost:10004 hardcoded,
45-
but we can make this dynamic if needed).
46-
47-
Returns:
48-
A string containing all formatted examples for the prompt.
49-
"""
50-
51-
# Pre-parse validator
52-
try:
53-
single_msg_schema = json.loads(A2UI_SCHEMA)
54-
# Examples are typically lists of messages
55-
list_schema = {"type": "array", "items": single_msg_schema}
56-
except json.JSONDecodeError:
57-
logger.error("Failed to parse A2UI_SCHEMA for validation")
58-
list_schema = None
59-
60-
examples_dir = Path(os.path.dirname(__file__)) / "examples"
61-
formatted_output = []
62-
63-
for curr_name, filename in EXAMPLE_FILES.items():
64-
file_path = examples_dir / filename
65-
try:
66-
content = file_path.read_text(encoding="utf-8")
67-
68-
# basic replacement if we decide to template the URL in JSON files
69-
# content = content.replace("{{BASE_URL}}", base_url)
70-
71-
# Validation
72-
if list_schema:
73-
try:
74-
data = json.loads(content)
75-
jsonschema.validate(instance=data, schema=list_schema)
76-
except (json.JSONDecodeError, jsonschema.ValidationError) as e:
77-
logger.warning(f"Example {filename} validation failed: {e}")
78-
79-
formatted_output.append(f"---BEGIN {curr_name}---")
80-
# Handle examples that include user/model text
81-
if curr_name == "ORG_CHART_EXAMPLE":
82-
formatted_output.append("User: Show me the org chart for Casey Smith")
83-
formatted_output.append("Model: Here is the organizational chart.")
84-
formatted_output.append("---a2ui_JSON---")
85-
elif curr_name == "MULTI_SURFACE_EXAMPLE":
86-
formatted_output.append("User: Full profile for Casey Smith")
87-
formatted_output.append(
88-
"Model: Here is the full profile including contact details and org chart."
89-
)
90-
formatted_output.append("---a2ui_JSON---")
91-
elif curr_name == "CHART_NODE_CLICK_EXAMPLE":
92-
formatted_output.append(
93-
'User: ACTION: chart_node_click (context: clickedNodeName="John Smith")'
94-
" (from modal)"
95-
)
96-
formatted_output.append("Model: Here is the profile for John Smith.")
97-
formatted_output.append("---a2ui_JSON---")
98-
99-
formatted_output.append(content.strip())
100-
formatted_output.append(f"---END {curr_name}---")
101-
formatted_output.append("") # Newline
102-
103-
except FileNotFoundError:
104-
logger.error(f"Example file not found: {file_path}")
105-
106-
return "\n".join(formatted_output)
107-
108-
10937
def load_floor_plan_example() -> str:
11038
"""Loads the floor plan example specifically."""
11139
examples_dir = Path(os.path.dirname(__file__)) / "examples"

0 commit comments

Comments
 (0)