File size: 7,963 Bytes
fa37f46
9c9ef1d
 
 
 
fa37f46
9c9ef1d
fa37f46
 
 
9c9ef1d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
---
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.