openenv_rl / server /app.py
yash-080706's picture
Upload folder using huggingface_hub
a73eb17 verified
Raw
History Blame Contribute Delete
5.57 kB
# 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()