Add Workers AI model catalogue and multi-shape inference adapter - #8
Merged
Merged
Conversation
runWorkersAi assumed one contract — {prompt, seed} in, binary image
bytes out — and threw `unexpected Workers AI response shape` on
anything else. Workers AI models do not share that contract, and the
differences don't follow vendor or task:
- flux-1-schnell takes `steps` and returns JSON {image: <base64>},
so the fastest model in the catalogue failed at render time, after
the renderer had already been debited.
- The Stable Diffusion family takes `num_steps` and returns a
ReadableStream.
- flux-2-* take multipart form-data, even for a prompt-only call.
- Leonardo's two models disagree with each other: phoenix-1.0
streams bytes, lucid-origin returns base64 JSON.
- Vision and instruct models return text, which had no output kind.
Adds src/ai/workersAiCatalogue.ts: one row per allowlisted model
recording its request shape, response shape, content type, defaults and
parameter allowlist, each checked against that model's own docs page.
runWorkersAi now dispatches on the row rather than guessing, and
img2img/inpaint/vision gain the binary inputs they always needed
(image as a uint8 array, mask for inpainting).
The same table closes a separate hole: provider_model_id was any
free-form string forwarded straight to env.AI.run(), so a typo or an
unpriced model published successfully and only failed once a user had
been charged. Registration now rejects ids outside the catalogue and
names what is allowed.
InferenceOutput gains a `text` kind for the companion lane. ModelKind is
deliberately unchanged — companion models are an internal assist for
prompt expansion and reverse-prompting, not something a creator
publishes; widening the published-model schema is a separate change.
Stored render objects were also always named .png regardless of what the
model emitted; the extension now follows the content type.
Verification: 15 new unit tests over the pure helpers (all four response
shapes, per-family request building, param allowlisting, catalogue
membership), 48/48 unit tests, 146/146 smoke checks including three new
end-to-end registration checks, typecheck clean, dry-run deploy clean on
both environments.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QuSa99HABSrWRarT9BQ5Tz
Contributor
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
generatedart | 1ba5757 | Commit Preview URL Branch Preview URL |
Sep 05 2026, 02:53 PM |
guillaumelauzier
marked this pull request as ready for review
September 5, 2026 15:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
runWorkersAiassumed a single contract —{prompt, seed}in, binary image bytes out — and threwunexpected Workers AI response shapeon anything else. Workers AI models don't share that contract, and the differences don't follow vendor or task:flux-1-schnellsteps{image: <base64>}num_stepsReadableStreamflux-2-dev/klein-4b/klein-9bleonardo/phoenix-1.0ReadableStreamleonardo/lucid-originimage+prompt/messagesTwo models from the same vendor (Leonardo) disagree with each other, so shape can't be inferred — it has to be recorded per model.
The live consequence:
flux-1-schnell, the fastest and cheapest model in the catalogue, failed at render time — after the renderer had been debited. img2img, inpainting, vision and instruct models couldn't work at all, since their inputs (image,mask,messages) were never sent and their text outputs had no output kind.Changes
src/ai/workersAiCatalogue.ts(new) — one row per allowlisted model recording request shape, response shape, content type, defaults and parameter allowlist. Every row was checked against that model's own docs page rather than copied from a neighbour.src/ai/inference.ts—runWorkersAidispatches on the catalogue row instead of guessing.img2img/inpaint/visiongain the binary inputs they always needed (image as a uint8 array, mask for inpainting).InferenceOutputgains atextkind for the companion lane.ModelKindis deliberately unchanged — companion models are an internal assist for prompt expansion and reverse-prompting, not something a creator publishes. Widening the published-model schema touches registration validation and job persistence, so it belongs in its own change.src/render/handlers.ts— closes a separate hole the same table fixes:provider_model_idwas any free-form string up to 200 chars forwarded straight toenv.AI.run(), so a typo or an unpriced model published fine and only failed once a user had been charged. Registration now rejects ids outside the catalogue and names what is allowed. Also fixes stored render objects always being named.pngregardless of what the model emitted.Note on cost
costUnitsis a relative weight (cheapest = 1), clearly marked as a placeholder. Real per-model pricing is published per model page and is variously per-step, per-512px-tile, or both — this is an ordering for a future billing layer to reason about, not a dollar amount. Substitute real figures before charging against them.Testing
json_base64_imagecase that was the bug), per-family request building, thestepsvsnum_stepsdivergence, param allowlisting, and catalogue membership.output_hashbehaviour for existing binary models is unchanged, so reproducibility of already-recorded jobs is preserved.wrangler dev, including 3 new end-to-end checks that registration rejects an unlisted id and accepts a catalogued one.npm run typecheckclean;wrangler deploy --dry-runclean on both environments.One caveat worth knowing: running the smoke suite twice back-to-back trips the SIWE rate limiter and fails four auth-abuse checks. That's pre-existing test-environment behaviour, not a regression — a single run against fresh state is 146/146.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QuSa99HABSrWRarT9BQ5Tz
Generated by Claude Code