Skip to content

Commit b70d611

Browse files
committed
Update the rizzcharts sample
It updates the sample to use the A2uiSchemaManager from the a2ui-agent python SDK. Tested: - [x] The `rizzcharts` Angular client successfully connected to the `rizzcharts` agent and rendered the response correctly.
1 parent 59c5e44 commit b70d611

File tree

7 files changed

+305
-470
lines changed

7 files changed

+305
-470
lines changed

a2a_agents/python/a2ui_agent/src/a2ui/extension/send_a2ui_to_client_toolset.py

Lines changed: 49 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@
3939
4040
```python
4141
# Simple boolean and dict
42-
toolset = SendA2uiToClientToolset(a2ui_enabled=True, a2ui_schema=MY_SCHEMA)
42+
toolset = SendA2uiToClientToolset(a2ui_enabled=True, a2ui_catalog=MY_CATALOG)
4343
4444
# Async providers
4545
async def check_enabled(ctx: ReadonlyContext) -> bool:
4646
return await some_condition(ctx)
4747
48-
async def get_schema(ctx: ReadonlyContext) -> dict[str, Any]:
49-
return await fetch_schema(ctx)
48+
async def get_catalog(ctx: ReadonlyContext) -> A2uiCatalog:
49+
return await fetch_catalog(ctx)
5050
51-
toolset = SendA2uiToClientToolset(a2ui_enabled=check_enabled, a2ui_schema=get_schema)
51+
toolset = SendA2uiToClientToolset(a2ui_enabled=check_enabled, a2ui_catalog=get_catalog)
5252
```
5353
5454
2. Integration with Agent:
@@ -60,7 +60,7 @@ async def get_schema(ctx: ReadonlyContext) -> dict[str, Any]:
6060
tools=[
6161
SendA2uiToClientToolset(
6262
a2ui_enabled=True,
63-
a2ui_schema=MY_SCHEMA
63+
a2ui_catalog=MY_CATALOG
6464
)
6565
]
6666
)
@@ -87,7 +87,7 @@ async def get_schema(ctx: ReadonlyContext) -> dict[str, Any]:
8787

