--- title: MyVillage Activity Generation API emoji: 🌱 colorFrom: green colorTo: blue sdk: docker app_port: 7860 pinned: false --- # MyVillage Activity Generation API One lightweight **FastAPI** wrapper around three separately deployed Hugging Face Spaces: 1. Progression: what the villager needs to do next. 2. Composition: activity title and description from the progression plan. 3. Instruction: step-by-step instructions from the progression and composition results. Only the **final title, description and instructions** leave this API. No Qwen weights, GGUF files, Transformers, or GPU are needed in this Space. ## Endpoint `POST /generate-activity` with this JSON body: ```json { "version": "v1", "knowledge_submission": "I reviewed 25 saved chatbot conversations. I noticed the refund responses sometimes contradicted the policy, but I have not systematically documented the errors.", "wisdom": { "content": "Testing systems requires comparing observations with expected outcomes.", "chapterTitle": "Testing With Purpose", "BookTypeName": "Personal Records", "BookType": "personal_record" }, "village_goal": "Build and evaluate a reliable customer support chatbot", "previous_activity_title": "Test Chatbot Conversations" } ``` Response shape (generated text varies): ```json { "output": { "activity_title": "Evaluate Chatbot Response Accuracy", "activity_description": "Review saved chatbot conversations to identify recurring errors in responses.", "instructions": [ "Review the 25 saved chatbot conversations.", "Compare responses with expected answers.", "List recurring errors by category." ] } } ``` Input wisdom uses the **exact existing** field names `content`, `chapterTitle`, `BookTypeName`, `BookType`. The wrapper does not synthesize wisdom from the current knowledge submission. Progression fields are passed through: **no `success_evidence` field is fabricated or required**. Other endpoints: `GET /health` (liveness), `GET /docs` (Swagger), `GET /openapi.json`. ## Deploy on Hugging Face Spaces (Docker) 1. Create a **Docker** Space named e.g. `mjpsm/activity-generation-api`. 2. Copy the entire repository content into that Space (including `Dockerfile`, `app/`, `requirements.txt`, and this `README.md`). 3. Under **Settings → Variables and secrets**, set the three `*_API_URL` variables if needed, and optionally set `API_KEY` as a secret. Set `HF_TOKEN` as a secret only if the upstream Spaces require authentication. 4. Wait for Space status **Running**, then open `https://mjpsm-activity-generation-api.hf.space/docs` (assuming that is the Space name you selected). 5. Use **Try it out** for `POST /generate-activity`, or issue the cURL request below. ### Important: Live contract verification The three Space repository identities have been checked, **but their live OpenAPI JSON could not be fetched during development**. This project does **not** claim the live POST paths or exact intermediate request bodies were verified. To avoid inventing endpoint paths, each default API URL points to its **base**; on first use, the wrapper asks that Space for `/openapi.json` and automatically selects the POST route when unambiguous. If the upstream Space has multiple POST routes, set its URL to the **full POST endpoint** from that Space's `/docs`, such as `https://...hf.space/` (not `/docs`). ### Intermediate request body contract By default, this wrapper uses: - **Progression** request: original input JSON as sent to `/generate-activity`. - **Composition** request: `{"progression": }`. - **Instruction** request: `{"progression": , "composition": {"activity_title": "...", "activity_description": "..."}}`. Common response envelopes `{"output": ...}` and `{"progression": ...}` are unwrapped before forwarding. Composition requires non-empty `activity_title` and `activity_description`. Instruction requires a nonempty array of nonblank strings in `instructions`. If a deployed Space's actual request schema nests its inputs differently, use the corresponding **Variables** (no code edits needed): | Variable | Value | Request body | |---|---|---| | `COMPOSITION_INPUT_STYLE` | `direct` (default) | `{"progression": {...}}` | | | `input` | `{"input": {"progression": {...}}}` | | | `bare` | progression object directly | | `INSTRUCTION_INPUT_STYLE` | `direct` (default) | `{"progression": {...}, "composition": {"activity_title": "...", "activity_description": "..."}}` | | | `nested_output` | `{"progression": {...}, "composition": {"output": {"activity_title": "...", "activity_description": "..."}}}` | | | `input` | wraps the `direct` request in `{"input": ...}` | | | `input_nested_output` | wraps the `nested_output` request in `{"input": ...}` | A 502 `upstream_http_error` with HTTP 422 often indicates a request shape mismatch; check the upstream Swagger docs and change the applicable variable. Avoid silently retrying arbitrary alternate bodies or hiding schema mismatches. ### Calling your wrapper ```bash curl -X POST 'http://localhost:7860/generate-activity' \ -H 'Content-Type: application/json' \ -d '{ "version": "v1", "knowledge_submission": "I tested the chatbot in 25 conversations but have not documented its recurring errors.", "wisdom": { "content": "Compare actual outputs against expectations when testing.", "chapterTitle": "Test With Purpose", "BookTypeName": "Personal Records", "BookType": "personal_record" }, "village_goal": "Build a reliable customer service chatbot", "previous_activity_title": "Test Chatbot Conversations" }' ``` If you set the `API_KEY` secret, also include `-H 'X-API-Key: your-secret-key'` in client calls. Treat this like a server-side key; don't embed it in public web frontend JavaScript. ## Run locally ```bash python3 -m venv .venv source .venv/bin/activate pip install -r requirements-dev.txt cp .env.example .env # optional reference; configure env vars in shell to apply them uvicorn app.main:app --reload --port 7860 ``` **Note:** This template intentionally does not load `.env` automatically, to avoid another dependency. To use `.env` locally, export the file's values into your shell or use a runner that reads dotenv files. Run tests: ```bash pytest -q ``` The tests mock all upstream model APIs. They validate orchestration, payload shapes, retries/errors, and output validation without inference costs. **They are not live end-to-end verification**. ## Error handling and safety - Input validation returns **422** before calling any model. - Upstream errors return **502/503**, and exhausted timeouts return **504**. The error contains the failing `stage` and a short code, but **not submitted knowledge or model output**. - Each model call is sequential; the wrapper stops on any error (no partial activities). - Transient **429/502/503/504**, timeouts, and network failures are retried once by default; HTTP 422 contract errors are **not** retried. - Set `READ_TIMEOUT_SECONDS` for cold Spaces; default 240s. `MAX_ATTEMPTS` defaults to 2. - Logs include stage names, timing and a request identifier, not user input. - `GET /health` only checks the wrapper. It does not prove the three upstream Spaces are awake. - Configure `API_KEY` and keep upstream `HF_TOKEN` secrets private before production use. ## Check the live APIs before your first end-to-end test On a machine with internet access, run: ```bash python scripts/inspect_upstreams.py ``` This performs **read-only GET requests** to the three Spaces' OpenAPI documents. It shows their actual POST URLs, top-level request properties, and required properties without making inference calls. Compare its output with the `COMPOSITION_INPUT_STYLE` and `INSTRUCTION_INPUT_STYLE` settings above. You can set the three service URLs in the shell if they have changed.