BHaritha/msme-openenv
1
1"""2server.py - FastAPI server with typed Pydantic models (OpenEnv spec compliant).3"""4from fastapi import FastAPI, HTTPException5from fastapi.responses import JSONResponse6from .environment import MSMEDisputeEnv7from .models import ResetRequest, StepRequest, ResetResponse, StepResponse8 9app = FastAPI(10 title="MSME Payment Dispute — OpenEnv",11 version="2.0.0",12 description="OpenEnv environment for MSME payment dispute resolution tasks."13)14env = MSMEDisputeEnv()15 16@app.get("/")17def root():18 return {"status": "ok", "env": "msme-dispute", "version": "2.0.0",19 "modes": ["single", "sequential"]}20 21@app.get("/health")22def health():23 return {"status": "ok"}24 25@app.post("/reset", response_model=ResetResponse)26def reset(req: ResetRequest | None = None):27 try:28 if req is None:29 req = ResetRequest()30 obs = env.reset(task_id=req.task_id, seed=req.seed, mode=req.mode)31 return ResetResponse(observation=obs, state=env.state())32 except ValueError as e:33 raise HTTPException(status_code=400, detail=str(e))34 35@app.post("/step", response_model=StepResponse)36def step(req: StepRequest):37 try:38 result = env.step(req.action)39 return StepResponse(**result)40 except RuntimeError as e:41 raise HTTPException(status_code=400, detail=str(e))42 43@app.get("/state")44def state():45 return env.state()46 47@app.get("/tasks")48def list_tasks():49 return {50 "tasks": [51 {"task_id": 1, "name": "classify_dispute",52 "difficulty": "easy",53 "description": "Classify a payment dispute email into delayed_payment, partial_payment, or payment_denial.",54 "action_schema": {"label": "str — one of [delayed_payment, partial_payment, payment_denial]"},55 "reward": "0.999 exact, 0.40 adjacent class, 0.001 wrong"},56 {"task_id": 2, "name": "extract_facts",57 "difficulty": "medium",58 "description": "Extract claimant, opponent, amount, due_date, days_overdue from a formal notice.",59 "action_schema": {"claimant": "str", "opponent": "str", "amount": "int", "due_date": "str", "days_overdue": "int"},60 "reward": "weighted field accuracy — clamped strictly inside (0, 1)"},61 {"task_id": 3, "name": "draft_demand_letter",62 "difficulty": "hard",63 "description": "Draft a professional MSME payment demand letter. Sequential mode chains Tasks 1+2 context.",64 "action_schema": {"letter": "str — min 150 words"},65 "reward": "0.5 * rule_based + 0.5 * llm_judge (temp=0), clamped strictly inside (0, 1)"},66 ],67 "modes": {68 "single": "One task per episode. reset(task_id=1|2|3)",69 "sequential": "All 3 tasks chained. Agent answers flow into later tasks."70 }71 }72 