--- title: Piclets Discovery Server emoji: 🔮 colorFrom: purple colorTo: indigo sdk: gradio sdk_version: 5.49.0 app_file: app.py pinned: false short_description: Backend for the Piclets monster-discovery game --- # 🔮 Piclets Discovery Server The backend for **Piclets**, a photo-to-monster discovery game. Photograph a real object, and this server turns it into a collectible creature and saves it to a shared, public database — so the first person to scan a given object *discovers* its canonical monster, and everyone else who scans it finds the same one. This Space does **only two things**: 1. **Orchestrates the AI pipeline** for a scan, forwarding the *player's* Hugging Face token to three ZeroGPU Spaces so the GPU time is billed to the player, not to this server: ``` photo ──▶ identify object (VLM) ──▶ design monster as JSON (LLM) ──▶ art (T2I) ``` 2. **Writes new monsters** to a public HF Dataset in a single commit, and keeps the small aggregate index files (dex / feed / leaderboard / stats) in sync. Everything the frontend *reads* — the dex, the feed, the leaderboard, a player's collection — is fetched **directly from the dataset CDN**, never through this server. That asymmetry is the whole design; see **[ARCHITECTURE.md](ARCHITECTURE.md)**. > **Note on `hf_oauth`:** this server does **not** set `hf_oauth`. The *frontend* > Space runs "Sign in with Hugging Face" and forwards the resulting access token > here as a parameter; this server just verifies it against the HF userinfo > endpoint. OAuth config belongs on the frontend, not here. ## Setup 1. **Create the dataset** (the shared database). Run once, locally, with a token that has write access: ```bash HF_TOKEN=hf_xxx DATASET_REPO=/piclets python init_dataset.py ``` 2. **Create this Space** (Gradio SDK, free CPU basic) and push these files. 3. **Add secrets** in *Space Settings → Variables and secrets*: | Name | Type | Purpose | | -------------- | ------ | -------------------------------------------------- | | `HF_TOKEN` | secret | **This server's** write token for the dataset. | | `DATASET_REPO` | var | The dataset id, e.g. `you/piclets`. | | `ADMIN_TOKEN` | secret | *(optional)* guards the `/admin_*` endpoints. | Optional tuning vars: `CAPTION_SPACE`, `CONCEPT_SPACE`, `IMAGE_SPACE`, `MAX_IMAGE_BYTES`, `OUTPUT_IMAGE_MAX`, `SCAN_MAX_IN_WINDOW`, `SCAN_WINDOW_S`, `CONCURRENCY`. 4. **Verify** the AI Space signatures before trusting a scan (they change over time). See [ARCHITECTURE.md → *Swapping the AI Spaces*](ARCHITECTURE.md). Quick check: ```python from gradio_client import Client Client("multimodalart/Qwen-Image-Fast").view_api() ``` ## API One write endpoint (the UI on this page is just for manual testing): - **`/scan`** — inputs `image` (file) and `hf_token` (the player's OAuth access token). Returns: ```json {"success": true, "status": "new", "descriptor": "...", "monster": { ... }, "message": "You discovered ...!"} ``` `status` is `"new"` (a monster was created and committed) or `"existing"` (the object was already discovered — returned read-only, no commit, minimal GPU). - **`/admin_delete`**, **`/admin_rebuild`** — moderation tools, gated by `ADMIN_TOKEN`. Read paths for the frontend (direct from the CDN): ``` https://huggingface.co/datasets//resolve/main/index/monsters.json https://huggingface.co/datasets//resolve/main/index/feed.json https://huggingface.co/datasets//resolve/main/index/leaderboard.json https://huggingface.co/datasets//resolve/main/index/stats.json https://huggingface.co/datasets//resolve/main/monsters/.json https://huggingface.co/datasets//resolve/main/images/.webp ``` ## Limits worth knowing - **Per-player GPU quota** (ZeroGPU, daily): ~2 min anonymous, ~5 min free, ~40 min Pro. A scan is three GPU calls, so a free player gets on the order of a handful of *new* discoveries per day. Repeat scans of known objects are deduped before the expensive stages and cost almost nothing. - **Write ceiling:** every discovery commits under this server's single token, and HF rate-limits commits (the exact number is undocumented). `huggingface_hub` ≥ 1.2.0 auto-retries on 429. New-monster writes naturally slow as common objects get claimed. - **Free CPU Space** sleeps after 48h idle and wakes on the next visit (cold start up to ~a minute). It's a single replica — which is why one in-process lock is enough to serialize all writes. Full reasoning, the exact numbers, and how to stress-test them are in **[ARCHITECTURE.md](ARCHITECTURE.md)**. Development notes are in **[CLAUDE.md](CLAUDE.md)**.