| """FastAPI server for ForgeEnv (OpenEnv-compliant).
|
|
|
| Exposes /reset, /step, /state HTTP endpoints via OpenEnv's `create_app`.
|
| HF Spaces sets PORT=7860 automatically.
|
| """
|
| from __future__ import annotations
|
|
|
| import os
|
|
|
| from fastapi.responses import HTMLResponse
|
| from openenv.core import create_app
|
|
|
| from forgeenv.env.actions import ForgeAction
|
| from forgeenv.env.forge_environment import ForgeEnvironment
|
| from forgeenv.env.observations import ForgeObservation
|
|
|
| app = create_app(
|
| env=ForgeEnvironment,
|
| action_cls=ForgeAction,
|
| observation_cls=ForgeObservation,
|
| env_name="forgeenv",
|
| )
|
|
|
|
|
| _LANDING_HTML = """<!doctype html>
|
| <html lang="en">
|
| <head>
|
| <meta charset="utf-8">
|
| <title>ForgeEnv — OpenEnv server</title>
|
| <meta name="viewport" content="width=device-width,initial-scale=1">
|
| <style>
|
| :root { color-scheme: light dark; }
|
| body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
| max-width: 760px; margin: 2.5rem auto; padding: 0 1.25rem;
|
| line-height: 1.55; color: #1f2937; background: #fafafa; }
|
| @media (prefers-color-scheme: dark) { body { color: #e5e7eb; background: #0f172a; } }
|
| h1 { font-size: 1.65rem; margin-bottom: 0.25rem; }
|
| .sub { color: #6b7280; margin-top: 0; }
|
| code, pre { font-family: ui-monospace, "SF Mono", Menlo, monospace; }
|
| pre { background: rgba(127,127,127,0.12); padding: 0.9rem; border-radius: 8px;
|
| overflow-x: auto; }
|
| table { border-collapse: collapse; width: 100%; margin: 0.75rem 0 1.25rem; }
|
| td, th { text-align: left; padding: 0.5rem 0.75rem;
|
| border-bottom: 1px solid rgba(127,127,127,0.25); }
|
| th { font-weight: 600; }
|
| a { color: #2563eb; text-decoration: none; } a:hover { text-decoration: underline; }
|
| .ok { color: #16a34a; font-weight: 600; }
|
| .muted { color: #6b7280; font-size: 0.9rem; }
|
| .pill { display: inline-block; padding: 0.1rem 0.5rem; border-radius: 999px;
|
| background: rgba(34,197,94,0.15); color: #16a34a; font-size: 0.85rem; }
|
| </style>
|
| </head>
|
| <body>
|
| <h1>ForgeEnv 🔧 <span class="pill">running</span></h1>
|
| <p class="sub">OpenEnv-compliant RL environment for HuggingFace
|
| ecosystem repair under library version drift.</p>
|
|
|
| <p>This URL serves the environment over HTTP. It is not a UI — it's the
|
| runtime that <strong>training notebooks connect to</strong>. Open one of
|
| the endpoints below, or use the demo Space to try the trained Repair
|
| Agent in a browser.</p>
|
|
|
| <h2>Endpoints</h2>
|
| <table>
|
| <tr><th>Method</th><th>Path</th><th>Purpose</th></tr>
|
| <tr><td>GET </td><td><a href="/health">/health</a></td><td>Health probe</td></tr>
|
| <tr><td>POST</td><td><code>/reset</code></td><td>Sample task, return drift-gen observation</td></tr>
|
| <tr><td>POST</td><td><code>/step</code></td><td>Apply <code>ForgeAction</code> (breakage or repair)</td></tr>
|
| <tr><td>GET </td><td><a href="/state">/state</a></td><td>Current internal state</td></tr>
|
| <tr><td>GET </td><td><a href="/metadata">/metadata</a></td><td>Env name + version + schema URLs</td></tr>
|
| <tr><td>GET </td><td><a href="/schema">/schema</a></td><td>Action / observation JSON schemas</td></tr>
|
| <tr><td>GET </td><td><a href="/docs">/docs</a></td><td>Interactive Swagger UI</td></tr>
|
| </table>
|
|
|
| <h2>Quick start (Python)</h2>
|
| <pre><code>import asyncio
|
| from openenv.core import GenericEnvClient
|
|
|
| async def go():
|
| client = GenericEnvClient(base_url="https://akhiilll-forgeenv.hf.space")
|
| obs = await client.reset()
|
| print(obs.observation["current_phase"], obs.observation["task_id"])
|
|
|
| asyncio.run(go())</code></pre>
|
|
|
| <h2>Project links</h2>
|
| <ul>
|
| <li>Space card & README:
|
| <a href="https://huggingface.co/spaces/akhiilll/forgeenv" target="_blank" rel="noopener noreferrer">huggingface.co/spaces/akhiilll/forgeenv</a></li>
|
| <li>Gradio demo:
|
| <a href="https://huggingface.co/spaces/akhiilll/forgeenv-demo" target="_blank" rel="noopener noreferrer">huggingface.co/spaces/akhiilll/forgeenv-demo</a></li>
|
| <li>Trained model (LoRA) <span class="muted">— published after the Colab training run finishes</span>:
|
| <a href="https://huggingface.co/akhiilll/forgeenv-repair-agent" target="_blank" rel="noopener noreferrer">huggingface.co/akhiilll/forgeenv-repair-agent</a></li>
|
| </ul>
|
| <p class="muted">Tip: if links don't open from inside the embedded Space frame,
|
| right-click and choose <em>Open in new tab</em>, or open this URL directly
|
| at <a href="https://akhiilll-forgeenv.hf.space/" target="_blank" rel="noopener noreferrer">akhiilll-forgeenv.hf.space</a>.</p>
|
| </body>
|
| </html>"""
|
|
|
|
|
| def _attach_supplementary_routes(_app) -> None:
|
| """Add /health and a friendly GET / landing page if not present."""
|
| existing = {
|
| getattr(r, "path", None) for r in getattr(_app, "routes", [])
|
| }
|
|
|
| if "/health" not in existing:
|
| @_app.get("/health")
|
| def _health() -> dict:
|
| return {"status": "ok", "env": "forgeenv"}
|
|
|
| if "/" not in existing:
|
| @_app.get("/", response_class=HTMLResponse, include_in_schema=False)
|
| def _root() -> str:
|
| return _LANDING_HTML
|
|
|
|
|
| _attach_supplementary_routes(app)
|
|
|
|
|
| if __name__ == "__main__":
|
| import uvicorn
|
|
|
| port = int(os.environ.get("PORT", "7860"))
|
| uvicorn.run(app, host="0.0.0.0", port=port)
|
|
|