Files
ComfyAssets-ComfyUI_PromptM…/tests/test_pair_image_linking.py
T
Vito Sansevero dd4e3125e9 fix(resolver): identical sampler branches resolve to their shared prompt pair
A LoRA epoch-compare grid runs one sampler per epoch, every one fed by the
same positive encode and the same ConditioningZeroOut negative, and saves
one concatenated image. `_resolve` returned "Multiple sampler branches"
(ambiguous) whenever more than one sampler was found, even though every
branch used the same pair, so the workspace inspector fell back to the
legacy parser and the image could not be paired.

When every branch resolves to identical texts and stored recipes, the
result is now that pair: state resolved, no sampler named (none alone made
the output), reason "Every sampler branch uses the same prompt pair", the
first branch's sides and provenance, and every branch kept in `candidates`
as evidence. A branch that differs, cannot be read, or is not resolved
keeps the result ambiguous. Both profiles share the rule; the default
profile still cannot read ConditioningZeroOut, so the zero-out grid stays
ambiguous for ingestion and keeps the scan's legacy-parser fallback.

Workspace facts: a resolved result without a named sampler over several
listed samplers merges facts per field, as an ambiguous one does (the
grid's shared seed/steps are known, the per-epoch model is ambiguous).

Tests that encoded the old behavior (identical branches => ambiguous):
- test_prompt_pair_resolver: test_multiple_samplers_are_ambiguous_even_when_texts_match
  -> test_multiple_samplers_with_matching_texts_resolve_to_the_shared_pair
  (plus a new differing-texts case that stays ambiguous);
  test_raw_and_detailed_saves_stay_ambiguous_without_an_anchor (same prompt)
  -> ..._of_one_prompt_share_it_without_an_anchor (anchored cases unchanged).
- test_pair_scan: two samplers sharing one prompt now ingest the pair
  ("cat"/"blur") instead of the positive-only fallback.
- test_api_prompt_inspector, test_inspection_outcomes, test_pair_image_linking:
  their "ambiguous" fixtures now use samplers with different prompts.
No parity vector changes (f01/f09 branches are not all resolved).
2026-10-21 09:32:23 -07:00

70 lines
2.7 KiB
Python

"""Live metadata annotation uses the same conservative ingestion as scan."""
import logging
import tempfile
import unittest
from pathlib import Path
try:
from tests.open_handles import assert_closed
except ImportError: # discovered with tests/ as the top-level directory
from open_handles import assert_closed
from database.operations import PromptDatabase
from utils.hashing import generate_prompt_hash
from utils.image_monitor import ImageGenerationHandler
def metadata(positive="cat", negative="blur"):
return {
"prompt": {
"1": {"class_type": "PromptManager", "inputs": {"text": positive}},
"2": {"class_type": "CLIPTextEncode", "inputs": {"text": negative}},
"3": {
"class_type": "KSampler",
"inputs": {"positive": ["1", 0], "negative": ["2", 0]},
},
}
}
class TestPairImageLinking(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.db = PromptDatabase(str(Path(self.temp.name) / "db.sqlite"))
self.path = str(Path(self.temp.name) / "image.png")
self.pid = self.db.save_prompt("cat", prompt_hash=generate_prompt_hash("cat"))
self.handler = ImageGenerationHandler.__new__(ImageGenerationHandler)
self.handler.db_manager = self.db
self.handler.logger = logging.getLogger("test.pairlink")
def tearDown(self):
self.db.close_all()
assert_closed(self, self.temp.name)
self.temp.cleanup()
def test_exact_metadata_creates_pair_link_without_counting_a_run(self):
self.handler.link_image_to_prompt(self.path, {"id": self.pid}, metadata())
self.handler.link_image_to_prompt(self.path, {"id": self.pid}, metadata())
pairs = self.db.list_prompt_pairs()["pairs"]
self.assertEqual(len(pairs), 1)
self.assertEqual(pairs[0]["image_count"], 1)
self.assertEqual(pairs[0]["run_count"], 0)
def test_stale_context_or_ambiguous_metadata_does_not_create_pair(self):
meta = metadata(positive="dog")
self.handler.link_image_to_prompt(self.path, {"id": self.pid}, meta)
self.assertEqual(self.db.list_prompt_pairs()["total"], 0)
meta = metadata()
# Two samplers with different positives (#249: identical ones resolve).
meta["prompt"]["5"] = {
"class_type": "CLIPTextEncode",
"inputs": {"text": "dog"},
}
meta["prompt"]["4"] = {
"class_type": "KSampler",
"inputs": {"positive": ["5", 0], "negative": ["2", 0]},
}
self.handler.link_image_to_prompt(self.path + "2", {"id": self.pid}, meta)
self.assertEqual(self.db.list_prompt_pairs()["total"], 0)