Art Studio: carry generation-parameter overrides to the worker (#842) #845

Open
opened 2026-07-25 20:21:55 +00:00 by spikerj · 1 comment
Owner

Adds RequestArtAssetStageCommand.GenerationParamOverrides as a SEPARATE field from ArtAssetStageMethod.Params — those are static server config and caller input must not overwrite one by colliding on a key.

Enforcement is asymmetric on purpose: the API reports a bad value so the caller gets a 400 naming the real limit; the worker drops it with a warning instead, because a message that already passed validation should never fail a job that has burned GPU time.

prompt/negative_prompt are rejected, not clamped — both are moderated at submit and the restart path does not re-moderate new text, so accepting either would turn a knob into a laundering route for a blocked prompt on a platform serving minors.

Overrides are stage-scoped: carried on a same-stage failover republish (an alternate model must reproduce the same intent), omitted from the next-stage publish.

Also adds per-method capability descriptors to GET options so the composer renders the right control per model. Known limitation: those descriptors are DECLARED in the allow-list rather than read from the models' artpipe.json, because the manifests live on the worker's pipeline root and the API host has no copy. Keep the two in sync.

Adds `RequestArtAssetStageCommand.GenerationParamOverrides` as a SEPARATE field from `ArtAssetStageMethod.Params` — those are static server config and caller input must not overwrite one by colliding on a key. Enforcement is asymmetric on purpose: the API reports a bad value so the caller gets a 400 naming the real limit; the worker drops it with a warning instead, because a message that already passed validation should never fail a job that has burned GPU time. `prompt`/`negative_prompt` are rejected, not clamped — both are moderated at submit and the restart path does not re-moderate new text, so accepting either would turn a knob into a laundering route for a blocked prompt on a platform serving minors. Overrides are stage-scoped: carried on a same-stage failover republish (an alternate model must reproduce the same intent), omitted from the next-stage publish. Also adds per-method capability descriptors to `GET options` so the composer renders the right control per model. Known limitation: those descriptors are DECLARED in the allow-list rather than read from the models' `artpipe.json`, because the manifests live on the worker's pipeline root and the API host has no copy. Keep the two in sync.
Author
Owner

Audited against origin/mastersubstantially done; two gaps. Staying open.

Landed: GenerationParamOverrides as a field genuinely separate from ArtAssetStageMethod.Params (RequestArtAssetStageCommand.cs:351-352 vs :375); Overridable (:29-32) / Reserved (:47-55) key sets; prompt/negative_prompt rejected before any clamp (ArtStudioGenerationParamPolicy.cs:92-98) with 400s that name the real limits (:102, :145, :156-166); capability descriptors (ArtStudioStageMethodOptions.cs:546, seeded :102-148) exposed on GET options (ArtStudioController.cs:877-897); overrides carried on same-stage failover (ArtPipeStageOrchestrator.cs:1447-1464) and correctly omitted on next-stage publish (:672-690) — both with explanatory comments and tests.

Remaining 1 — "the worker drops it with a warning" is actually silent. Clamping happens, but nothing is logged: sdxl_lightning.py:140, flux_schnell.py:147-152, qr_monster.py:161,166,170,176,181. The only warning anywhere in the parameter path is for an unparseable seed (generation_params.py:87). So a user whose override is silently clamped has no signal, and neither does anyone reading worker logs afterwards — which defeats the point of allowing overrides at all.

Remaining 2 — GET options doesn't filter by source type. GetArtStudioMethodOptionsQuery.cs:14 takes only bool IsStaff, so the composer is handed methods that cannot honour a reference image. Related to the guard-reach problem noted on #846.

Audited against `origin/master` — **substantially done; two gaps.** Staying open. **Landed:** `GenerationParamOverrides` as a field genuinely separate from `ArtAssetStageMethod.Params` (`RequestArtAssetStageCommand.cs:351-352` vs `:375`); `Overridable` (`:29-32`) / `Reserved` (`:47-55`) key sets; `prompt`/`negative_prompt` rejected **before** any clamp (`ArtStudioGenerationParamPolicy.cs:92-98`) with 400s that name the real limits (`:102`, `:145`, `:156-166`); capability descriptors (`ArtStudioStageMethodOptions.cs:546`, seeded `:102-148`) exposed on `GET options` (`ArtStudioController.cs:877-897`); overrides carried on same-stage failover (`ArtPipeStageOrchestrator.cs:1447-1464`) and correctly omitted on next-stage publish (`:672-690`) — both with explanatory comments and tests. **Remaining 1 — "the worker drops it with a warning" is actually silent.** Clamping happens, but nothing is logged: `sdxl_lightning.py:140`, `flux_schnell.py:147-152`, `qr_monster.py:161,166,170,176,181`. The only warning anywhere in the parameter path is for an unparseable seed (`generation_params.py:87`). So a user whose override is silently clamped has no signal, and neither does anyone reading worker logs afterwards — which defeats the point of allowing overrides at all. **Remaining 2 — `GET options` doesn't filter by source type.** `GetArtStudioMethodOptionsQuery.cs:14` takes only `bool IsStaff`, so the composer is handed methods that cannot honour a reference image. Related to the guard-reach problem noted on #846.
Sign in to join this conversation.