showcase.py
"""
AG-UI Showcase
==============
Single server exposing all AG-UI Dojo demo endpoints.
Run this to test AG-UI integration with the Dojo frontend at localhost:3000.
Imports agents from individual files and mounts them at Dojo-compatible paths.
"""
from agent_with_media import media_agent
from agentic_chat import agentic_chat_agent
from agno.os import AgentOS
from agno.os.interfaces.agui import AGUI
from backend_feedback import backend_feedback_agent
from backend_tool_rendering import backend_tool_agent
from human_in_the_loop import hitl_agent
from reasoning_agent import chat_agent as reasoning_agent
from shared_state import shared_state_agent
from tool_based_generative_ui import generative_ui_agent
from tool_confirmation import tool_confirmation_agent
from user_input import user_input_agent
agent_os = AgentOS(
agents=[
agentic_chat_agent,
backend_tool_agent,
hitl_agent,
generative_ui_agent,
shared_state_agent,
reasoning_agent,
media_agent,
tool_confirmation_agent,
user_input_agent,
backend_feedback_agent,
],
interfaces=[
AGUI(agent=agentic_chat_agent, prefix="/agentic_chat"),
AGUI(agent=backend_tool_agent, prefix="/backend_tool_rendering"),
AGUI(agent=hitl_agent, prefix="/human_in_the_loop"),
AGUI(agent=generative_ui_agent, prefix="/tool_based_generative_ui"),
AGUI(agent=shared_state_agent, prefix="/shared_state"),
AGUI(agent=reasoning_agent, prefix="/agentic_chat_reasoning"),
AGUI(agent=media_agent, prefix="/agentic_chat_multimodal"),
AGUI(agent=tool_confirmation_agent, prefix="/tool_confirmation"),
AGUI(agent=user_input_agent, prefix="/user_input"),
AGUI(agent=backend_feedback_agent, prefix="/backend_feedback"),
],
)
app = agent_os.get_app()
if __name__ == "__main__":
print("AG-UI Showcase Server")
print("Endpoints:")
print(" /agentic_chat — Chat, Tools, Streaming")
print(" /backend_tool_rendering — Backend tool rendering")
print(" /human_in_the_loop — Task planning with step selection")
print(" /tool_based_generative_ui — Generative UI (action), Tools")
print(" /shared_state — Agent State, Collaborating")
print(" /agentic_chat_reasoning — Chat, Tools, Streaming, Reasoning")
print(" /agentic_chat_multimodal — Chat, Multimodal, Streaming")
print(" /tool_confirmation — Email/delete with confirmation")
print(" /user_input — Text/secret input collection")
print(" /backend_feedback — Multiple choice selection")
agent_os.serve(app="showcase:app", reload=True, port=9001)
agent_with_media.py
"""
Agent With Media
================
AG-UI agent that accepts multimodal input (images, audio, video, documents).
Uses Google Gemini to analyze attached files. Set GOOGLE_API_KEY env var.
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.google import Gemini
from agno.os import AgentOS
from agno.os.interfaces.agui import AGUI
# ---------------------------------------------------------------------------
# Create Example
# ---------------------------------------------------------------------------
media_agent = Agent(
name="Media Agent",
model=Gemini(id="gemini-2.5-flash"),
db=SqliteDb(db_file="/tmp/agui_agent_with_media.db"),
instructions="Analyze any image, audio, video, or document the user sends and answer their question about it.",
add_datetime_to_context=True,
markdown=True,
)
# Setup your AgentOS app
# Dojo expects: http://localhost:9001/agentic_chat_multimodal/agui
agent_os = AgentOS(
agents=[media_agent],
interfaces=[AGUI(agent=media_agent, prefix="/agentic_chat_multimodal")],
)
app = agent_os.get_app()
# ---------------------------------------------------------------------------
# Run Example
# ---------------------------------------------------------------------------
if __name__ == "__main__":
agent_os.serve(app="agent_with_media:app", port=9001, reload=True)
agentic_chat.py
"""
Agentic Chat — Dojo Demo
========================
Frontend tool: change_background (external_execution)
Backend tool: get_weather (renders as card via useRenderTool)
Dojo expects:
- change_background(background: str) -> changes CSS background
- get_weather(location: str) -> dict with city, temperature, humidity, wind_speed, conditions
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
from agno.tools import tool
@tool(external_execution=True, external_execution_silent=True)
def change_background(background: str) -> str:
"""Change the background color of the chat. Can be anything that the CSS background attribute accepts. Regular colors, linear or radial gradients etc."""
return f"Background changed to {background}"
@tool
def get_weather(location: str) -> dict:
"""Get the current weather for a location."""
data = {
"San Francisco": {
"city": "San Francisco",
"temperature": 18,
"humidity": 65,
"wind_speed": 12,
"conditions": "Sunny",
},
"New York": {
"city": "New York",
"temperature": 22,
"humidity": 55,
"wind_speed": 8,
"conditions": "Cloudy",
},
"Tokyo": {
"city": "Tokyo",
"temperature": 26,
"humidity": 70,
"wind_speed": 5,
"conditions": "Rainy",
},
"London": {
"city": "London",
"temperature": 15,
"humidity": 80,
"wind_speed": 15,
"conditions": "Overcast",
},
"Paris": {
"city": "Paris",
"temperature": 20,
"humidity": 60,
"wind_speed": 10,
"conditions": "Partly cloudy",
},
}
return data.get(
location,
{
"city": location,
"temperature": 20,
"humidity": 60,
"wind_speed": 10,
"conditions": "Partly cloudy",
},
)
agentic_chat_agent = Agent(
name="agentic_chat",
model=OpenAIResponses(id="gpt-5.5"),
db=SqliteDb(db_file="/tmp/agentic_chat.db"),
tools=[change_background, get_weather],
instructions="""You are a helpful assistant with frontend and backend capabilities.
Tools available:
- change_background: Changes the page background. Accepts CSS values (colors, gradients). Only use when explicitly asked.
- get_weather: Gets weather for a location. Returns temperature, humidity, wind speed, and conditions.
Be helpful and use tools when appropriate.""",
markdown=True,
)
backend_feedback.py
"""
Backend Feedback — Dojo Demo
============================
Frontend-provided tool: get_user_choice (via useHumanInTheLoop)
The frontend defines this tool for presenting multiple choices to the user.
When called, it renders a card with radio buttons for selection.
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
backend_feedback_agent = Agent(
name="backend_feedback",
model=OpenAIResponses(id="gpt-5.5"),
db=SqliteDb(db_file="/tmp/agui_backend_feedback.db"),
instructions="""You are an assistant that helps users make decisions.
When you need the user to choose from options:
- Call get_user_choice with: question, options (array of strings)
- Wait for user to select an option
Example get_user_choice call:
{
"question": "What type of cuisine would you prefer for dinner?",
"options": ["Italian", "Japanese", "Mexican", "Indian", "Thai"]
}
After receiving the selection:
- Acknowledge their choice
- Provide relevant recommendations or next steps based on their selection
- Ask follow-up questions if needed using get_user_choice again""",
markdown=True,
)
backend_tool_rendering.py
"""
Backend Tool Rendering — Dojo Demo
===================================
Backend tool: get_weather (renders as weather card via useRenderTool)
Dojo expects get_weather(location: str) with detailed return:
- city, temperature, humidity, wind_speed, conditions
- Rendered as a styled weather card in the frontend
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
from agno.tools import tool
@tool
def get_weather(location: str) -> dict:
"""Get detailed weather for a location. Returns structured data for frontend rendering."""
data = {
"San Francisco": {
"city": "San Francisco",
"temperature": 18,
"humidity": 65,
"wind_speed": 12,
"conditions": "Sunny",
},
"New York": {
"city": "New York",
"temperature": 22,
"humidity": 55,
"wind_speed": 8,
"conditions": "Cloudy",
},
"Tokyo": {
"city": "Tokyo",
"temperature": 26,
"humidity": 70,
"wind_speed": 5,
"conditions": "Rainy",
},
"London": {
"city": "London",
"temperature": 15,
"humidity": 80,
"wind_speed": 15,
"conditions": "Overcast",
},
"Paris": {
"city": "Paris",
"temperature": 20,
"humidity": 60,
"wind_speed": 10,
"conditions": "Partly cloudy",
},
}
return data.get(
location,
{
"city": location,
"temperature": 20,
"humidity": 60,
"wind_speed": 10,
"conditions": "Partly cloudy",
},
)
backend_tool_agent = Agent(
name="backend_tool_rendering",
model=OpenAIResponses(id="gpt-5.5"),
db=SqliteDb(db_file="/tmp/agui_backend_tool_rendering.db"),
tools=[get_weather],
instructions="""You help users check weather. When asked about weather, always use the get_weather tool.
The tool returns structured data that the frontend will render as a weather card.""",
markdown=True,
)
human_in_the_loop.py
"""
Human in the Loop — Dojo Demo
==============================
Frontend-provided tool: generate_task_steps (external_execution)
The frontend defines this tool via useHumanInTheLoop hook. The agent calls it,
and the frontend renders an interactive step-selector UI where the user can
toggle steps on/off and confirm.
Backend does NOT define the tool — it comes from the frontend in the request.
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
hitl_agent = Agent(
name="human_in_the_loop",
model=OpenAIResponses(id="gpt-5.5"),
db=SqliteDb(db_file="/tmp/agui_human_in_the_loop.db"),
instructions="""You are a task planning assistant. When asked to plan something:
1. **IMMEDIATELY call the generate_task_steps tool** with a list of steps
2. Generate exactly 10 clear, actionable steps
3. Each step must be an object with:
- description: Brief imperative form (e.g., "Research travel options")
- status: Set to "enabled" initially
Example tool call for "plan a trip to Mars":
{
"steps": [
{"description": "Research Mars travel options", "status": "enabled"},
{"description": "Prepare necessary equipment", "status": "enabled"},
{"description": "Complete health screenings", "status": "enabled"},
...
]
}
After calling the tool:
- Briefly confirm: "I've created a 10-step plan for you!"
- Don't repeat the steps (they're visible in the UI)
- Ask the user to review and select which steps to perform
When user provides feedback (after clicking "Perform Steps"):
- Acknowledge which steps were approved
- Provide a brief summary of next actions""",
markdown=True,
)
reasoning_agent.py
"""
Reasoning Agent
===============
Demonstrates reasoning agent.
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
from agno.os import AgentOS
from agno.os.interfaces.agui import AGUI
from agno.tools.websearch import WebSearchTools
# ---------------------------------------------------------------------------
# Create Example
# ---------------------------------------------------------------------------
chat_agent = Agent(
name="Assistant",
model=OpenAIResponses(id="o4-mini"),
db=SqliteDb(db_file="/tmp/agui_reasoning_agent.db"),
instructions="You are a helpful AI assistant.",
add_datetime_to_context=True,
add_history_to_context=True,
add_location_to_context=True,
timezone_identifier="Etc/UTC",
markdown=True,
tools=[WebSearchTools()],
)
# Setup your AgentOS app
agent_os = AgentOS(
agents=[chat_agent],
interfaces=[AGUI(agent=chat_agent)],
)
app = agent_os.get_app()
# ---------------------------------------------------------------------------
# Run Example
# ---------------------------------------------------------------------------
if __name__ == "__main__":
"""Run your AgentOS.
You can see the configuration and available apps at:
http://localhost:9001/config
"""
agent_os.serve(app="reasoning_agent:app", reload=True, port=9001)
shared_state.py
"""
Shared State — Dojo Demo
=========================
Agent with session state that syncs with frontend.
Dojo expects state structure:
{
"recipe": {
"title": str,
"skill_level": "Beginner" | "Intermediate" | "Advanced",
"cooking_time": "5 min" | "15 min" | "30 min" | "45 min" | "60+ min",
"special_preferences": List[str], # "High Protein", "Low Carb", "Spicy", etc.
"ingredients": List[{icon: str, name: str, amount: str}],
"instructions": List[str]
}
}
The agent uses update_session_state tool to modify state, which triggers
STATE_DELTA events that the frontend uses to update the recipe UI.
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
shared_state_agent = Agent(
name="shared_state",
model=OpenAIResponses(id="gpt-5.5"),
db=SqliteDb(db_file="/tmp/agui_shared_state.db"),
session_state={
"recipe": {
"title": "Make Your Recipe",
"skill_level": "Intermediate",
"cooking_time": "45 min",
"special_preferences": [],
"ingredients": [
{"icon": "🥕", "name": "Carrots", "amount": "3 large, grated"},
{"icon": "🌾", "name": "All-Purpose Flour", "amount": "2 cups"},
],
"instructions": ["Preheat oven to 350°F (175°C)"],
}
},
add_session_state_to_context=True,
enable_agentic_state=True,
instructions="""You are a recipe assistant. The current recipe state is shown in <session_state>.
Use update_session_state to modify the recipe. The structure is:
- title: Recipe name (string)
- skill_level: "Beginner", "Intermediate", or "Advanced"
- cooking_time: "5 min", "15 min", "30 min", "45 min", or "60+ min"
- special_preferences: List of strings like "High Protein", "Low Carb", "Spicy", "Budget-Friendly", "One-Pot Meal", "Vegetarian", "Vegan"
- ingredients: List of objects with {icon: emoji, name: string, amount: string}
- instructions: List of step strings
When modifying:
1. Read the current state from <session_state>
2. Use update_session_state with the fields you want to change
3. Preserve existing values for fields you don't change
Example: To add an ingredient, include the existing ingredients plus the new one.""",
markdown=True,
)
tool_based_generative_ui.py
"""
Tool Based Generative UI — Dojo Demo
=====================================
Frontend tool: generate_haiku (external_execution)
Dojo expects generate_haiku with:
- japanese: List[str] - 3 lines of haiku in Japanese
- english: List[str] - 3 lines translated to English
- image_name: str - One of the valid image names
- gradient: str - CSS gradient for background
Valid image names (from Dojo):
- Osaka_Castle_Turret_Stone_Wall_Pine_Trees_Daytime.jpg
- Tokyo_Skyline_Night_Tokyo_Tower_Mount_Fuji_View.jpg
- Itsukushima_Shrine_Miyajima_Floating_Torii_Gate_Sunset_Long_Exposure.jpg
- Takachiho_Gorge_Waterfall_River_Lush_Greenery_Japan.jpg
- Bonsai_Tree_Potted_Japanese_Art_Green_Foliage.jpeg
- Shirakawa-go_Gassho-zukuri_Thatched_Roof_Village_Aerial_View.jpg
- Ginkaku-ji_Silver_Pavilion_Kyoto_Japanese_Garden_Pond_Reflection.jpg
- Senso-ji_Temple_Asakusa_Cherry_Blossoms_Kimono_Umbrella.jpg
- Cherry_Blossoms_Sakura_Night_View_City_Lights_Japan.jpg
- Mount_Fuji_Lake_Reflection_Cherry_Blossoms_Sakura_Spring.jpg
"""
from typing import List
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
from agno.tools import tool
VALID_IMAGE_NAMES = [
"Osaka_Castle_Turret_Stone_Wall_Pine_Trees_Daytime.jpg",
"Tokyo_Skyline_Night_Tokyo_Tower_Mount_Fuji_View.jpg",
"Itsukushima_Shrine_Miyajima_Floating_Torii_Gate_Sunset_Long_Exposure.jpg",
"Takachiho_Gorge_Waterfall_River_Lush_Greenery_Japan.jpg",
"Bonsai_Tree_Potted_Japanese_Art_Green_Foliage.jpeg",
"Shirakawa-go_Gassho-zukuri_Thatched_Roof_Village_Aerial_View.jpg",
"Ginkaku-ji_Silver_Pavilion_Kyoto_Japanese_Garden_Pond_Reflection.jpg",
"Senso-ji_Temple_Asakusa_Cherry_Blossoms_Kimono_Umbrella.jpg",
"Cherry_Blossoms_Sakura_Night_View_City_Lights_Japan.jpg",
"Mount_Fuji_Lake_Reflection_Cherry_Blossoms_Sakura_Spring.jpg",
]
@tool(external_execution=True, external_execution_silent=True)
def generate_haiku(
japanese: List[str], english: List[str], image_name: str, gradient: str
) -> str:
"""Generate and display a haiku with image and styling.
Args:
japanese: 3 lines of haiku in Japanese
english: 3 lines of haiku translated to English
image_name: One relevant image name from the valid list
gradient: CSS gradient color for the background (e.g., "linear-gradient(135deg, #667eea 0%, #764ba2 100%)")
"""
return "Haiku generated and displayed in frontend"
generative_ui_agent = Agent(
name="tool_based_generative_ui",
model=OpenAIResponses(id="gpt-5.5"),
db=SqliteDb(db_file="/tmp/agui_tool_based_generative_ui.db"),
tools=[generate_haiku],
instructions=f"""You are a haiku poet. When asked to create a haiku:
1. Create a beautiful haiku in both English (5-7-5 syllables) and Japanese
2. Choose a relevant image from: {", ".join(VALID_IMAGE_NAMES)}
3. Choose a beautiful CSS gradient for the background
4. Use the generate_haiku tool with all parameters
Example gradient: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
The frontend will render your haiku with the image and gradient as a beautiful card.""",
markdown=True,
)
tool_confirmation.py
"""
Tool Confirmation — Dojo Demo
=============================
Frontend-provided tools: send_email, delete_files (via useHumanInTheLoop)
The frontend defines these tools as requiring confirmation before execution.
When the agent calls them, the frontend renders a confirmation card.
User can approve or reject the action.
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
tool_confirmation_agent = Agent(
name="tool_confirmation",
model=OpenAIResponses(id="gpt-5.5"),
db=SqliteDb(db_file="/tmp/agui_tool_confirmation.db"),
instructions="""You are an assistant that can send emails and delete files.
Both actions require user confirmation before execution.
When asked to send an email:
- Call the send_email tool with: to, subject, body
- Wait for user confirmation
When asked to delete files:
- Call the delete_files tool with: action, description, details
- Wait for user confirmation
Example email tool call:
{
"to": "alice@example.com",
"subject": "Hello!",
"body": "Just wanted to say hi."
}
Example delete tool call:
{
"action": "Delete temporary files",
"description": "Remove all .tmp files from the project",
"details": {"count": "15 files", "size": "2.3 MB"}
}
After user confirms or rejects:
- Acknowledge the decision
- If confirmed, confirm the action was taken
- If rejected, acknowledge and ask if there's anything else""",
markdown=True,
)
user_input.py
"""
User Input — Dojo Demo
======================
Frontend-provided tools: get_user_text, get_secret_input (via useHumanInTheLoop)
The frontend defines these tools for collecting user input inline.
- get_user_text: Shows a text input field
- get_secret_input: Shows a password-style input for sensitive data
"""
from agno.agent.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
user_input_agent = Agent(
name="user_input",
model=OpenAIResponses(id="gpt-5.5"),
db=SqliteDb(db_file="/tmp/agui_user_input.db"),
instructions="""You are an assistant that can request input from the user.
When you need text input from the user:
- Call get_user_text with: prompt, placeholder (optional)
- Wait for user to provide input
When you need sensitive input (API keys, passwords):
- Call get_secret_input with: prompt, service (optional)
- Wait for user to provide input
Example get_user_text call:
{
"prompt": "What would you like to search for?",
"placeholder": "Enter search term..."
}
Example get_secret_input call:
{
"prompt": "Please enter your OpenAI API key",
"service": "OpenAI"
}
After receiving input:
- Acknowledge what was provided (don't echo secrets)
- Use the input to complete the user's request""",
markdown=True,
)
Run the Example
1
Set up your virtual environment
uv venv --python 3.12
source .venv/bin/activate
uv venv --python 3.12
.venv\Scripts\activate
2
Install dependencies
uv pip install -U "agno[agui,os]" ddgs google-genai openai requests
3
Export your API keys
export GOOGLE_API_KEY="your_google_api_key_here"
export OPENAI_API_KEY="your_openai_api_key_here"
$Env:GOOGLE_API_KEY="your_google_api_key_here"
$Env:OPENAI_API_KEY="your_openai_api_key_here"
4
Run the example
Save the code blocks above as
showcase.py, agent_with_media.py, agentic_chat.py, backend_feedback.py, backend_tool_rendering.py, human_in_the_loop.py, reasoning_agent.py, shared_state.py, tool_based_generative_ui.py, tool_confirmation.py, user_input.py in the same directory, then run:python showcase.py