Spaces:
Paused
Paused
File size: 11,010 Bytes
28dd5a4 | 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 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | ---
title: Viraltest — Creator Optimization Agent
emoji: 📊
colorFrom: yellow
colorTo: indigo
sdk: docker
pinned: false
app_port: 8000
base_path: /web
tags:
- openenv
---
# Viraltest — RL-Based Creator Optimization Environment
An [OpenEnv](https://github.com/meta-pytorch/OpenEnv) environment that simulates a social media creator’s weekly posting lifecycle. An AI agent learns **when to post**, **what format**, **which tags**, and **how to differentiate from competitors** — maximizing engagement while managing burnout and sleep.
## Submission requirements — how this repo maps
Use this table to confirm Phase 1 (automated) gates before you submit.
| Requirement | Status in this repo | Where to verify |
|---------------|---------------------|-----------------|
| Real-world task (not a toy/game) | **Met** — creator scheduling, energy, trends, competitors | `server/viraltest_environment.py`, `DESIGN.md` |
| Full OpenEnv spec: `openenv.yaml`, typed models, HTTP API | **Met** | `openenv.yaml`, `models.py`, `server/app.py` (`create_app`) |
| `step()` / `reset()` / `state()` | **Met** — standard OpenEnv HTTP endpoints | Run `openenv validate` |
| ≥3 tasks with graders (easy → hard), scores in **0.0–1.0** | **Met** — `weekly_engage`, `weekly_strategic`, `weekly_competitive` | `_run_grader()` in `server/viraltest_environment.py` |
| Meaningful reward + partial progress | **Met** — per-step `_compute_reward()` | `_compute_reward()` |
| Baseline inference script, reproducible | **Met** — root `inference.py` | See **Baseline inference** below |
| `Dockerfile` builds | **Expected** — root `Dockerfile` | `docker build -t viraltest .` (run locally) |
| HF Space deploys; `POST /reset` returns **200** | **You must configure** | See **Hugging Face Spaces** — ping **Space root**, not only `/web` |
| `openenv validate` passes | **Met** in dev (`.venv/bin/openenv validate`) | CI / local |
| Env vars: `API_BASE_URL`, `MODEL_NAME`, `HF_TOKEN` | **Documented** — `inference.py` reads them (see **Environment variables**) | HF Space **Settings → Secrets** |
| `inference.py` at repo root; OpenAI client for LLM calls | **Met** | `inference.py` |
| Structured stdout: `[START]`, `[STEP]`, `[END]` | **Met** — match field order in `log_*` helpers | `inference.py` |
| Inference under 20 minutes; 2 vCPU / 8 GB | **Check** — 3 tasks × up to 168 steps each = many LLM calls; use a fast endpoint and sensible `MAX_TOKENS` | `inference.py` |
### Minor items to double-check before judging
1. **`[STEP]` `error=` field** — The spec asks for the raw `last_action_error` or `null`. This repo logs errors with spaces replaced by underscores so each line stays a single token after `error=`. If the organizer’s parser expects literal spaces inside unquoted messages, align with their sample; otherwise this is fine for one-line logs.
2. **Default `API_BASE_URL` in `inference.py`** — Defaults are for local dev. On Hugging Face, set **`API_BASE_URL`** (e.g. `https://router.huggingface.co/v1`) and **`MODEL_NAME`** in Secrets so evaluation matches your setup.
3. **Space URL for the validator** — The official script POSTs to `{your_space_url}/reset` with body `{}`. That must be the **root** of the Space (e.g. `https://YOURNAME-spacename.hf.space`), not the Gradio path under `base_path: /web`. Confirm with curl (see **Pre-submission validation**).
---
## Why this matters
Many creators burn out while optimizing posting times and formats. This environment turns that tradeoff into a reproducible simulation so agents can be trained and compared on the same weekly horizon (**168** hourly steps).
---
## Quick Start (Python)
The HTTP client is **async** (same pattern as root `inference.py`):
```python
import asyncio
from viraltest import ViraltestAction, ViraltestEnv
async def main():
env = ViraltestEnv(base_url="http://localhost:8000")
try:
result = await env.reset(task="weekly_engage")
action = ViraltestAction(
action_type="post",
content_type="reel",
topic="AI trends",
tags=["ai", "coding", "devtools"],
)
result = await env.step(action)
print(result.observation.engagement_rate, result.observation.creator_energy)
finally:
await env.close()
asyncio.run(main())
```
---
## Action space
| Field | Type | Description |
|-------|------|-------------|
| `action_type` | `"post" \| "rest" \| "create_content"` | What the agent does this hour |
| `content_type` | `"reel" \| "story" \| "carousel" \| "text_post"` | Required when posting |
| `topic` | `str` (≤200 chars) | Post topic |
| `tags` | `list[str]` (≤5) | Tags from the environment tag pool |
---
## Observation space (high level)
| Field | Description |
|-------|-------------|
| `current_hour`, `day_of_week`, `days_elapsed` | Simulated calendar |
| `creator_energy`, `hours_since_sleep`, `sleep_debt` | Burnout and sleep |
| `follower_count`, `engagement_rate` | Growth and rolling engagement |
| `trending_topics`, `trending_tags`, `tag_performance` | Trends and learned tag quality |
| `competitor_recent_posts`, `competitor_avg_engagement`, `niche_saturation` | Competition |
| `error`, `reward`, `done`, `metadata` | Errors, shaping reward, termination, **`metadata["grader_score"]` at episode end** |
Full schema: `GET /schema` when the server is running.
---
## Tasks and graders (168 steps each)
| Task | Difficulty | Grader focus |
|------|------------|--------------|
| `weekly_engage` | Easier | Total engagement vs theoretical max; burnout penalty |
| `weekly_strategic` | Medium | Engagement + tag discovery/exploitation + energy + consistency |
| `weekly_competitive` | Hard | Adds growth vs competitors, differentiation, diversity constraints |
Episode ends after **168** steps or if **energy ≤ 0**. Final normalized score is in **`observation.metadata["grader_score"]`** in **\[0, 1\]**.
---
## Reward shaping
Per-step reward in **`[0, 1]`** combines engagement, energy change, posting consistency, tags, and competitor differentiation (`_compute_reward` in `server/viraltest_environment.py`). It is dense enough for learning signals before the terminal grader runs.
---
## Local development
```bash
git clone <your-repo-url>
cd viral-posts-env # or your fork name
# Install (uv recommended; pip works too)
uv sync
# source .venv/bin/activate # optional
# Terminal 1 — API server
uvicorn viraltest.server.app:app --host 0.0.0.0 --port 8000
# Terminal 2 — optional UI
# Open http://localhost:8000/dashboard (see server routes in server/app.py)
```
Validate the OpenEnv layout:
```bash
.venv/bin/openenv validate
# Expect: [OK] ... Ready for multi-mode deployment
```
---
## Docker
From the repository root (same directory as `Dockerfile`):
```bash
docker build -t viraltest-env:latest .
docker run --rm -p 8000:8000 viraltest-env:latest
```
Smoke test:
```bash
curl -s -o /dev/null -w "%{http_code}" -X POST -H "Content-Type: application/json" -d '{}' http://localhost:8000/reset
# Expect: 200
```
---
## Hugging Face Spaces — deploy
1. **Create a Space** with **Docker** SDK (this repo’s README frontmatter uses `sdk: docker`).
2. **Push this repository** (or connect GitHub) so the Space builds from the root `Dockerfile`.
3. **Settings → Variables and secrets** — add at least:
- **`HF_TOKEN`** — Hugging Face API token for inference (and Space pull if private).
- **`API_BASE_URL`** — OpenAI-compatible base URL (e.g. `https://router.huggingface.co/v1`).
- **`MODEL_NAME`** — Model id for that router (e.g. `Qwen/Qwen2.5-72B-Instruct`).
4. **App port** — `8000` (see frontmatter `app_port: 8000`).
5. **`base_path: /web`** — Used for the bundled web UI; the **REST** endpoints (`/reset`, `/step`, `/state`) remain on the **Space root host** as required by the submission validator. **Always test** `https://<your-space>.hf.space/reset` (not only `/web/...`).
Optional CLI (if you use OpenEnv’s tooling):
```bash
pip install openenv-core
openenv push # follow OpenEnv docs for auth and target Space
```
---
## Baseline inference (`inference.py`)
**Location:** repository root — **`inference.py`** (required by the hackathon).
**LLM client:** OpenAI-compatible client (`from openai import OpenAI`) using:
| Variable | Role |
|----------|------|
| `API_BASE_URL` | OpenAI-compatible API base |
| `MODEL_NAME` | Model name for `chat.completions` |
| `HF_TOKEN` | Preferred API key (fallbacks: `OPENAI_API_KEY`, `API_KEY`) |
| `IMAGE_NAME` / `LOCAL_IMAGE_NAME` | If using `ViraltestEnv.from_docker_image(...)` instead of HTTP |
| `ENV_BASE_URL` | HTTP server URL (default `http://localhost:8000`) |
**Stdout format (must not change field names or order):**
```text
[START] task=<name> env=<benchmark> model=<model>
[STEP] step=<n> action=<str> reward=<0.00> done=<true|false> error=<msg|null>
[END] success=<true|false> steps=<n> score=<0.00> rewards=<r1,r2,...>
```
Run locally (server on port 8000):
```bash
export HF_TOKEN=hf_...
export API_BASE_URL=https://router.huggingface.co/v1
export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
uv sync && .venv/bin/python inference.py
```
**Short episodes for debugging** — `ALLOW_SHORT_EPISODE=1` and `MAX_STEPS` can shorten runs; full weekly tasks still use **168** steps unless you override (see comments in `inference.py`).
---
## Pre-submission validation
Use the provided script (same checks as the official template: ping Space, Docker build, `openenv validate`):
```bash
chmod +x validate-submission.sh
./validate-submission.sh https://YOUR-SPACE.hf.space /path/to/viral-posts-env
```
Or download the organizer’s script from their repo and pass your Space URL.
**Manual ping (required to pass automated gate):**
```bash
curl -s -o /dev/null -w "%{http_code}\n" -X POST \
-H "Content-Type: application/json" -d '{}' \
https://YOUR-SPACE.hf.space/reset
# Must print: 200
```
---
## Baseline scores (reference)
Deterministic dashboard agents (not the LLM) — see `README` tables in-repo history / `DESIGN.md` for methodology. Your **`inference.py`** scores will vary by model and endpoint; keep runs under the **20-minute** inference budget.
---
## Project structure
```
.
├── inference.py # Hackathon-required baseline (LLM + [START]/[STEP]/[END])
├── openenv.yaml # OpenEnv manifest
├── models.py # ViraltestAction, ViraltestObservation
├── client.py # ViraltestEnv client
├── Dockerfile
├── validate-submission.sh # Local preflight
├── test_scenarios.py # Offline env tests
├── DESIGN.md # Deep design / research notes
└── server/
├── app.py # FastAPI + create_app
├── viraltest_environment.py
└── dashboard.html
```
---
## License
See `LICENSE` in the repository root (BSD-style per upstream OpenEnv examples).
|