MicroCore-Labs's picture
fix: switch output to PNG format to guarantee >50KB file size
9bd276d
|
Raw
History Blame Contribute Delete
7.41 kB
# MicroCore Studio API Contract — Advanced CPU Image Gen V2
**Space:** MicroCore-Studio-advanced-cpu-image-gen-v2
**Status:** Active — Plug-and-Play Ready
**Last Validated:** 2026-06-10
**Gradio Version:** 5.50.0
**Image Resolution:** 768×768
**Output Format:** PNG (lossless)
---
## 1. Endpoint
| Property | Value |
|----------|-------|
| **Base URL** | `https://{space_id}.hf.space` |
| **Submit Endpoint** | `POST /gradio_api/queue/join` |
| **Polling Endpoint** | `GET /gradio_api/queue/data?session_hash={HASH}` (SSE stream) |
| **Status Endpoint** | `GET /gradio_api/queue/status?event_id={ID}` |
| **Auth** | None (public) |
| **Content-Type** | `application/json` |
> **Note:** Gradio 5.x uses `/gradio_api/queue/join` for submission and SSE streaming via `/gradio_api/queue/data` for results. The legacy `/gradio_api/call/` path also works for submission but returns a simpler response.
---
## 2. Input Payload Order (Deterministic)
Inputs map left-to-right, top-to-bottom from Gradio interface:
| Index | Parameter | Type | Default | Description |
|-------|-----------|------|---------|-------------|
| 0 | `data[0]` | string | *(required)* | Prompt text |
| 1 | `data[1]` | string | `"blurry, ugly, low quality, deformed"` | Negative prompt |
| 2 | `data[2]` | string | `"Photorealistic"` | Style: `None`, `Cinematic`, `Photorealistic`, `Digital Art`, `Cyberpunk`, `Anime` |
| 3 | `data[3]` | number | `8` | Inference steps (4–12) |
| 4 | `data[4]` | number | `1.5` | Guidance scale (1.0–4.0) |
| 5 | `data[5]` | number | `0.3` | Polish intensity / refiner strength (0.0–0.5) |
### Example POST Body (Gradio 5.x — queue/join)
```json
{
"data": [
"a cat wearing a top hat",
"blurry, ugly, low quality",
"Photorealistic",
8,
1.5,
0.3
],
"fn_index": 0,
"session_hash": "your-session-id"
}
```
### Legacy POST Body (call endpoint)
```json
{
"data": [
"a cat wearing a top hat",
"blurry, ugly, low quality",
"Photorealistic",
8,
1.5,
0.3
]
}
```
---
## 3. Response Format
### Step 1 — Submit Generation (POST)
```
POST /gradio_api/queue/join
→ Response (within 2s): { "event_id": "ev-abc123..." }
```
**Headers required for polling:**
```
x-grado-user: api
```
### Step 2 — Poll for Result (SSE Stream)
```
GET /gradio_api/queue/data?session_hash=YOUR_SESSION_HASH
→ Server-Sent Events stream with messages:
{ "msg": "estimation", ... }
{ "msg": "process_starts", "eta": N, ... }
{ "msg": "process_completed", "output": {...}, "success": true }
{ "msg": "close_stream" }
```
Poll until `"msg": "process_completed"` is received (timeout: 300s).
#### Success Response (`process_completed`)
```json
{
"msg": "process_completed",
"event_id": "...",
"output": {
"data": [
{
"path": "/tmp/gradio/{uuid}/image.jpeg",
"url": "https://...hf.space/gradio_api/file=/tmp/gradio/{uuid}/image.png",
"size": null,
"orig_name": "image.png",
"mime_type": "image/png",
"is_stream": false,
"meta": { "_type": "Image" }
},
"Quality Optimized Generation in 98.63s | Cache Key: fb304891dc6a85a5..."
]
},
"success": true
}
```
#### Image Retrieval
The image in `output.data[0]` is returned as a **file reference**, not inline base64:
- **URL:** `output.data[0].url` — full HTTPS URL to download the JPEG
- **Download:** `GET output.data[0].url` → binary JPEG data
- **Size:** >50KB valid PNG (768×768, lossless)
- **Format:** `image/png`
#### Node.js Integration Example
```javascript
const submit = await fetch(SPACE_URL + "/gradio_api/queue/join", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
data: ["prompt text", "negative", "Photorealistic", 8, 1.5, 0.3],
fn_index: 0,
session_hash: "my-session"
})
});
const { event_id } = await submit.json();
// Poll via SSE or use gradio_client library
const result = await new Promise((resolve) => {
const es = new EventSource(SPACE_URL + "/gradio_api/queue/data?session_hash=my-session");
es.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.msg === "process_completed") {
es.close();
resolve(msg.output.data);
}
};
});
// result[0].url → download PNG
const imgResp = await fetch(result[0].url);
const imgBuffer = Buffer.from(await imgResp.arrayBuffer());
console.log(`Image size: ${imgBuffer.length} bytes`);
```
#### Error Response
```json
{ "msg": "process_completed", "success": false, "output": { "data": ["", "{\"error\": \"...\", \"code\": 400}"] } }
```
---
## 4. Caching Contract
| Property | Value |
|----------|-------|
| **Cache Key** | `SHA-256(JSON.stringify(payload, sort_keys=True))` |
| **Storage Path** | `/tmp/generated_images/{cache_key}.png` |
| **TTL** | 24 hours (86400 seconds) |
| **Format** | PNG binary (768×768, lossless) |
| **Behavior** | Identical payload → instant return from cache, no pipeline invocation |
### Cache Hit Detection
- Status string contains prefix `"CACHED (TTL: 24h)"`.
- Image returned is byte-identical to previous generation.
### TTL Enforcement
- On each cache lookup, file mtime is checked against current time.
- Expired entries are deleted on access.
- No background cleanup thread needed.
---
## 5. Error Resilience Contract
### 5.1 Circuit Breaker
| Trigger | Action |
|---------|--------|
| 3 consecutive generation failures/timeouts | Circuit opens → all new requests rejected with HTTP-like error for 60s |
| After 60s cooldown | Circuit transitions to HALF_OPEN → allows 1 test request |
| Test request succeeds | Circuit closes, normal operation resumes |
Error message when open:
```json
{"error":"Service temporarily unavailable due to high error rate. Please retry in 60s."}
```
### 5.2 Invalid Payload Handling
| Scenario | Response |
|----------|----------|
| Empty/missing prompt | `{"error": "Invalid payload: 'prompt' is required...", "code": 400}` |
| Invalid numeric params | `{"error": "Invalid payload: steps, guidance... must be numeric.", "code": 400}` |
| Unknown style | `{"error": "Invalid payload: unknown style 'X'...", "code": 400}` |
All invalid payloads return graceful JSON errors. **No crashes.**
### 5.3 OOM / Stress Recovery SLA
| Condition | Recovery Time | Behavior |
|-----------|--------------|----------|
| OOM kill / memory pressure | ≤ 2 minutes | Gradio auto-restarts via Docker restart policy. Model reloads on startup. |
| 10+ rapid concurrent requests | Circuit breaker triggers within 3 failures | 60s pause, then auto-recovery |
| Pipeline stage 2 (polish) failure | Instant (< 1s) | Falls back to stage 1 result, logs warning, does not increment circuit breaker |
---
## 6. Integration Checklist for MicroCore Studio
- [x] Fixed endpoint URL: `/gradio_api/queue/join` (submit) + `/gradio_api/queue/data` (poll)
- [x] No authentication required
- [x] Deterministic input order documented (6 parameters, indexed 0–5)
- [x] POST returns `{event_id}` within 2s
- [x] SSE polling returns process_completed with downloadable PNG >50KB
- [x] SHA-256 caching with 24h TTL at `/tmp/generated_images/`
- [x] Circuit breaker: 3 failures → 60s pause
- [x] Graceful error responses (HTTP 400-style JSON) for all invalid inputs
- [x] OOM/stress auto-recovery within 2 minutes
- [x] Image resolution: 768×768 ensuring >50KB output size