Skip to content

server: POST /v1/images/generations for an engine with an image capability (#1573) - #2253

Draft
devopsfortunato wants to merge 2 commits into
ashhart:mainfrom
devopsfortunato:task/Tensorfold-Contribut+t-mw2m47ye-s9riow-route
Draft

devopsfortunato wants to merge 2 commits into
ashhart:mainfrom
devopsfortunato:task/Tensorfold-Contribut+t-mw2m47ye-s9riow-route

Conversation

@devopsfortunato

@devopsfortunato devopsfortunato commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

Draft, stacked on #2248. Base cde4fe7d9; this branch carries #2248's commit d00584834 plus one commit of its own, 3bbf90e78. Merge #2248 first (it is expected after the current cut), then this. The MOSS-Image host that fills the capability is not here — see the note at the end.

Before: an engine can state an image capability, but the server has no route that asks it for a picture — POST /v1/images/generations answers the unknown-path 404 even on an engine that serves images. After: an engine whose Info.image is set is served at POST /v1/images/generations — one picture a request as a base64 PNG, in the shape agreed on the issue: n 1 only, b64_json only, every unknown field refused by name, the pipeline's own options at their defaults (negative_prompt, seed, steps, guidance), the API's limits enforced here and tightened by the host's own ImageInfo (1536 a side, 1,572,864 pixels, 1 to 200 steps, a 16 KiB prompt and the host's token cap), nothing written to the connection before the engine's finished event, and a client that leaves cancelled through the existing settle path. An engine without the capability is unchanged (404), and a text engine's routes are untouched.

The route follows /v1/audio/speech's pattern for a model that is not a token family, in the maintainer's shape: n 1 only; b64_json only; unknown fields refused by name; an image capability beside SpeechInfo; one request at a time; every text-engine option refused for the checkpoint. Capacity is a 503 with Retry-After; a refusal is a 400 in the API's invalid_request_error shape; a failed pass is a 500 carrying the engine's own message (copied into the request arena before the mailbox is freed).

Checks on this head

  • Head commit: 3bbf90e78 (parent d00584834 from engine api: an optional image capability beside speech (#1573) #2248, on main cde4fe7d9)
  • Lean checker, python3 tools/zig/lean_check.py, on the base (cde4fe7d9, 5520 files) and on this head (5524 files): 425 and 425 problems, 0 new findings.
  • Tests run on this head, on Linux/aarch64 with Zig 0.17.0 from .zig-version:
    • zig build test — exit 0, Build Summary: 545/545 steps succeeded; 6101/6137 tests passed (36 skipped)
    • zig build test-golden — exit 0, 5/5 steps succeeded
    • zig build test-server-security test-pull-security — exit 0, 14/14 steps succeeded; 29/29 tests passed
    • zig build test-kernel-contract — exit 0, 22/22 steps succeeded; 180/180 tests passed
    • zig fmt --check build.zig zig — exit 0
  • New tests: eleven in zig/src/server/images_generations_test.zig, over a Unix socket pair against a stub image engine (the harness speech_stream_test.zig uses): an accepted request reaches the engine once with the parsed fields and the reply's data[0].b64_json decodes to the stub's PNG; defaults 42/50/3/empty/1024x1024; a 17-row refusal table each a 400 invalid_request_error naming the field or limit and never reaching the engine (n 2 and 0, sizes 1000x1024 / 2048x2048 / 1552x1024 / 1536x1040, response_format url, steps 0 and 201, guidance 0.5 and 1e300, quality/style/user, an unknown key, an empty prompt, an unserved model); a prompt past 16 KiB; a negative_prompt past 16 KiB or the token cap; a failed pass as a 500 with the engine's message; a text route on an image engine as a 400 naming this route; this route on a text engine as the 404; error.Busy as a 503 with Retry-After; error.InvalidRequest as a 400; and nothing on the wire before finished.
  • They fail before the change: with the route's dispatch line removed, zig build test exits 1 and 9 of the 11 fail (the two that stay green assert route-level refusals that do not depend on dispatch); restored, exit 0.
  • CI on head 3bbf90e78: green as a draft — zig 18m14s, zig-windows 23m23s, zig-macos-dev-tools 7m36s, release-paths, fmt 16s, benchmark-clients on ubuntu-latest and windows-latest. No failures.
  • Not run, and why: no device or checkpoint receipt — no engine in the tree sets Info.image, and this machine has no MOSS-Image checkpoint, so nothing serves a real picture here. Metal, HIP and XPU were not run.

Receipt

n/a — this change cannot reach inference on its own: no engine in the tree states an image capability, so no picture is produced. The route and its admission are exercised by the host tests against a stub image engine, and test-golden's recorded server corpus is unchanged.

The MOSS-Image host is outstanding

@BobClawblaw handed both halves over on #1573 and left a reference branch (BobClawblaw/TensorFold@4f204cf35) with native/moss_image_host.zig, verified byte-equal against tf-moss-image-generate on an M5 Ultra. That host is Metal (@import("metal"), mtl.Device) and this machine has no Mac and no MOSS-Image checkpoint, so it cannot be compiled or run here and is not in this pull request. The route is complete and gated without it; the served-PNG equality and the text-family A/B need a Mac with the checkpoint, which is the receipt that would close #1573. The generate tool's own gate (from #1531) is also unfinished per that handoff, and the served PNG has nothing to equal until it runs.

Checklist

  • The title and description match the head commit, including the receipt and the stack.
  • New tests fail before the change, pass after it, and skip cleanly without their dependency (the socket-pair test file is imported only on non-Windows, like speech_stream_test.zig).
  • Comments and docstrings are one line. No measurements or history in the source.
  • No personal data, machine names, internal hosts or local paths in the diff or its commits. No AI attribution lines.
  • No precision traded and no lossy format; text models' replies are unchanged (this route is unreachable on them).

@devopsfortunato
devopsfortunato force-pushed the task/Tensorfold-Contribut+t-mw2m47ye-s9riow-route branch from e92ce5e to 95aebb7 Compare November 5, 2026 22:58
@devopsfortunato

Copy link
Copy Markdown
Contributor Author

CI note: the runs for the rebased head 95aebb741 were cancelled at 22:58 (the rebase push cancelled them and no new run started). Reopened the draft to re-trigger them; the branch and its commits are unchanged. #2248's body shas are also updated as asked (127762e97 on 474c3b048).

@ashhart

ashhart commented Nov 6, 2026

Copy link
Copy Markdown
Owner

Thanks @devopsfortunato. The route reads well: every check happens before the engine is called, a client that
leaves cancels and waits for finished before the request's frame goes away, nothing reaches the wire before
finished, and an engine without the capability still answers the 404 it does today. Once #2248 lands, three small
things for the restack:

  1. Keep only the route's CHANGELOG line, citing "(server: POST /v1/images/generations for an engine with an image capability (#1573) #2253, part of moss_image: serve pictures over POST /v1/images/generations #1573)". The engine-API line is engine api: an optional image capability beside speech (#1573) #2248's and lands
    with it.
  2. Update the body to the current head and base. It still names cde4fe7d9, d00584834 and 3bbf90e78.
  3. Mark it ready for review when CI is green on the restacked head.
    One nit, not blocking: when size is omitted, the 1024×1024 default isn't checked against the host's max_side
    and max_pixels, so a host with smaller limits would answer with the engine's generic refusal instead of the
    route's own sentence naming the limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

moss_image: serve pictures over POST /v1/images/generations

2 participants