File size: 5,781 Bytes
4189f0e
 
 
 
 
 
 
e91d5da
 
 
 
 
 
4189f0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4ec4df3
4189f0e
 
 
 
 
 
 
 
 
 
 
 
 
e91d5da
 
 
 
 
 
 
 
4189f0e
 
 
e91d5da
4189f0e
e91d5da
 
 
 
4189f0e
e91d5da
4189f0e
6a80f43
4189f0e
4ec4df3
4189f0e
4ec4df3
 
4189f0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e91d5da
 
 
 
 
 
4189f0e
 
 
e91d5da
4189f0e
e91d5da
4189f0e
 
 
 
e91d5da
4189f0e
e91d5da
4189f0e
 
 
 
e91d5da
 
 
 
 
 
 
 
4189f0e
e91d5da
4189f0e
 
 
 
 
 
 
4ec4df3
4189f0e
4ec4df3
 
e91d5da
4ec4df3
4189f0e
 
 
e91d5da
 
 
 
 
 
 
 
 
 
 
 
 
 
4189f0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8828035
edd5fd2
 
 
 
 
 
 
 
 
 
cb86053
8828035
edd5fd2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import subprocess
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel

from app.environment import DataEngEnvironment
from app.models import Action, Observation, Reward, StepResponse, StateResponse, TaskInfo
from app.tasks import (
    stage1_data_repair,
    stage2_training_monitor,
    stage3_eval_validation,
    stage4_deploy_gate,
)

app = FastAPI(title="DataEngEnv API", version="1.0.0")

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Singleton Environment Instance
env = DataEngEnvironment()

class TaskIdRequest(BaseModel):
    task_id: int | None = 1

class ErrorResponse(BaseModel):
    error: str

class HealthResponse(BaseModel):
    status: str
    env: str
    version: str

class BaselineResponse(BaseModel):
    success: bool
    output: str

class PipelineStatusResponse(BaseModel):
    current_stage: int
    loop_count: int
    stages_completed: list[int]
    total_steps: int
    episode_score: float
    actor_feedback: str

@app.on_event("startup")
async def startup_event():
    print("DataEngEnv ready")
    # Call generate_scenario() on all 4 stages to validate they load properly at startup
    try:
        stage1_data_repair.generate_scenario()
        stage2_training_monitor.generate_scenario()
        stage3_eval_validation.generate_scenario()
        stage4_deploy_gate.generate_scenario()
    except Exception as e:
        print(f"Warning: A stage failed to load during startup validation: {e}")


@app.post("/reset", response_model=Observation | ErrorResponse)
async def api_reset(req: TaskIdRequest | None = None):
    try:
        tid = req.task_id if req is not None and req.task_id is not None else 1
        return env.reset(tid)
    except Exception as e:
        return ErrorResponse(error=str(e))

@app.post("/step", response_model=StepResponse | ErrorResponse)
async def api_step(action: Action):
    try:
        return env.step(action)
    except Exception as e:
        return ErrorResponse(error=str(e))

@app.get("/state", response_model=StateResponse | ErrorResponse)
async def api_state():
    try:
        return env.state()
    except Exception as e:
        return ErrorResponse(error=str(e))

@app.get("/tasks", response_model=list[TaskInfo] | ErrorResponse)
async def api_tasks():
    try:
        scenarios = {
            1: stage1_data_repair.generate_scenario(),
            2: stage2_training_monitor.generate_scenario(),
            3: stage3_eval_validation.generate_scenario(),
            4: stage4_deploy_gate.generate_scenario(),
        }
        return [
            TaskInfo(
                task_id=1,
                name="Stage 1: Data Repair",
                difficulty="easy",
                description=scenarios[1]["task_description"],
                action_schema={}
            ),
            TaskInfo(
                task_id=2,
                name="Stage 2: Training Monitor",
                difficulty="medium",
                description=scenarios[2]["task_description"],
                action_schema={}
            ),
            TaskInfo(
                task_id=3,
                name="Stage 3: Eval Validation",
                difficulty="hard",
                description=scenarios[3]["task_description"],
                action_schema={}
            ),
            TaskInfo(
                task_id=4,
                name="Stage 4: Deployment Gate",
                difficulty="hard",
                description=scenarios[4]["task_description"],
                action_schema={}
            )
        ]
    except Exception as e:
        return ErrorResponse(error=str(e))

@app.post("/grader", response_model=Reward | ErrorResponse)
async def api_grader(req: TaskIdRequest | None = None):
    try:
        tid = req.task_id if req is not None and req.task_id is not None else env.current_task_id
        if tid != env.current_task_id:
            return ErrorResponse(error="task_id mismatch - call /reset first; the pipeline controls stage advancement")
        return env.submit_grader(tid)
    except Exception as e:
        return ErrorResponse(error=str(e))

@app.get("/pipeline_status", response_model=PipelineStatusResponse | ErrorResponse)
async def api_pipeline_status():
    try:
        return PipelineStatusResponse(
            current_stage=env.current_stage,
            loop_count=env.loop_count,
            stages_completed=list(env.stages_completed),
            total_steps=env.step_number,
            episode_score=float(env.episode_score),
            actor_feedback=env.actor_feedback,
        )
    except Exception as e:
        return ErrorResponse(error=str(e))

@app.get("/baseline", response_model=BaselineResponse | ErrorResponse)
async def api_baseline():
    try:
        # Max timeout set to 300s safely bounds execution
        result = subprocess.run(["python", "baseline/run_baseline.py"], capture_output=True, text=True, timeout=300)
        return BaselineResponse(success=(result.returncode == 0), output=result.stdout + result.stderr)
    except Exception as e:
        return ErrorResponse(error=str(e))

@app.get("/health", response_model=HealthResponse | ErrorResponse)
async def api_health():
    try:
        return HealthResponse(status="ok", env="DataEngEnv", version="1.0.0")
    except Exception as e:
        return ErrorResponse(error=str(e))

import gradio as gr
import urllib.request
import time

try:
    from gradio_app import demo
    app = gr.mount_gradio_app(app, demo, path="/")
except ImportError as e:
    print(f"Warning: Could not import gradio dashboard: {e}")

def main():
    import uvicorn
    uvicorn.run("app.main:app", host="0.0.0.0", port=7860)