Spaces:
Sleeping
Sleeping
| # Piclets Discovery Server API Documentation | |
| ## Overview | |
| The Piclets Discovery Server provides a Gradio-based API for the Piclets discovery game. Each real-world object has ONE canonical Piclet, with variations tracked based on attributes. All data is stored in a public HuggingFace Dataset. | |
| ## Quick Start | |
| ### Running Locally | |
| ```bash | |
| pip install -r requirements.txt | |
| python app.py | |
| ``` | |
| ### Accessing the API | |
| - **Web Interface**: http://localhost:7860 | |
| - **Programmatic Access**: Use Gradio Client to connect to the space | |
| ### Frontend Integration | |
| ```javascript | |
| import { Client } from "@gradio/client"; | |
| const client = await Client.connect("Fraser/piclets-server"); | |
| const result = await client.predict("/search_piclet", { | |
| object_name: "pillow", | |
| attributes: ["velvet"] | |
| }); | |
| ``` | |
| ## API Endpoints | |
| ### 1. Search Piclet | |
| **Endpoint**: `/search_piclet` | |
| **Purpose**: Search for canonical Piclet or variations | |
| **Method**: Gradio function call | |
| **Input Parameters**: | |
| ```json | |
| { | |
| "object_name": "pillow", | |
| "attributes": ["velvet", "blue"] | |
| } | |
| ``` | |
| **Response Types**: | |
| **New Object** (no Piclet exists): | |
| ```json | |
| { | |
| "status": "new", | |
| "message": "No Piclet found for 'pillow'", | |
| "piclet": null | |
| } | |
| ``` | |
| **Existing Canonical** (exact match): | |
| ```json | |
| { | |
| "status": "existing", | |
| "message": "Found canonical Piclet for 'pillow'", | |
| "piclet": { | |
| "objectName": "pillow", | |
| "typeId": "pillow_canonical", | |
| "discoveredBy": "user123", | |
| "discoveredAt": "2024-07-26T10:30:00", | |
| "scanCount": 42, | |
| "picletData": { /* full Piclet data */ } | |
| } | |
| } | |
| ``` | |
| **Variation Found**: | |
| ```json | |
| { | |
| "status": "variation", | |
| "message": "Found variation of 'pillow'", | |
| "piclet": { /* variation data */ }, | |
| "canonicalId": "pillow_canonical" | |
| } | |
| ``` | |
| **New Variation Suggested**: | |
| ```json | |
| { | |
| "status": "new_variation", | |
| "message": "No variation found for 'pillow' with attributes ['velvet', 'blue']", | |
| "canonicalId": "pillow_canonical", | |
| "piclet": null | |
| } | |
| ``` | |
| ### 2. Create Canonical | |
| **Endpoint**: `/create_canonical` | |
| **Purpose**: Register the first discovery of an object with OAuth verification | |
| **Method**: Gradio function call | |
| **Input Parameters**: | |
| ```json | |
| { | |
| "object_name": "pillow", | |
| "piclet_data": "{ /* JSON string of Piclet instance */ }", | |
| "token_or_username": "hf_xxxxxxxxxxxxx" // OAuth token or username for testing | |
| } | |
| ``` | |
| **Success Response**: | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Created canonical Piclet for 'pillow'", | |
| "piclet": { | |
| "objectName": "pillow", | |
| "typeId": "pillow_canonical", | |
| "discoveredBy": "username123", | |
| "discovererSub": "987654321", | |
| "discovererUsername": "username123", | |
| "discovererName": "Display Name", | |
| "discovererPicture": "https://avatars.huggingface.co/...", | |
| "discoveredAt": "2024-07-26T10:30:00", | |
| "scanCount": 1, | |
| "picletData": { /* full Piclet data */ } | |
| } | |
| } | |
| ``` | |
| **Error Responses**: | |
| ```json | |
| { | |
| "success": false, | |
| "error": "Invalid OAuth token" | |
| } | |
| ``` | |
| ```json | |
| { | |
| "success": false, | |
| "error": "Failed to save canonical Piclet" | |
| } | |
| ``` | |
| **Notes**: | |
| - If `token_or_username` starts with `hf_`, it's verified as an OAuth token | |
| - Token verification calls `https://huggingface.co/oauth/userinfo` | |
| - User profile is created/updated with cached OAuth fields | |
| - Legacy mode: Plain usernames create `legacy_{username}` profiles | |
| ### 3. Create Variation | |
| **Endpoint**: `/create_variation` | |
| **Purpose**: Add a variation to an existing canonical Piclet with OAuth verification | |
| **Method**: Gradio function call | |
| **Input Parameters**: | |
| ```json | |
| { | |
| "canonical_id": "pillow_canonical", | |
| "attributes": ["velvet", "blue"], | |
| "piclet_data": "{ /* JSON string of variation data */ }", | |
| "token_or_username": "hf_xxxxxxxxxxxxx", // OAuth token or username for testing | |
| "object_name": "pillow" | |
| } | |
| ``` | |
| **Success Response**: | |
| ```json | |
| { | |
| "success": true, | |
| "message": "Created variation of 'pillow'", | |
| "piclet": { | |
| "typeId": "pillow_001", | |
| "attributes": ["velvet", "blue"], | |
| "discoveredBy": "player456", | |
| "discovererSub": "123456789", | |
| "discovererUsername": "player456", | |
| "discovererName": "Player Name", | |
| "discovererPicture": "https://avatars.huggingface.co/...", | |
| "discoveredAt": "2024-07-26T11:00:00", | |
| "scanCount": 1, | |
| "picletData": { /* variation data */ } | |
| } | |
| } | |
| ``` | |
| **Error Responses**: | |
| ```json | |
| { | |
| "success": false, | |
| "error": "Invalid OAuth token" | |
| } | |
| ``` | |
| ```json | |
| { | |
| "success": false, | |
| "error": "Canonical Piclet not found for 'pillow'" | |
| } | |
| ``` | |
| **Notes**: | |
| - Same OAuth verification as create_canonical | |
| - User profile updated with variation discovery (+50 rarity points) | |
| - Variation numbering is automatic (pillow_001, pillow_002, etc.) | |
| ### 4. Increment Scan Count | |
| **Endpoint**: `/increment_scan_count` | |
| **Purpose**: Track how many times a Piclet has been discovered | |
| **Method**: Gradio function call | |
| **Input Parameters**: | |
| ```json | |
| { | |
| "piclet_id": "pillow_canonical", | |
| "object_name": "pillow" | |
| } | |
| ``` | |
| **Success Response**: | |
| ```json | |
| { | |
| "success": true, | |
| "scanCount": 43 | |
| } | |
| ``` | |
| ### 5. Get Recent Activity | |
| **Endpoint**: `/get_recent_activity` | |
| **Purpose**: Get global discovery feed | |
| **Method**: Gradio function call | |
| **Input Parameters**: | |
| ```json | |
| { | |
| "limit": 20 | |
| } | |
| ``` | |
| **Response**: | |
| ```json | |
| { | |
| "success": true, | |
| "activities": [ | |
| { | |
| "type": "discovery", | |
| "objectName": "pillow", | |
| "typeId": "pillow_canonical", | |
| "discoveredBy": "user123", | |
| "discoveredAt": "2024-07-26T10:30:00", | |
| "scanCount": 42 | |
| }, | |
| { | |
| "type": "variation", | |
| "objectName": "pillow", | |
| "typeId": "pillow_001", | |
| "attributes": ["velvet", "blue"], | |
| "discoveredBy": "user456", | |
| "discoveredAt": "2024-07-26T11:00:00", | |
| "scanCount": 5 | |
| } | |
| ] | |
| } | |
| ``` | |
| ### 6. Get Leaderboard | |
| **Endpoint**: `/get_leaderboard` | |
| **Purpose**: Get top discoverers by rarity score | |
| **Method**: Gradio function call | |
| **Input Parameters**: | |
| ```json | |
| { | |
| "limit": 10 | |
| } | |
| ``` | |
| **Response**: | |
| ```json | |
| { | |
| "success": true, | |
| "leaderboard": [ | |
| { | |
| "rank": 1, | |
| "username": "explorer123", | |
| "totalFinds": 156, | |
| "uniqueFinds": 45, | |
| "rarityScore": 2340 | |
| }, | |
| { | |
| "rank": 2, | |
| "username": "hunter456", | |
| "totalFinds": 134, | |
| "uniqueFinds": 38, | |
| "rarityScore": 1890 | |
| } | |
| ] | |
| } | |
| ``` | |
| ### 7. Get User Profile | |
| **Endpoint**: `/get_user_profile` | |
| **Purpose**: Get individual user's discovery statistics | |
| **Method**: Gradio function call | |
| **Input Parameters**: | |
| ```json | |
| { | |
| "sub": "987654321" // HuggingFace user ID (preferred) or username for legacy | |
| } | |
| ``` | |
| **Response**: | |
| ```json | |
| { | |
| "success": true, | |
| "profile": { | |
| "sub": "987654321", | |
| "preferred_username": "player123", | |
| "name": "Player Display Name", | |
| "picture": "https://avatars.huggingface.co/...", | |
| "email": "[email protected]", | |
| "joinedAt": "2024-07-01T10:00:00", | |
| "lastSeen": "2024-07-26T12:00:00", | |
| "discoveries": ["pillow_canonical", "chair_002", "lamp_canonical"], | |
| "uniqueFinds": 2, | |
| "totalFinds": 3, | |
| "rarityScore": 250, | |
| "visibility": "public" | |
| } | |
| } | |
| ``` | |
| **Notes**: | |
| - Profile keyed by `sub` (stable HF user ID), not username | |
| - OAuth fields (preferred_username, name, picture) cached and refreshed on each login | |
| - Legacy profiles have `sub = "legacy_{username}"` | |
| - Visibility can be "public" or "private" (future feature) | |
| ## Object Normalization Rules | |
| The server normalizes object names for consistent storage: | |
| 1. Convert to lowercase | |
| 2. Remove articles (the, a, an) | |
| 3. Handle pluralization: | |
| - `pillows` β `pillow` | |
| - `berries` β `berry` | |
| - `leaves` β `leaf` | |
| - `boxes` β `box` | |
| 4. Replace spaces with underscores | |
| 5. Remove special characters | |
| Examples: | |
| - `"The Blue Pillow"` β `pillow` | |
| - `"wooden chairs"` β `wooden_chair` | |
| - `"A pair of glasses"` β `pair_of_glass` | |
| ## Rarity Tiers | |
| Based on scan count: | |
| - **Legendary**: β€ 5 scans | |
| - **Epic**: 6-20 scans | |
| - **Rare**: 21-50 scans | |
| - **Uncommon**: 51-100 scans | |
| - **Common**: > 100 scans | |
| ## Scoring System | |
| - **Canonical Discovery**: +100 rarity points | |
| - **Variation Discovery**: +50 rarity points | |
| - **Scan Bonus**: Additional points based on rarity tier | |
| ## Error Handling | |
| All endpoints return consistent error structures: | |
| ```json | |
| { | |
| "success": false, | |
| "error": "Description of what went wrong" | |
| } | |
| ``` | |
| Common error scenarios: | |
| - Piclet not found | |
| - Invalid JSON data | |
| - Failed to save to dataset | |
| - Network/connection errors | |
| ## Rate Limiting | |
| Currently no rate limiting implemented. For production: | |
| - Consider adding per-user rate limits | |
| - Implement cooldowns for discoveries | |
| - Cache frequent requests | |
| ## Authentication | |
| **OAuth Token Verification** (Production Mode): | |
| - Frontend sends `Authorization: Bearer <hf_token>` headers | |
| - Server verifies tokens via `https://huggingface.co/oauth/userinfo` | |
| - Returns user info: `sub` (stable ID), `preferred_username`, `name`, `picture`, `email` | |
| - User profiles keyed by `sub` (HF user ID) instead of username | |
| - Usernames can change, but `sub` remains stable | |
| **Legacy Mode** (Testing Only): | |
| - For backward compatibility, endpoints accept plain usernames | |
| - If token doesn't start with `hf_`, treated as username | |
| - Creates legacy user profile with `sub = "legacy_{username}"` | |
| **Example OAuth Flow**: | |
| ```javascript | |
| // Frontend: Get OAuth token from HuggingFace Space | |
| import { HfInference } from "https://cdn.jsdelivr.net/npm/@huggingface/inference/+esm"; | |
| const auth = await hfAuth.signIn(); | |
| // Make authenticated request | |
| const response = await fetch('/api/endpoint', { | |
| headers: { | |
| 'Authorization': `Bearer ${auth.accessToken}`, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ /* payload */ }) | |
| }); | |
| ``` | |
| **Token Verification Process**: | |
| 1. Extract Bearer token from Authorization header | |
| 2. Call `https://huggingface.co/oauth/userinfo` with token | |
| 3. Verify response status 200 | |
| 4. Extract user info (sub, preferred_username, name, picture) | |
| 5. Get or create user profile using `sub` as key | |
| 6. Cache profile fields on each request | |
| ## Data Storage | |
| All data stored in HuggingFace Dataset: | |
| - Repository: `Fraser/piclets` | |
| - Type: Public dataset | |
| - Structure: | |
| - `piclets/` - Canonical and variation data | |
| - `users/` - User profiles | |
| - `metadata/` - Global statistics | |
| ## Best Practices | |
| 1. **Always normalize object names** before searching | |
| 2. **Check for existing Piclets** before creating new ones | |
| 3. **Increment scan counts** when rediscovering | |
| 4. **Cache responses** on the client side | |
| 5. **Handle network errors** gracefully | |
| 6. **Validate JSON data** before sending | |
| ## Example Workflow | |
| 1. User scans an object (e.g., pillow) | |
| 2. Extract object name and attributes from caption | |
| 3. Search for existing Piclet | |
| 4. If new: | |
| - Create canonical Piclet | |
| - Award discovery bonus | |
| 5. If variation: | |
| - Create or retrieve variation | |
| - Update scan count | |
| 6. Update user profile | |
| 7. Refresh activity feed | |
| ## Support | |
| For issues or questions: | |
| - Check CLAUDE.md for implementation details | |
| - Review example code in app.py | |
| - Open an issue in the repository |