JUNAID0990
Add homepage UI for Space
616f9b6
from __future__ import annotations
import os
from fastapi.responses import HTMLResponse
from openenv.core.env_server.http_server import create_app
from models import CareerPlanningAction, CareerPlanningObservation, CareerPlanningState
from server.career_planning_environment import CareerPlanningEnvironment
app = create_app(
CareerPlanningEnvironment,
CareerPlanningAction,
CareerPlanningObservation,
env_name="career_planning",
max_concurrent_envs=4,
)
@app.get("/", include_in_schema=False)
def root() -> HTMLResponse:
return HTMLResponse(
"""
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AI Career Advisor</title>
<style>
:root {
--bg: #f4f7f1;
--card: #ffffff;
--ink: #172119;
--muted: #5e6c60;
--accent: #1f8f5f;
--accent-dark: #136746;
--line: #d9e5da;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: "Segoe UI", Arial, sans-serif;
color: var(--ink);
background:
radial-gradient(circle at top left, #dff6df 0, transparent 35%),
linear-gradient(160deg, #f8fbf5 0%, var(--bg) 100%);
}
.wrap {
max-width: 980px;
margin: 0 auto;
padding: 48px 20px 64px;
}
.hero {
background: rgba(255,255,255,0.86);
border: 1px solid var(--line);
border-radius: 24px;
padding: 28px;
box-shadow: 0 18px 50px rgba(16, 37, 23, 0.08);
}
h1 {
margin: 0 0 10px;
font-size: clamp(2rem, 4vw, 3.5rem);
line-height: 1;
}
p {
margin: 0;
color: var(--muted);
font-size: 1.05rem;
line-height: 1.6;
}
.links, .grid {
display: grid;
gap: 16px;
}
.links {
margin-top: 24px;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
a.link, button {
display: inline-flex;
justify-content: center;
align-items: center;
text-decoration: none;
border-radius: 14px;
padding: 14px 16px;
border: 0;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
}
a.link.primary, button.primary {
background: var(--accent);
color: white;
}
a.link.secondary {
background: white;
color: var(--ink);
border: 1px solid var(--line);
}
.grid {
margin-top: 28px;
grid-template-columns: 1.2fr 0.8fr;
}
.card {
background: var(--card);
border: 1px solid var(--line);
border-radius: 20px;
padding: 22px;
}
.card h2 {
margin: 0 0 14px;
font-size: 1.15rem;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
textarea, pre {
width: 100%;
border-radius: 14px;
border: 1px solid var(--line);
background: #fbfdf9;
}
textarea {
min-height: 110px;
padding: 14px;
resize: vertical;
font: inherit;
}
pre {
min-height: 260px;
padding: 14px;
overflow: auto;
margin: 0;
white-space: pre-wrap;
word-break: break-word;
}
.hint {
margin-top: 10px;
font-size: 0.92rem;
color: var(--muted);
}
ul {
margin: 0;
padding-left: 18px;
color: var(--muted);
}
li + li { margin-top: 8px; }
@media (max-width: 800px) {
.grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="wrap">
<section class="hero">
<h1>AI Career Advisor</h1>
<p>Test the OpenEnv deployment directly from the browser. The API remains fully compatible with the automated checker, and this page gives you a simple way to explore it manually.</p>
<div class="links">
<a class="link primary" href="/docs">Open API Docs</a>
<a class="link secondary" href="/health">Health Check</a>
<a class="link secondary" href="/schema">Schema</a>
<a class="link secondary" href="/openapi.json">OpenAPI JSON</a>
</div>
</section>
<section class="grid">
<div class="card">
<h2>Run Reset</h2>
<label for="payload">Reset payload</label>
<textarea id="payload">{}</textarea>
<div style="margin-top: 14px;">
<button class="primary" onclick="runReset()">POST /reset</button>
</div>
<div class="hint">Example: {"user_skills": ["python", "sql"]}</div>
</div>
<div class="card">
<h2>About This Space</h2>
<ul>
<li>`POST /reset` returns `observation`, `reward`, and `done`.</li>
<li>`POST /step` accepts actions like `choose_career`, `learn_skill`, and `work`.</li>
<li>`GET /state` returns the current episode state.</li>
<li>The automated Phase 1 checks target these API endpoints.</li>
</ul>
</div>
</section>
<section class="card" style="margin-top: 16px;">
<h2>Response</h2>
<pre id="result">Click "POST /reset" to test the deployed environment.</pre>
</section>
</div>
<script>
async function runReset() {
const result = document.getElementById("result");
const payload = document.getElementById("payload").value || "{}";
result.textContent = "Loading...";
try {
const response = await fetch("/reset", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: payload
});
const data = await response.json();
result.textContent = JSON.stringify(data, null, 2);
} catch (error) {
result.textContent = String(error);
}
}
</script>
</body>
</html>
"""
)
def main(host: str = "0.0.0.0", port: int | None = None) -> None:
import uvicorn
uvicorn.run(app, host=host, port=port or int(os.environ.get("PORT", "7860")))
if __name__ == "__main__":
main()