auto-dev-agent / ui /components.py
Siva sai Yadav
ready for HuggingFace Space deployment
8edee29
Raw
History Blame Contribute Delete
16 kB
"""
ui/components.py
----------------
Reusable Gradio component builders for AutoDevAgent.
Each function returns a configured Gradio component (or group of
components) that can be assembled into the main app.py layout.
Components built here:
- build_hitl_panel() Human-in-the-Loop panel β€” shown when max
retries is hit. Displays the failed code,
the last error, 2-3 fix options, and an
inline code editor.
- build_reflection_panel() Self-reflection display β€” shows the
debug agent's structured reasoning per
iteration ("saw X, thought Y, did Z").
- build_iteration_counter() Live iteration + token + time display.
- build_model_switcher() Toggle between Llama 8B and 70B.
- build_language_selector() Language radio with auto-detect badge.
Design:
- All components are built with Gradio's Blocks API.
- State is managed via gr.State objects passed through event handlers.
- Components are returned as named dicts so app.py can wire them
into event handlers without importing internals.
Usage:
import gradio as gr
from ui.components import build_hitl_panel, build_reflection_panel
with gr.Blocks() as demo:
hitl = build_hitl_panel()
reflex = build_reflection_panel()
"""
import gradio as gr
from typing import Any
# ------------------------------------------------------------------ #
# Human-in-the-Loop Panel #
# ------------------------------------------------------------------ #
def build_hitl_panel() -> dict[str, Any]:
"""
Build the Human-in-the-Loop panel shown when max retries is hit.
Layout:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ ⚠ Max retries reached β”‚
β”‚ Last error: <error text> β”‚
β”‚ β”‚
β”‚ Fix options: β”‚
β”‚ β—‹ Option 1 description β”‚
β”‚ β—‹ Option 2 description β”‚
β”‚ β—‹ Option 3 description β”‚
β”‚ β”‚
β”‚ Or edit the code directly: β”‚
β”‚ [code editor textbox] β”‚
β”‚ β”‚
β”‚ [Apply fix] [Resubmit edited code] β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Returns:
Dict with keys:
- "panel": gr.Group β€” the whole panel
- "error_display": gr.Textbox β€” shows the last error
- "options_radio": gr.Radio β€” 2-3 fix options
- "code_editor": gr.Code β€” inline code editor
- "apply_btn": gr.Button β€” apply chosen fix option
- "resubmit_btn": gr.Button β€” resubmit edited code
"""
with gr.Group(visible=False) as panel:
gr.Markdown("### ⚠ Max retries reached β€” Human input needed")
with gr.Row():
gr.Markdown(
"The agent could not fix the code automatically. "
"Choose a fix strategy or edit the code directly below."
)
error_display = gr.Textbox(
label="Last error",
lines=4,
interactive=False,
elem_id="hitl_error_display",
)
options_radio = gr.Radio(
choices=[],
label="Suggested fix strategies",
value=None,
elem_id="hitl_options_radio",
)
gr.Markdown("**Or edit the code directly:**")
code_editor = gr.Code(
label="Code editor",
language="python",
lines=20,
interactive=True,
elem_id="hitl_code_editor",
)
with gr.Row():
apply_btn = gr.Button(
value="Apply selected fix",
variant="primary",
size="sm",
)
resubmit_btn = gr.Button(
value="Resubmit edited code",
variant="secondary",
size="sm",
)
return {
"panel": panel,
"error_display": error_display,
"options_radio": options_radio,
"code_editor": code_editor,
"apply_btn": apply_btn,
"resubmit_btn": resubmit_btn,
}
def update_hitl_panel(
hitl_components: dict[str, Any],
error_msg: str,
options: list[str],
broken_code: str,
) -> None:
"""
Populate the HITL panel with the current error state.
Called from app.py when the pipeline returns AWAITING_HUMAN status.
Updates the error display, radio options, and code editor in place.
Args:
hitl_components: Dict returned by build_hitl_panel().
error_msg: The last error message from the executor.
options: List of 2-3 fix option strings from DebugAgent.
broken_code: The most recent failing code to pre-populate
the inline editor.
"""
hitl_components["error_display"].value = error_msg
hitl_components["options_radio"].choices = options
hitl_components["options_radio"].value = None
hitl_components["code_editor"].value = broken_code
hitl_components["panel"].visible = True
# ------------------------------------------------------------------ #
# Clarification Panel #
# ------------------------------------------------------------------ #
def build_clarification_panel() -> dict[str, Any]:
"""
Build the clarification panel shown when the agent needs more info.
Layout:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ πŸ€” Task needs clarification β”‚
β”‚ <agent's question> β”‚
β”‚ [user input textbox] β”‚
β”‚ [Submit Clarification] β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Returns:
Dict with keys:
- "panel": gr.Group β€” container (visible=False by default)
- "question_md": gr.Markdown β€” displays the agent's question
- "answer_input": gr.Textbox β€” user types their clarification
- "submit_btn": gr.Button β€” submits the clarification
"""
with gr.Group(visible=False, elem_id="clarification_panel") as panel:
gr.Markdown("### πŸ€” Task needs clarification")
question_md = gr.Markdown(
value="",
elem_id="clarification_question",
)
answer_input = gr.Textbox(
label="Your clarification",
placeholder="Type your answer here...",
lines=2,
elem_id="clarification_answer",
)
with gr.Row():
submit_btn = gr.Button(
value="Submit Clarification β–Ά",
variant="primary",
size="sm",
elem_id="clarification_submit_btn",
)
return {
"panel": panel,
"question_md": question_md,
"answer_input": answer_input,
"submit_btn": submit_btn,
}
# ------------------------------------------------------------------ #
# Self-Reflection Panel #
# ------------------------------------------------------------------ #
def build_reflection_panel() -> dict[str, Any]:
"""
Build the self-reflection panel displayed during each debug iteration.
Shows the debug agent's structured reasoning before each rewrite:
- What I saw: the exact error observed
- What I think: the agent's hypothesis for the root cause
- What I will do: the specific change the agent will make
Returns:
Dict with keys:
- "panel": gr.Accordion β€” collapsible wrapper
- "saw_box": gr.Textbox β€” what the agent observed
- "think_box": gr.Textbox β€” the agent's hypothesis
- "do_box": gr.Textbox β€” the planned fix
- "iteration_md": gr.Markdown β€” current iteration label
"""
with gr.Accordion(
label="Self-reflection (debug agent reasoning)",
open=False,
visible=False,
) as panel:
iteration_md = gr.Markdown("**Iteration:** β€”")
with gr.Row():
saw_box = gr.Textbox(
label="What I saw",
lines=2,
interactive=False,
elem_id="reflection_saw",
)
with gr.Row():
think_box = gr.Textbox(
label="What I think caused it",
lines=2,
interactive=False,
elem_id="reflection_think",
)
with gr.Row():
do_box = gr.Textbox(
label="What I will change",
lines=2,
interactive=False,
elem_id="reflection_do",
)
return {
"panel": panel,
"saw_box": saw_box,
"think_box": think_box,
"do_box": do_box,
"iteration_md": iteration_md,
}
def update_reflection_panel(
reflection_components: dict[str, Any],
what_i_saw: str,
what_i_think: str,
what_i_will_do: str,
iteration: int,
max_retries: int,
) -> None:
"""
Populate the reflection panel with the latest debug iteration data.
Args:
reflection_components: Dict returned by build_reflection_panel().
what_i_saw: The error or wrong output observed.
what_i_think: The agent's hypothesis.
what_i_will_do: The planned fix.
iteration: Current iteration number (1-based).
max_retries: Configured max retries for the label.
"""
reflection_components["iteration_md"].value = (
f"**Iteration:** {iteration} / {max_retries}"
)
reflection_components["saw_box"].value = what_i_saw
reflection_components["think_box"].value = what_i_think
reflection_components["do_box"].value = what_i_will_do
reflection_components["panel"].visible = True
reflection_components["panel"].open = True
# ------------------------------------------------------------------ #
# Iteration Counter + Observability Strip #
# ------------------------------------------------------------------ #
def build_stats_strip() -> dict[str, Any]:
"""
Build the live stats strip showing iteration count, exec time,
and Groq token usage.
Displayed as a compact row of metric cards beneath the main output.
Returns:
Dict with keys:
- "iterations_md": gr.Markdown β€” debug iteration count
- "exec_time_md": gr.Markdown β€” execution time in seconds
- "tokens_md": gr.Markdown β€” total Groq tokens used
- "warning_md": gr.Markdown β€” rate limit warning (hidden by default)
"""
with gr.Row(elem_id="stats_strip"):
iterations_md = gr.Markdown(
value="**Iterations:** 0",
elem_id="stats_iterations",
)
exec_time_md = gr.Markdown(
value="**Time:** 0.0s",
elem_id="stats_exec_time",
)
tokens_md = gr.Markdown(
value="**Tokens:** 0",
elem_id="stats_tokens",
)
warning_md = gr.Markdown(
value="",
visible=False,
elem_id="stats_warning",
)
return {
"iterations_md": iterations_md,
"exec_time_md": exec_time_md,
"tokens_md": tokens_md,
"warning_md": warning_md,
}
def update_stats_strip(
stats_components: dict[str, Any],
iterations: int,
exec_time: float,
total_tokens: int,
rate_limit_warning: bool = False,
) -> None:
"""
Update the stats strip with current pipeline run metrics.
Args:
stats_components: Dict returned by build_stats_strip().
iterations: Number of debug iterations completed.
exec_time: Total wall-clock time in seconds.
total_tokens: Total Groq tokens used this run.
rate_limit_warning: If True, show a rate limit warning.
"""
stats_components["iterations_md"].value = f"**Iterations:** {iterations}"
stats_components["exec_time_md"].value = f"**Time:** {exec_time:.1f}s"
stats_components["tokens_md"].value = f"**Tokens:** {total_tokens:,}"
if rate_limit_warning:
stats_components["warning_md"].value = (
"⚠ Approaching Groq rate limit β€” consider switching to llama3-8b"
)
stats_components["warning_md"].visible = True
else:
stats_components["warning_md"].visible = False
# ------------------------------------------------------------------ #
# Model Switcher #
# ------------------------------------------------------------------ #
def build_model_switcher() -> dict[str, Any]:
"""
Build the model switcher toggle between Llama 3.1 8B and 70B.
8B is faster and cheaper β€” good for simple tasks and when
approaching rate limits. 70B gives higher quality for complex tasks.
Returns:
Dict with keys:
- "radio": gr.Radio β€” model selection
"""
radio = gr.Radio(
choices=["llama3-8b-8192", "llama3-70b-8192"],
value="llama3-70b-8192",
label="Model",
info="8B is faster Β· 70B is more capable",
elem_id="model_switcher",
)
return {"radio": radio}
# ------------------------------------------------------------------ #
# Language Selector with Auto-detect Badge #
# ------------------------------------------------------------------ #
def build_language_selector() -> dict[str, Any]:
"""
Build the language selector radio with auto-detect status display.
The auto-detect badge shows the detected language and confidence
so the user knows whether to trust the pre-selection.
Returns:
Dict with keys:
- "radio": gr.Radio β€” Python / SQL selector
- "badge_md": gr.Markdown β€” auto-detect result badge
"""
with gr.Group():
radio = gr.Radio(
choices=["python", "sql"],
value="python",
label="Language",
elem_id="language_selector",
)
badge_md = gr.Markdown(
value="",
visible=False,
elem_id="language_badge",
)
return {
"radio": radio,
"badge_md": badge_md,
}
def update_language_badge(
selector_components: dict[str, Any],
detected_language: str,
confidence: str,
reason: str,
) -> None:
"""
Update the auto-detect badge after detection runs.
Args:
selector_components: Dict returned by build_language_selector().
detected_language: The detected language string.
confidence: 'high', 'medium', or 'low'.
reason: One-sentence explanation of the detection.
"""
emoji = {"high": "🟒", "medium": "🟑", "low": "πŸ”΄"}.get(confidence, "βšͺ")
selector_components["badge_md"].value = (
f"{emoji} Auto-detected: **{detected_language}** "
f"({confidence} confidence) β€” {reason}"
)
selector_components["badge_md"].visible = True
selector_components["radio"].value = detected_language