Files
IAMCCS-IAMCCS-nodes/tests/test_h3_reference_image.py

124 lines
6.1 KiB
Python

import importlib.util
import json
import os
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
from PIL import Image
ROOT = Path(__file__).resolve().parents[1]
def load(name):
spec = importlib.util.spec_from_file_location(name, ROOT / (name + '.py'))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
reference = load('iamccs_h3_reference_image')
core = load('iamccs_minimax_h3_shotboard_core')
advisor = load('iamccs_h3_advisor')
class ReferenceTests(unittest.TestCase):
def test_text_only_real_planner_and_profile_retained(self):
plan = core.build_shotplan(timeline_data='{}', global_prompt='A bronze vase',
duration_seconds=5/24, task_mode='ref2va', width=640, height=384)
plan['sampling'] = {'steps':16, 'seed':123}
plan['performance_profile'] = 'rtx_xx60_safe'
result = reference.reference_image_plan(plan, 'A bronze vase')
self.assertEqual(result['total_segments'], 1)
self.assertEqual(result['chunks'][0]['frame_count'], 5)
self.assertEqual(result['chunks'][0]['task_mode'], 'ref2va')
self.assertEqual(result['sampling'], plan['sampling'])
self.assertEqual(result['performance_profile'], 'rtx_xx60_safe')
self.assertFalse(result['upscale_enabled'])
self.assertEqual(result['reference_image']['source_policy'], 'prompter_ref2v_then_references')
self.assertEqual(result['reference_image']['prompter_paths'], [])
self.assertEqual(result['reference_image']['fallback_paths'], [])
self.assertNotIn('reference_image', plan)
def test_empty_description_rejected(self):
with self.assertRaises(ValueError):
reference.reference_image_plan({}, ' ')
def test_family_and_serialization(self):
self.assertEqual(advisor.task_family('reference_image'), 'ref2va')
self.assertEqual(advisor.task_family('i2va'), 'fl2va')
import json
fixture = os.environ.get('IAMCCS_TEST_REFERENCE_IMAGE_API', '')
if not fixture:
self.skipTest('Set IAMCCS_TEST_REFERENCE_IMAGE_API to test an external workflow')
path = Path(fixture)
prompt = json.loads(path.read_text(encoding='utf-8'))['prompt']
self.assertEqual(prompt['811']['inputs']['task_mode'], 'reference_image')
self.assertEqual(prompt['9']['inputs']['model'], ['8',0])
self.assertEqual(prompt['9']['inputs']['clip'], ['625',0])
self.assertFalse(any('Editor' in n['class_type'] for n in prompt.values()))
for node in prompt.values():
for value in node['inputs'].values():
if isinstance(value, list) and len(value) == 2 and isinstance(value[1], int):
self.assertIn(value[0], prompt)
def test_prompter_saved_images_supply_backend_without_direct_socket(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
for name, color in (("character.png", (40, 80, 120)), ("prop.png", (120, 80, 40))):
Image.new("RGB", (32, 24), color).save(root / name)
project = {
"image_preset": "single_image",
"request": "Create one studio portrait from Picture 1.",
"ai_visual_files": [{"path": "character.png"}, {"path": "prop.png"}],
}
linx = {"iamccs_minimax_h3_settings": {"settings": {
"task_mode": "reference_image", "h3_reference_image_source": "auto_prompter_then_direct",
"width": 3840, "height": 2160,
}}}
bridge = reference.IAMCCS_R42ReferenceImagePromptBridge()
with patch.object(reference.folder_paths, "get_input_directory", return_value=directory), \
patch.object(reference.folder_paths, "get_annotated_filepath", side_effect=lambda name: str(root / name)):
self.assertEqual(bridge.check_lazy_status("prompt", json.dumps(project), linx), [])
result = bridge.adapt("prompt", json.dumps(project), linx)
self.assertEqual(tuple(result[3].shape), (1, 24, 32, 3))
self.assertEqual(tuple(result[8].shape), (1, 24, 32, 3))
self.assertIsNone(result[9])
self.assertIn("prompter_ref2v+inject", result[7])
def test_empty_character_sheet_request_is_valid_with_picture_one(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
Image.new("RGB", (32, 24), (40, 80, 120)).save(root / "character.png")
project = {
"image_preset": "reference_sheet",
"request": "",
"ai_visual_files": [{"path": "character.png"}],
}
linx = {"iamccs_minimax_h3_settings": {"settings": {
"task_mode": "reference_image", "h3_reference_image_source": "prompter_ref2v",
"h3_reference_image_preset": "character_sheet", "width": 3840, "height": 2160,
}}}
prompt = "<Picture 1> character sheet generated from visual evidence."
bridge = reference.IAMCCS_R42ReferenceImagePromptBridge()
with patch.object(reference.folder_paths, "get_input_directory", return_value=directory), \
patch.object(reference.folder_paths, "get_annotated_filepath", side_effect=lambda name: str(root / name)):
result = bridge.adapt(prompt, json.dumps(project), linx)
self.assertIsNotNone(result[3])
self.assertIn("preset=character_sheet", result[7])
def test_conflicting_sheet_and_single_image_presets_are_rejected(self):
project = {"image_preset": "reference_sheet", "request": "", "ai_visual_files": []}
linx = {"iamccs_minimax_h3_settings": {"settings": {
"task_mode": "reference_image", "h3_reference_image_preset": "single_image",
}}}
with self.assertRaisesRegex(ValueError, "conflicting image presets"):
reference.IAMCCS_R42ReferenceImagePromptBridge().adapt(
"old cached prompt", json.dumps(project), linx,
)
if __name__ == '__main__':
unittest.main()