Studio Images: paste generation parameters, inpaint mask blur and only-masked area, seed buttons by danielhanchen · Pull Request #14508 · unslothai/unsloth · GitHub
Skip to content

Studio Images: paste generation parameters, inpaint mask blur and only-masked area, seed buttons - #14508

Open
danielhanchen wants to merge 35 commits into
mainfrom
studio-images-paste-params
Open

danielhanchen wants to merge 35 commits into
mainfrom
studio-images-paste-params

Conversation

@danielhanchen

@danielhanchen danielhanchen commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Summary

Refs #10695. This adds four Images-page items from the settings gap list.

1. Paste generation parameters

  • Paste entry points:
    • Preset menu: a new "Paste generation parameters" entry opens a text box.
    • Prompt box: pasting a whole AUTOMATIC1111 / Forge / Civitai block (prompt, Negative prompt:, and a Steps: line with a seed, sampler or CFG) into the prompt box fills the form instead of adding the text to the prompt.
    • Unsloth recipes: pasting an Unsloth JSON recipe also works.
  • One parser: paste and image drop share one parser (parseA1111Parameters in lib/image-metadata.ts) and one restore path (applyImportedRecipe). That path also produces the "not applied" toast.
  • Format: the parser follows modules/infotext_utils.py parse_generation_parameters:
    • The last line is the settings line when it has Steps:, or 3+ fields including a known key (the re_param rule, :254-257).
    • Quoted values are unquoted.
    • JSON values (Civitai's Civitai resources: [...], Hashes: {...}) stay whole instead of being split at their commas.
  • New keys:
    • Clip skip (A1111 numbering)
    • CFG Rescale / RescaleCFG / Forge's latent_modifier_rescale_cfg_phi
    • Variation seed / Variation seed strength
    • Hires *
    • Mask blur, Inpaint area, Masked area padding
  • LoRA tags: <lora:name:weight> tags (also te= / unet= and <lyco:...>) are removed from the prompt. Each one selects the installed local LoRA with the same file name, at that weight clamped to 0-2. Tags with no match are reported as "LoRA x (not installed)". Other tags such as <hypernet:...> are removed from the prompt and reported too.
  • Not applied: settings this build cannot apply are listed in the toast instead of being dropped without a word. Examples: Clip skip above 2, CFG rescale, variation seed, hires fix, VAE, ADetailer keys, and samplers the loaded model does not offer.
  • Clip skip 1 and 2 are not reported. On SDXL, A1111 and Forge render both from the penultimate layer, and that is already what Studio renders.
  • Ignored on purpose: file-identity keys such as Model hash, Lora hashes and Version are read and ignored.

2. Inpaint mask blur and "Only masked"

  • Backend: three optional request fields.
    • mask_blur (0-64 px): a Gaussian blur of the mask. This is what diffusers VaeImageProcessor.blur does.
    • inpaint_only_masked
    • inpaint_padding (0-256, 32 when left out, the A1111 default). It maps to diffusers padding_mask_crop.
  • Gating:
    • The fields apply only in the inpaint workflow.
    • "Only masked" is accepted only when the inpaint pipeline's __call__ takes padding_mask_crop. Otherwise the request gets a 400.
    • A new status flag supports_inpaint_only_masked reports this. It is False on the native and MLX engines.
    • Applied values are saved in the gallery record and written to the image's parameters text, so they restore.
  • Frontend: a "Mask blur" slider in Inpaint. The "Only masked" switch and its Padding slider appear only when the loaded model supports them. At their defaults the controls send nothing.

3. Seed buttons

"Random" (clears the Seed field: a new random seed each run) and "Reuse last seed" sit next to Seed. Reuse fills in the base seed of this session's last run. Before any run this session, it uses the newest gallery record's seed.

4. Steps slider max

  • Problem: the slider stopped at 50, but Krea-2-raw's default is 52 steps.
  • Fix: the max is now min(100, max(50, 2 x model default, current value)). 100 is the request's limit.
  • Effect: models with a default of 25 or less keep the 50 max. Imported or restored values above 50 can now be shown.

Testing

  • Frontend unit tests: new tests/image-paste-parameters.test.ts and tests/image-seed-inpaint-steps.test.ts.
    • Before (origin/main): both fail at import, because the parser, seed, inpaint and steps helpers do not exist there.
    • After: all pass.
    • Base parser on a Civitai block: it leaves <lora:...> in the prompt and drops Clip skip, CFG Rescale and Variation seed with no message.
  • Changed tests in tests/image-metadata.test.ts:
    • It used cfg_rescale as its example of an unknown key. That key is now listed by label, so the example moved to tiling.
    • One page-source anchor was updated for the new not-applied list.
  • Full frontend suite: node --experimental-strip-types --test "tests/**/*.test.ts": 13244 tests, 0 failures. node scripts/typecheck.mjs --tests is clean.
  • ESLint: images-page.tsx has 37 errors on both base and head, so this change adds none.
  • New backend tests:
    • tests/test_diffusion_inpaint_mask.py (CPU, 14 tests): request bounds, blur, padding, the status flag, and the route passing fields only when set.
    • tests/test_diffusion_inpaint_mask_sdxl.py (5 tests, through Studio's real DiffusionBackend with a tiny SDXL inpaint pipeline on GPU), which checks that:
      • Leaving the fields out gives the same pixels as setting them to None, 0 or False, and no padding_mask_crop is passed.
      • mask_blur=8 changes the mask the pipeline receives, and the output.
      • "Only masked" passes padding_mask_crop, keeps the source size, and leaves pixels away from the mask unchanged.
      • A pipeline without padding_mask_crop is refused.
  • Bit-identity against origin/main: a default inpaint render with seeds 7 and 8 gives the same pixel hashes on base and head (maximum pixel difference 0).
  • Existing related backend tests: diffusion backend, routes, sampler, SDXL, ControlNet, image gallery, request validation and the generate-image tool. Head: 971 passed, 0 failed. In a full origin/main checkout the same files pass too.

Not covered

  • The only inpaint pipeline actually rendered was the tiny SDXL one. FLUX / Qwen-Image / Z-Image / FLUX.2-klein inpaint also report padding_mask_crop support but were not rendered.
  • No HTTP round trip through the generate route. The route helper is covered by unit tests only.
  • There is no browser screenshot of the new controls.
  • Clip skip, CFG rescale and variation seed are parsed and reported as not applied. They become applied once the request accepts those fields.

Review follow-ups

  • An A1111 block with Mask blur, Inpaint area or masked padding next to Denoising strength now imports as inpaint, not img2img.
  • Restoring an inpaint record resets mask blur, Only masked and padding to their defaults when the record omits them.
  • A run whose response was lost still records its base seed for Reuse last seed.
  • Reuse last seed is disabled, rather than offering an older pinned image's seed, when the loaded page is all pinned and more images exist.
  • An imported Only masked area is listed under "Not applied" when the loaded pipeline cannot crop.
  • The external-connection route refuses mask_blur, inpaint_only_masked and inpaint_padding with a 400, like the other conditioning fields.
  • Mask blur now feathers the result: the diffusers inpaint mask processors binarize mask_image, so with Only masked off the render is pasted back over the source through the blurred mask (A1111's apply_overlay). With Only masked on, padding_mask_crop already overlays with the blurred mask, so it is not applied twice.
  • Pasting parameters with <lora:...> tags before a LoRA-capable model is loaded matches the tags against the full local LoRA list instead of reporting installed adapters as missing.
  • The Reuse last seed button uses lucide Recycle instead of a HugeIcons barrel import, which kept the eager startup bundle under its budget (main + PR: 2023.3 KB transfer, over the 2023.0 KB budget; with this change 2022.9 KB). The screenshots below were taken before this swap, so they show the old icon.

Screenshots: before (merge base dff0fb917) vs after (44cf94475)

Two separate install.sh --local builds of Studio, one from each commit. Both load the same model: a tiny SDXL pipeline with random weights (hf-internal-testing/tiny-stable-diffusion-xl-pipe), run as family sdxl from a local folder whose name contains krea-2-raw. Studio looks up a model's default steps by its name, so that folder name gives a resident default of 52 steps on both sides (the backend also reports generation_defaults.steps = 52). That shows the Krea-2-raw case without downloading Krea-2-raw itself. The noise image in the gallery is what random weights produce.

Steps slider at a 52-step model default. Before, the slider stops at 50 and the thumb sits pinned at the end while the field reads 52. After, the slider goes up to 100 (aria-valuemax 50 -> 100) and the thumb sits at 52.

Steps slider, before vs after

Seed buttons. Before, there is only the Seed field. After, Random and Reuse last seed sit next to it. This shot was taken after one generation with seed 424242. Clicking Reuse put 424242 in the field, and the tooltip reads "Reuse last seed (424242)".

Seed row, before vs after

Preset menu. After, the menu has a new "Paste generation parameters" entry under "Load settings from an image".

Preset menu, before vs after

Pasting a Civitai-style A1111 block. The block includes <lora:sloth_style_v2:0.8>, Clip skip 2, CFG Rescale 0.7 and Variation seed 42. Before, pasting it into the prompt box drops the whole block into the prompt as plain text. After, the block goes into the Paste generation parameters dialog.

Paste, before vs after

The form after the paste. Before, the prompt holds the raw block, including the LoRA tag. Steps stays at 52, the size stays 1024x1024 and the seed stays empty. After:

  • The prompt has the LoRA tag removed.
  • The negative prompt is filled in.
  • Steps 30, CFG 6.5, sampler DPM++ 2M / Karras, seed 1234567890 and 832x1216 are all applied.
  • The toast lists what was not applied: "CFG rescale 0.7, variation seed 42 (strength 0.3), LoRA sloth_style_v2 (not installed)". Clip skip 2 is not listed, which is intended: SDXL already renders from the penultimate layer.

Form after paste, before vs after

Inpaint with a source image. Before, only Strength is shown. After, there is a Mask blur slider (0-64, default 0) and an Only masked switch, which appears because supports_inpaint_only_masked is true for SDXL. Turning the switch on shows a Padding slider (0-256, default 32).

Inpaint controls, before vs after

Optional mask_blur, inpaint_only_masked and inpaint_padding request fields
(AUTOMATIC1111 Mask blur / Inpaint area: Only masked / Masked area padding).
Unset keeps the plain inpaint call. Only masked uses diffusers
padding_mask_crop and is reported by supports_inpaint_only_masked.
…r range

Paste AUTOMATIC1111 / Forge / Civitai parameter text into the form through
the same parser as image drop. The parser now reads Clip skip, CFG rescale,
variation seed, hires keys, inpaint keys, JSON values and lora tags; tags
select installed LoRAs and anything Studio cannot apply is listed as not
applied. Adds Random and Reuse last seed buttons, inpaint mask controls, and
lets the Steps slider reach models whose default is above 50.
@danielhanchen danielhanchen self-assigned this Oct 21, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

@chatgpt-codex-connector

Copy link
Copy Markdown

The account paying for this security review has reached its Codex usage limits. The payer can check the Codex usage dashboard. For personal accounts, using credits requires enabling “Use credits for security reviews” in Code review settings. If you do not manage the paying account, contact this repository's admins.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

danielhanchen pushed a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Oct 21, 2026
danielhanchen pushed a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Oct 21, 2026
danielhanchen pushed a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Oct 21, 2026
danielhanchen pushed a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Oct 21, 2026
danielhanchen pushed a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Oct 21, 2026
danielhanchen pushed a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Oct 21, 2026
chatgpt-codex-connector[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 21, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 21, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 21, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 21, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 21, 2026
chatgpt-codex-connector[bot]

This comment was marked as resolved.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39edebbaa5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const networks = extractExtraNetworks(promptLines.join("\n").trim());
const out: ImportedRecipe = {
prompt: promptLines.join("\n").trim(),
prompt: networks.prompt,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report unsupported A1111 prompt emphasis

When an imported A1111/Civitai prompt contains standard attention syntax such as (masterpiece:1.2) or [background], this preserves the syntax and reports the prompt as applied. On the diffusers engine, DiffusionBackend.generate forwards this string directly to the pipeline and the repository has no A1111 attention parser, so the punctuation and weight are tokenized literally rather than reproducing A1111 conditioning. Translate this syntax or list prompt emphasis as not applied.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 026d85d: A1111 attention syntax in a pasted prompt or negative prompt ((x:1.2), (x), [x]; escaped brackets are literal) is now listed as prompt emphasis passed as plain text when the loaded engine does not parse it (diffusers, MLX) or no model is loaded. The prompt text is kept unchanged. The native sd.cpp engine has its own A1111 parser (parse_prompt_attention), so nothing is reported there.

@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 21, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 21, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 21, 2026
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

1 similar comment
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: ce6b4b2bb9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

1 similar comment
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 026d85d7ff

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +704 to +706
if (ch === "\\") {
i++;
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Unescape A1111's literal bracket syntax

When an imported prompt contains escaped delimiters such as \(literal\) or \[literal\], the current fix skips them here but leaves the prompt text unchanged. On diffusers and MLX, which have no A1111 attention parser, those backslashes are therefore tokenized even though A1111 removes them and encodes the brackets literally; the import also emits no not-applied warning. Unescape these delimiters for non-parsing engines or report them as unsupported.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 18c5644: escaped A1111 brackets (( ) [ ]) are now listed as passed with their backslashes when the loaded engine does not parse A1111 syntax (diffusers, MLX) or no model is loaded. The prompt is kept as written, since the engine can change later; on sd.cpp nothing is reported.

Comment on lines +37 to +40

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include archived images when choosing the last seed

After the user archives their newest local image and reloads the page, sessionSeed is null and gallery contains only the active shelf because getGallery defaults to archived=false. This scan therefore selects an older active image and the “Reuse last seed” button silently uses the wrong seed. Compare against the newest archived record as well, or disable the gallery fallback when that shelf has not been considered.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6fc4725: the gallery fallback for Reuse last seed is now used only when the whole active gallery is loaded and the archive is confirmed empty (one archived=true, limit 1 request, repeated when the strip changes). With any archived image, or before that check returns, only this session's seed is offered, so an archived newest image is never skipped.

This branch has not been deployed

No deployments
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.

1 participant