Skip to content

Commit d3049f4

Browse files
authored
Merge pull request #3120 from bghira/issue/3100
Keep live dataset runtime config authoritative
2 parents dc051ed + 5474e84 commit d3049f4

2 files changed

Lines changed: 150 additions & 4 deletions

File tree

simpletuner/helpers/data_backend/factory.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3162,6 +3162,8 @@ def _configure_metadata_backend(self, backend: Dict[str, Any], init_backend: Dic
31623162
**metadata_backend_args,
31633163
)
31643164

3165+
# Restore the live-authoritative runtime config after metadata cache loading.
3166+
StateTracker.set_data_backend_config(init_backend["id"], init_backend["config"])
31653167
metadata_backend = init_backend["metadata_backend"]
31663168
if isinstance(getattr(metadata_backend, "aspect_ratio_bucket_indices", None), dict):
31673169
metadata_backend.aspect_ratio_bucket_indices = _coerce_bucket_keys(metadata_backend.aspect_ratio_bucket_indices)
@@ -3345,10 +3347,16 @@ def _handle_bucket_operations(
33453347

33463348
def _handle_config_versioning(self, backend: Dict[str, Any], init_backend: Dict[str, Any]) -> None:
33473349
"""Handle configuration versioning and validation."""
3348-
runtime_mutable_keys = ("train_batch_size",)
3350+
runtime_mutable_keys = (
3351+
"train_batch_size",
3352+
"repeats",
3353+
"start_epoch",
3354+
"start_step",
3355+
"end_epoch",
3356+
"end_step",
3357+
)
33493358
excluded_keys = [
33503359
"probability",
3351-
"repeats",
33523360
"ignore_epochs",
33533361
"caption_filter_list",
33543362
"vae_cache_clear_each_epoch",
@@ -3359,8 +3367,6 @@ def _handle_config_versioning(self, backend: Dict[str, Any], init_backend: Dict[
33593367
"video",
33603368
"conditioning_data",
33613369
"conditioning",
3362-
"start_step",
3363-
"start_epoch",
33643370
"hash_filenames", # always enabled, not user-configurable
33653371
"_s2v_audio_autoinjected", # runtime flag, not user-configurable
33663372
*runtime_mutable_keys,

tests/test_factory_edge_cases.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,146 @@ def test_cached_batch_size_does_not_override_changed_dataset_value(self):
645645
self.assertEqual(backend["train_batch_size"], 4)
646646
self.assertEqual(result["config"]["train_batch_size"], 4)
647647

648+
def test_cached_runtime_config_does_not_override_live_values(self):
649+
from simpletuner.helpers.data_backend.factory import FactoryRegistry, init_backend_config
650+
651+
self.args.train_batch_size = 8
652+
backend = {
653+
"id": "image-runtime-config",
654+
"type": "local",
655+
"dataset_type": "image",
656+
"instance_data_dir": self.temp_dir,
657+
"train_batch_size": 4,
658+
"repeats": 2,
659+
"start_epoch": 3,
660+
"start_step": 12000,
661+
"end_epoch": 6,
662+
"end_step": 24000,
663+
}
664+
init_backend = init_backend_config(backend, self.args, self.accelerator)
665+
expected_runtime_config = {
666+
key: init_backend["config"][key]
667+
for key in ("train_batch_size", "repeats", "start_epoch", "start_step", "end_epoch", "end_step")
668+
}
669+
metadata_backend = MagicMock()
670+
metadata_backend.config = {
671+
"train_batch_size": 1,
672+
"repeats": 0,
673+
"start_epoch": 1,
674+
"start_step": 0,
675+
"end_epoch": None,
676+
"end_step": None,
677+
}
678+
metadata_backend.__len__.return_value = 1
679+
init_backend["metadata_backend"] = metadata_backend
680+
681+
factory = FactoryRegistry(
682+
args=self.args,
683+
accelerator=self.accelerator,
684+
text_encoders=self.text_encoders,
685+
tokenizers=self.tokenizers,
686+
model=self.model,
687+
)
688+
with patch("simpletuner.helpers.data_backend.factory.StateTracker.set_data_backend_config"):
689+
factory._handle_config_versioning(backend, init_backend)
690+
691+
self.assertEqual(
692+
{key: init_backend["config"][key] for key in expected_runtime_config},
693+
expected_runtime_config,
694+
)
695+
self.assertEqual(
696+
{key: metadata_backend.config[key] for key in expected_runtime_config},
697+
expected_runtime_config,
698+
)
699+
self.assertEqual(
700+
{key: backend[key] for key in expected_runtime_config},
701+
expected_runtime_config,
702+
)
703+
704+
def test_cached_runtime_defaults_are_removed_when_absent_from_live_config(self):
705+
from simpletuner.helpers.data_backend.factory import FactoryRegistry, init_backend_config
706+
707+
backend = {
708+
"id": "image-runtime-defaults",
709+
"type": "local",
710+
"dataset_type": "image",
711+
"instance_data_dir": self.temp_dir,
712+
}
713+
init_backend = init_backend_config(backend, self.args, self.accelerator)
714+
metadata_backend = MagicMock()
715+
metadata_backend.config = {
716+
"repeats": 4,
717+
"end_epoch": 8,
718+
"end_step": 16000,
719+
}
720+
metadata_backend.__len__.return_value = 1
721+
init_backend["metadata_backend"] = metadata_backend
722+
723+
factory = FactoryRegistry(
724+
args=self.args,
725+
accelerator=self.accelerator,
726+
text_encoders=self.text_encoders,
727+
tokenizers=self.tokenizers,
728+
model=self.model,
729+
)
730+
with patch("simpletuner.helpers.data_backend.factory.StateTracker.set_data_backend_config"):
731+
factory._handle_config_versioning(backend, init_backend)
732+
733+
self.assertNotIn("repeats", init_backend["config"])
734+
self.assertIsNone(init_backend["config"]["end_epoch"])
735+
self.assertIsNone(init_backend["config"]["end_step"])
736+
self.assertNotIn("repeats", metadata_backend.config)
737+
self.assertIsNone(metadata_backend.config["end_epoch"])
738+
self.assertIsNone(metadata_backend.config["end_step"])
739+
740+
def test_metadata_cache_reload_does_not_leave_stale_runtime_config_active(self):
741+
from simpletuner.helpers.data_backend.factory import FactoryRegistry, init_backend_config
742+
743+
backend = {
744+
"id": "image-runtime-reload",
745+
"type": "local",
746+
"dataset_type": "image",
747+
"instance_data_dir": self.temp_dir,
748+
"repeats": 3,
749+
"start_step": 12000,
750+
}
751+
init_backend = init_backend_config(backend, self.args, self.accelerator)
752+
init_backend["data_backend"] = MagicMock(id=backend["id"])
753+
init_backend["instance_data_dir"] = backend["instance_data_dir"]
754+
live_config = init_backend["config"].copy()
755+
observed_configs = []
756+
757+
def set_config(data_backend_id, config):
758+
self.assertEqual(data_backend_id, backend["id"])
759+
observed_configs.append(config.copy())
760+
761+
def create_metadata_backend(**kwargs):
762+
set_config(backend["id"], {**live_config, "repeats": 0, "start_step": 0})
763+
metadata_backend = MagicMock()
764+
metadata_backend.aspect_ratio_bucket_indices = {}
765+
return metadata_backend
766+
767+
factory = FactoryRegistry(
768+
args=self.args,
769+
accelerator=self.accelerator,
770+
text_encoders=self.text_encoders,
771+
tokenizers=self.tokenizers,
772+
model=self.model,
773+
)
774+
with (
775+
patch(
776+
"simpletuner.helpers.metadata.backends.discovery.DiscoveryMetadataBackend",
777+
side_effect=create_metadata_backend,
778+
),
779+
patch(
780+
"simpletuner.helpers.data_backend.factory.StateTracker.set_data_backend_config",
781+
side_effect=set_config,
782+
),
783+
):
784+
factory._configure_metadata_backend(backend, init_backend)
785+
786+
self.assertEqual(observed_configs[-1], live_config)
787+
648788
def test_inline_conditioning_auto_generation_for_image_dataset(self):
649789
"""Inline conditioning blocks on image datasets should spawn auto-generated conditioning datasets."""
650790
from simpletuner.helpers.data_backend.factory import FactoryRegistry

0 commit comments

Comments
 (0)