mjpsm's picture
Deploy MyVillage Activity Generation API
9c9ef1d verified
|
Raw History Blame Contribute Delete
7.96 kB
---
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/<actual-post-path>` (not `/docs`).
### Intermediate request body contract
By default, this wrapper uses:
- **Progression** request: original input JSON as sent to `/generate-activity`.
- **Composition** request: `{"progression": <unwrapped progression object>}`.
- **Instruction** request: `{"progression": <unwrapped progression object>, "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.