8888
from a2a import types as a2a_types
8989
from a2ui.extension.a2ui_extension import create_a2ui_part
90-
from a2ui.extension.a2ui_schema_utils import wrap_as_json_array
90+
from a2ui.inference.schema.catalog import A2uiCatalog
9191
from google.adk.a2a.converters import part_converter
9292
from google.adk.agents.readonly_context import ReadonlyContext
9393
from google.adk.models import LlmRequest
@@ -102,8 +102,11 @@ async def get_schema(ctx: ReadonlyContext) -> dict[str, Any]:
102102
A2uiEnabledProvider: TypeAlias = Callable[
103103
[ReadonlyContext], Union[bool, Awaitable[bool]]
104104
]
105-
A2uiSchemaProvider: TypeAlias = Callable[
106-
[ReadonlyContext], Union[dict[str, Any], Awaitable[dict[str, Any]]]
105+
A2uiCatalogProvider: TypeAlias = Callable[
106+
[ReadonlyContext], Union[A2uiCatalog, Awaitable[A2uiCatalog]]
107+
]
108+
A2uiExamplesProvider: TypeAlias = Callable[
109+
[ReadonlyContext], Union[str, Awaitable[str]]
107110
]
108111

109112

@@ -114,11 +117,12 @@ class SendA2uiToClientToolset(base_toolset.BaseToolset):
114117
def __init__(
115118
self,
116119
a2ui_enabled: Union[bool, A2uiEnabledProvider],
117-
a2ui_schema: Union[dict[str, Any], A2uiSchemaProvider],
120+
a2ui_catalog: Union[A2uiCatalog, A2uiCatalogProvider],
121+
a2ui_examples: Union[str, A2uiExamplesProvider],
118122
):
119123
super().__init__()
120124
self._a2ui_enabled = a2ui_enabled
121-
self._ui_tools = [self._SendA2uiJsonToClientTool(a2ui_schema)]
125+
self._ui_tools = [self._SendA2uiJsonToClientTool(a2ui_catalog, a2ui_examples)]
122126

123127
async def _resolve_a2ui_enabled(self, ctx: ReadonlyContext) -> bool:
124128
"""The resolved self.a2ui_enabled field to construct instruction for this agent.
@@ -165,8 +169,13 @@ class _SendA2uiJsonToClientTool(BaseTool):
165169
A2UI_JSON_ARG_NAME = "a2ui_json"
166170
TOOL_ERROR_KEY = "error"
167171

168-
def __init__(self, a2ui_schema: Union[dict[str, Any], A2uiSchemaProvider]):
169-
self._a2ui_schema = a2ui_schema
172+
def __init__(
173+
self,
174+
a2ui_catalog: Union[A2uiCatalog, A2uiCatalogProvider],
175+
a2ui_examples: Union[str, A2uiExamplesProvider],
176+
):
177+
self._a2ui_catalog = a2ui_catalog
178+
self._a2ui_examples = a2ui_examples
170179
super().__init__(
171180
name=self.TOOL_NAME,
172181
description=(
@@ -196,34 +205,39 @@ def _get_declaration(self) -> genai_types.FunctionDeclaration | None:
196205
),
197206
)
198207

199-
async def _resolve_a2ui_schema(self, ctx: ReadonlyContext) -> dict[str, Any]:
200-
"""The resolved self.a2ui_schema field to construct instruction for this agent.
208+
async def _resolve_a2ui_examples(self, ctx: ReadonlyContext) -> str:
209+
"""The resolved self.a2ui_examples field to construct instruction for this agent.
201210
202211
Args:
203212
ctx: The ReadonlyContext to resolve the provider with.
204213
205214
Returns:
206-
The A2UI schema to send to the client.
215+
The A2UI examples string.
207216
"""
208-
if isinstance(self._a2ui_schema, dict):
209-
return self._a2ui_schema
217+
if isinstance(self._a2ui_examples, str):
218+
return self._a2ui_examples
210219
else:
211-
a2ui_schema = self._a2ui_schema(ctx)
212-
if inspect.isawaitable(a2ui_schema):
213-
a2ui_schema = await a2ui_schema
214-
return a2ui_schema
220+
a2ui_examples = self._a2ui_examples(ctx)
221+
if inspect.isawaitable(a2ui_examples):
222+
a2ui_examples = await a2ui_examples
223+
return a2ui_examples
215224

216-
async def get_a2ui_schema(self, ctx: ReadonlyContext) -> dict[str, Any]:
217-
"""Retrieves and wraps the A2UI schema.
225+
async def _resolve_a2ui_catalog(self, ctx: ReadonlyContext) -> A2uiCatalog:
226+
"""The resolved self.a2ui_catalog field to construct instruction for this agent.
218227
219228
Args:
220-
ctx: The ReadonlyContext for resolving the schema.
229+
ctx: The ReadonlyContext to resolve the provider with.
221230
222231
Returns:
223-
The wrapped A2UI schema.
232+
The A2UI catalog object.
224233
"""
225-
a2ui_schema = await self._resolve_a2ui_schema(ctx)
226-
return wrap_as_json_array(a2ui_schema)
234+
if isinstance(self._a2ui_catalog, A2uiCatalog):
235+
return self._a2ui_catalog
236+
else:
237+
a2ui_catalog = self._a2ui_catalog(ctx)
238+
if inspect.isawaitable(a2ui_catalog):
239+
a2ui_catalog = await a2ui_catalog
240+
return a2ui_catalog
227241

228242
async def process_llm_request(
229243
self, *, tool_context: ToolContext, llm_request: LlmRequest
@@ -232,15 +246,14 @@ async def process_llm_request(
232246
tool_context=tool_context, llm_request=llm_request
233247
)
234248

235-
a2ui_schema = await self.get_a2ui_schema(tool_context)
249+
a2ui_catalog = await self._resolve_a2ui_catalog(tool_context)
236250

237-
llm_request.append_instructions([f"""
238-
---BEGIN A2UI JSON SCHEMA---
239-
{json.dumps(a2ui_schema)}
240-
---END A2UI JSON SCHEMA---
241-
"""])
251+
instruction = a2ui_catalog.render_as_llm_instructions()
252+
examples = await self._resolve_a2ui_examples(tool_context)
242253

243-
logger.info("Added a2ui_schema to system instructions")
254+
llm_request.append_instructions([instruction, examples])
255+
256+
logger.info("Added A2UI schema and examples to system instructions")
244257

245258
async def run_async(
246259
self, *, args: dict[str, Any], tool_context: ToolContext
@@ -253,44 +266,8 @@ async def run_async(
253266
f" arg {self.A2UI_JSON_ARG_NAME} "
254267
)
255268

256-
a2ui_schema = await self.get_a2ui_schema(tool_context)
257-
258-
try:
259-
# Attempt to parse and validate
260-
a2ui_json_payload = json.loads(a2ui_json)
261-
262-
# Auto-wrap single object in list
263-
if not isinstance(a2ui_json_payload, list):
264-
logger.info(
265-
"Received a single JSON object, wrapping in a list for validation."
266-
)
267-
a2ui_json_payload = [a2ui_json_payload]
268-
269-
jsonschema.validate(instance=a2ui_json_payload, schema=a2ui_schema)
270-
271-
except (jsonschema.exceptions.ValidationError, json.JSONDecodeError) as e:
272-
logger.warning(f"Initial A2UI JSON validation failed: {e}")
273-
274-
# Run Fixer
275-
fixed_a2ui_json = re.sub(r",(?=\s*[\]}])", "", a2ui_json)
276-
277-
if fixed_a2ui_json != a2ui_json:
278-
# Emit Warning
279-
logger.warning("Detected trailing commas in LLM output; applied autofix.")
280-
281-
# Re-parse and Re-validate
282-
a2ui_json_payload = json.loads(fixed_a2ui_json)
283-
284-
# Auto-wrap single object in list
285-
if not isinstance(a2ui_json_payload, list):
286-
logger.info(
287-
"Received a single JSON object, wrapping in a list for validation."
288-
)
289-
a2ui_json_payload = [a2ui_json_payload]
290-
291-
jsonschema.validate(instance=a2ui_json_payload, schema=a2ui_schema)
292-
else:
293-
raise e
269+
a2ui_catalog = await self._resolve_a2ui_catalog(tool_context)
270+
a2ui_json_payload = a2ui_catalog.payload_fixer.fix(a2ui_json)
294271

295272
logger.info(
296273
f"Validated call to tool {self.TOOL_NAME} with {self.A2UI_JSON_ARG_NAME}"

0 commit comments

Comments
 (0)