File size: 7,819 Bytes
42ca926
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c1b700d
42ca926
 
c1b700d
 
 
 
42ca926
c1b700d
 
 
 
 
 
 
 
 
 
 
 
42ca926
 
 
 
 
 
 
 
 
 
 
 
 
c1b700d
 
42ca926
c1b700d
 
 
 
 
 
 
 
 
 
 
 
 
42ca926
c1b700d
42ca926
c1b700d
 
42ca926
 
 
 
 
 
 
 
 
 
 
939683f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42ca926
 
 
 
 
 
c1b700d
42ca926
c1b700d
 
 
42ca926
c1b700d
 
 
42ca926
 
 
 
c1b700d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42ca926
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# MOSS-Image API spec (node4)

HTTP image-generation API for the MOSS-Image TTNN bundle. Diffusion model β€” **no tokens, no
KV cache, no streaming**; requests are image jobs.

## Base URL
- From node4 itself: `http://127.0.0.1:8000`
- From other boxes on the LAN: `http://192.168.50.97:8000` (node4), or `http://node4:8000` if
  your `/etc/hosts`/DNS resolves it.
- The server binds `0.0.0.0:$PORT` (default **8000**). Open that port in any firewall between
  boxes. No auth (intended for a trusted LAN); put it behind a proxy if you need auth.

> Status note: the API shape below is identical whether it is served by the device bundle
> (TTNN on the Blackhole card) or the CPU reference β€” same endpoints, same schemas. Only speed
> differs.

## Endpoints

### `GET /health`
Liveness + which model/mesh is loaded, and the capability contract.
```json
{ "status": "ok", "model": "OpenMOSS-Team/MOSS-Image-Base",
  "mesh": "P150", "mesh_shape": "1x1", "vae_device": true,
  "pipeline_loaded": true, "kind": "tt-dit-server",
  "progress_reporting": true, "job_persistence": true,
  "private_ephemeral_jobs": true, "private_result_ttl_s": 900 }
```
- `vae_device` β€” `true` when the VAE decodes on the device (pure `ttnn`, the default;
  validated vs host fp32 to PCC β‰₯0.9999 at all resolutions). Force host with `MOSS_VAE_DEVICE=0`.
- `mesh_shape` β€” DiT topology (`MOSS_MESH`): `1x1` single-chip (default), `1x2`/`1x4` mesh.
- `private_ephemeral_jobs` / `private_result_ttl_s` β€” see **Private jobs** below.

### Private jobs (default for new `POST /generate`)
Send an `X-Moss-Job-Token` header on `POST /generate` (or let the server mint one and return it
as `job_token`). The job is then token-scoped: **not** listed in `GET /jobs`, **never** written
to disk (result held in memory only), and reaped after a **900 s** TTL. The same token is
required on `GET /jobs/{id}`, `GET /images/{id}.png`, and `DELETE /jobs/{id}`; a missing/wrong
token returns **404**. Limits: max 8 private jobs, 64 MiB encoded result each. Legacy disk-backed
jobs (created without a token) remain listable and survive restarts.

### `GET /v1/models`
OpenAI-style model list.
```json
{ "object": "list", "data": [
  { "id": "OpenMOSS-Team/MOSS-Image-Base", "object": "model", "owned_by": "openmoss" } ] }
```

### `POST /generate`  β†’ submit an async job (recommended)
Request body (JSON):
| field | type | default | notes |
| --- | --- | --- | --- |
| `prompt` | string | β€” | **required** |
| `height` | int | 1024 | 256–2048, multiple of 16 |
| `width` | int | 1024 | 256–2048, multiple of 16 |
| `num_inference_steps` | int | 50 | 1–200; β‰₯28 for quality |
| `guidance_scale` | float | 3.0 | 0–20; CFG scale (1.0 disables CFG) |
| `negative_prompt` | string | `""` | used when `guidance_scale != 1` |
| `seed` | int\|null | null | reproducibility (CPU RNG) |
| `max_sequence_length` | int | 4096 | 1–4096; max prompt tokens |
| `shift` | float\|null | nullβ†’5.0 | 1–10; flow-match scheduler shift |
| `uncond_skip_layers` | list[int] | `[22]` | DiT layers skipped on the CFG negative branch; `[]` disables |
| `slg_layers` | list[int]\|null | null | skip-layer-guidance layers; null/`[]` disables the extra forward |
| `slg_scale` | float | 0.0 | 0–20; `prediction += slg_scale*(cond βˆ’ cond_skipped)` |
| `slg_start` | float | 0.0 | 0–1; SLG/skip window start (fraction of steps) |
| `slg_stop` | float | 1.0 | 0–1; SLG/skip window stop (fraction of steps) |

The advanced knobs mirror `reference/pipeline_moss_image.py __call__`. Call **`GET /v1/parameters`**
(alias `GET /config`) for this schema with ranges at runtime.

