Skip to content

Conversation

@faizan842
Copy link

@faizan842 faizan842 commented Jan 19, 2026

Description

This PR adds a new sample agent (samples/agent/langgraph/restaurant_finder) implemented using LangGraph.

This sample demonstrates how to build A2UI-compliant applications using the LangGraph framework, replicating the functionality of the existing ADK restaurant finder. It provides a reference architecture for handling the A2UI protocol (UI generation, event handling, and data updates) within a LangGraph state machine.

Key improvements included in this implementation:

  • Robust JSON Handling: Includes validation logic to auto-repair common LLM syntax errors (e.g. unquoted keys).
  • Correct Event Parsing: Updates the executor logic to correctly handle book_restaurant and submit_booking events.
  • Stateless Design: Configured without a checkpointer to handle independent A2A server requests cleanly.

Pre-launch Checklist

  • I signed the CLA.
  • I read the Contributors Guide.
  • I read the Style Guide.
  • I have added updates to the CHANGELOG.
  • I updated/added relevant documentation (Added README.md for the sample).
  • My code changes (if any) have tests (Manually verified determinism and logic).

If you need help, consider asking for advice on the discussion board.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new sample agent for a restaurant finder using LangGraph. The implementation is comprehensive, covering the agent logic, server setup, and UI examples. My review focuses on improving code quality, fixing a critical issue with a hardcoded URL that would affect functionality, and ensuring the sample is easy for developers to use. I've provided suggestions for refactoring to handle state correctly, cleaning up unused code and developer comments, and aligning the project's configuration with its documentation.

Comment on lines 54 to 64
# Create a simple mock context that mimics what the tool expects
# In a real app we might want to pass the real context if we had one
# For now, we hardcode localhost or inject from somewhere if needed,
# but the tool just uses it for replacing URL in data.
# We can try to get base_url from environment or defaults.
base_url = "http://localhost:10002"
# Ideally should come from config, but inside a tool we don't have easy access to state unless we bind it.
# We can rely on default logic inside tool or pass valid one.

ctx = MockToolContext(base_url)
return get_restaurants(cuisine=cuisine or "", location=location or "", tool_context=ctx, count=count)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The base_url is hardcoded within the search_restaurants tool. This will cause incorrect image URLs to be generated if the server is run on a different host or port. The base_url is available in the AgentState, but the tool, being globally defined, cannot access it.

To fix this, you should refactor the graph and tool creation to be stateful, for instance, by moving them inside the RestaurantAgent class. This would allow the tool to be created with access to the base_url from the class instance.

from a2a.types import AgentCapabilities, AgentCard, AgentSkill
from a2ui.a2ui_extension import get_a2ui_agent_extension
from agent_executor import RestaurantAgentExecutor
from agent import RestaurantAgent # To get SUPPORTED_CONTENT_TYPES ideally, but we can hardcode for now or add it to class
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The import RestaurantAgent is not used in this file and should be removed. The associated comment appears to be a developer note that is no longer relevant.

Additionally, there are other developer comments that should be cleaned up to improve code clarity:

  • Lines 66-68: Comment about hardcoding SUPPORTED_CONTENT_TYPES.
  • Lines 103-105: Comment about the images directory.

return {"error_message": "Empty JSON part."}

# Attempt to repair common JSON errors (like unquoted keys)
import re
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The import re statement is inside the validate_response function. According to the PEP 8 style guide, imports should be at the top of the file. Please move this import to the top-level of the module.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plz fix

version = "0.1.0"
description = "Sample LangGraph-based Restaurant finder agent that uses a2ui UI."
readme = "README.md"
requires-python = ">=3.13"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The requires-python is set to >=3.13, but the README.md for this sample specifies Python 3.9 or higher. These should be consistent to avoid confusion for users trying to run the sample. Given that Python 3.13 is a very recent release, you might want to verify if the code can run on an earlier, more common version (like 3.9+) and update this requirement accordingly for broader compatibility.

logger = logging.getLogger(__name__)


def get_restaurants(cuisine: str, location: str, tool_context: ToolContext, count: int = 5) -> str:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The get_restaurants function's tool_context: ToolContext parameter introduces an unnecessary dependency on the ADK (google.adk.tools.tool_context). This requires creating a MockToolContext in agent.py just to pass the base_url.

To simplify and make the code more self-contained within the LangGraph context, consider passing base_url directly as a string argument. This would also require updating the implementation to use the base_url string directly instead of tool_context.state.get("base_url").

Suggested change
def get_restaurants(cuisine: str, location: str, tool_context: ToolContext, count: int = 5) -> str:
def get_restaurants(cuisine: str, location: str, base_url: str, count: int = 5) -> str:

@faizan842
Copy link
Author

Hi @wrenj could you please review this PR? Thanks!

# limitations under the License.

# The A2UI schema remains constant for all A2UI responses.
A2UI_SCHEMA = r'''
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you forked restaurant finder, but let's still clean up some of the noise so we can focus on the langgraph code

load schema from its json file like here
https://github.com/google/A2UI/blob/main/samples/agent/adk/mcp/server.py#L28

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
from a2ui_examples import RESTAURANT_UI_EXAMPLES


def get_ui_prompt(base_url: str, examples: str) -> str:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once you remove the code above now maybe just move these to agent.py and delete this file

# limitations under the License.

RESTAURANT_UI_EXAMPLES = """
---BEGIN SINGLE_COLUMN_LIST_EXAMPLE---
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move examples to standalone json file and load them like here
https://github.com/google/A2UI/blob/main/samples/agent/adk/mcp/server.py#L65

base_url = state.get("base_url", "http://localhost:10002")

# Build system prompt
from langchain_core.messages import SystemMessage
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import at top

response = model_with_tools.invoke(messages_for_model)
return {"messages": [response], "attempts": state.get("attempts", 0) + 1}

def validate_response(state: AgentState):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to do structured output in langgraph? here is how we do it now for adk. Optional if this is a big refactor

https://github.com/google/A2UI/blob/main/a2a_agents/python/a2ui_agent/src/a2ui/extension/send_a2ui_to_client_toolset.py

return {"error_message": "Empty JSON part."}

# Attempt to repair common JSON errors (like unquoted keys)
import re
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plz fix

@wrenj
Copy link
Collaborator

wrenj commented Jan 26, 2026

Hi @wrenj could you please review this PR? Thanks!

sure left some comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants