Commit graph

69 commits

Author SHA1 Message Date
Daniel Han
90827bc05c Studio: gate gallery serve/export on ownership; keep image progress active until persisted; reserve diffusion training before the dataset scan 2026-07-13 14:40:18 +00:00
pre-commit-ci[bot]
3fa9fdbc0d [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-13 10:04:07 +00:00
Daniel Han
5eef2f4003 Studio: preserve foreign gallery files, force safetensors on remote ControlNets, and close dataset/seed/GPU gaps
Gallery clear/delete now scope to Studio-owned files: image_gallery and
video_gallery skip PNGs / MP4s without a readable recipe (a hand-dropped or
orphan file the listing already hides), so clear() and a guessed-id delete no
longer destroy files the gallery never surfaced.

Remote ControlNets now force use_safetensors: a bare owner/name reaches
from_pretrained without the base trust gate, and the Hub scan fails open when
unavailable, so requiring safetensors closes the pickle deserialization vector.

POSIX uninstall now stops resident sd-server / sd-cli under an owned sd.cpp root
before removing the tree (marker-gated), mirroring the Windows stop-before-delete
scan; a live native server no longer survives unlinking its binary.

Diffusion dataset containment: the training-start read path and the discovery
picker route bare names through the protected resolver, so a symlinked dataset
is rejected / not advertised like the caption/delete routes already do. Uploads
gain the inference decode guard (oversized real images 400 before OOMing the
trainer) and dataset upload/caption/delete/import are blocked with 409 while a
diffusion run is active.

JSONL readers (trainer + routes) tolerate non-object JSON and invalid UTF-8
instead of raising AttributeError / 500.

LoRA family compatibility is enforced in the shared resolver, not only the
picker, so a direct API client cannot apply a mismatched-family adapter.

GPU arbiter gains release_if so the image/video unload idle-check and release
are atomic against a concurrent same-owner load's registration. Native batch
recipes persist the base batch_seed and restore replays from it, so a native
batch_index>0 image no longer advances its seed twice.

FLUX.2-klein selects its sd.cpp text encoder by variant (4B -> Qwen3-4B,
9B -> Qwen3-8B) instead of the single family default.
2026-07-13 10:02:42 +00:00
Daniel Han
5f65f01e2e Fix training start blocking, dataset resolution shadowing, duplicate uploads, partial-caption gate
Run backend.start_training off the event loop with asyncio.to_thread so the
synchronous diffusion/video unload calls (which wait on engine generation
locks) cannot freeze concurrent requests; guard against overlapping starts
with a _start_in_progress compare-and-set under the service lock.

Resolve bare diffusion dataset names directly under datasets_root() before
falling back to the generic resolver, so an unrelated LLM upload file or
recipe folder sharing the name cannot shadow the image dataset.

Reject exact duplicate filenames within one multipart upload batch: two
parts staged to the same destination would let the later tmp.replace
silently discard the earlier file. Case variants stay exempt per the
existing stem-guard contract.

Require an instance prompt in the train panel when only some images have
captions, since backend discovery silently skips uncaptioned images.
2026-07-10 06:56:32 +00:00
Daniel Han
d2e6192eb2 Honor the configured SDXL LoRA batch size on datasets smaller than the batch
The SDXL trainer drew min(train_batch_size, len(pairs)) indices, so a dataset with
fewer images than the batch trained at a smaller effective batch than configured
while the scheduler and samples-per-second still assumed the full batch. The shared
PermutationBatchSampler already refills across permutation cycles to return exactly k
indices, and the DiT trainer calls it with the full batch size, so drop the clamp and
pass train_batch_size through for parity and to honor the configured batch.
2026-07-08 01:50:23 +00:00
Daniel Han
a091a862df Add krea-2 to bf16-only preflight; match dataset stems case-insensitively
Two evict/corruption fixes surfaced by review of the diffusion training path:

- krea-2 sets force_bf16 in the DiT trainer spec, but the route-level
  _FORCE_BF16_FAMILIES preflight listed only qwen-image and z-image, so a
  krea-2 start with mixed_precision=fp16 passed the route check, reserved
  training and evicted resident GPU models, and only the child trainer then
  raised. Add krea-2 to the set and a drift-guard test asserting it equals
  the trainer specs whose force_bf16 is set.

- The dataset-upload same-stem duplicate check compared stems
  case-sensitively, so on case-insensitive filesystems (Windows / default
  macOS) sample.png and Sample.jpg both passed even though their caption
  sidecars sample.txt / Sample.txt resolve to the same file, silently
  sharing and corrupting one caption. Compare stems and the same-name guard
  with casefold at both the on-disk and in-batch sites.
2026-07-07 18:07:48 +00:00
Daniel Han
ffa8a56391 Merge image-generation bug fixes (#6872)
Fold PR #6872's image-generation fixes into the branch, deduped against the
round-12 dataset-upload and gallery integrity work already on image-generation.

Fixes carried forward from #6872:
- fp8 single-file transformer memory estimate: an fp8 checkpoint loads with no
  quantization_config and diffusers upcasts it to bf16 (~2x resident), so budget
  it accordingly in _plan_memory and estimate_safetensors_dense_mib.
- dense-quant OOM-evict preflight: when the GGUF fits resident but the dense bf16
  transformer this path materializes does not, skip the fast path up front rather
  than evict the current pipeline and OOM in finalization. Combined with the
  existing offload->resident candidate re-plan so both the family-table estimate
  and the on-disk shard measurement gate engagement (unified on the
  transformer_resident_override_mib plan override).
- ControlNet: evict the previous module and its from_pipe wrapper before loading a
  new one so swapping ControlNets within a base-model load cannot accumulate to OOM.
- ControlNet union_control_mode: raise on an unknown control type instead of
  silently defaulting to canny.
- edit-family mask rejection: raise instead of silently dropping a mask on an
  image-editing model that has no inpaint pipeline.
- companion cache: walk the snapshot dir and exclude transformer/ so the
  dense-quant prefetch's cached shards do not inflate the companion total and
  wrongly force offload.
- training: drop piecewise_constant from the LR scheduler enum and force bf16 for
  fp16-incompatible families.
- dataset upload: batch-atomic staging with the same-stem duplicate guard.
- images page: guard negative-prompt restore on guidance>0, clear stale ControlNet
  selection on restore, and revert an optimistic quant label when a pipeline load
  never starts.
- uninstall (sh + ps1): keep the owner-marker guard on sd.cpp removal.

Conflicts resolved in favour of image-generation's evolved memory system,
loadSpecFor catalog, and stop-and-save (lora_path) run detection; #6872's fp8 and
dense-preflight fixes carried forward on top. All affected backend tests pass
(test_diffusion_backend, test_diffusion_training, test_diffusion_lora_trainer,
test_video_gallery, test_diffusion_controlnet).
2026-07-07 16:14:06 +00:00
Daniel Han
123b3ea4d6 Make the diffusion-training reservation a compare-and-set
Two overlapping /diffusion/start requests can interleave between the is_active()
check and the reservation, so reserve() itself must reject a second reservation
atomically. Otherwise both callers reserve, both free the GPU's resident chat or
image model, and the loser only 409s after the eviction -- the evict-then-fail the
reservation exists to prevent. reserve() now raises under the lock if a start is
already reserved or a job is already running.
2026-07-07 11:57:37 +00:00
Daniel Han
39ee329b26 Reserve the diffusion-training slot before freeing GPU residents
start_diffusion_training freed resident GPU models and only then called
service.start(config), which is where is_active() first flips true. During that
free-then-spawn window a concurrent /images/load or /video/load saw training as
inactive, passed its training guard, acquired the GPU, and began a background load,
so the trainer and that pipeline both allocated VRAM. Add reserve()/unreserve() to
the training service (is_active() also reports the reservation) and reserve BEFORE
the free, in a try/finally so a failed start rolls the reservation back. An
overlapping load's guard now refuses during the window. Regression tests: the route
reserves before the free (and the free sees an active service), and the service
reservation marks active then rolls back.
2026-07-07 10:58:13 +00:00
Daniel Han
5eeea1407b Normalize dataset upload filenames so the UI can manage them
The dataset upload took Path(filename).name, which on POSIX does not split on a
backslash, so a Windows client sending a backslash path in the multipart filename
stored the name verbatim. The caption/thumbnail/delete endpoints then run it
through _safe_dataset_image_path, which rejects backslashes and '..', so the
labeling grid could list an image it could never preview, caption, or delete (an
orphan). Fold backslashes to forward slashes before taking the basename so the
true name is stored, and reject a basename that still contains '..' at upload
rather than persisting an unmanageable entry. Regression test covers a Windows
backslash path (stored and served under the clean basename) and a '..' rejection.
2026-07-07 09:51:51 +00:00
Daniel Han
eee5a53658 Offload the gated-base HEAD preflight to a worker thread
start_diffusion_training is async but called _preflight_gated_base inline; it does
a blocking urllib urlopen HEAD to Hugging Face (up to a 5s timeout) to detect a
gated/unauthorized base repo. On a slow or unreachable network that stalled the
FastAPI event loop, freezing every concurrent status/progress/cancel request until
it returned or timed out. Wrap it in asyncio.to_thread, matching the dataset
preflight and GPU cleanup just below it. Regression test asserts it runs off the
coroutine thread.
2026-07-07 08:55:23 +00:00
Daniel Han
8476f02763 Offload the diffusion-training GPU cleanup to a worker thread
start_diffusion_training is an async route, but it called the blocking
_free_gpu_for_diffusion_training() inline. That teardown waits on generation
locks and joins the export subprocess, so it can block for seconds and freeze the
FastAPI event loop, stalling every concurrent status/progress/cancel request
until it finishes. Wrap it in asyncio.to_thread, mirroring the dataset-preflight
call just above it and the inference routes' load/unload offloading. Add a
regression test asserting the cleanup runs off the coroutine thread.
2026-07-07 06:58:07 +00:00
Daniel Han
f88d5c9f6b Studio: gate untrusted companion base_repo on image loads, track image/video generation for the training guard, and body-cap the whole /v1 surface
- Image load now trust-gates a client-supplied base_repo. validate_load_request
  rejects a base_repo that is not an unsloth/* repo, an allowlisted official base, or a
  local path, mirroring the repo_id gate and the video loader. The route passes
  base_repo into that pre-eviction validation, so an authenticated client can no longer
  keep model_path on a trusted GGUF while pointing base_repo at an arbitrary remote repo
  that the server would download and deserialize (a from_pretrained pickle/config path),
  and no resident model is evicted for the rejected load.
- The keepwarm middleware now tracks the image and video generation routes
  (/images/generate, /images/generations, /video/generate), so
  other_inference_request_count() sees an in-flight generation and an API-key training
  start is refused (409) before its unload would cancel that generation. endswith keeps
  the GET *-progress and */cancel variants untracked.
- The OpenAI-compatible /v1 surface is now blanket body-capped like /api/inference,
  instead of only /v1/chat/completions and /v1/completions. Every /v1 POST route
  (images/generations, audio, embeddings, responses, messages, ...) buffers a JSON body
  and none is a multipart-upload passthrough, so an unbounded ImageGenerationRequest
  prompt on /v1/images/generations can no longer be buffered outside the request limit.

Adds regression tests: the base_repo trust gate at both the backend (untrusted remote
raises, local passes) and the route (untrusted base_repo returns 400 with no load), the
keepwarm tracking of the image/video generation paths (and not the progress/cancel
variants), and the /v1 surface being body-protected.
2026-07-07 05:04:49 +00:00
Daniel Han
b6ab866c46 Studio: defer chat GPU handoff, unload video before training, stage example imports, name-aware Wan GGUF video tag, and diffusion upload body passthrough
- Chat load defers the CHAT arbiter handoff until after identifier / gpu_ids /
  training-memory validation, so a doomed chat load (bad id, unsupported gpu_ids on
  GGUF, or a training 409) no longer evicts a resident Images/Video pipeline and then
  errors. The already-loaded fast paths re-assert CHAT ownership themselves. Mirrors
  the image and video loaders, which validate before acquire_for.
- Both training-start GPU cleanups now unload a resident Video pipeline and release the
  VIDEO arbiter owner, not just Images/DIFFUSION, so starting LLM or diffusion training
  after a video generation session no longer competes with the still-resident video
  model and OOMs the run.
- Example dataset import materializes into a private staging dir and promotes into the
  dataset folder only after the whole import succeeds. A materialize that fails partway
  no longer leaves a partial dataset that a retry would treat as complete (imported=0),
  stranding the user with a truncated dataset. Hidden dirs are skipped by the dataset
  scan so the staging dir never surfaces as a dataset.
- Cached/local Wan GGUFs are now classified name-aware: arch "wan" alone is ambiguous
  between the loadable single-DiT TI2V-5B and the dual-expert A14B MoE the loader
  refuses, so _arch_to_task falls back to the repo/file name (as the loader's own
  detect_video_family does) and tags only a non-MoE match text-to-video, surfacing
  loadable Wan GGUFs in the Video picker without surfacing unloadable A14B files.
- The diffusion dataset upload route is added to the MaxBodyMiddleware upload
  passthrough, so its own get_upload_limit_bytes() cap (plus multipart overhead, and a
  raised max_upload_size_mb) applies instead of the default body limit rejecting
  near-limit batches with 413 before the handler runs.

Adds regression tests for each: the chat handoff not evicting on a doomed load, the
video unload on diffusion-training start, the atomic import leaving no partial dataset,
the name-aware Wan GGUF classification (TI2V-5B video, A14B unsupported, bare arch
unsupported), and the diffusion upload passthrough cap.
2026-07-07 04:08:41 +00:00
Daniel Han
fb94a79337 Studio: fix dataset upload data loss, caption over-count, and custom-root sd.cpp uninstall
- Diffusion dataset upload now streams each file into a sibling temp file and
  atomically os.replace()s it into place only after the whole file is written and
  within the size cap. A mid-batch 413 (or any abort) removes the temp, never an
  example already stored under the same name, so re-uploading a too-large batch can
  no longer truncate or delete a previously uploaded image.
- _diffusion_dataset_summary counts an image as captioned only when it resolves to a
  non-empty caption via the same sidecar-over-metadata precedence the trainer uses. An
  empty (tombstone) sidecar shadows a metadata row and makes the trainer skip the
  image, so counting it over-reported caption_count and mislabeled an effectively
  uncaptioned dataset as captioned.
- uninstall.sh/.ps1 now remove a custom/env-mode Studio's native diffusion build that
  installs beside the root as a stable-diffusion.cpp sibling (find_sd_cpp_binary
  resolves it from the Studio home's parent), guarded by the same unsafe-path check,
  and stop processes locking the default-mode stable-diffusion.cpp before removing it.

Adds regression tests for the upload data-loss and caption-count paths and a hermetic
shell test for the custom-root stable-diffusion.cpp removal.
2026-07-07 02:16:56 +00:00
Daniel Han
e1dd2dda6b Merge remote-tracking branch 'origin/diffusion-train-perf2' into fold-integration
# Conflicts:
#	studio/backend/core/training/diffusion_dit_trainer.py
#	studio/backend/core/training/diffusion_train_common.py
2026-07-07 01:06:42 +00:00
Daniel Han
b52e7a5cc2 Merge remote-tracking branch 'origin/diffusion-train-tab-2' into fold-integration 2026-07-07 01:01:00 +00:00
Daniel Han
aa54a062ec Gate DiT training on functional torchao for explicit int8; hide always-400 DiT modes on non-bf16 GPUs
The start route preflight only rejected non-bf16 GPUs; an explicit int8 request on
a host with a missing or stub torchao passed the preflight, evicted resident GPU
workloads, then died in the trainer child (its int8 base quantizer has no fallback).
Fold both gates into training_precision_preflight_error so int8-without-torchao fails
fast before eviction. Also empty the advertised DiT precision_modes (and surface the
reason in vram_note, drop compile) whenever the bf16 preflight would reject the family,
so /info never offers an nf4 DiT option the route always 400s.
2026-07-06 13:35:43 +00:00
pre-commit-ci[bot]
04ddf5449c [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-06 13:18:23 +00:00
oobabooga
9d8bf002f8 Merge remote-tracking branch 'origin/image-generation' into fix/imggen-review-bugs
# Conflicts:
#	studio/backend/core/inference/diffusion.py
#	studio/backend/core/training/diffusion_train_common.py
#	studio/backend/routes/training.py
#	studio/backend/tests/test_diffusion_dataset_api.py
2026-07-06 09:16:30 -03:00
Daniel Han
6bd3e87c6f Gate DiT training precision: deny fp8 for Qwen, gate explicit int8 on torchao, gate advertised dense modes + route on bf16
- normalized() + family_train_infos() mirror the inference fp8 deny for
  Qwen-Image (activation outliers exceed fp8's range and corrupt the trained
  result); int8 stays allowed and the UI no longer advertises fp8 for it.
- _resolve_base_precision() gates an explicit int8 on a FUNCTIONAL torchao, the
  same gate auto and /info already apply, so a missing/stub torchao fails fast
  instead of silently loading dense with compile disabled.
- train_precision_modes() gates the dense modes (bf16/int8/fp8/auto) on
  torch.cuda.is_bf16_supported(), so a non-bf16 CUDA GPU (T4/V100/RTX 20xx) is
  offered only nf4 instead of a start that evicts resident models and then fails.
- start_diffusion_training preflights bf16 support for the DiT families BEFORE
  _free_gpu_for_diffusion_training(), so any DiT start (nf4 included, since the
  trainer requires bf16 unconditionally on CUDA) fails fast without eviction.
2026-07-06 11:04:07 +00:00
oobabooga
01d0aa1a83 Preserve pre-existing files on upload rollback and budget the dense transformer in the quant preflight 2026-07-06 03:07:57 -03:00
Daniel Han
66387c1533 Merge branch 'diffusion-krea2' into diffusion-train-perf2 2026-07-05 11:39:16 +00:00
Daniel Han
5979d68a03 Merge branch 'diffusion-train-precision' into diffusion-train-tab-2 2026-07-05 11:39:14 +00:00
Daniel Han
b88d0d49b8 Merge branch 'image-generation' into diffusion-train-perf 2026-07-05 11:39:11 +00:00
Daniel Han
fc1e099124 Harden ControlNet loads, thumbnail cache keys, API training guard, and picker roving keys
Review follow-ups on the image-generation PR:

- ControlNet: resolve_controlnet accepts a bare owner/name repo without the
  non-GGUF base trust gate, and _controlnet_pipe hands it straight to
  from_pretrained. A malicious pickle .bin would deserialize on load, so run
  the same Hugging Face malware preflight (evaluate_file_security) the chat and
  export loaders use before any remote ControlNet load; local dirs are exempt.
- Dataset thumbnails: key the cache on the full filename instead of the stem so
  sample.png and sample.jpg no longer collide on one .thumbs file (which could
  serve or delete the wrong image); the delete cleanup globs the same key.
- Diffusion training start: mirror start_training's API-key guard so an API
  client cannot start training (which frees VRAM by unloading chat) while an
  inference request is streaming; it now returns 409 before any GPU is freed.
- Model picker: include the curated safetensors row keys in the recommended
  roving key list so arrow-key navigation reaches those rows instead of hitting
  the duplicate option-missing id.

Tests: ControlNet malware gate (remote blocked before from_pretrained, local
skipped), thumbnail same-stem cache separation, API-key diffusion-start 409
before GPU free. Full diffusion suites green.
2026-07-05 11:36:58 +00:00
Daniel Han
466f853f14 Merge branch 'diffusion-krea2' into diffusion-train-perf2 2026-07-05 07:56:51 +00:00
Daniel Han
da3a79468e Use permutation-cycle index sampling in diffusion trainers and guard non-object run records
Replace the with-replacement per-batch index draw in the SDXL and DiT LoRA
trainers with a shared PermutationBatchSampler that visits every image once per
cycle before repeating, so short runs cover the whole dataset. The sampler
reshuffles from the run's rng so the index stream stays seed-deterministic.

Guard the diffusion run detail route against a valid-JSON non-object record,
which previously raised TypeError and returned a 500; it now 404s like the list
path's shape check.

Add regression tests for both.
2026-07-05 07:49:30 +00:00
Daniel Han
d9d3ef462d Merge branch 'diffusion-krea2' into diffusion-train-perf2 2026-07-05 04:56:21 +00:00
Daniel Han
45f5ee7628 Merge branch 'diffusion-train-precision' into diffusion-train-tab-2 2026-07-05 04:55:07 +00:00
Daniel Han
e0f7ac68a4 Merge branch 'image-generation' into diffusion-train-perf 2026-07-05 04:55:04 +00:00
Daniel Han
c53a6cb65e Address review findings: dataset preflight, sd.cpp unload barrier, caption tombstone, ControlNet cache race
- Run the trainer's caption discovery in the start route BEFORE freeing GPU
  residents, so a missing or uncaptionable dataset 400s without evicting the
  loaded chat/Images model.
- sd.cpp unload now waits out a cancelled one-shot generation on the generate
  lock before reporting the device free, matching the diffusers backend.
- Clearing a caption that came from metadata.jsonl writes an empty sidecar
  tombstone instead of unlinking (both readers treat an existing sidecar as
  authoritative), so the cleared label cannot resurface.
- The ControlNet wrapper pipe is only cached while its load is still current,
  closing the unload race the model cache already handled.
2026-07-05 04:53:37 +00:00
Daniel Han
551c38bd4a Merge branch 'diffusion-krea2' into diffusion-train-perf2 2026-07-05 02:11:26 +00:00
Daniel Han
a99b951c33 Merge branch 'diffusion-train-precision' into diffusion-train-tab-2
# Conflicts:
#	studio/backend/tests/test_diffusion_training.py
2026-07-05 02:11:15 +00:00
Daniel Han
c2ab1a0e61 Merge branch 'image-generation' into diffusion-train-perf
# Conflicts:
#	studio/backend/core/training/diffusion_dit_trainer.py
#	studio/backend/core/training/diffusion_train_common.py
2026-07-05 02:09:39 +00:00
Daniel Han
e605075508 Fix diffusion training validation and honor lr_scheduler and batch size in the DiT trainer 2026-07-05 01:51:34 +00:00
pre-commit-ci[bot]
3bb3734879 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-04 14:11:00 +00:00
oobabooga
e5c63cdfff Fix diffusion training validation, dataset upload atomicity, and LoRA error mapping 2026-07-04 03:17:12 -03:00
pre-commit-ci[bot]
003e28730c [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-04 03:31:57 +00:00
pre-commit-ci[bot]
d8495d058f [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-04 03:30:53 +00:00
pre-commit-ci[bot]
32556949ce [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-04 03:29:49 +00:00
pre-commit-ci[bot]
07f27b23e8 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2026-07-04 03:29:17 +00:00
Daniel Han
95f783ae1e Merge diffusion-krea2: Raw training default + stacked review fixes
# Conflicts:
#	studio/backend/core/training/diffusion_train_common.py
#	studio/backend/tests/test_diffusion_dit_trainer.py
#	studio/backend/tests/test_diffusion_training.py
2026-07-04 03:28:32 +00:00
Daniel Han
7a8f363c52 Merge diffusion-train-precision: torchao functional probe + image-generation review fixes 2026-07-04 03:25:08 +00:00
Daniel Han
7d6c022489 Merge image-generation: review fixes (engine unload, caption precedence, dataset counts, local diffusers tagging, family LoRA targets)
# Conflicts:
#	studio/backend/core/training/diffusion_dit_trainer.py
2026-07-04 03:24:38 +00:00
Daniel Han
51de9da488 Fix review findings: run history robustness, epoch-mode sentinel, seed 0, history refresh race
- list_diffusion_runs skips wrong-shape records and the runs route tolerates
  per-record ValidationError so one bad file never breaks the panel
- max_steps: 0 epoch-mode sentinel no longer trips train_steps validation
  before epochs are resolved
- numberField keeps an explicit 0 (Seed, LR warmup) instead of falling back
- previous-runs list refetches once more shortly after a terminal status so
  the just-finished run appears even if the record write races the fetch
2026-07-04 03:23:20 +00:00
Daniel Han
bfbb902610 Fix review findings: engine unload before training, caption precedence, dataset caption counts, local diffusers tagging, family LoRA targets
- Unload the ACTIVE image engine (sd_cpp or diffusers) before diffusion training starts, not just the diffusers singleton
- Count metadata.jsonl captions in dataset summaries so metadata-captioned datasets are not reported as uncaptioned
- Sidecar captions now override metadata rows everywhere (grid edits win); trainer and dataset API agree
- Tag local diffusers image checkpoints with text-to-image so they appear in the Images picker
- Family LoRA targets (_FLUX_TARGETS etc) apply when the config carries the generic defaults; explicit overrides still win
2026-07-04 03:23:05 +00:00
Daniel Han
14b2a68025 Merge branch 'diffusion-krea2' into diffusion-train-perf2
# Conflicts:
#	studio/backend/core/training/diffusion_dit_trainer.py
#	studio/backend/core/training/diffusion_train_common.py
#	studio/frontend/src/features/images/train/diffusion-train-panel.tsx
2026-07-04 01:32:02 +00:00
Daniel Han
84e760808e Merge branch 'diffusion-train-precision' into diffusion-train-tab-2
# Conflicts:
#	studio/backend/core/training/diffusion_training_service.py
2026-07-04 01:29:37 +00:00
Daniel Han
39bc37712b Address review: fp32 latent cache stats + strict-JSON-safe progress floats
- Latent caches (DiT + SDXL) now hold the posterior mean/std in fp32 and draw the
  per-step sample in fp32, casting only the result to the training dtype. This
  matches the in-loop path (encode fp32 -> sample fp32 -> cast) exactly instead of
  sampling in bf16; the cache is tiny so the doubled RAM is negligible.
- The training service nulls non-finite floats (NaN/Inf loss, avg_loss,
  learning_rate) at its single ingestion point so status snapshots and persisted
  run records stay strict-JSON serializable; the metric history skips non-finite
  loss points. Test covers NaN/Inf progress followed by a finite point.
2026-07-04 01:27:32 +00:00