Response (every new job is private by default β€” keep the `job_token`):
```json
{ "job_id": "ff70ee2b28e4", "status": "queued", "poll": "/jobs/ff70ee2b28e4",
  "job_token": "<43–128 url-safe chars>", "storage": "memory", "private": true, "result_ttl_s": 900 }
```
Jobs are serialized through one worker (one model / one device); submitting many just queues them.

### `GET /jobs/{job_id}`  β†’ poll status
```json
{ "job_id": "ff70ee2b28e4", "status": "done",
  "request": { ... }, "image_url": "/images/ff70ee2b28e4.png" }
```
`status` ∈ `queued` | `running` | `done` | `error`. On `error`, an `error` field carries the message.

### `GET /images/{job_id}.png`  β†’ fetch the result
Returns `image/png` once the job is `done` (404 until then). Finished images are
saved to disk (`$MOSS_OUTPUT_DIR`, default `./moss_outputs`) and freed from memory, so
this is served from the saved file after the job completes.

### `GET /jobs`  β†’ list jobs
```json
{ "jobs": [ { "job_id": "ff70ee2b28e4", "status": "done",
             "file_path": "/.../moss_outputs/ff70ee2b28e4.png", "prompt": "a red fox in snow" } ] }
```

### `DELETE /jobs/{job_id}`  β†’ delete a job + its saved image
Drops the job from memory and removes its PNG from disk.
```json
{ "deleted": true, "job_id": "ff70ee2b28e4", "file_removed": true }
```

### `POST /generate/sync`  β†’ blocking, returns PNG directly
Same body as `/generate`; responds with `image/png`. Convenient but holds the connection for the
full generation (minutes at 1024Β²/50) β€” prefer the async flow for anything non-trivial.

### `POST /v1/images/generations`  β†’ OpenAI-compatible (blocking)
Request (superset of the OpenAI schema β€” the full control set is accepted here too):
```json
{ "prompt": "a red fox in snow", "n": 1, "size": "1024x1024", "seed": 42,
  "width": 1024, "height": 1024, "num_inference_steps": 50, "guidance_scale": 3.0,
  "negative_prompt": "", "shift": 5.0, "slg_layers": [24], "slg_scale": 3.0 }
```
`size` is `WIDTHxHEIGHT`; explicit `width`+`height` override it. All `/generate` advanced knobs
(`max_sequence_length`, `shift`, `uncond_skip_layers`, `slg_*`) are accepted. Response (base64
PNGs, OpenAI Images shape):
```json
{ "created": 1760000000, "data": [ { "b64_json": "<base64 png>" } ] }
```

### `GET /v1/parameters` (alias `GET /config`)  β†’ controllable-parameter schema
A UI reads this to render generation controls. Each entry has `name`, `type`, `default`, and
`min`/`max`/`multiple_of`/`max_length` where applicable.
```json
{ "model": "OpenMOSS-Team/MOSS-Image-Base",
  "endpoints": ["/generate", "/v1/images/generations"],
  "parameters": [
    { "name": "num_inference_steps", "type": "int", "default": 50, "min": 1, "max": 200 },
    { "name": "guidance_scale", "type": "float", "default": 3.0, "min": 0.0, "max": 20.0 },
    { "name": "shift", "type": "float|null", "default": 5.0, "min": 1.0, "max": 10.0 },
    { "name": "width", "type": "int", "default": 1024, "min": 256, "max": 2048, "multiple_of": 16 }
    /* …height, seed, negative_prompt, max_sequence_length, uncond_skip_layers, slg_* … */
  ] }
```

## Example (async)
```bash
HOST=http://192.168.50.97:8000
JID=$(curl -s -X POST $HOST/generate -H 'content-type: application/json' \
  -d '{"prompt":"a cozy bookshop, warm light, a cat asleep on a chair","height":1024,"width":1024,"num_inference_steps":50,"seed":7}' \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["job_id"])')
until curl -s $HOST/jobs/$JID | grep -q '"status":"done"'; do sleep 5; done
curl -s $HOST/images/$JID.png -o out.png
```

## Notes for client/agent builders
- **Latency** (single p150, device backend): ~12–25 s for 256Β²/4-step warm; ~235 s for
  1024Β²/50-step. Set generous client timeouts; prefer `/generate` + poll over `/generate/sync`.
- **Concurrency:** requests queue (single device). Don't fan out expecting parallelism.
- **Resolution:** `height`/`width` must be multiples of 16.
- **Determinism:** pass `seed` for reproducible images.
- **Errors:** poll `/jobs/{id}` and check `status == "error"` + the `error` string; HTTP 4xx for
  bad input (e.g. malformed `size`), 404 for an unknown job or an image not yet ready.