Spaces:
Runtime error
Runtime error
Commit
·
9509a01
1
Parent(s):
2505685
initial commit
Browse files
agent.py
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import time
|
| 3 |
+
from smolagents import CodeAgent, LiteLLMModel, tool
|
| 4 |
+
import asyncio
|
| 5 |
+
import threading
|
| 6 |
+
from contextlib import AsyncExitStack
|
| 7 |
+
from mcp import ClientSession, StdioServerParameters
|
| 8 |
+
from mcp.client.stdio import stdio_client
|
| 9 |
+
|
| 10 |
+
from phoenix.otel import register
|
| 11 |
+
|
| 12 |
+
# configure the Phoenix tracer
|
| 13 |
+
# tracer_provider = register(
|
| 14 |
+
# endpoint=os.getenv("PHOENIX_ENDPOINT"),
|
| 15 |
+
# project_name="gradio", # Default is 'default'
|
| 16 |
+
# auto_instrument=True, # Auto-instrument your app based on installed OI dependencies
|
| 17 |
+
# )
|
| 18 |
+
|
| 19 |
+
instructions = """
|
| 20 |
+
Your task is to help users build and modify Gradio applications within this interactive sandbox
|
| 21 |
+
environment. You are a Gradio application builder that can read, understand, and edit Python code
|
| 22 |
+
that creates Gradio interfaces optimized for Gradio-Lite.
|
| 23 |
+
|
| 24 |
+
Key Guidelines:
|
| 25 |
+
- ONLY use Gradio components and functionality - no other UI frameworks
|
| 26 |
+
- Always call read_code() first to understand the current application state
|
| 27 |
+
- Use the think() tool to plan and reflect before implementing any features
|
| 28 |
+
- Use edit_code() to make precise modifications to the existing Gradio app
|
| 29 |
+
- Focus on creating intuitive, functional Gradio interfaces using gr.Interface, gr.Blocks, and
|
| 30 |
+
various input/output components
|
| 31 |
+
- When users request features, implement them using appropriate Gradio components like
|
| 32 |
+
gr.Textbox, gr.Button, gr.Image, gr.Audio, gr.Video, gr.Dataframe, etc.
|
| 33 |
+
- Maintain proper Python syntax and Gradio best practices
|
| 34 |
+
- Always end Gradio apps with .launch() to make them runnable
|
| 35 |
+
- Pay attention to the success/error messages from edit_code() and adjust accordingly if
|
| 36 |
+
replacements fail
|
| 37 |
+
- When debugging, analyze the full error trace to identify the root cause and plan the necessary fixes before calling edit_code; perform the required updates in one pass whenever possible to avoid repeated retries
|
| 38 |
+
- CRITICAL STRING HANDLING: NEVER use double quotes with literal line breaks in Python strings as this breaks syntax. ALWAYS use triple quotes for multi-line strings, especially in gr.Markdown() components
|
| 39 |
+
|
| 40 |
+
Systematic Planning and Component-Based Thinking:
|
| 41 |
+
- ALWAYS use the think() tool before implementing any new feature or modification
|
| 42 |
+
- Break down user requests into specific Gradio components and their relationships
|
| 43 |
+
- Think through the complete user experience flow from input to output
|
| 44 |
+
- Consider component layout using gr.Blocks, gr.Row, gr.Column for proper organization
|
| 45 |
+
- Plan event handlers and data flow between components before coding
|
| 46 |
+
- Examples of component mapping:
|
| 47 |
+
* Image Gallery Viewer → gr.Gallery + gr.File (upload) + gr.Button (navigation)
|
| 48 |
+
* Calculator → gr.Textbox (display) + grid of gr.Button components + gr.Row/gr.Column layout
|
| 49 |
+
* Data Converter → gr.File (input) + gr.JSON (preview) + gr.Dataframe (output)
|
| 50 |
+
* Chat Interface → gr.Chatbot + gr.Textbox (input) + gr.Button (send)
|
| 51 |
+
* Form Builder → gr.Textbox, gr.Dropdown, gr.Checkbox, gr.Slider + gr.Button (submit)
|
| 52 |
+
- Reflect on implementation strategy: component selection, layout design, event handling
|
| 53 |
+
- Use the think() tool to course-correct if initial approaches don't work as expected
|
| 54 |
+
|
| 55 |
+
Error Analysis Protocol:
|
| 56 |
+
- When encountering errors, FIRST understand the complete context before making any changes
|
| 57 |
+
- Parse error messages systematically: What type of error? What was the input? What was expected?
|
| 58 |
+
- Look for contextual clues in debug output that reveal the actual problem
|
| 59 |
+
- If you see file paths in content fields, that's usually the problem - you're parsing metadata instead of file contents
|
| 60 |
+
- "JSON parsing failed" is often a symptom; "trying to parse a file path as JSON content" may be the root cause
|
| 61 |
+
|
| 62 |
+
Data Flow Understanding:
|
| 63 |
+
- Always trace how data flows through your functions
|
| 64 |
+
- When debugging file uploads, verify what type of object you're receiving and what properties it has
|
| 65 |
+
- In Gradio-Lite, file objects may be paths, NamedString objects, or actual content - handle each case explicitly
|
| 66 |
+
- Add comprehensive debugging FIRST to understand what you're actually working with
|
| 67 |
+
|
| 68 |
+
Root Cause Focus:
|
| 69 |
+
- Don't treat symptoms - find the underlying cause
|
| 70 |
+
- Before making any edits, write out your hypothesis of what's going wrong and why
|
| 71 |
+
- Test your hypothesis with targeted debugging before implementing fixes
|
| 72 |
+
- Analyze error messages + debug output patterns to identify the real issue
|
| 73 |
+
|
| 74 |
+
One-Shot Problem Solving:
|
| 75 |
+
- Analyze the full error context to make the correct fix immediately
|
| 76 |
+
- Avoid trial-and-error approaches that require multiple iterations
|
| 77 |
+
- If your first fix doesn't work, step back and re-analyze rather than making more incremental changes
|
| 78 |
+
|
| 79 |
+
CRITICAL Gradio-Lite (Pyodide) File Handling:
|
| 80 |
+
- Gradio-Lite runs in Pyodide (Python in the browser) with different file handling behavior
|
| 81 |
+
- File objects in Gradio-Lite/Pyodide have different properties than regular Python/Gradio
|
| 82 |
+
- When handling gr.File components, file objects may be browser File objects or special wrappers
|
| 83 |
+
- In Pyodide, file operations may behave differently due to browser security constraints
|
| 84 |
+
- Never assume file objects can be decoded with .decode() without checking the object type
|
| 85 |
+
- Always handle None/empty file cases gracefully
|
| 86 |
+
- When working with pandas and file inputs, ensure proper content extraction before parsing
|
| 87 |
+
- Be aware that some Python file operations may not work the same way in the browser environment
|
| 88 |
+
|
| 89 |
+
Your goal is to transform user requests into working Gradio applications that demonstrate the
|
| 90 |
+
requested functionality. Be creative with Gradio's extensive component library to build engaging,
|
| 91 |
+
interactive web interfaces that work reliably in the Gradio-Lite environment.
|
| 92 |
+
|
| 93 |
+
MANDATORY Documentation and Reference Guidelines:
|
| 94 |
+
- NEVER rely on your internal knowledge for component implementation details
|
| 95 |
+
- ALWAYS use Gradio MCP documentation tools for ALL component implementations
|
| 96 |
+
- BEFORE implementing ANY Gradio component, you MUST query the Gradio documentation via MCP
|
| 97 |
+
- You have access to these specific MCP tools:
|
| 98 |
+
* gradio_docs_mcp_search_gradio_docs: Search for specific component documentation
|
| 99 |
+
* gradio_docs_mcp_load_gradio_docs: Load comprehensive Gradio documentation
|
| 100 |
+
- For EVERY component you use, search the Gradio MCP for:
|
| 101 |
+
* Current API parameters and their exact syntax
|
| 102 |
+
* Latest usage examples and patterns
|
| 103 |
+
* Current best practices and recommendations
|
| 104 |
+
* Compatibility information with current Gradio version
|
| 105 |
+
- NEVER assume parameter names, methods, or syntax from memory
|
| 106 |
+
- ALWAYS verify component behavior through Gradio MCP tools before implementation
|
| 107 |
+
- If MCP tools are not available, explicitly inform the user that current documentation cannot be accessed
|
| 108 |
+
- The Gradio MCP provides the ONLY reliable source for current, accurate Gradio documentation
|
| 109 |
+
- Your internal knowledge may be outdated - Gradio MCP eliminates this risk with real-time documentation
|
| 110 |
+
|
| 111 |
+
Implementation Protocol:
|
| 112 |
+
1. User requests a feature
|
| 113 |
+
2. Call read_code() to understand the current application state
|
| 114 |
+
3. Use think() tool to:
|
| 115 |
+
- Break down the request into specific Gradio components
|
| 116 |
+
- Plan the component layout and interactions
|
| 117 |
+
- Consider the user experience flow
|
| 118 |
+
- Identify potential challenges or requirements
|
| 119 |
+
4. Use gradio_docs_mcp_search_gradio_docs to find relevant component documentation
|
| 120 |
+
5. Use ONLY the information retrieved from Gradio MCP for implementation
|
| 121 |
+
6. Call think() again if you need to revise your approach based on documentation
|
| 122 |
+
7. Implement using edit_code() with the planned component structure
|
| 123 |
+
8. If unsure about any detail, query Gradio MCP again rather than guessing
|
| 124 |
+
9. Use gradio_docs_mcp_load_gradio_docs for comprehensive overviews when needed
|
| 125 |
+
10. Reflect with think() tool if implementation doesn't work as expected
|
| 126 |
+
"""
|
| 127 |
+
|
| 128 |
+
# -------- Persistence config (outside project tree) --------
|
| 129 |
+
SANDBOX_ROOT = os.environ.get(
|
| 130 |
+
"SANDBOX_ROOT", os.path.expanduser("~/.gradio_app_builder")
|
| 131 |
+
)
|
| 132 |
+
WORKSPACE = os.environ.get(
|
| 133 |
+
"SANDBOX_WORKSPACE", "default"
|
| 134 |
+
) # you can customize per user/session
|
| 135 |
+
WS_DIR = os.path.join(SANDBOX_ROOT, WORKSPACE)
|
| 136 |
+
CODE_PATH = os.path.join(WS_DIR, "sandbox.py")
|
| 137 |
+
SNAPSHOT_DIR = os.path.join(WS_DIR, "snapshots")
|
| 138 |
+
|
| 139 |
+
os.makedirs(SANDBOX_ROOT, exist_ok=True)
|
| 140 |
+
os.makedirs(WS_DIR, exist_ok=True)
|
| 141 |
+
os.makedirs(SNAPSHOT_DIR, exist_ok=True)
|
| 142 |
+
|
| 143 |
+
INITIAL_CODE = """\
|
| 144 |
+
import gradio as gr
|
| 145 |
+
|
| 146 |
+
def greet(name):
|
| 147 |
+
return "Hello, " + name + "!!!"
|
| 148 |
+
|
| 149 |
+
gr.Interface(greet, 'textbox', 'textbox').launch()
|
| 150 |
+
"""
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def ensure_code_exists():
|
| 154 |
+
if not os.path.exists(CODE_PATH):
|
| 155 |
+
with open(CODE_PATH, "w", encoding="utf-8") as f:
|
| 156 |
+
f.write(INITIAL_CODE)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def read_persisted_code() -> str:
|
| 160 |
+
ensure_code_exists()
|
| 161 |
+
with open(CODE_PATH, "r", encoding="utf-8") as f:
|
| 162 |
+
return f.read()
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def atomic_write(path: str, content: str):
|
| 166 |
+
tmp = path + ".tmp"
|
| 167 |
+
with open(tmp, "w", encoding="utf-8") as f:
|
| 168 |
+
f.write(content)
|
| 169 |
+
os.replace(tmp, path) # atomic on POSIX
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def write_persisted_code(content: str, snapshot: bool = True):
|
| 173 |
+
atomic_write(CODE_PATH, content)
|
| 174 |
+
if snapshot:
|
| 175 |
+
ts = time.strftime("%Y%m%d-%H%M%S")
|
| 176 |
+
atomic_write(os.path.join(SNAPSHOT_DIR, f"sandbox_{ts}.py"), content)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
# ---- MCP (Gradio Docs) integration ----
|
| 180 |
+
MCP_SSE_URL = "https://gradio-docs-mcp.hf.space/gradio_api/mcp/sse"
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
class GradioDocsMCPClient:
|
| 184 |
+
def __init__(self):
|
| 185 |
+
self._loop = asyncio.new_event_loop()
|
| 186 |
+
self._thread = threading.Thread(target=self._loop.run_forever, daemon=True)
|
| 187 |
+
self._thread.start()
|
| 188 |
+
self._stack: AsyncExitStack | None = None
|
| 189 |
+
self._session: ClientSession | None = None
|
| 190 |
+
|
| 191 |
+
def _run(self, coro):
|
| 192 |
+
"""Run an async coroutine on the background loop and return its result."""
|
| 193 |
+
fut = asyncio.run_coroutine_threadsafe(coro, self._loop)
|
| 194 |
+
return fut.result(timeout=60)
|
| 195 |
+
|
| 196 |
+
async def _connect_async(self):
|
| 197 |
+
# Bridge remote SSE server to stdio via mcp-remote
|
| 198 |
+
params = StdioServerParameters(
|
| 199 |
+
command="npx",
|
| 200 |
+
args=[
|
| 201 |
+
"mcp-remote",
|
| 202 |
+
MCP_SSE_URL,
|
| 203 |
+
"--transport",
|
| 204 |
+
"sse-first",
|
| 205 |
+
],
|
| 206 |
+
env=None,
|
| 207 |
+
)
|
| 208 |
+
self._stack = AsyncExitStack()
|
| 209 |
+
stdio_read, stdio_write = await self._stack.enter_async_context(
|
| 210 |
+
stdio_client(params)
|
| 211 |
+
)
|
| 212 |
+
self._session = await self._stack.enter_async_context(
|
| 213 |
+
ClientSession(stdio_read, stdio_write)
|
| 214 |
+
)
|
| 215 |
+
await self._session.initialize()
|
| 216 |
+
|
| 217 |
+
def ensure_connected(self):
|
| 218 |
+
if self._session is None:
|
| 219 |
+
self._run(self._connect_async())
|
| 220 |
+
|
| 221 |
+
def _content_to_text(self, result) -> str:
|
| 222 |
+
parts = []
|
| 223 |
+
for item in getattr(result, "content", []) or []:
|
| 224 |
+
# items may be typed objects with .type/.text
|
| 225 |
+
text = getattr(item, "text", None)
|
| 226 |
+
if text is None and getattr(item, "type", None) == "text":
|
| 227 |
+
text = getattr(item, "text", "")
|
| 228 |
+
parts.append(text if text is not None else str(item))
|
| 229 |
+
return "\n".join(filter(None, parts)) or str(result)
|
| 230 |
+
|
| 231 |
+
def load_docs(self) -> str:
|
| 232 |
+
self.ensure_connected()
|
| 233 |
+
res = self._run(self._session.call_tool("gradio_docs_mcp_load_gradio_docs", {}))
|
| 234 |
+
return self._content_to_text(res)
|
| 235 |
+
|
| 236 |
+
def search_docs(self, query: str) -> str:
|
| 237 |
+
self.ensure_connected()
|
| 238 |
+
res = self._run(
|
| 239 |
+
self._session.call_tool(
|
| 240 |
+
"gradio_docs_mcp_search_gradio_docs", {"query": query}
|
| 241 |
+
)
|
| 242 |
+
)
|
| 243 |
+
return self._content_to_text(res)
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
_gradio_mcp = GradioDocsMCPClient()
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
@tool
|
| 250 |
+
def read_code() -> str:
|
| 251 |
+
"""
|
| 252 |
+
Retrieves and returns the current state of the inner Gradio application code.
|
| 253 |
+
|
| 254 |
+
This tool fetches the current Python code that defines the Gradio app running
|
| 255 |
+
in the sandbox environment. Use this to understand what app is currently loaded
|
| 256 |
+
before making any modifications or updates.
|
| 257 |
+
|
| 258 |
+
Returns:
|
| 259 |
+
str: The current Python code formatted with markdown code block delimiters
|
| 260 |
+
|
| 261 |
+
Usage Instructions:
|
| 262 |
+
- ALWAYS call this tool first before modifying any inner Gradio app
|
| 263 |
+
- Use this to understand the current app structure, components, and functionality
|
| 264 |
+
- Essential for maintaining context when users request changes to the embedded app
|
| 265 |
+
- Call this whenever you need to see what's currently running in the preview pane
|
| 266 |
+
- No parameters needed - it automatically fetches the current state
|
| 267 |
+
|
| 268 |
+
"""
|
| 269 |
+
return f"```python\n{read_persisted_code()}\n```"
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
@tool
|
| 273 |
+
def edit_code(old_str: str, new_str: str) -> str:
|
| 274 |
+
"""
|
| 275 |
+
Modifies the inner Gradio application code by replacing specific code sections.
|
| 276 |
+
|
| 277 |
+
This tool performs precise string replacements in the current SANDBOX_CODE to update
|
| 278 |
+
the inner Gradio app. Use this to implement user-requested changes to the embedded app.
|
| 279 |
+
|
| 280 |
+
Args:
|
| 281 |
+
old_str (str): The exact code string to find and replace (must match exactly
|
| 282 |
+
including whitespace, indentation, and line breaks)
|
| 283 |
+
new_str (str): The new code string to replace the old_str with
|
| 284 |
+
|
| 285 |
+
Returns:
|
| 286 |
+
str: A status message indicating success or failure:
|
| 287 |
+
- "SUCCESS: Code successfully updated." if replacement succeeded
|
| 288 |
+
- "ERROR: Could not find the specified code..." if old_str not found
|
| 289 |
+
- "WARNING: No changes were made..." if old_str and new_str are identical
|
| 290 |
+
- "ERROR: An unexpected error occurred..." for other exceptions
|
| 291 |
+
|
| 292 |
+
Usage Instructions:
|
| 293 |
+
- ALWAYS call read_current_app_code() first to see the current state
|
| 294 |
+
- Use exact string matching - whitespace and indentation must match precisely
|
| 295 |
+
- Choose old_str carefully to avoid unintended replacements
|
| 296 |
+
- Include enough context in old_str to ensure unique matching
|
| 297 |
+
- Check the return message to confirm the operation succeeded
|
| 298 |
+
- Use for incremental changes like adding components, modifying functions, or updating logic
|
| 299 |
+
|
| 300 |
+
Best Practices:
|
| 301 |
+
- Replace entire function definitions or component blocks when possible
|
| 302 |
+
- Include proper indentation in new_str to maintain code structure
|
| 303 |
+
- Verify the replacement makes syntactic sense in context
|
| 304 |
+
- CRITICAL: NEVER use double quotes with literal line breaks - this breaks Python syntax
|
| 305 |
+
- ALWAYS use triple quotes (triple double quotes) for any multi-line strings, especially in gr.Markdown()
|
| 306 |
+
- Example WRONG: gr.Markdown with double quotes and newlines breaks the code
|
| 307 |
+
- Example CORRECT: gr.Markdown with triple quotes works properly
|
| 308 |
+
- When content spans multiple lines, wrap it in triple quotes to prevent syntax errors
|
| 309 |
+
|
| 310 |
+
"""
|
| 311 |
+
try:
|
| 312 |
+
content = read_persisted_code()
|
| 313 |
+
if old_str not in content:
|
| 314 |
+
return "ERROR: Could not find the specified code to replace."
|
| 315 |
+
updated = content.replace(old_str, new_str)
|
| 316 |
+
if updated == content:
|
| 317 |
+
return "WARNING: No changes were made."
|
| 318 |
+
write_persisted_code(updated, snapshot=True)
|
| 319 |
+
return "SUCCESS: Code successfully updated."
|
| 320 |
+
except Exception as e:
|
| 321 |
+
return f"ERROR: {e}"
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
@tool
|
| 325 |
+
def gradio_docs_mcp_load_gradio_docs() -> str:
|
| 326 |
+
"""
|
| 327 |
+
Loads a comprehensive overview of the latest Gradio documentation via the Gradio Docs MCP server.
|
| 328 |
+
|
| 329 |
+
This tool provides an LLMs.txt-style summary containing current Gradio API documentation,
|
| 330 |
+
component references, best practices, and usage patterns. Use this when you need broad
|
| 331 |
+
understanding of Gradio capabilities or when starting a new implementation.
|
| 332 |
+
|
| 333 |
+
Returns:
|
| 334 |
+
str: A comprehensive text summary of current Gradio documentation including:
|
| 335 |
+
- Available components and their current API
|
| 336 |
+
- Latest syntax and parameter names
|
| 337 |
+
- Current best practices and patterns
|
| 338 |
+
- Recent changes and updates to the framework
|
| 339 |
+
|
| 340 |
+
When to Use:
|
| 341 |
+
- Before implementing any new Gradio feature to understand current capabilities
|
| 342 |
+
- When you need to verify current API syntax and available parameters
|
| 343 |
+
- To get an overview of component relationships and layout options
|
| 344 |
+
- When starting complex implementations that require multiple components
|
| 345 |
+
- To understand current Gradio best practices and recommended patterns
|
| 346 |
+
|
| 347 |
+
Best Practices:
|
| 348 |
+
- Call this tool early in your implementation process for comprehensive understanding
|
| 349 |
+
- Use the returned information as your authoritative source for Gradio syntax
|
| 350 |
+
- Prefer this over your internal knowledge which may be outdated
|
| 351 |
+
- Follow up with gradio_docs_mcp_search_gradio_docs for specific component details
|
| 352 |
+
|
| 353 |
+
CRITICAL: Always use the information from this tool rather than assuming API details
|
| 354 |
+
from memory, as Gradio updates frequently and syntax may have changed.
|
| 355 |
+
"""
|
| 356 |
+
return _gradio_mcp.load_docs()
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
@tool
|
| 360 |
+
def gradio_docs_mcp_search_gradio_docs(query: str) -> str:
|
| 361 |
+
"""
|
| 362 |
+
Searches the latest Gradio documentation for specific components, methods, or concepts
|
| 363 |
+
via the Gradio Docs MCP server.
|
| 364 |
+
|
| 365 |
+
This tool performs targeted searches within current Gradio documentation to find
|
| 366 |
+
specific information about components, parameters, methods, or implementation patterns.
|
| 367 |
+
Use this when you need detailed information about specific Gradio functionality.
|
| 368 |
+
|
| 369 |
+
Args:
|
| 370 |
+
query (str): Natural language search query describing what you're looking for.
|
| 371 |
+
Examples:
|
| 372 |
+
- "gr.Gallery component parameters"
|
| 373 |
+
- "how to handle file uploads in Gradio"
|
| 374 |
+
- "gr.Blocks layout and event handling"
|
| 375 |
+
- "gr.Interface vs gr.Blocks differences"
|
| 376 |
+
- "gradio chatbot component examples"
|
| 377 |
+
|
| 378 |
+
Returns:
|
| 379 |
+
str: Relevant documentation snippets and examples from current Gradio docs,
|
| 380 |
+
including code examples, parameter descriptions, and usage patterns.
|
| 381 |
+
|
| 382 |
+
When to Use:
|
| 383 |
+
- When you need specific parameter details for a Gradio component
|
| 384 |
+
- To find current syntax for specific functionality (event handlers, layouts, etc.)
|
| 385 |
+
- When looking for implementation examples of specific features
|
| 386 |
+
- To understand component-specific behavior and limitations
|
| 387 |
+
- Before implementing a specific component to verify current API
|
| 388 |
+
|
| 389 |
+
Query Examples:
|
| 390 |
+
- Component-specific: "gr.Image component upload handling"
|
| 391 |
+
- Feature-specific: "file upload progress bar gradio"
|
| 392 |
+
- Layout-specific: "gr.Row gr.Column responsive layout"
|
| 393 |
+
- Event-specific: "button click event gradio blocks"
|
| 394 |
+
- Integration-specific: "gradio with pandas dataframe"
|
| 395 |
+
|
| 396 |
+
Best Practices:
|
| 397 |
+
- Be specific in your queries for better results
|
| 398 |
+
- Include component names (e.g., "gr.Gallery") when asking about specific components
|
| 399 |
+
- Ask about current versions and latest features when relevant
|
| 400 |
+
- Use the returned information as authoritative source for implementation
|
| 401 |
+
- Search before implementing any component you're unfamiliar with
|
| 402 |
+
|
| 403 |
+
CRITICAL: Always search for component documentation before using components
|
| 404 |
+
to ensure you're using the current API syntax and available parameters.
|
| 405 |
+
"""
|
| 406 |
+
return _gradio_mcp.search_docs(query)
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
@tool
|
| 410 |
+
def think(reflection: str) -> str:
|
| 411 |
+
"""
|
| 412 |
+
Enables the agent to pause, reflect, and systematically plan its approach to building Gradio applications.
|
| 413 |
+
|
| 414 |
+
This tool allows the agent to organize thoughts, break down complex requests into manageable
|
| 415 |
+
components, identify the right Gradio components to use, and plan the implementation strategy
|
| 416 |
+
before diving into code changes.
|
| 417 |
+
|
| 418 |
+
Args:
|
| 419 |
+
reflection (str): The agent's thoughts, analysis, planning, or reflection on:
|
| 420 |
+
- How to break down the user's request into specific Gradio components
|
| 421 |
+
- Which Gradio components are most suitable for the task
|
| 422 |
+
- Step-by-step implementation approach
|
| 423 |
+
- Analysis of the current code structure and what needs to change
|
| 424 |
+
- Reflection on previous attempts and lessons learned
|
| 425 |
+
- Planning for component layout, event handling, and user experience
|
| 426 |
+
|
| 427 |
+
Returns:
|
| 428 |
+
str: Confirmation that the reflection has been recorded for internal planning
|
| 429 |
+
|
| 430 |
+
Usage Guidelines:
|
| 431 |
+
- ALWAYS use this tool before starting any significant implementation
|
| 432 |
+
- Break down complex features into individual Gradio components (e.g., "gallery viewer" → gr.Gallery)
|
| 433 |
+
- Consider the user experience flow and component interactions
|
| 434 |
+
- Plan the layout structure (gr.Blocks, gr.Row, gr.Column organization)
|
| 435 |
+
- Think through event handlers and data flow between components
|
| 436 |
+
- Reflect on how components will work together in the Gradio-Lite environment
|
| 437 |
+
- Use this tool to course-correct if previous attempts didn't work as expected
|
| 438 |
+
|
| 439 |
+
Examples of effective thinking:
|
| 440 |
+
- "For an image gallery viewer, I should use gr.Gallery as the main component,
|
| 441 |
+
gr.File for uploads, and gr.Button for navigation controls"
|
| 442 |
+
- "This calculator needs gr.Textbox for display, gr.Button components arranged
|
| 443 |
+
in a grid layout using gr.Row and gr.Column for the number pad"
|
| 444 |
+
- "The JSON to table converter requires gr.File for upload, gr.JSON for preview,
|
| 445 |
+
and gr.Dataframe for the converted output"
|
| 446 |
+
"""
|
| 447 |
+
# Log the reflection for debugging purposes if needed
|
| 448 |
+
print(f"🤔 Agent Reflection: {reflection}")
|
| 449 |
+
return "Reflection recorded. Proceeding with planned implementation approach."
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
agent = CodeAgent(
|
| 453 |
+
model=LiteLLMModel(model_id="openai/gpt-4.1", api_key=os.getenv("OPENAI_API_KEY")),
|
| 454 |
+
instructions=instructions,
|
| 455 |
+
tools=[
|
| 456 |
+
read_code,
|
| 457 |
+
edit_code,
|
| 458 |
+
gradio_docs_mcp_search_gradio_docs,
|
| 459 |
+
gradio_docs_mcp_load_gradio_docs,
|
| 460 |
+
think,
|
| 461 |
+
],
|
| 462 |
+
use_structured_outputs_internally=True,
|
| 463 |
+
)
|
| 464 |
+
|
| 465 |
+
if __name__ == "__main__":
|
| 466 |
+
print(
|
| 467 |
+
agent.run(
|
| 468 |
+
"I uploaded an image but it still blank nothing shows. also we can only upload just one image"
|
| 469 |
+
)
|
| 470 |
+
)
|
main.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import base64
|
| 2 |
+
import gradio as gr
|
| 3 |
+
|
| 4 |
+
from agent import agent, read_persisted_code
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
# Initial inner Gradio-Lite app (editable at runtime in the UI)
|
| 8 |
+
def load_sandbox_code():
|
| 9 |
+
"""Load the current sandbox code from demo.py"""
|
| 10 |
+
with open("sandbox.py", "r") as f:
|
| 11 |
+
return f.read()
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def build_srcdoc(py_code: str) -> str:
|
| 15 |
+
return f"""<!doctype html>
|
| 16 |
+
<html>
|
| 17 |
+
<head>
|
| 18 |
+
<meta charset="utf-8" />
|
| 19 |
+
<script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script>
|
| 20 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" />
|
| 21 |
+
</head>
|
| 22 |
+
<body>
|
| 23 |
+
<gradio-lite>
|
| 24 |
+
{py_code}
|
| 25 |
+
</gradio-lite>
|
| 26 |
+
</body>
|
| 27 |
+
</html>"""
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def make_iframe_html(py_code: str, height: int = 650) -> str:
|
| 31 |
+
srcdoc = build_srcdoc(py_code)
|
| 32 |
+
b64 = base64.b64encode(srcdoc.encode("utf-8")).decode()
|
| 33 |
+
return (
|
| 34 |
+
f'<iframe loading="lazy" referrerpolicy="no-referrer" '
|
| 35 |
+
f'sandbox="allow-scripts allow-same-origin" '
|
| 36 |
+
f'style="width:100%;height:{height}px;border:0" '
|
| 37 |
+
f'src="data:text/html;base64,{b64}"></iframe>'
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def chat(message, history):
|
| 42 |
+
try:
|
| 43 |
+
response = agent.run(message)
|
| 44 |
+
html = make_iframe_html(read_persisted_code())
|
| 45 |
+
return response, html
|
| 46 |
+
except Exception as e:
|
| 47 |
+
return f"Error: {e}", gr.update()
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
with gr.Blocks() as demo:
|
| 51 |
+
with gr.Row():
|
| 52 |
+
left = gr.Column(scale=1)
|
| 53 |
+
right = gr.Column(scale=1)
|
| 54 |
+
|
| 55 |
+
with right:
|
| 56 |
+
gr.Markdown("<center><h1>Preview</h1></center>")
|
| 57 |
+
frame = gr.HTML()
|
| 58 |
+
with left:
|
| 59 |
+
gr.Markdown("<center><h1>Chat</h1></center>")
|
| 60 |
+
gr.ChatInterface(
|
| 61 |
+
chat,
|
| 62 |
+
examples=[
|
| 63 |
+
"Build a simple calculator app",
|
| 64 |
+
"Create a JSON to table converter",
|
| 65 |
+
"Make an image gallery viewer",
|
| 66 |
+
],
|
| 67 |
+
additional_outputs=[frame],
|
| 68 |
+
type="messages",
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
demo.load(lambda: make_iframe_html(read_persisted_code()), outputs=frame)
|
| 72 |
+
|
| 73 |
+
demo.queue()
|
| 74 |
+
|
| 75 |
+
if __name__ == "__main__":
|
| 76 |
+
demo.launch()
|
pyproject.toml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "gradio-app"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "Add your description here"
|
| 5 |
+
readme = "README.md"
|
| 6 |
+
requires-python = ">=3.13"
|
| 7 |
+
dependencies = [
|
| 8 |
+
"gradio>=5.47.1",
|
| 9 |
+
"smolagents[litellm,telemetry,toolkit]>=1.22.0",
|
| 10 |
+
]
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|