SyntheticMDProductions's picture
Some of Adams structure
e0265b9 verified
Raw
History Blame
4.12 kB
from __future__ import annotations
from pathlib import Path
from adam.models import ExecutionPlan, PlanStep
from adam.studio import (
PreviewEvaluation,
StudioStore,
TrainingRecipe,
caption_path,
exact_duplicate_groups,
image_files,
)
from adam.training_assistant import estimate_plan
def test_dataset_review_and_recipe_store_round_trip(tmp_path: Path) -> None:
dataset = tmp_path / "dataset"
dataset.mkdir()
first = dataset / "first.png"
second = dataset / "second.png"
first.write_bytes(b"same image bytes")
second.write_bytes(b"same image bytes")
caption_path(first).write_text("subject portrait\n", encoding="utf-8")
assert image_files(dataset) == [first, second]
assert exact_duplicate_groups([first, second]) == [[str(first), str(second)]]
store = StudioStore(tmp_path)
store.set_decision(str(dataset), str(first), "keep")
recipe = store.add_recipe(
TrainingRecipe("Portrait recipe", "lora", 80, preview_prompt="portrait")
)
store.add_evaluation(
PreviewEvaluation("model-1", "checkpoint", "portrait", 42, 4)
)
assert store.toggle_best("model-1")
restored = StudioStore(tmp_path)
review = restored.review(str(dataset))
assert review.decisions[str(first.resolve())] == "keep"
assert restored.recipes[0].id == recipe.id
assert restored.evaluations[0].seed == 42
assert "model-1" in restored.best_models
def test_plan_estimate_is_labelled_and_bounded(tmp_path: Path) -> None:
dataset = tmp_path / "images"
dataset.mkdir()
for index in range(3):
(dataset / f"{index}.jpg").write_bytes(b"image")
plan = ExecutionPlan(
request="train",
summary="Train",
steps=[
PlanStep(
"lora_trainer",
"Train LoRA",
"Training",
{"dataset_dir": str(dataset), "epochs": 10},
)
],
)
estimates = estimate_plan(plan)
assert len(estimates) == 3
assert all(item.level == "estimate" for item in estimates)
assert "30 image-epochs" in estimates[0].message
assert "Rough duration" in estimates[1].message
assert "VRAM" in estimates[2].message
def test_rejected_images_are_quarantined_and_recoverable(tmp_path: Path) -> None:
dataset = tmp_path / "dataset"
dataset.mkdir()
image = dataset / "bad.png"
image.write_bytes(b"bad image")
caption_path(image).write_text("wrong subject", encoding="utf-8")
store = StudioStore(tmp_path)
store.set_decision(str(dataset), str(image), "reject")
assert store.apply_rejections(str(dataset)) == 1
assert not image.exists()
assert not caption_path(image).exists()
assert store.restore_rejections(str(dataset)) == 1
assert image.is_file()
assert caption_path(image).is_file()
def test_dataset_review_can_keep_all_then_reject_one(tmp_path: Path) -> None:
dataset = tmp_path / "dataset"
dataset.mkdir()
images = [dataset / "one.png", dataset / "two.png", dataset / "three.png"]
for image in images:
image.write_bytes(b"image")
store = StudioStore(tmp_path)
assert store.set_all_decisions(str(dataset), images, "keep") == 3
store.set_decision(str(dataset), str(images[1]), "reject")
decisions = store.review(str(dataset)).decisions
assert decisions[str(images[0].resolve())] == "keep"
assert decisions[str(images[1].resolve())] == "reject"
assert decisions[str(images[2].resolve())] == "keep"
def test_dataset_review_applies_mixed_decisions_in_one_operation(tmp_path: Path) -> None:
dataset = tmp_path / "dataset"
dataset.mkdir()
first = dataset / "first.png"; first.write_bytes(b"one")
second = dataset / "second.png"; second.write_bytes(b"two")
store = StudioStore(tmp_path)
changed = store.apply_decisions(
str(dataset), {str(first): "keep", str(second): "reject"}
)
assert changed == 2
assert store.review(str(dataset)).decisions[str(first.resolve())] == "keep"
assert store.review(str(dataset)).decisions[str(second.resolve())] == "reject"