File size: 4,124 Bytes
e0265b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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"