kneifftools / tests /test_image_caption_progress.py
kneiff's picture
chore(history)!: publish anonymous repository root
2857cf3
Raw
History Blame Contribute Delete
4.83 kB
from __future__ import annotations
from pathlib import Path
from types import SimpleNamespace
from PIL import Image
import pytest
import kneiff.utils.image.caption.blip as caption_blip
import kneiff.utils.image.caption.server as caption_server
from kneiff.progress import ProgressUpdate
from kneiff.utils.image.caption.batch import (
CaptionBatchItemResult,
plan_caption_batch,
)
from kneiff.utils.image.caption.io import CaptionSidecarIO
def test_caption_batch_plan_excludes_existing_sidecars(tmp_path: Path) -> None:
first = tmp_path / "first.png"
second = tmp_path / "second.png"
Image.new("RGB", (2, 2), color="red").save(first)
Image.new("RGB", (2, 2), color="blue").save(second)
first.with_suffix(".cap.txt").write_text("existing", encoding="utf-8")
jobs = plan_caption_batch(tmp_path, CaptionSidecarIO())
assert [job.image_path for job in jobs] == [second]
def test_server_caption_batch_reports_written_and_caught_failures(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
first = tmp_path / "first.png"
second = tmp_path / "second.png"
Image.new("RGB", (2, 2), color="red").save(first)
Image.new("RGB", (2, 2), color="blue").save(second)
sidecar_io = CaptionSidecarIO()
jobs = plan_caption_batch(tmp_path, sidecar_io)
calls = 0
monkeypatch.setattr(
caption_server,
"load_openai_backend_config",
lambda **kwargs: SimpleNamespace(model="test-model"),
)
monkeypatch.setattr(caption_server, "build_openai_client", lambda cfg: object())
monkeypatch.setattr(
caption_server,
"image_to_data_url",
lambda *args, **kwargs: "data:image/jpeg;base64,test",
)
def fake_caption_one(*args: object, **kwargs: object) -> str:
nonlocal calls
calls += 1
if calls == 2:
raise RuntimeError("server unavailable")
return "A concise caption."
monkeypatch.setattr(caption_server, "caption_one", fake_caption_one)
updates: list[ProgressUpdate] = []
items: list[CaptionBatchItemResult] = []
instructions: list[str] = []
result = caption_server.run_caption_batch_with_server(
jobs,
sidecar_io=sidecar_io,
model_name="test-model",
progress_callback=updates.append,
instruction_callback=instructions.append,
item_callback=items.append,
)
assert result.count("written") == 1
assert result.count("failed") == 1
assert first.with_suffix(".cap.txt").is_file()
assert not second.with_suffix(".cap.txt").exists()
assert len(instructions) == 2
assert [item.status for item in items] == ["written", "failed"]
assert sum(update.advance for update in updates) == 2
class _FakeCaptioner:
def caption(self, image: Image.Image, instruction: str, gen: object) -> str:
del image, instruction, gen
return "A local caption."
def test_blip_caption_batch_advances_for_image_open_skip(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
invalid = tmp_path / "invalid.png"
valid = tmp_path / "valid.png"
invalid.write_bytes(b"not an image")
Image.new("RGB", (2, 2), color="green").save(valid)
sidecar_io = CaptionSidecarIO()
jobs = plan_caption_batch(tmp_path, sidecar_io)
monkeypatch.setattr(
caption_blip,
"build_captioner",
lambda model_name, backend: _FakeCaptioner(),
)
updates: list[ProgressUpdate] = []
result = caption_blip.run_caption_batch_with_blip(
jobs,
sidecar_io=sidecar_io,
progress_callback=updates.append,
)
assert result.count("skipped") == 1
assert result.count("written") == 1
assert valid.with_suffix(".cap.txt").is_file()
assert sum(update.advance for update in updates) == 2
def test_blip_inference_failure_propagates_with_incomplete_progress(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
source = tmp_path / "source.png"
Image.new("RGB", (2, 2), color="green").save(source)
sidecar_io = CaptionSidecarIO()
jobs = plan_caption_batch(tmp_path, sidecar_io)
class FailingCaptioner:
def caption(self, image: Image.Image, instruction: str, gen: object) -> str:
del image, instruction, gen
raise RuntimeError("inference failed")
monkeypatch.setattr(
caption_blip,
"build_captioner",
lambda model_name, backend: FailingCaptioner(),
)
updates: list[ProgressUpdate] = []
with pytest.raises(RuntimeError, match="inference failed"):
caption_blip.run_caption_batch_with_blip(
jobs,
sidecar_io=sidecar_io,
progress_callback=updates.append,
)
assert updates == []
assert not source.with_suffix(".cap.txt").exists()