sukhrobnurali's picture
Deploy Observable Agent Space
f019486 verified
Raw
History Blame Contribute Delete
4.19 kB
"""Gradio Space: watch a ReAct agent think. Streams every step live to a timeline."""
import time
import gradio as gr
from agent import config, graph, llm, render, tools
EXAMPLES = [
"What's the population of the capital of the country that won the most recent FIFA World Cup, divided by 1000?",
"Find when the Eiffel Tower was completed, then calculate how many years ago that was from 2026.",
"What is 17% of the year the first iPhone was released?",
"What is the height of Mount Everest in meters, multiplied by 3?",
]
HOW_IT_WORKS = """
This agent runs a **ReAct loop**: it alternates between *reasoning* (🧠 thought),
*acting* (🔧 calling a real tool), and *observing* (📄 the tool's result), until it can answer.
Every step is **streamed live** as it happens — you are watching the model think, not a replay.
When a tool returns a weak or irrelevant result, the agent reconsiders and re-queries; that moment
is marked with **↻ revision**. Backend model is Gemini 2.5 Flash (swappable).
"""
def run_agent(task, runs_used):
task = (task or "").strip()
if not task:
yield render.render_timeline([], status="Enter a task above, then press Run."), "", runs_used
return
allowed, message = render.check_run_allowed(runs_used)
if not allowed:
yield render.render_timeline([], status=message), "", runs_used
return
runs_used += 1
events, status, tool_calls = [], "starting...", 0
start = time.perf_counter()
yield render.render_timeline(events, status=status), "", runs_used
try:
client = llm.get_client()
stream = graph.stream_run(
task, client=client, tool_fns=tools.tool_callables(),
declarations=tools.declarations(), system_prompt=config.SYSTEM_PROMPT,
)
for event in stream:
if event["kind"] == "status":
status = event["text"]
else:
status = None
events.append(event)
if event["kind"] == "tool_call":
tool_calls += 1
steps = max((e.get("step", 0) for e in events), default=0)
stats = render.render_stats(steps=steps, tool_calls=tool_calls,
seconds=time.perf_counter() - start)
yield render.render_timeline(events, status=status), stats, runs_used
except llm.QuotaExhaustedError as exc:
yield render.render_timeline(events, status=str(exc)), "", runs_used
except Exception as exc: # never leak a stack trace to the user
msg = f"Something went wrong while running the agent ({type(exc).__name__}). Please try again."
yield render.render_timeline(events, status=msg), "", runs_used
def build_theme():
# Gradio 6.0+ moved theme/css off the Blocks constructor onto launch().
return gr.themes.Base(primary_hue="emerald", neutral_hue="slate").set(
body_background_fill="#0b0f14", block_background_fill="#121821",
)
def build_demo():
with gr.Blocks(title="Observable Agent — Watch It Think") as demo:
gr.Markdown("# 🧠 Observable Agent — *Watch It Think*\nGive it a task and watch the ReAct loop run live.")
runs_state = gr.State(0)
with gr.Row():
task = gr.Textbox(label="Task", placeholder="Ask something that needs a few steps...", scale=4)
run_btn = gr.Button("Run", variant="primary", scale=1)
gr.Examples(examples=EXAMPLES, inputs=task, label="Example tasks")
stats = gr.Markdown("", elem_id="oa-stats")
timeline = gr.HTML(render.render_timeline([], status="Idle. Pick an example or type a task."))
with gr.Accordion("How this works", open=False):
gr.Markdown(HOW_IT_WORKS)
run_btn.click(run_agent, inputs=[task, runs_state], outputs=[timeline, stats, runs_state])
task.submit(run_agent, inputs=[task, runs_state], outputs=[timeline, stats, runs_state])
return demo
if __name__ == "__main__":
build_demo().launch(theme=build_theme(), css=render.TIMELINE_CSS)