Config-debug / server /app.py
affu143
one more
6079dab
Raw
History Blame Contribute Delete
6.08 kB
"""FastAPI application for ConfigDebugEnv.
Uses OpenEnv's create_fastapi_app() for standard framework compatibility.
The framework handles /reset, /step, /state, /health, /schema, /metadata, /ws.
"""
import gradio as gr
from openenv.core.env_server import create_fastapi_app
from server.models import ConfigDebugAction, ConfigDebugObservation, ConfigDebugState
from server.config_debug_environment import ConfigDebugEnvironment
from server.tasks.task_registry import get_task, TASK_ORDER
# ---- Create the standard OpenEnv FastAPI app ----
# The framework registers: /reset, /step, /state, /health, /schema, /metadata, /ws
app = create_fastapi_app(
ConfigDebugEnvironment,
ConfigDebugAction,
ConfigDebugObservation,
)
# ---- Startup Diagnostics ----
print("[APP_INIT] ConfigDebugEnvironment initialization started")
print(f"[APP_INIT] Loaded {len(TASK_ORDER)} tasks: {TASK_ORDER}")
for task_id in TASK_ORDER:
try:
task = get_task(task_id)
print(f"[APP_INIT] Task '{task_id}' loaded: grader={task.grader.__name__}")
except Exception as e:
print(f"[APP_INIT] ERROR loading task '{task_id}': {str(e)}")
# ---- Custom endpoints (non-conflicting with framework) ----
@app.get("/info")
def info():
return {"name": "ConfigDebugEnv", "version": "1.0.0", "status": "running"}
@app.get("/tasks")
def tasks():
task_grader_map = {
"task1_json": "Task1Grader",
"task2_yaml": "Task2Grader",
"task3_dockerfile": "Task3Grader",
"task4_compose": "Task4Grader",
"task5_k8s": "Task5Grader",
"task6_github_actions": "Task6Grader",
"task7_nginx": "Task7Grader",
}
return {
"tasks": [
{
"id": tid,
"name": get_task(tid).description,
"difficulty": get_task(tid).difficulty,
"file_type": get_task(tid).file_type,
"num_bugs": get_task(tid).num_bugs,
"has_grader": True,
"grader": f"server.graders.grader_api:{task_grader_map[tid]}",
}
for tid in TASK_ORDER
],
"total_tasks": len(TASK_ORDER),
"tasks_with_graders": len(TASK_ORDER),
}
# ---- Gradio Web UI ----
_ui_env = ConfigDebugEnvironment()
def format_state(env):
state = env.state
progress_bar = "\u2588" * int(state.progress_ratio * 10) + "\u2591" * (10 - int(state.progress_ratio * 10))
return f"""
Task Progress: {len(state.tasks_completed)+1}/7
Progress: {progress_bar} ({int(state.progress_ratio*100)}%)
Total Reward: {state.total_reward:.2f}
Current Task: {state.current_task_id}
Difficulty: {state.current_difficulty}
Bugs Found: {state.bugs_found_so_far}
Error: {state.current_error_message or 'None'}
Completed: {', '.join(state.tasks_completed) if state.tasks_completed else 'None'}
Remaining: {', '.join(state.tasks_remaining[:3]) if state.tasks_remaining else 'None'}
"""
def ui_get_state():
return format_state(_ui_env)
def ui_reset():
_ui_env.reset()
obs = _ui_env._build_observation()
return (
f"Task: {obs.task_id} | Difficulty: {obs.difficulty} | Bugs: {obs.num_bugs}",
obs.task_description,
obs.broken_config,
obs.error_message,
format_state(_ui_env),
"Environment reset. Submit a fixed config to begin.",
)
def ui_step(fixed_config):
if _ui_env._done:
return (
"All tasks completed!",
"",
"",
"Episode done. Click Reset to start again.",
format_state(_ui_env),
f"Final score: {_ui_env.total_reward:.1f} / {len(TASK_ORDER)}.0",
)
action = ConfigDebugAction(fixed_config=fixed_config)
obs = _ui_env.step(action)
history = f"Reward: {obs.reward:.2f} | Bugs found: {obs.bugs_found_so_far}/{obs.num_bugs}\nFeedback: {obs.error_message}"
return (
f"Task: {obs.task_id} | Difficulty: {obs.difficulty} | Bugs: {obs.num_bugs}",
obs.task_description,
obs.broken_config,
obs.error_message,
format_state(_ui_env),
history,
)
with gr.Blocks(title="ConfigDebugEnv") as demo:
gr.Markdown("# ConfigDebugEnv")
gr.Markdown("An RL environment for debugging broken config files across 7 real-world formats.")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Agent Interface")
task_info = gr.Textbox(label="Current Task", interactive=False)
task_desc = gr.Textbox(label="Task Description", interactive=False, lines=2)
broken_config = gr.Textbox(label="Broken Config", interactive=False, lines=10)
error_msg = gr.Textbox(label="Error Message", interactive=False, lines=2)
gr.Markdown("### Take Action")
fixed_config_input = gr.Textbox(label="Your Fixed Config", placeholder="Paste your fixed configuration here...", lines=10)
with gr.Row():
reset_btn = gr.Button("Reset Environment", variant="secondary")
step_btn = gr.Button("Step", variant="primary")
state_btn = gr.Button("Get State", variant="secondary")
with gr.Column(scale=1):
gr.Markdown("### State Observer")
state_display = gr.Textbox(label="Current State (with RL Signals)", interactive=False, lines=14)
history_display = gr.Textbox(label="Action History / Reward", interactive=False, lines=4)
reset_btn.click(
fn=ui_reset,
outputs=[task_info, task_desc, broken_config, error_msg, state_display, history_display],
)
step_btn.click(
fn=ui_step,
inputs=[fixed_config_input],
outputs=[task_info, task_desc, broken_config, error_msg, state_display, history_display],
)
state_btn.click(
fn=ui_get_state,
outputs=[state_display],
)
app = gr.mount_gradio_app(app, demo, path="/")
def main(host: str = "0.0.0.0", port: int = 7860):
import uvicorn
uvicorn.run(app, host=host, port=port)
if __name__ == "__main__":
main()