Spaces:
Sleeping
Sleeping
| import os | |
| from fastapi import FastAPI, Query, HTTPException | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.responses import JSONResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from env import ExecOpsEnv, Action, grade | |
| app = FastAPI(title="AI Executive Operations Manager", version="1.0.0") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Global environment instance (single session) | |
| env = ExecOpsEnv("easy") | |
| def reset(task: str = Query("easy")): | |
| """Reset the environment to a fresh state for the given task.""" | |
| global env | |
| valid_tasks = ["easy", "medium", "hard"] | |
| if task not in valid_tasks: | |
| raise HTTPException(status_code=400, detail=f"Invalid task. Choose from: {valid_tasks}") | |
| env = ExecOpsEnv(task) | |
| return env.reset() | |
| def step(action: Action): | |
| """Execute one action step in the environment.""" | |
| global env | |
| try: | |
| result = env.step(action) | |
| return result | |
| except (ValueError, RuntimeError) as e: | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| def get_state(): | |
| """Return the full current environment state.""" | |
| global env | |
| return env.get_state() | |
| def get_grade(): | |
| """Return the current grade score (0.0-1.0).""" | |
| global env | |
| return {"score": grade(env._state), "task_id": env.task_id} | |
| def health(): | |
| """OpenEnv runtime health check.""" | |
| return {"status": "healthy"} | |
| def metadata(): | |
| """OpenEnv runtime metadata.""" | |
| return { | |
| "name": "ai-executive-ops-manager", | |
| "description": ( | |
| "Simulates a startup CEO's operational day. An AI agent manages a realistic " | |
| "executive inbox under time pressure, making triage decisions - reply, schedule, " | |
| "delegate, or ignore - to handle conflicting priorities and cascading crises." | |
| ), | |
| "version": "1.0.0", | |
| "tasks": ["easy", "medium", "hard"], | |
| } | |
| def schema(): | |
| """OpenEnv runtime schema for action, observation, and state.""" | |
| return { | |
| "action": { | |
| "type": "object", | |
| "properties": { | |
| "type": {"type": "string", "enum": ["reply", "schedule", "delegate", "ignore"]}, | |
| "email_id": {"type": "string", "description": "ID of the email to act on (e.g. 'e1')"}, | |
| }, | |
| "required": ["type", "email_id"], | |
| }, | |
| "observation": { | |
| "type": "object", | |
| "properties": { | |
| "time": {"type": "string"}, | |
| "step": {"type": "integer"}, | |
| "max_steps": {"type": "integer"}, | |
| "steps_remaining": {"type": "integer"}, | |
| "inbox": {"type": "array", "items": {"type": "object"}}, | |
| "calendar": {"type": "array", "items": {"type": "object"}}, | |
| "goals": {"type": "array", "items": {"type": "object"}}, | |
| "pending_goals": {"type": "array", "items": {"type": "object"}}, | |
| "total_reward": {"type": "number"}, | |
| "done": {"type": "boolean"}, | |
| }, | |
| }, | |
| "state": { | |
| "type": "object", | |
| "properties": { | |
| "current_step": {"type": "integer"}, | |
| "max_steps": {"type": "integer"}, | |
| "current_time": {"type": "string"}, | |
| "inbox": {"type": "array"}, | |
| "calendar": {"type": "array"}, | |
| "goals": {"type": "array"}, | |
| "history": {"type": "array"}, | |
| "total_reward": {"type": "number"}, | |
| "done": {"type": "boolean"}, | |
| "task_id": {"type": "string"}, | |
| }, | |
| }, | |
| } | |
| def mcp(request: dict = None): | |
| """Minimal MCP/JSON-RPC endpoint for OpenEnv runtime compliance.""" | |
| return {"jsonrpc": "2.0", "result": {"tools": []}, "id": None} | |
| def list_tasks(): | |
| """Return available task descriptions.""" | |
| return { | |
| "tasks": [ | |
| { | |
| "id": "easy", | |
| "name": "Monday Morning Catchup", | |
| "difficulty": "Easy", | |
| "description": "A manageable Monday inbox. Priorities are clear.", | |
| "max_steps": 6, | |
| }, | |
| { | |
| "id": "medium", | |
| "name": "Investor Demo Day Prep", | |
| "difficulty": "Medium", | |
| "description": "Conflicting priorities on demo day. Scheduling required.", | |
| "max_steps": 8, | |
| }, | |
| { | |
| "id": "hard", | |
| "name": "Series B Crisis Day", | |
| "difficulty": "Hard", | |
| "description": "Cascading crises. Impossible to handle everything.", | |
| "max_steps": 8, | |
| }, | |
| ] | |
| } | |
| # Serve React frontend - MUST come after all API routes | |
| FRONTEND_DIR = os.path.join(os.path.dirname(__file__), "frontend", "build") | |
| if os.path.exists(FRONTEND_DIR): | |
| app.mount("/", StaticFiles(directory=FRONTEND_DIR, html=True), name="frontend") | |
| else: | |
| # Fallback: return health check at root if frontend not built yet | |
| def root(): | |
| return {"status": "ok", "message": "AI Executive Operations Manager API", "frontend": "not built"} | |