Siteshcodes's picture
Fix: clamp all scores/rewards strictly to (0,1) exclusive range
9fecec8
import importlib
import yaml
from fastapi import FastAPI
from pydantic import BaseModel
from environment.api_triage_env import APITriageEnv
app = FastAPI(title="API Triage Agent", version="1.0.0")
env = APITriageEnv()
class ActionRequest(BaseModel):
action: str
# load task definitions from openenv.yaml
def _load_tasks():
with open("openenv.yaml", "r") as f:
cfg = yaml.safe_load(f)
return cfg.get("tasks", [])
@app.get("/")
def root():
return {"status": "ok", "environment": "api-triage-agent"}
@app.get("/health")
def health():
return {"status": "healthy"}
@app.post("/reset")
def reset():
state = env.reset()
return {
"observation": {
"step": state.step,
"max_steps": state.max_steps,
"incident_summary": state.incident_summary,
"logs": state.logs,
"response_code": state.response_code,
"fix_applied": state.fix_applied,
"is_resolved": state.is_resolved,
},
"reward": None,
"done": False,
}
@app.get("/state")
def state():
current = env.state()
return {
"step": current.step,
"max_steps": current.max_steps,
"incident_summary": current.incident_summary,
"logs": current.logs,
"response_code": current.response_code,
"fix_applied": current.fix_applied,
"is_resolved": current.is_resolved,
}
@app.post("/step")
def step(request: ActionRequest):
action = request.action
observation, reward, done, info = env.step(action)
# Clamp reward to strictly (0, 1) for OpenEnv compliance
clamped_reward = min(max(reward / 20.5, 0.001), 0.999)
return {
"observation": {
"step": observation.step,
"max_steps": observation.max_steps,
"incident_summary": observation.incident_summary,
"logs": observation.logs,
"response_code": observation.response_code,
"fix_applied": observation.fix_applied,
"is_resolved": observation.is_resolved,
},
"reward": clamped_reward,
"done": done,
"info": info,
}
@app.get("/tasks")
def list_tasks():
"""Return all tasks defined in openenv.yaml with their graders."""
tasks = _load_tasks()
return {
"tasks": [
{
"id": t["id"],
"name": t["name"],
"description": t["description"],
"difficulty": t["difficulty"],
"grader": t["grader"],
}
for t in tasks
]
}
@app.post("/grade/{task_id}")
def grade_task(task_id: str):
"""Run the grader for a specific task and return the score."""
tasks = _load_tasks()
task = next((t for t in tasks if t["id"] == task_id), None)
if task is None:
return {"error": f"Task '{task_id}' not found", "score": 0.0}
grader_ref = task["grader"]
module_path, func_name = grader_ref.rsplit(":", 1)
mod = importlib.import_module(module_path)
grade_fn = getattr(mod, func_name)
score = grade_fn()
return {"task_id": task_id, "score": score}
def main():
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=7860)
if __name__ == "__main__":
main()