| """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) |
|
|