Spaces:
Sleeping
Sleeping
File size: 7,565 Bytes
362bbff c64fcea 362bbff 329e3d3 362bbff 329e3d3 362bbff 329e3d3 362bbff 329e3d3 362bbff 329e3d3 362bbff 329e3d3 362bbff 329e3d3 362bbff c64fcea 329e3d3 c64fcea 362bbff | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | """
FastAPI application for the Shopify Store Audit & Remediation Environment.
Endpoints:
POST /reset - Reset the environment (accepts task selection)
POST /step - Execute an action
GET /state - Get current environment state
GET /schema - Get action/observation schemas
WS /ws - WebSocket for persistent sessions
"""
try:
from openenv.core.env_server.http_server import create_app
except Exception as e:
raise ImportError(
"openenv is required. Install with: pip install openenv-core[core]"
) from e
try:
from ..models import ShopifyStoreAuditAction, ShopifyStoreAuditObservation
from .shopify_store_audit_environment import ShopifyStoreAuditEnvironment
except ImportError:
from models import ShopifyStoreAuditAction, ShopifyStoreAuditObservation
from server.shopify_store_audit_environment import ShopifyStoreAuditEnvironment
from fastapi.responses import HTMLResponse, JSONResponse
try:
from .tasks import ALL_TASKS
except ImportError:
from server.tasks import ALL_TASKS
try:
from .graders import grade_product_listing_qa, grade_seo_collection_optimization, grade_full_store_audit
except ImportError:
from server.graders import grade_product_listing_qa, grade_seo_collection_optimization, grade_full_store_audit
GRADER_MAP = {
"product_listing_qa": grade_product_listing_qa,
"seo_collection_optimization": grade_seo_collection_optimization,
"full_store_audit": grade_full_store_audit,
}
app = create_app(
ShopifyStoreAuditEnvironment,
ShopifyStoreAuditAction,
ShopifyStoreAuditObservation,
env_name="shopify_store_audit",
max_concurrent_envs=2,
)
LANDING_HTML = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Shopify Store Audit - OpenEnv</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
background:#0a0a0a;color:#e5e5e5;min-height:100vh;display:flex;align-items:center;justify-content:center}
.container{max-width:720px;padding:48px 32px;text-align:center}
h1{font-size:2.2rem;margin-bottom:8px;background:linear-gradient(135deg,#34d399,#3b82f6);
-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.subtitle{color:#a3a3a3;font-size:1.05rem;margin-bottom:36px}
.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:36px}
.card{background:#171717;border:1px solid #262626;border-radius:12px;padding:20px 16px;text-align:left}
.card h3{font-size:.95rem;margin-bottom:4px;color:#f5f5f5}
.card .tag{display:inline-block;font-size:.7rem;padding:2px 8px;border-radius:99px;
margin-bottom:10px;font-weight:600;text-transform:uppercase}
.easy .tag{background:#064e3b;color:#6ee7b7}.med .tag{background:#78350f;color:#fcd34d}
.hard .tag{background:#7f1d1d;color:#fca5a5}
.card p{font-size:.82rem;color:#a3a3a3;line-height:1.45}
.endpoints{background:#171717;border:1px solid #262626;border-radius:12px;
padding:24px;text-align:left;margin-bottom:28px}
.endpoints h2{font-size:1rem;margin-bottom:14px;color:#d4d4d4}
.ep{display:flex;align-items:center;gap:10px;margin-bottom:8px;font-size:.85rem}
.method{font-weight:700;font-size:.75rem;padding:2px 8px;border-radius:4px;min-width:48px;text-align:center}
.get{background:#1e3a5f;color:#93c5fd}.post{background:#3b1f0b;color:#fdba74}
.ws{background:#312e81;color:#c4b5fd}
code{color:#a78bfa;font-size:.82rem}
.try-it{margin-top:24px;font-size:.82rem;color:#737373}
.try-it code{background:#262626;padding:3px 8px;border-radius:4px;font-size:.78rem}
.footer{color:#525252;font-size:.78rem;margin-top:8px}
.footer a{color:#60a5fa;text-decoration:none}
</style>
</head>
<body>
<div class="container">
<h1>馃洅 Shopify Store Audit</h1>
<p class="subtitle">RL environment with real Shopify product data — 45 products, 184 discoverable issues, shaped rewards</p>
<div class="cards">
<div class="card easy"><span class="tag">Easy 路 Full hints</span>
<h3>Product Listing QA</h3>
<p>8 issues 路 25 steps<br>Issues listed with suggested commands. Agent fills in params.</p></div>
<div class="card med"><span class="tag">Medium 路 Descriptions only</span>
<h3>SEO & Collections</h3>
<p>12 issues 路 35 steps<br>Issue descriptions shown. Agent picks commands & params.</p></div>
<div class="card hard"><span class="tag">Hard 路 Explore</span>
<h3>Full Store Audit</h3>
<p>20 issues 路 50 steps<br>Only category counts. Agent must discover issues itself.</p></div>
</div>
<div class="endpoints">
<h2>API Endpoints</h2>
<div class="ep"><span class="method get">GET</span><code>/health</code> — health check</div>
<div class="ep"><span class="method get">GET</span><code>/tasks</code> — enumerate tasks & graders</div>
<div class="ep"><span class="method post">POST</span><code>/reset</code> — reset (body: <code>{}</code>)</div>
<div class="ep"><span class="method post">POST</span><code>/step</code> — action (body: <code>{"action":{"command":"...","params":{}}}</code>)</div>
<div class="ep"><span class="method get">GET</span><code>/state</code> — current state</div>
<div class="ep"><span class="method get">GET</span><code>/schema</code> — action/observation schemas</div>
<div class="ep"><span class="method post">POST</span><code>/grade</code> — run grader on sample</div>
<div class="ep"><span class="method ws">WS</span><code>/ws</code> — persistent WebSocket session</div>
</div>
<p class="footer">Real CSV data · 18 Shopify GraphQL commands · Shaped rewards · Regression penalties · Randomised episodes<br>
<a href="https://huggingface.co/spaces/devaatmik/shopify-store-audit/blob/main/README.md">Full docs</a> ·
Built for <a href="https://huggingface.co/blog/openenv">OpenEnv</a></p>
</div>
</body>
</html>"""
@app.get("/tasks")
async def list_tasks():
"""Enumerate all tasks with their grader info. Used by competition validators."""
tasks = []
for task_id, cfg in ALL_TASKS.items():
tasks.append({
"id": cfg.task_id,
"title": cfg.title,
"description": cfg.description,
"difficulty": cfg.difficulty,
"max_steps": cfg.max_steps,
"num_issues": cfg.num_issues,
"hint_level": cfg.hint_level,
"has_grader": task_id in GRADER_MAP,
"grader": f"server.graders.grade_{task_id}",
})
return {"tasks": tasks, "count": len(tasks)}
@app.post("/grade")
async def grade_task(body: dict):
"""Run a grader on the provided observation/sample. Used by competition validators."""
task_id = body.get("task_id", body.get("task", ""))
sample = body.get("sample", body.get("observation", body))
grader = GRADER_MAP.get(task_id)
if grader is None:
return JSONResponse(
status_code=400,
content={"error": f"Unknown task '{task_id}'. Available: {list(GRADER_MAP.keys())}"},
)
try:
score = grader(sample)
return {"task_id": task_id, "score": score, "valid": 0.0 <= score <= 1.0}
except Exception as e:
return JSONResponse(status_code=500, content={"error": str(e)})
@app.get("/", response_class=HTMLResponse)
async def root():
return LANDING_HTML
def main(host: str = "0.0.0.0", port: int = 8000):
import uvicorn
uvicorn.run(app, host=host, port=port)
if __name__ == "__main__":
main()
|