Update React Shell to A2UI v0.9 and Add A2A Middleware#1186
Update React Shell to A2UI v0.9 and Add A2A Middleware#1186jacobsimionato wants to merge 3 commits intogoogle:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request migrates the React shell and restaurant finder sample to the A2UI v0.9 protocol. Key changes include refactoring the React application to use the new MessageProcessor and A2uiSurface components, updating mock data to the v0.9 schema, and introducing a Vite middleware to proxy A2A agent communication with SSE streaming support. Several high-severity issues were identified, including a protocol mismatch when handling user actions in App.tsx and an incorrect SSE error format in the middleware that will cause client-side runtime errors. Additionally, the middleware lacks request body size limits, the agent card URL is hardcoded, and a TypeScript build artifact was accidentally committed.
| console.log('User action:', action); | ||
| if (sendAndProcessRef.current) { | ||
| sendAndProcessRef.current(actionMessage); | ||
| sendAndProcessRef.current(action as any); |
There was a problem hiding this comment.
The action object is being passed directly to sendAndProcess. However, getMockResponse (line 147) and the A2UI v0.9 protocol expect a message wrapper containing the action (i.e., { action: ... }). This mismatch will cause mock actions to fail and may lead to issues with the agent middleware.
| sendAndProcessRef.current(action as any); | |
| sendAndProcessRef.current({ action } as A2uiClientMessage); |
| res.setHeader("Content-Type", "application/json"); | ||
| res.end(JSON.stringify({ error: e.message || String(e) })); | ||
| } else { | ||
| res.write(`data: ${JSON.stringify({ error: e.message || String(e) })}\n\n`); |
There was a problem hiding this comment.
The SSE error response is sent as a single object, but the client-side parser in client.ts (line 69) expects an array of Part objects. Attempting to iterate over a non-array object using for...of will throw a runtime error on the client.
| res.write(`data: ${JSON.stringify({ error: e.message || String(e) })}\n\n`); | |
| res.write(`data: ${JSON.stringify([{ kind: "error", text: e.message || String(e) }])}\n\n`); |
| req.on("data", (chunk) => { | ||
| originalBody += chunk.toString(); | ||
| }); |
| // Create a client pointing to the agent's Agent Card URL. | ||
| // Default to localhost:10002 for the restaurant agent. | ||
| client = await A2AClient.fromCardUrl( | ||
| "http://localhost:10002/.well-known/agent-card.json", |
There was a problem hiding this comment.
The agent card URL is hardcoded to localhost:10002. For better maintainability and flexibility, this should be configurable via an environment variable.
| "http://localhost:10002/.well-known/agent-card.json", | |
| process.env["AGENT_CARD_URL"] || "http://localhost:10002/.well-known/agent-card.json", |
| if (done) break; | ||
| buffer += decoder.decode(value, { stream: true }); | ||
|
|
||
| const lines = buffer.split('\n\n'); |
There was a problem hiding this comment.
| @@ -0,0 +1 @@ | |||
| {"root":["./src/app.tsx","./src/client.ts","./src/main.tsx","./src/configs/index.ts","./src/configs/restaurant.ts","./src/configs/types.ts","./src/mock/index.ts","./src/mock/restaurantmessages.ts","./src/theme/default-theme.ts"],"version":"5.9.3"} No newline at end of file | |||
NOTE: This is a library-level fix, not a shell-sample fix, and is NOT required for PR google#1186 to merge. Including it on this branch to move quickly — surfaced while smoke-testing google#1186. Cherry-pick or drop independently of the shell commits. Three small things in basic catalog v0.9: 1. The 'weight' schema property (CommonProps in basic_components.ts) was not implemented in any React v0.9 component. Agents emitting weight: 1 on Image and weight: 2 on a sibling Column got default flex behavior (effectively no proportional sizing). 2. Image used width: 100% with no min-width: 0, so as a flex child it couldn't shrink below its intrinsic width. With full-resolution agent- served images this pushed sibling columns out of view. 3. Image inherited align-items: stretch from its parent Row, then objectFit: 'fill' (the default) distorted the image vertically. Fix: in Image.tsx and Column.tsx, when props.weight is a number apply it as `flex: <w> <w> 0`; add `min-width: 0`; and on Image, `align-self: flex-start` so the row's stretch alignment doesn't deform photos. Default objectFit / max-height for Image and a sensible card max-width in the shell are still open design questions; not addressed here.
andrewkolos
left a comment
There was a problem hiding this comment.
Oops, I didn't use GitHub's review UI for my last comment.
Anyway, it looks like a bunch of changes landed upstream since I cloned yesterday. I tested against the in-repo alpha.1 versions (whatever your branch had checked in), which might be out of date. I'll try to check whether any of this or not addressed the problems I ran into.
My original comment was on the wrong thread. Oops. Re-posting it here: I checked this out locally and tried to run the react shell demo with the restaurant finder agent, but ran into issues. What I did (more or less): Here's what I found:
I used an agent to produce this branch off of this PR with atomic fixes to individual issues it reported. On this branch I was able to go through the restaurant finder experience without any noticeable issues. I've published it and you can view individual commit messages and diffs here: jacobsimionato/A2UI@react-samples-4...andrewkolos:A2UI:fix/react-shell-v0.9-smoke-test-fixes. Normally, I would do a full walkthrough and validation of each change, file pre-existing issues separately, document remaining bugs in detail here, etc.; but I'm guessing 1) we would like to move as quickly as possible here and 2) if you happen to be more experienced with the code files under change, you could more quickly swat some of the changes on my branch as hallucinated red-herrings (or hacky "fixes") if they happen to be so. |
Hey thanks so much for investigating this - your changes LGTM! Could you please package up my original PR and your tweaks into a new PR and mail it to David and Jose tomorrow? If you're able to reduce the diff against main where possible, that would be great too e.g. maybe we can revert some of the changes to the example JSON in the restaurant finder agent. Re the management of surfaces - it's true that the way the restaurant finder agent does this is undesirable and not really in the spirit of A2UI. I think the workaround you're doing to avoid duplicate surface errors is good for now. In the future, we should redesign the agent so that it manages surfaces in a better way e.g. deleting the surface before recreating it, or explicitly reusing the surface, with no duplicate
I was aware of the first issue but not the second. If the second issue is also present, then we should consider how to solve that at the library level, so that developers can use ADK / A2A with A2UI easily. But this is a future problem - your workaround LGTM for now. |
|
Closing as superseded by #1262. |
Description of Changes
This pull request updates the React Shell sample to support A2UI version 0.9. Key changes include:
A2uiSurfacecomponent andMessageProcessorfrom@a2ui/web_core.samples/client/react/shell/middleware/a2a.ts) to handle communication with A2A agents. This middleware supports both standard POST requests and Server-Sent Events (SSE) for streaming responses.A2UIClientin the React Shell to support streaming A2UI messages from the agent via the new middleware.Rationale
The A2UI protocol is evolving to v0.9, which introduces a more streamlined message format and better support for dynamic UI updates. Updating the React Shell ensures it remains a reference implementation for the latest version of the protocol. The addition of the A2A middleware simplifies client-side development by providing a unified way to talk to A2A agents with built-in streaming support.
Testing/Running Instructions
Prerequisites
uvinstalled for running the Python agent.npminstalled.Steps
cd samples/client/react/shell npm install npm run devhttp://localhost:5003in your browser.