moss-image / API_SPEC.md
mando2222's picture
Docs: release update
c1b700d verified
|
Raw History Blame
7.82 kB

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.

{ "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.

{ "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):

{ "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

{ "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

{ "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.

{ "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):

{ "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):

{ "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.

{ "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)

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.