Spaces:
Sleeping
Sleeping
Merged Proxy: Streamlit Dashboard now primary UI on port 7860
Browse files- requirements.txt +1 -0
- server/app.py +51 -38
- start.sh +13 -2
requirements.txt
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
# Core
|
| 2 |
openenv-core[ui]>=0.2.0
|
| 3 |
openai>=1.0.0
|
|
|
|
| 4 |
numpy>=1.24.0
|
| 5 |
PyYAML>=6.0
|
| 6 |
python-dotenv>=1.0.0
|
|
|
|
| 1 |
# Core
|
| 2 |
openenv-core[ui]>=0.2.0
|
| 3 |
openai>=1.0.0
|
| 4 |
+
httpx>=0.25.0
|
| 5 |
numpy>=1.24.0
|
| 6 |
PyYAML>=6.0
|
| 7 |
python-dotenv>=1.0.0
|
server/app.py
CHANGED
|
@@ -30,7 +30,10 @@ from openenv.core import create_app
|
|
| 30 |
|
| 31 |
from patchhawk.agent.environment import PatchHawkEnv
|
| 32 |
from patchhawk.env_models import PatchHawkAction, PatchHawkObservation
|
| 33 |
-
from fastapi.responses import HTMLResponse
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
|
| 36 |
def _env_factory() -> PatchHawkEnv:
|
|
@@ -51,44 +54,54 @@ def create_openenv_app():
|
|
| 51 |
|
| 52 |
app = create_openenv_app()
|
| 53 |
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
<head>
|
| 60 |
-
<title>PatchHawk | Autonomous DevSecOps SOC</title>
|
| 61 |
-
<style>
|
| 62 |
-
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #0d1117; color: #c9d1d9; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; }
|
| 63 |
-
.container { background: #161b22; padding: 40px; border-radius: 12px; border: 1px solid #30363d; box-shadow: 0 10px 30px rgba(0,0,0,0.5); text-align: center; max-width: 600px; }
|
| 64 |
-
h1 { color: #58a6ff; margin-bottom: 10px; }
|
| 65 |
-
p { font-size: 1.1em; color: #8b949e; line-height: 1.6; }
|
| 66 |
-
.status { display: inline-block; padding: 5px 15px; border-radius: 20px; background: #238636; color: white; font-weight: bold; margin: 20px 0; }
|
| 67 |
-
.links { display: flex; gap: 10px; justify-content: center; margin-top: 30px; }
|
| 68 |
-
.btn { text-decoration: none; padding: 12px 25px; border-radius: 6px; font-weight: bold; transition: 0.3s; }
|
| 69 |
-
.btn-blue { background: #1f6feb; color: white; }
|
| 70 |
-
.btn-blue:hover { background: #388bfd; }
|
| 71 |
-
.btn-outline { border: 1px solid #30363d; color: #58a6ff; }
|
| 72 |
-
.btn-outline:hover { background: #30363d; }
|
| 73 |
-
.badge { background: #30363d; padding: 4px 10px; border-radius: 4px; font-family: monospace; }
|
| 74 |
-
</style>
|
| 75 |
-
</head>
|
| 76 |
-
<body>
|
| 77 |
-
<div class="container">
|
| 78 |
-
<h1>π¦
PatchHawk SOC</h1>
|
| 79 |
-
<p>Autonomous Supply-Chain Vulnerability & Patching Agent</p>
|
| 80 |
-
<div class="status">β ENVIRONMENT LIVE</div>
|
| 81 |
-
<p>The OpenEnv API Spec is running correctly at <span class="badge">port: 7860</span>.</p>
|
| 82 |
-
|
| 83 |
-
<div class="links">
|
| 84 |
-
<a href="/web" class="btn btn-blue">Open Env Explorer</a>
|
| 85 |
-
<a href="/docs" class="btn btn-outline">API Docs (Swagger)</a>
|
| 86 |
-
</div>
|
| 87 |
-
<p style="margin-top:20px; font-size:0.9em;">Evaluation URL: <span class="badge">/reset</span></p>
|
| 88 |
-
</div>
|
| 89 |
-
</body>
|
| 90 |
-
</html>
|
| 91 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
|
| 93 |
|
| 94 |
def main(port: int | None = None) -> None:
|
|
|
|
| 30 |
|
| 31 |
from patchhawk.agent.environment import PatchHawkEnv
|
| 32 |
from patchhawk.env_models import PatchHawkAction, PatchHawkObservation
|
| 33 |
+
from fastapi.responses import HTMLResponse, StreamingResponse
|
| 34 |
+
import httpx
|
| 35 |
+
from starlette.requests import Request
|
| 36 |
+
from starlette.background import BackgroundTask
|
| 37 |
|
| 38 |
|
| 39 |
def _env_factory() -> PatchHawkEnv:
|
|
|
|
| 54 |
|
| 55 |
app = create_openenv_app()
|
| 56 |
|
| 57 |
+
# ββ Streamlit Proxy Configuration βββββββββββββββββββββββββββββββββββββ
|
| 58 |
+
STREAMLIT_URL = "http://localhost:8501"
|
| 59 |
+
|
| 60 |
+
@app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"])
|
| 61 |
+
async def proxy_streamlit(request: Request, path: str):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
"""
|
| 63 |
+
Proxies all requests not handled by OpenEnv routes to the local Streamlit server.
|
| 64 |
+
This ensures the Streamlit Dashboard is the primary UI on port 7860.
|
| 65 |
+
"""
|
| 66 |
+
# Skip proxying for OpenEnv specific routes so the grader still works
|
| 67 |
+
openenv_routes = ["reset", "step", "docs", "openapi.json", "web", "assets"]
|
| 68 |
+
if any(path.startswith(r) for r in openenv_routes) or path == "":
|
| 69 |
+
if path == "":
|
| 70 |
+
# Redirect root to Streamlit
|
| 71 |
+
pass
|
| 72 |
+
else:
|
| 73 |
+
# Let FastAPI handle it
|
| 74 |
+
return
|
| 75 |
+
|
| 76 |
+
client = httpx.AsyncClient(base_url=STREAMLIT_URL)
|
| 77 |
+
url = httpx.URL(path=request.url.path, query=request.url.query.encode("utf-8"))
|
| 78 |
+
|
| 79 |
+
# Filter out headers that might cause issues with the proxy
|
| 80 |
+
headers = {k: v for k, v in request.headers.items() if k.lower() not in ["host", "connection"]}
|
| 81 |
+
|
| 82 |
+
# Handle the request body
|
| 83 |
+
body = await request.body()
|
| 84 |
+
|
| 85 |
+
# Forward the request to Streamlit
|
| 86 |
+
rp_resp = await client.request(
|
| 87 |
+
method=request.method,
|
| 88 |
+
url=url,
|
| 89 |
+
headers=headers,
|
| 90 |
+
content=body,
|
| 91 |
+
follow_redirects=True,
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
return StreamingResponse(
|
| 95 |
+
rp_resp.aiter_raw(),
|
| 96 |
+
status_code=rp_resp.status_code,
|
| 97 |
+
headers=dict(rp_resp.headers),
|
| 98 |
+
background=BackgroundTask(client.aclose)
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
@app.get("/", response_class=HTMLResponse)
|
| 102 |
+
async def root_redirect(request: Request):
|
| 103 |
+
"""Force the root to proxy to Streamlit."""
|
| 104 |
+
return await proxy_streamlit(request, "")
|
| 105 |
|
| 106 |
|
| 107 |
def main(port: int | None = None) -> None:
|
start.sh
CHANGED
|
@@ -3,6 +3,17 @@
|
|
| 3 |
echo "Starting OpenEnv API server on port 7860..."
|
| 4 |
uvicorn server.app:app --host 0.0.0.0 --port 7860 &
|
| 5 |
|
| 6 |
-
# Start the Streamlit Dashboard (User UI)
|
| 7 |
echo "Starting Streamlit Dashboard on port 8501..."
|
| 8 |
-
streamlit run patchhawk/app/dashboard.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
echo "Starting OpenEnv API server on port 7860..."
|
| 4 |
uvicorn server.app:app --host 0.0.0.0 --port 7860 &
|
| 5 |
|
| 6 |
+
# Start the Streamlit Dashboard (User UI) in the background with Proxy-friendly settings
|
| 7 |
echo "Starting Streamlit Dashboard on port 8501..."
|
| 8 |
+
streamlit run patchhawk/app/dashboard.py \
|
| 9 |
+
--server.port 8501 \
|
| 10 |
+
--server.address 0.0.0.0 \
|
| 11 |
+
--server.enableCORS false \
|
| 12 |
+
--server.enableXsrfProtection false \
|
| 13 |
+
--server.headless true \
|
| 14 |
+
--browser.gatherUsageStats false &
|
| 15 |
+
|
| 16 |
+
# Start the OpenEnv API server (Hackathon Compliance) on Port 7860
|
| 17 |
+
# This server now PROXIES all UI requests to Streamlit on 8501
|
| 18 |
+
echo "Starting OpenEnv API server on port 7860..."
|
| 19 |
+
uvicorn server.app:app --host 0.0.0.0 --port 7860
|