Spaces:
Sleeping
Sleeping
| # Copyright (c) Meta Platforms, Inc. and affiliates. | |
| # All rights reserved. | |
| # | |
| # This source code is licensed under the BSD-style license found in the | |
| # LICENSE file in the root directory of this source tree. | |
| """ | |
| FastAPI application for the Emailtriage Environment. | |
| Endpoints: | |
| - POST /reset: Reset the environment (accepts optional task_id) | |
| - POST /step: Execute an action | |
| - GET /state: Get current environment state | |
| - GET /schema: Get action/observation schemas | |
| - GET /health: Health check | |
| - WS /ws: WebSocket endpoint for persistent sessions | |
| """ | |
| import argparse | |
| from pathlib import Path | |
| from typing import Any | |
| import uvicorn | |
| from fastapi import APIRouter | |
| try: | |
| from openenv.core.env_server.http_server import create_app | |
| except Exception as e: # pragma: no cover | |
| raise ImportError( | |
| "openenv is required for the web interface. " | |
| "Install dependencies with '\n uv sync\n'" | |
| ) from e | |
| try: | |
| from ..models import EmailtriageAction, EmailtriageObservation | |
| from .EmailTriage_environment import EmailtriageEnvironment | |
| except ImportError: | |
| from models import EmailtriageAction, EmailtriageObservation | |
| from server.EmailTriage_environment import EmailtriageEnvironment | |
| # Create the app with web interface and README integration | |
| app = create_app( | |
| EmailtriageEnvironment, | |
| EmailtriageAction, | |
| EmailtriageObservation, | |
| env_name="EmailTriage", | |
| max_concurrent_envs=1, | |
| ) | |
| # Keep docs metadata explicit to avoid generic duplicate naming in Swagger UI. | |
| app.title = "EmailTriage Environment API" | |
| app.description = ( | |
| "HTTP API for the dynamic EmailTriage OpenEnv environment " | |
| "with 3 difficulty-graded tasks (easy, medium, hard)." | |
| ) | |
| def _load_readme_content() -> str: | |
| """Load environment README markdown for metadata endpoint.""" | |
| readme_path = Path(__file__).resolve().parents[1] / "README.md" | |
| try: | |
| return readme_path.read_text(encoding="utf-8") | |
| except Exception: | |
| return "EmailTriage environment documentation is unavailable." | |
| def _replace_route( | |
| path: str, | |
| method: str, | |
| endpoint, | |
| *, | |
| summary: str, | |
| ) -> None: | |
| """Replace an existing route path+method with a custom handler.""" | |
| method_upper = method.upper() | |
| app.router.routes = [ | |
| route | |
| for route in app.router.routes | |
| if not ( | |
| getattr(route, "path", None) == path | |
| and method_upper in getattr(route, "methods", set()) | |
| ) | |
| ] | |
| router = APIRouter() | |
| router.add_api_route( | |
| path, | |
| endpoint, | |
| methods=[method_upper], | |
| summary=summary, | |
| ) | |
| app.include_router(router) | |
| def _metadata_payload() -> dict[str, Any]: | |
| """Build non-null metadata for /metadata.""" | |
| return { | |
| "name": "EmailTriage", | |
| "description": ( | |
| "Dynamic multi-turn email triage environment for OpenEnv " | |
| "post-training and evaluation with 3 difficulty-graded tasks" | |
| ), | |
| "readme_content": _load_readme_content(), | |
| "version": "1.0.0", | |
| "author": "Galcogens", | |
| "documentation_url": "/docs", | |
| "tasks": [ | |
| { | |
| "id": "easy", | |
| "name": "Quick Sort", | |
| "description": "Archive 3 spam/newsletter emails", | |
| "difficulty": "easy", | |
| }, | |
| { | |
| "id": "medium", | |
| "name": "Priority Triage", | |
| "description": ( | |
| "Triage 5 mixed-priority emails with " | |
| "calendar scheduling" | |
| ), | |
| "difficulty": "medium", | |
| }, | |
| { | |
| "id": "hard", | |
| "name": "Dynamic Crisis", | |
| "description": ( | |
| "Handle 7-10 emails with dynamic events " | |
| "and escalations" | |
| ), | |
| "difficulty": "hard", | |
| }, | |
| ], | |
| } | |
| def _root_payload() -> dict[str, Any]: | |
| """Return a simple JSON index for localhost:8000.""" | |
| return { | |
| "service": "EmailTriage OpenEnv API", | |
| "status": "ok", | |
| "docs": "/docs", | |
| "metadata": "/metadata", | |
| "endpoints": [ | |
| "/reset", | |
| "/step", | |
| "/state", | |
| "/schema", | |
| "/metadata", | |
| "/health", | |
| "/web", | |
| "/ws", | |
| ], | |
| } | |
| def _health_payload() -> dict[str, Any]: | |
| """Health check endpoint for Docker and load balancers.""" | |
| return {"status": "ok"} | |
| _replace_route( | |
| "/metadata", | |
| "GET", | |
| _metadata_payload, | |
| summary="Get environment metadata", | |
| ) | |
| _replace_route( | |
| "/", | |
| "GET", | |
| _root_payload, | |
| summary="Get API index", | |
| ) | |
| _replace_route( | |
| "/health", | |
| "GET", | |
| _health_payload, | |
| summary="Health check", | |
| ) | |
| def run_server(host: str = "0.0.0.0", port: int = 8000) -> None: | |
| """Entry point for direct execution via uv run or python -m.""" | |
| uvicorn.run(app, host=host, port=port) | |
| def main() -> None: | |
| """CLI-compatible entrypoint expected by OpenEnv validator.""" | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--host", type=str, default="0.0.0.0") | |
| parser.add_argument("--port", type=int, default=8000) | |
| args = parser.parse_args() | |
| run_server(host=args.host, port=args.port) | |
| if __name__ == "__main__": | |
| main() |