Files
slahiriandClaude Opus 4.5 7f1186d412 feat: add prompt composition pipeline and tokenization spec
New Features:
- SID_PromptCompose node with Standard and Enhance with AI modes
- SID_PromptSynthesis node for VLM-based prompt generation
- SID_ImageAnalysis node for comprehensive image analysis
- Text LLM support (Qwen2.5 0.5B-7B) for prompt enhancement
- PixAI tagger integration

Documentation:
- docs/image_tokenization_spec.md - Full technical specification
  for tokenization → classification → assembly pipeline
- docs/implementation_plan.md - 8-phase implementation roadmap
- docs/SID_PromptCompose.md - Node usage documentation

Infrastructure:
- core/compose/ module with base, pipeline, standard, llm generators
- core/log.py centralized logging
- Config files for Qwen2.5 text models (YAML)
- Web assets for ComfyUI integration

This commit establishes the foundation for rewriting the compose
pipeline based on the tokenization specification.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-27 02:33:17 +05:30

197 lines
5.9 KiB
Python

"""
Debug Analyzer - Uses Claude to identify gaps between source image and generated prompt.
Only runs when DEBUG_ENABLED=true and ANTHROPIC_API_KEY is set in .env file.
If not configured, gracefully skips without errors.
"""
import os
import base64
from pathlib import Path
from typing import Optional
from PIL import Image
import io
def _load_env():
"""Load .env file from toolkit root directory."""
env_path = Path(__file__).parent.parent / ".env"
if not env_path.exists():
return {}
env_vars = {}
try:
with open(env_path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
if "=" in line:
key, value = line.split("=", 1)
env_vars[key.strip()] = value.strip()
except Exception:
pass
return env_vars
def _is_debug_enabled() -> bool:
"""Check if debug mode is enabled."""
env = _load_env()
debug_enabled = env.get("DEBUG_ENABLED", "").lower()
api_key = env.get("ANTHROPIC_API_KEY", "")
# Both must be present and debug must be "true"
return debug_enabled == "true" and api_key and api_key.startswith("sk-ant-")
def _get_api_key() -> Optional[str]:
"""Get Anthropic API key from .env."""
env = _load_env()
return env.get("ANTHROPIC_API_KEY")
def _image_to_base64(image: Image.Image, max_size: int = 1024) -> str:
"""Convert PIL Image to base64 string, resizing if needed."""
# Resize if too large
if image.width > max_size or image.height > max_size:
ratio = min(max_size / image.width, max_size / image.height)
new_size = (int(image.width * ratio), int(image.height * ratio))
image = image.resize(new_size, Image.Resampling.LANCZOS)
# Convert to RGB if needed
if image.mode != "RGB":
image = image.convert("RGB")
# Save to bytes
buffer = io.BytesIO()
image.save(buffer, format="JPEG", quality=85)
buffer.seek(0)
return base64.standard_b64encode(buffer.read()).decode("utf-8")
def analyze_prompt_gaps(
source_image: Image.Image,
generated_prompt: str,
metadata: Optional[dict] = None,
verbose: bool = False,
) -> Optional[dict]:
"""
Use Claude to analyze gaps between source image and generated prompt.
Args:
source_image: The original image that was analyzed
generated_prompt: The prompt generated by the synthesis node
metadata: Optional metadata from image analysis stage
verbose: Print debug info
Returns:
Dict with analysis results, or None if debug not enabled/configured
"""
if not _is_debug_enabled():
if verbose:
print("[Debug] Debug mode not enabled or API key not configured")
return None
api_key = _get_api_key()
if not api_key:
return None
try:
import anthropic
except ImportError:
if verbose:
print("[Debug] anthropic package not installed. Run: pip install anthropic")
return None
if verbose:
print("[Debug] Running Claude analysis...")
# Convert image to base64
image_b64 = _image_to_base64(source_image)
# Build analysis prompt
analysis_prompt = f"""Analyze this image and compare it to the generated prompt below.
Identify any gaps, inaccuracies, or missing elements.
GENERATED PROMPT:
{generated_prompt}
Please analyze and provide:
1. **INACCURACIES** - Things in the prompt that are WRONG (e.g., wrong colors, wrong garment types, wrong positions)
2. **MISSING ELEMENTS** - Important visible elements NOT mentioned in the prompt (e.g., exposed skin areas, accessories, headwear layers)
3. **HALLUCINATIONS** - Things mentioned in the prompt that are NOT visible in the image (e.g., "hands in pockets" when hands are visible elsewhere)
4. **COLOR ERRORS** - Any color descriptions that don't match (e.g., "white" when it's actually "gray")
5. **SUGGESTED CORRECTIONS** - Brief corrected descriptions for the main issues
Be specific and concise. Focus on the most important gaps that would affect image regeneration accuracy."""
try:
client = anthropic.Anthropic(api_key=api_key)
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1500,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": image_b64,
},
},
{
"type": "text",
"text": analysis_prompt,
},
],
}
],
)
analysis_text = message.content[0].text
if verbose:
print(f"[Debug] Claude analysis complete ({len(analysis_text)} chars)")
return {
"analysis": analysis_text,
"model": "claude-sonnet-4-20250514",
"prompt_length": len(generated_prompt),
}
except anthropic.APIError as e:
if verbose:
print(f"[Debug] Anthropic API error: {e}")
return {"error": str(e)}
except Exception as e:
if verbose:
print(f"[Debug] Analysis error: {e}")
return {"error": str(e)}
def print_analysis(analysis: Optional[dict]) -> None:
"""Pretty print the analysis results."""
if not analysis:
return
if "error" in analysis:
print(f"\n[Debug Analysis Error] {analysis['error']}")
return
print("\n" + "=" * 60)
print("DEBUG ANALYSIS - Gaps in Generated Prompt")
print("=" * 60)
print(analysis.get("analysis", "No analysis available"))
print("=" * 60 + "\n")