141 lines
4.5 KiB
Python
141 lines
4.5 KiB
Python
"""Owned, deterministic physical-file evidence for the full inspector workflows."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
from typing import TYPE_CHECKING
|
|
|
|
from PIL import Image
|
|
from PIL.PngImagePlugin import PngInfo
|
|
|
|
from tests.browser.workspace_visual_fixture import seed_workspace_visual
|
|
|
|
if TYPE_CHECKING:
|
|
from py.api import PromptManagerAPI
|
|
|
|
|
|
INSPECTOR_POSITIVE = (
|
|
"Inspector portrait 猫 😀\n" + "Full prompt context, fine geometry and light. " * 40
|
|
)
|
|
INSPECTOR_NEGATIVE = "blurred edges, watermark"
|
|
|
|
|
|
def _graph(positive: str, negative: str) -> dict:
|
|
return {
|
|
"1": {"class_type": "CLIPTextEncode", "inputs": {"text": positive}},
|
|
"2": {"class_type": "CLIPTextEncode", "inputs": {"text": negative}},
|
|
"3": {
|
|
"class_type": "KSampler",
|
|
"inputs": {
|
|
"positive": ["1", 0],
|
|
"negative": ["2", 0],
|
|
"model": ["4", 0],
|
|
"seed": 9007199254740993,
|
|
"steps": 28,
|
|
"cfg": 6.5,
|
|
"sampler_name": "dpmpp_2m",
|
|
"scheduler": "karras",
|
|
},
|
|
},
|
|
"4": {
|
|
"class_type": "CheckpointLoaderSimple",
|
|
"inputs": {"ckpt_name": "fixture-model.safetensors"},
|
|
},
|
|
}
|
|
|
|
|
|
def _image(path: Path, index: int, metadata: dict) -> None:
|
|
info = PngInfo()
|
|
for key, value in metadata.items():
|
|
text = (
|
|
value if isinstance(value, str) else json.dumps(value, ensure_ascii=False)
|
|
)
|
|
info.add_itxt(key, text, zip=True)
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
with Image.new("RGB", (720, 480), (30 + index * 9, 74, 98)) as image:
|
|
image.save(path, pnginfo=info)
|
|
stamp = 1_780_000_000 + index
|
|
os.utime(path, (stamp, stamp))
|
|
|
|
|
|
def seed_workspace_inspector(
|
|
api: PromptManagerAPI,
|
|
root: Path,
|
|
*,
|
|
extra_images: int = 0,
|
|
include_video: bool = False,
|
|
) -> None:
|
|
"""Seed only disposable harness media and exact saved associations."""
|
|
seed_workspace_visual(api, root)
|
|
complete = _graph(INSPECTOR_POSITIVE, INSPECTOR_NEGATIVE)
|
|
workflow = {
|
|
"nodes": [
|
|
{
|
|
"id": 3,
|
|
"type": "KSampler",
|
|
"widgets_values": [
|
|
9007199254740993,
|
|
"fixed",
|
|
28,
|
|
6.5,
|
|
"dpmpp_2m",
|
|
"karras",
|
|
1,
|
|
],
|
|
}
|
|
],
|
|
"links": [],
|
|
}
|
|
# Two branches that differ only in seed: facts merge per field (spec section 10).
|
|
branch = {**complete["3"], "inputs": {**complete["3"]["inputs"], "seed": 7}}
|
|
ambiguous = {**complete, "5": branch}
|
|
examples = (
|
|
("complete.png", {"prompt": complete, "workflow": workflow}),
|
|
("empty.png", {}),
|
|
("explicit-empty.png", {"prompt": _graph("Explicit empty negative", "")}),
|
|
("ambiguous.png", {"prompt": ambiguous}),
|
|
("workflow-only.png", {"workflow": workflow}),
|
|
("malformed.png", {"prompt": "not-json", "workflow": "{broken"}),
|
|
("control-heavy.png", {"parameters": "\x01" * 1_450_000}),
|
|
)
|
|
for index, (name, metadata) in enumerate(examples):
|
|
_image(root / "output" / "inspector" / name, index, metadata)
|
|
_image(
|
|
root / "input" / "inspector" / "complete.png",
|
|
8,
|
|
{"prompt": _graph("Input collision", "")},
|
|
)
|
|
pair = api.db.save_prompt_pair(
|
|
INSPECTOR_POSITIVE,
|
|
INSPECTOR_NEGATIVE,
|
|
title="Inspector recipe",
|
|
category="Inspection",
|
|
tags=["fixture", "geometric"],
|
|
)
|
|
positive = api.db.get_prompt_pair(pair)["positive_prompt_id"]
|
|
image = api.db.link_image_to_prompt(
|
|
positive, str(root / "output" / "inspector" / "complete.png")
|
|
)
|
|
api.db.link_image_to_pair(pair, image, {"source": "fixture"})
|
|
api.db.link_image_to_prompt(
|
|
positive, str(root / "output" / "inspector" / "workflow-only.png")
|
|
)
|
|
for number in range(extra_images):
|
|
_image(
|
|
root / "output" / "inspector" / f"extra-{number:03}.png",
|
|
9 + number % 12,
|
|
{"prompt": complete},
|
|
)
|
|
if include_video:
|
|
shutil.copyfile(
|
|
Path(__file__).resolve().parents[1] / "fixtures" / "tiny.webm",
|
|
root / "output" / "inspector" / "clip.webm",
|
|
)
|
|
os.utime(
|
|
root / "output" / "inspector" / "clip.webm", (1_780_000_100, 1_780_000_100)
|
|
)
|
|
api.invalidate_gallery_cache()
|