Add files via upload

This commit is contained in:
Drift Johnson
2025-11-22 13:19:18 +00:00
committed by GitHub
commit dbc5fa191d
10 changed files with 3957 additions and 0 deletions
+213
View File
@@ -0,0 +1,213 @@
# ComfyUI Installation Guide - Prompt Orchestration Nodes
## Quick Installation
### Method 1: Manual Installation (Recommended)
1. **Locate your ComfyUI custom_nodes directory**
```
ComfyUI/
└── custom_nodes/
└── (install here)
```
2. **Copy the entire `comfyui_nodes` folder**
```bash
# Copy the entire folder to ComfyUI/custom_nodes/
cp -r comfyui_nodes ComfyUI/custom_nodes/comfyui_prompt_orchestration
```
3. **Restart ComfyUI**
- Restart your ComfyUI instance
- Look for the loading message in console
- Nodes will appear under "Prompt Orchestration" category
### Method 2: Git Clone (If using Git)
```bash
cd ComfyUI/custom_nodes
git clone <your-repo-url> comfyui_prompt_orchestration
# Or manually copy the folder
```
### Method 3: Symlink (For Development)
```bash
cd ComfyUI/custom_nodes
ln -s /path/to/comfyui_nodes comfyui_prompt_orchestration
```
---
## Directory Structure
After installation, your structure should look like:
```
ComfyUI/
└── custom_nodes/
└── comfyui_prompt_orchestration/
├── __init__.py
├── prompt_orchestration_nodes.py
├── prompt_orchestrator.py
├── batch_processor.py
└── README_COMFYUI.md
```
---
## Verification
### 1. Check Console Output
When ComfyUI starts, you should see:
```
================================================================================
Prompt Orchestration Nodes v1.0.0 loaded successfully!
- 9 custom nodes available
- 20 lighting modifiers (DRY/WET categories)
- 10+ built-in style templates
- Category: 'Prompt Orchestration'
================================================================================
```
### 2. Check Node Menu
In ComfyUI:
1. Right-click on canvas → "Add Node"
2. Look for "Prompt Orchestration" category
3. You should see 9 nodes:
- Lighting Modifier (Single)
- Batch Variation Generator
- Style Transform
- Smart Variation (NLP)
- Preset Collection
- Modifier Info
- Prompt Combiner
- Random Modifier
- Multi-Style Layer
---
## Troubleshooting
### Nodes Don't Appear
**Problem**: Nodes not showing in menu
**Solutions**:
1. Check folder name: Must be in `custom_nodes/` directory
2. Check console for errors
3. Verify all files are present (especially `__init__.py`)
4. Restart ComfyUI completely (not just refresh)
### Import Errors
**Problem**: "Could not import prompt_orchestrator" error
**Solutions**:
1. Ensure `prompt_orchestrator.py` is in the same folder as `__init__.py`
2. Ensure `batch_processor.py` is in the same folder
3. Check file permissions (should be readable)
### Python Version Issues
**Problem**: Syntax errors or import failures
**Requirements**:
- Python 3.7 or higher (same as ComfyUI requirement)
- No additional packages needed (uses stdlib only)
---
## Uninstallation
To remove the nodes:
```bash
rm -rf ComfyUI/custom_nodes/comfyui_prompt_orchestration
```
Then restart ComfyUI.
---
## Updating
To update to a new version:
1. **Backup your workflows** (recommended)
2. Delete the old folder
3. Copy the new version
4. Restart ComfyUI
---
## Platform-Specific Notes
### Windows
```batch
# Copy command
xcopy /E /I comfyui_nodes "C:\path\to\ComfyUI\custom_nodes\comfyui_prompt_orchestration"
```
### macOS / Linux
```bash
# Copy command
cp -r comfyui_nodes ~/ComfyUI/custom_nodes/comfyui_prompt_orchestration
```
---
## File Checklist
Make sure these files are present after installation:
- [x] `__init__.py` - Package initialization
- [x] `prompt_orchestration_nodes.py` - Node definitions
- [x] `prompt_orchestrator.py` - Core engine
- [x] `batch_processor.py` - Batch operations
- [x] `README_COMFYUI.md` - Documentation
- [x] `INSTALLATION.md` - This file
---
## Next Steps
After successful installation:
1. **Read the README**: Check `README_COMFYUI.md` for usage guide
2. **Load Example Workflow**: Import example JSON workflows
3. **Try Basic Node**: Start with "Lighting Modifier (Single)"
4. **Explore Presets**: Use "Preset Collection" for quick results
---
## Support
If you encounter issues:
1. Check the console for error messages
2. Verify file structure matches expected layout
3. Ensure Python version compatibility
4. Check file permissions
---
## Requirements
- **ComfyUI**: Latest version recommended
- **Python**: 3.7 or higher
- **Dependencies**: None (uses Python stdlib only)
- **Platform**: Windows, macOS, Linux
---
## Success!
If you see the nodes in your ComfyUI interface under "Prompt Orchestration", you're ready to go! 🎉
Check `README_COMFYUI.md` for detailed usage instructions and examples.
+614
View File
@@ -0,0 +1,614 @@
# Node Reference Guide - Technical Specifications
Complete technical reference for all Prompt Orchestration nodes in ComfyUI.
---
## Node Specifications
### 1. PromptLightingModifier
**Class**: `PromptLightingModifierNode`
**Display Name**: "Lighting Modifier (Single)"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"modifier_id": ("INT", {
"default": 7,
"min": 1,
"max": 20,
"step": 1
}),
"preserve_base": ("BOOLEAN", {
"default": True
})
}
}
```
#### Output Specification
- **RETURN_TYPES**: `("STRING",)`
- **RETURN_NAMES**: `("modified_prompt",)`
- **OUTPUT_IS_LIST**: False
#### Function
- **FUNCTION**: `"apply_modifier"`
- **Description**: Applies single lighting modifier from ID 1-20
#### Parameters
- `text` (STRING): Base prompt to modify
- `modifier_id` (INT 1-20): Which lighting modifier to apply
- `preserve_base` (BOOLEAN):
- True: Append modifier to text (recommended)
- False: Replace lighting info in text
#### Usage Example
```python
Input:
text = "Sports car on highway"
modifier_id = 7
preserve_base = True
Output:
"Sports car on highway, golden hour, warm low-angle side-light, clear sky"
```
---
### 2. PromptBatchVariation
**Class**: `PromptBatchVariationNode`
**Display Name**: "Batch Variation Generator"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"category": (["all", "dry", "wet", "custom"],)
},
"optional": {
"custom_ids": ("STRING", {
"default": "1,7,18",
"multiline": False
})
}
}
```
#### Output Specification
- **RETURN_TYPES**: 10x `("STRING",)`
- **RETURN_NAMES**: `("var_1", "var_2", ..., "var_10")`
- **OUTPUT_IS_LIST**: (True, True, ..., True) - 10 times
#### Function
- **FUNCTION**: `"generate_variations"`
- **Description**: Generates up to 10 prompt variations
#### Parameters
- `text` (STRING): Base prompt
- `category` (ENUM): "all" (1-20), "dry" (1-16), "wet" (17-20), "custom"
- `custom_ids` (STRING): Comma-separated IDs when category="custom"
#### Category Mapping
- **"all"**: Modifiers 1-20 (all modifiers)
- **"dry"**: Modifiers 1-16 (clear conditions)
- **"wet"**: Modifiers 17-20 (wet conditions)
- **"custom"**: User-specified IDs from custom_ids
#### Output Behavior
- Returns list outputs (can connect to batch nodes)
- Pads to 10 outputs if fewer variations
- Padding uses original text
---
### 3. PromptStyleTransform
**Class**: `PromptStyleTransformNode`
**Display Name**: "Style Transform"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"text": ("STRING", multiline=True),
"style": ([
"custom", "pop_art_1960s", "bauhaus", "art_deco",
"cyberpunk", "minimalist", "impressionist", "noir",
"futuristic", "vintage", "brutalist"
],),
"maintain_composition": ("BOOLEAN", default=True)
},
"optional": {
"custom_style": ("STRING", default="")
}
}
```
#### Output Specification
- **RETURN_TYPES**: `("STRING",)`
- **RETURN_NAMES**: `("styled_prompt",)`
#### Function
- **FUNCTION**: `"apply_style"`
#### Parameters
- `text`: Base prompt
- `style`: Pre-defined style or "custom"
- `maintain_composition`:
- True: Explicitly states to maintain composition
- False: Appends style without composition note
- `custom_style`: Used when style="custom"
#### Built-in Styles
1. `pop_art_1960s`: "1960s pop art style with bright colors, bold graphics, and commercial aesthetics"
2. `bauhaus`: "Bauhaus style with geometric forms, primary colors, and modernist principles"
3. `art_deco`: "Art Deco style with geometric patterns, luxurious details, and streamlined forms"
4. `cyberpunk`: "cyberpunk aesthetic with neon lights, high-tech elements, and dystopian atmosphere"
5. `minimalist`: "minimalist style with clean lines, negative space, and essential elements only"
6. `impressionist`: "impressionist painting style with visible brushstrokes and emphasis on light"
7. `noir`: "film noir style with dramatic shadows, high contrast, and moody atmosphere"
8. `futuristic`: "futuristic design with sleek forms, advanced materials, and sci-fi elements"
9. `vintage`: "vintage aesthetic with retro colors, grain, and nostalgic atmosphere"
10. `brutalist`: "brutalist architecture style with raw concrete, geometric forms, and bold structure"
---
### 4. PromptSmartVariation
**Class**: `PromptSmartVariationNode`
**Display Name**: "Smart Variation (NLP)"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"text": ("STRING", multiline=True),
"intent": ("STRING", {
"multiline": False,
"default": "make it golden hour"
})
}
}
```
#### Output Specification
- **RETURN_TYPES**: `("STRING",)`
- **RETURN_NAMES**: `("smart_prompt",)`
#### Function
- **FUNCTION**: `"smart_vary"`
#### Intent Recognition
System recognizes keywords in intent string:
**Time-based intents**:
- "morning" → modifiers 1, 2, 3
- "afternoon" → modifiers 5, 6, 14
- "golden hour" → modifier 7
- "sunset" → modifier 8
- "night" → modifiers 11, 12
- "blue hour" → modifiers 10, 17, 18
- "wet" → modifiers 17, 18, 19, 20
**Style-based intents**:
- Detects style keywords → applies style transformation
- Falls back to custom style if no match
---
### 5. PromptPresetCollection
**Class**: `PromptPresetCollectionNode`
**Display Name**: "Preset Collection"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"text": ("STRING", multiline=True),
"preset": ([
"key_moments", "commercial", "artistic",
"full_day_cycle", "wet_night"
],)
}
}
```
#### Output Specification
- **RETURN_TYPES**: 8x `("STRING",)`
- **RETURN_NAMES**: `("var_1", ..., "var_8")`
- **OUTPUT_IS_LIST**: (True, ..., True) - 8 times
#### Function
- **FUNCTION**: `"apply_preset"`
#### Preset Definitions
**"key_moments"**:
- Modifier IDs: [1, 7, 8, 10, 18, 20]
- Count: 6 variations
- Use: Dramatic lighting variety
**"commercial"**:
- Modifier IDs: [3, 4, 7, 11]
- Count: 4 variations
- Use: Product photography
**"artistic"**:
- Modifier IDs: [7, 8, 9, 10]
- Styles: ["impressionist", "minimalist", "art_deco"]
- Count: 7 variations (4 lighting + 3 styles)
- Use: Creative projects
**"full_day_cycle"**:
- Modifier IDs: 1-16 (all DRY)
- Count: 16 variations
- Use: Time-lapse visualization
**"wet_night"**:
- Modifier IDs: 17-20 (all WET)
- Count: 4 variations
- Use: Dramatic urban/night scenes
---
### 6. PromptModifierInfo
**Class**: `PromptModifierInfoNode`
**Display Name**: "Modifier Info"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"modifier_id": ("INT", {
"default": 7,
"min": 1,
"max": 20
})
}
}
```
#### Output Specification
- **RETURN_TYPES**: `("STRING", "STRING", "STRING")`
- **RETURN_NAMES**: `("description", "category", "tags")`
#### Function
- **FUNCTION**: `"get_info"`
#### Outputs
- `description`: Full modifier text (e.g., "golden hour, warm low-angle side-light, clear sky")
- `category`: "dry" or "wet"
- `tags`: Comma-separated tags (e.g., "golden-hour, warm, side-light, clear")
---
### 7. PromptCombiner
**Class**: `PromptCombinerNode`
**Display Name**: "Prompt Combiner"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"text_1": ("STRING", multiline=True)
},
"optional": {
"text_2": ("STRING", multiline=True),
"text_3": ("STRING", multiline=True),
"text_4": ("STRING", multiline=True),
"separator": ("STRING", {
"default": ", ",
"multiline": False
})
}
}
```
#### Output Specification
- **RETURN_TYPES**: `("STRING",)`
- **RETURN_NAMES**: `("combined_text",)`
#### Function
- **FUNCTION**: `"combine"`
#### Behavior
- Combines non-empty text inputs
- Joins with separator string
- Ignores empty inputs
---
### 8. PromptRandomModifier
**Class**: `PromptRandomModifierNode`
**Display Name**: "Random Modifier"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"text": ("STRING", multiline=True),
"category": (["all", "dry", "wet"],),
"seed": ("INT", {
"default": 0,
"min": 0,
"max": 0xffffffffffffffff
})
}
}
```
#### Output Specification
- **RETURN_TYPES**: `("STRING", "INT")`
- **RETURN_NAMES**: `("modified_prompt", "used_modifier_id")`
#### Function
- **FUNCTION**: `"apply_random"`
#### Behavior
- Selects random modifier from category using seed
- Returns both modified prompt and which modifier was chosen
- Same seed = same modifier (reproducible)
---
### 9. PromptMultiStyle
**Class**: `PromptMultiStyleNode`
**Display Name**: "Multi-Style Layer"
**Category**: "Prompt Orchestration"
#### Input Specification
```python
INPUT_TYPES = {
"required": {
"text": ("STRING", multiline=True),
"style_1": ([style_list],)
},
"optional": {
"style_2": ([style_list],),
"style_3": ([style_list],)
}
}
```
#### Output Specification
- **RETURN_TYPES**: `("STRING",)`
- **RETURN_NAMES**: `("multi_styled_prompt",)`
#### Function
- **FUNCTION**: `"apply_multi_style"`
#### Behavior
- Applies up to 3 styles sequentially
- Each style builds on previous
- Set to "none" to skip a style slot
---
## Data Types
### STRING
- Multiline text input/output
- Used for prompts and descriptions
- Can be connected to CLIP Text Encode
### INT
- Integer values
- Used for modifier IDs (1-20) and seeds
- Can be connected to math/logic nodes
### BOOLEAN
- True/False toggle
- Used for behavior switches
- Displayed as checkbox in UI
### ENUM (Dropdown)
- Fixed list of options
- Used for categories, styles, presets
- Displayed as dropdown menu
---
## Connection Patterns
### String → String
Most common: Text flows through nodes
```
[Text Input] → [Node] → [CLIP Text Encode]
```
### String → Multiple Strings (Batch)
List outputs from batch nodes
```
[Text] → [Batch Node] → [var_1] → [CLIP 1]
→ [var_2] → [CLIP 2]
→ [var_3] → [CLIP 3]
```
### Multiple → Single (Combiner)
```
[Text 1] ─┐
[Text 2] ─┤→ [Combiner] → [Output]
[Text 3] ─┘
```
### Info Display
```
[Modifier ID] → [Info Node] → [description] → [Show Text]
→ [category] → [Display]
→ [tags] → [Display]
```
---
## Node Compatibility
### Compatible Input Nodes
- Primitive (String)
- Text Input
- Load Text
- Any node outputting STRING type
### Compatible Output Nodes
- CLIP Text Encode
- Save Text
- Show Text
- Any node accepting STRING input
### Batch Processing Compatible
- Batch Variation Generator
- Preset Collection
- Any node with OUTPUT_IS_LIST=True
---
## Performance Characteristics
### Execution Speed
- All nodes: <10ms typically
- No network calls
- In-memory processing only
### Memory Usage
- Minimal: ~1-2MB per node
- Scales linearly with text length
- No persistent state between executions
### Caching
- Nodes don't cache results
- Re-execute on each workflow run
- ComfyUI handles workflow-level caching
---
## Error Handling
### Invalid Modifier ID
- Returns error message in output
- Does not break workflow
- Check console for details
### Empty Inputs
- Returns original/default text
- Continues execution
- No workflow interruption
### Invalid Custom IDs
- Skips invalid IDs
- Uses valid ones only
- Falls back to default if all invalid
---
## Thread Safety
- All nodes are thread-safe
- Can run in parallel workflows
- No shared state between instances
---
## Version Compatibility
- **ComfyUI**: Latest version recommended
- **Python**: 3.7+ required
- **Backward Compatible**: Yes (JSON workflows)
- **Forward Compatible**: Should be (minimal dependencies)
---
## Extension Points
### Custom Modifiers
Add to `prompt_orchestrator.py`:
```python
custom = LightingModifier(
id=21,
description="your modifier",
category=ModifierCategory.DRY,
tags=["tag1", "tag2"]
)
orchestrator.modifiers[21] = custom
```
### Custom Styles
Add to style templates:
```python
orchestrator.custom_style_templates["my_style"] = "description"
```
---
## Debugging
### Enable Console Logging
Check ComfyUI console for:
- Node initialization messages
- Error messages
- Input/output values (if debugging enabled)
### Test Nodes Individually
1. Create minimal workflow
2. Add single node
3. Connect Show Text to output
4. Verify results
### Validate Inputs
Use Modifier Info node to check modifier descriptions before using.
---
## API Reference
All nodes implement ComfyUI's standard node interface:
```python
class CustomNode:
@classmethod
def INPUT_TYPES(cls):
# Define inputs
RETURN_TYPES = ()
RETURN_NAMES = ()
FUNCTION = "method_name"
CATEGORY = "category"
def method_name(self, **kwargs):
# Implementation
return outputs
```
---
## License
MIT License - Same as core system
---
**Last Updated**: v1.0.0
**For**: ComfyUI Custom Nodes
**Compatibility**: ComfyUI Latest
+417
View File
@@ -0,0 +1,417 @@
# ComfyUI Quick Start - Prompt Orchestration Nodes
Get started with prompt orchestration in ComfyUI in under 5 minutes!
---
## 🚀 Installation (30 seconds)
1. **Copy folder to ComfyUI**:
```
Copy comfyui_nodes → ComfyUI/custom_nodes/comfyui_prompt_orchestration
```
2. **Restart ComfyUI**
3. **Verify**: Look for "Prompt Orchestration" category in Add Node menu
✅ **Done!** If you see the category, you're ready to go.
---
## 🎯 Your First Workflow (2 minutes)
### Step 1: Create Basic Workflow
1. **Right-click canvas** → Add Node
2. **Navigate**: Prompt Orchestration → Lighting Modifier (Single)
3. **Connect**:
```
[Your Text Input] → [Lighting Modifier] → [CLIP Text Encode] → [Your Pipeline]
```
### Step 2: Configure Node
In the Lighting Modifier node:
- **text**: Enter your base prompt (e.g., "sports car on highway")
- **modifier_id**: Set to `7` (golden hour - most popular)
- **preserve_base**: Keep as `True`
### Step 3: Generate
Run your workflow! The prompt will automatically get golden hour lighting.
**Result**:
```
Input: "sports car on highway"
Output: "sports car on highway, golden hour, warm low-angle side-light, clear sky"
```
---
## 🌟 Top 5 Most Useful Nodes
### 1️⃣ Lighting Modifier (Single) - START HERE
**Use when**: You want specific lighting on one prompt
**Best setting**: modifier_id = 7 (golden hour)
**Connects to**: CLIP Text Encode
### 2️⃣ Batch Variation Generator
**Use when**: You want multiple lighting options to compare
**Best setting**: category = "key_moments"
**Outputs**: 10 variations ready to render
### 3️⃣ Preset Collection
**Use when**: You want optimized sets for specific use cases
**Best setting**: preset = "commercial" (for products) or "key_moments" (for variety)
**Outputs**: 8 professional variations
### 4️⃣ Style Transform
**Use when**: You want to add artistic style
**Best setting**: style = "cyberpunk" or "noir"
**Tip**: Apply AFTER Lighting Modifier
### 5️⃣ Smart Variation (NLP)
**Use when**: You want quick natural language edits
**Best setting**: intent = "make it golden hour" or "add wet street"
**Great for**: Fast iterations
---
## 💡 Common Workflows
### Workflow A: Single Perfect Shot
**Goal**: Get one image with perfect lighting
```
┌─────────────┐
│ Text Input │
│ "car scene" │
└──────┬──────┘
│
┌──────▼─────────────────┐
│ Lighting Modifier │
│ modifier_id: 7 │
│ (golden hour) │
└──────┬─────────────────┘
│
┌──────▼──────────┐
│ CLIP Text Encode│
└──────┬──────────┘
│
[REST OF PIPELINE]
```
**Time**: 30 seconds
**Output**: 1 perfectly lit image
---
### Workflow B: Compare 4 Lighting Options
**Goal**: See different times of day
```
┌─────────────┐
│ Text Input │
└──────┬──────┘
│
┌──────▼───────────────────┐
│ Batch Variation Generator│
│ category: custom │
│ custom_ids: "1,7,8,10" │
│ (sunrise,golden,sunset, │
│ blue hour) │
└─┬──┬──┬──┬───────────────┘
│ │ │ │
│ │ │ └→ CLIP → KSampler → Image 4
│ │ └───→ CLIP → KSampler → Image 3
│ └──────→ CLIP → KSampler → Image 2
└─────────→ CLIP → KSampler → Image 1
```
**Time**: 1 minute setup
**Output**: 4 comparison images
---
### Workflow C: Product Photography
**Goal**: Professional product shots
```
┌──────────────────┐
│ Text Input │
│ "watch on marble"│
└────────┬─────────┘
│
┌────────▼────────────┐
│ Preset Collection │
│ preset: commercial │
└─┬─┬─┬─┬────────────┘
│ │ │ │
│ │ │ └→ CLIP → KSampler → Shot 4
│ │ └──→ CLIP → KSampler → Shot 3
│ └────→ CLIP → KSampler → Shot 2
└──────→ CLIP → KSampler → Shot 1
```
**Time**: 30 seconds
**Output**: 4 optimized product shots
---
### Workflow D: Styled Variation
**Goal**: Same subject, different aesthetic
```
┌─────────────┐
│ Text Input │
└──────┬──────┘
│
┌──────▼──────────┐
│ Lighting Modifier│
│ modifier_id: 11 │
│ (night) │
└──────┬──────────┘
│
┌──────▼──────────┐
│ Style Transform │
│ style: cyberpunk│
└──────┬──────────┘
│
┌──────▼──────┐
│ CLIP Encode │
└──────┬──────┘
[PIPELINE]
```
**Time**: 1 minute
**Output**: 1 cyberpunk night scene
---
## 🎨 The 20 Modifiers - Quick Reference
### Most Popular (Use These First)
| ID | Name | When to Use |
|----|------|-------------|
| **7** | Golden Hour | 🌟 DEFAULT - Use for everything |
| **18** | Blue Hour Wet | Dramatic urban/car scenes |
| **4** | Midday | Product photography |
| **8** | Sunset | Romantic/warm scenes |
| **10** | Blue Hour | Moody atmospheres |
### Complete List
**DRY (Clear Sky, 1-16)**:
- 1-3: Morning (sunrise → late morning)
- 4-6: Afternoon (midday → late afternoon)
- 7-9: Evening (golden hour → twilight)
- 10-12: Night (blue hour → moonlit)
- 13-16: Specialized angles
**WET (Reflections, 17-20)**:
- 17-20: Wet road variations (predawn → night)
📋 **Full list**: Add "Modifier Info" node and connect to "Show Text"
---
## 🔧 Settings Guide
### Lighting Modifier Settings
**preserve_base**:
- ✅ **True** (recommended): Adds lighting to your prompt
- ❌ **False**: Replaces lighting keywords in prompt
**When to use False**: If your prompt already has lighting terms you want to replace
---
### Batch Variation Settings
**category**:
- **"all"**: All 20 modifiers (use for full exploration)
- **"dry"**: 16 modifiers (clear weather only)
- **"wet"**: 4 modifiers (dramatic wet scenes only)
- **"custom"**: Your specific IDs
**custom_ids** (when category="custom"):
```
Examples:
"7" → Just golden hour
"1,7,18" → Sunrise, golden hour, blue hour wet
"3,4,11" → Late morning, midday, night
```
---
### Preset Collection Settings
**preset**:
- **"key_moments"**: Best variety (6 vars) - 🌟 START HERE
- **"commercial"**: Product photos (4 vars)
- **"artistic"**: Creative projects (7 vars)
- **"full_day_cycle"**: Complete day (16 vars)
- **"wet_night"**: Dramatic night (4 vars)
---
## 💰 Cost Saving Tips
### Tip 1: Preview First
```
[Text] → [Lighting Modifier] → [Show Text Node]
```
See the prompt BEFORE rendering to avoid wasted generations.
### Tip 2: Use Key Moments
Instead of all 20 modifiers (expensive!), use "key_moments" preset (6 variations).
### Tip 3: Test with Low Steps
Use your batch variations with low step count first, then re-render winners at high quality.
---
## ⚡ Pro Tips
### Tip 1: Chain Modifiers for Complex Lighting
```
[Text] → [Modifier 7: golden hour] → [Modifier 19: wet road] → [CLIP]
```
Result: Golden hour with wet reflections!
### Tip 2: Use Random Modifier for Exploration
```
[Text] → [Random Modifier] → [Show "used_modifier_id"] → [CLIP]
```
Let the system surprise you, then see which modifier worked!
### Tip 3: Combine with Wildcards
```
[Wildcard: "__car__"] → [Lighting Modifier] → [CLIP]
```
Different cars, same perfect lighting!
### Tip 4: Save Preset Workflows
Save workflow with your favorite modifier settings, load when needed.
### Tip 5: Use Combiner for Complex Prompts
```
[Subject] ──┐
[Location] ─┤→ [Combiner] → [Lighting] → [CLIP]
[Details] ──┘
```
---
## 🐛 Troubleshooting Quick Fixes
### Problem: Nodes don't appear
**Fix**: Restart ComfyUI completely (not just refresh)
### Problem: Output is empty
**Fix**: Check that text input is actually connected
### Problem: All variations are the same
**Fix**: Use category="all" or specify more custom_ids
### Problem: Style not working
**Fix**: Make sure you selected a style (not left on "custom" without custom_style text)
---
## 📚 Learning Resources
1. **This guide** - Basic usage
2. **NODE_REFERENCE.md** - Technical details
3. **README_COMFYUI.md** - Complete documentation
4. **Example workflows** - In `example_workflows/` folder
---
## 🎓 5-Day Learning Plan
### Day 1: Single Modifier
- Use Lighting Modifier node
- Try modifier 7 (golden hour)
- Generate 1-5 images
### Day 2: Batch Comparison
- Use Batch Variation node
- Try category="key_moments"
- Compare results
### Day 3: Styles
- Add Style Transform node
- Try "cyberpunk", "noir", "minimalist"
- Combine with lighting
### Day 4: Presets
- Use Preset Collection
- Try "commercial" and "artistic"
- Find your favorite preset
### Day 5: Advanced
- Chain multiple nodes
- Use Random Modifier
- Build custom workflows
---
## 🏆 Success Checklist
After this guide, you should be able to:
- [ ] Install the nodes in ComfyUI
- [ ] Add Lighting Modifier to workflow
- [ ] Generate image with golden hour (modifier 7)
- [ ] Create batch variations
- [ ] Apply style transformations
- [ ] Use preset collections
- [ ] Combine multiple nodes
- [ ] Build your own workflows
---
## 🚀 Ready to Start?
### Absolute Beginner Path:
1. Install nodes
2. Add "Lighting Modifier (Single)"
3. Set modifier_id = 7
4. Connect to CLIP
5. Generate!
### Intermediate Path:
1. Install nodes
2. Add "Batch Variation Generator"
3. Set category = "key_moments"
4. Connect outputs to CLIPs
5. Compare 6 variations!
### Advanced Path:
1. Install nodes
2. Chain Lighting → Style → Multi-Style
3. Create complex transformations
4. Build reusable workflows!
---
## 📞 Next Steps
- ✅ **Installed?** → Try your first workflow above
- ✅ **Working?** → Check the example workflows folder
- ✅ **Advanced?** → Read NODE_REFERENCE.md
- ✅ **Issues?** → Check INSTALLATION.md troubleshooting
---
**Have fun creating amazing variations! 🎨**
_Tip: Bookmark this page and refer back when learning new nodes._
+621
View File
@@ -0,0 +1,621 @@
# Image Prompt Orchestration System
A powerful Python system for automating image prompt variations with 20 plug-and-play lighting modifiers and unlimited style transformations.
## Features
- **20 Pre-defined Lighting Modifiers** (anchor-agnostic, DRY/WET categories)
- **Style Transformation Engine** with 10+ built-in templates
- **Batch Processing** for multiple prompts
- **Smart Variation** with natural language intents
- **Multiple Export Formats** (JSON, CSV, TXT)
- **CLI Interface** for quick automation
- **Preset Collections** for common use cases
---
## Quick Start
### Installation
```bash
# No external dependencies required - uses Python standard library
python prompt_orchestrator.py # Run demo
```
### Basic Usage (Python API)
```python
from prompt_orchestrator import PromptOrchestrator
orchestrator = PromptOrchestrator()
# Apply a lighting modifier
base = "Sports car on coastal highway"
golden_hour = orchestrator.apply_lighting_modifier(base, 7)
print(golden_hour)
# → "Sports car on coastal highway, golden hour, warm low-angle side-light, clear sky"
# Apply style transformation
cyberpunk = orchestrator.apply_style_transformation(base, "cyberpunk")
print(cyberpunk)
# → "Transform to cyberpunk aesthetic with neon lights... Base scene: Sports car..."
# Smart variation with natural language
result = orchestrator.smart_variation(base, "make it a wet night scene")
print(result)
```
### Command Line Interface
```bash
# Single modifier
python cli.py --prompt "Luxury sedan in urban setting" --modifier 7
# All DRY modifiers
python cli.py --prompt "Modern architecture" --category dry --output results.json
# Style transformation
python cli.py --prompt "Vintage motorcycle" --style noir
# Preset collection
python cli.py --prompt "Product photography" --preset commercial
# Batch process from file
python cli.py --input-file prompts.txt --category all --output batch.csv
# Smart variation
python cli.py --prompt "Car on street" --smart "golden hour with warm lighting"
```
---
## Lighting Modifiers Reference
### DRY Category (1-16): Universal, Clear Conditions
| ID | Description |
|----|-------------|
| 1 | sunrise, car-friendly side-light, clear sky |
| 2 | early morning, subtle back-light along the road axis, clear sky |
| 3 | late morning, high-sun hard light for crisp body lines, clear sky |
| 4 | midday, crisp hard sunlight, clear sky |
| 5 | early afternoon, gentle side-light, clear sky |
| 6 | late afternoon, long shadow geometry, clear sky |
| 7 | golden hour, warm low-angle side-light, clear sky |
| 8 | sunset, soft rim-lit edges, clear sky |
| 9 | twilight, residual glow, clear sky |
| 10 | blue hour, deep clear sky |
| 11 | night, clear sky with clean highlights |
| 12 | moonlit night, clear sky |
| 13 | early morning, three-quarter back-light, clear sky |
| 14 | late afternoon, subtle back-light along the road axis, clear sky |
| 15 | evening, car-friendly side-light, clear sky |
| 16 | dawn, clean low-haze air, clear sky |
### WET Category (17-20): Low-Light, Wet Conditions
| ID | Description |
|----|-------------|
| 17 | predawn blue hour, wet road, subtle back-light along the road axis |
| 18 | blue hour, wet road, car-friendly side-light |
| 19 | twilight, wet road, residual glow, clear sky |
| 20 | night, wet street, clear sky (buildings visible → warm interior lights glowing) |
---
## Style Templates
Built-in style transformations:
- **pop_art_1960s** - Bright colors, bold graphics, commercial aesthetics
- **bauhaus** - Geometric forms, primary colors, modernist principles
- **art_deco** - Geometric patterns, luxurious details, streamlined forms
- **cyberpunk** - Neon lights, high-tech elements, dystopian atmosphere
- **minimalist** - Clean lines, negative space, essential elements only
- **impressionist** - Visible brushstrokes, emphasis on light
- **noir** - Dramatic shadows, high contrast, moody atmosphere
- **futuristic** - Sleek forms, advanced materials, sci-fi elements
- **vintage** - Retro colors, grain, nostalgic atmosphere
- **brutalist** - Raw concrete, geometric forms, bold structure
*Custom styles can be added using free-form descriptions.*
---
## API Documentation
### PromptOrchestrator Class
#### Core Methods
**`apply_lighting_modifier(base_prompt, modifier_id, preserve_base=True)`**
```python
result = orchestrator.apply_lighting_modifier(
"Modern building exterior",
modifier_id=7, # Golden hour
preserve_base=True # Append vs replace
)
```
**`apply_style_transformation(base_prompt, style, maintain_composition=True)`**
```python
result = orchestrator.apply_style_transformation(
"Vintage car in garage",
style="cyberpunk",
maintain_composition=True
)
```
**`generate_variation_batch(base_prompt, modifier_ids=None, include_styles=None)`**
```python
variations = orchestrator.generate_variation_batch(
"Sports car on highway",
modifier_ids=[1, 7, 18], # Specific modifiers
include_styles=["noir", "cyberpunk"]
)
# Returns list of dicts with keys: id, type, category, description, prompt
```
**`smart_variation(base_prompt, intent)`**
```python
result = orchestrator.smart_variation(
"Car on street",
intent="make it golden hour with dramatic lighting"
)
```
#### Utility Methods
**`get_modifiers_by_category(category: ModifierCategory)`**
```python
from prompt_orchestrator import ModifierCategory
dry_mods = orchestrator.get_modifiers_by_category(ModifierCategory.DRY)
wet_mods = orchestrator.get_modifiers_by_category(ModifierCategory.WET)
```
**`get_modifiers_by_tags(tags: List[str])`**
```python
night_mods = orchestrator.get_modifiers_by_tags(["night", "moonlit"])
```
**`export_variations(variations, format="txt")`**
```python
output = orchestrator.export_variations(variations, format="json")
print(output)
```
---
### BatchProcessor Class
#### Methods
**`process_single_prompt(base_prompt, config)`**
```python
from batch_processor import BatchProcessor
processor = BatchProcessor(output_dir="my_prompts")
config = {
"lighting_ids": "all", # or [1,7,18] or "dry" or "wet"
"styles": ["cyberpunk", "noir"],
"export_format": "json"
}
variations = processor.process_single_prompt("Base prompt here", config)
```
**`process_multiple_prompts(prompts, config, output_filename)`**
```python
prompts = [
"Sports car on highway",
"Modern building exterior",
"Product photography setup"
]
output_path = processor.process_multiple_prompts(
prompts=prompts,
config=config,
output_filename="batch_output"
)
```
**`create_preset_collection(base_prompt, preset)`**
```python
# Available presets:
# - "full_day_cycle": All DRY modifiers
# - "wet_night": All WET modifiers
# - "key_moments": Selected dramatic moments
# - "commercial": Best for commercial photography
# - "artistic": Artistic lighting + style variations
variations = processor.create_preset_collection(
"Product shot",
preset="commercial"
)
```
**`generate_comparison_set(base_prompt, modifier_pairs)`**
```python
comparisons = processor.generate_comparison_set(
"Architecture shot",
modifier_pairs=[
(4, 7), # Midday vs Golden Hour
(11, 18) # Night dry vs Night wet
]
)
```
---
## Use Cases & Examples
### 1. Automotive Photography Variations
```python
orchestrator = PromptOrchestrator()
base = "Luxury sports car on mountain road, dramatic landscape"
# Generate complete day cycle
variations = orchestrator.generate_variation_batch(
base,
modifier_ids=list(range(1, 17)) # All DRY modifiers
)
# Add wet night variations for drama
wet_variations = orchestrator.generate_variation_batch(
base,
modifier_ids=[17, 18, 19, 20]
)
```
### 2. Product Photography Set
```python
processor = BatchProcessor()
products = [
"Smartwatch on minimal white surface",
"Luxury perfume bottle with reflections",
"Designer sunglasses, clean background"
]
# Use commercial preset for all
for product in products:
vars = processor.create_preset_collection(product, "commercial")
# vars contains optimal lighting for product shots
```
### 3. Architectural Visualization
```python
base = "Modern glass office building, urban context"
# Morning through evening progression
morning_to_evening = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
variations = orchestrator.generate_variation_batch(
base,
modifier_ids=morning_to_evening
)
# Add style variations
style_vars = orchestrator.generate_variation_batch(
base,
modifier_ids=[7], # Just golden hour
include_styles=["brutalist", "minimalist", "futuristic"]
)
```
### 4. Batch Processing from File
Create `prompts.txt`:
```
Sports car on coastal highway
Modern architecture, glass facade
Vintage motorcycle in garage
Product photography, minimal setup
```
Then process:
```bash
python cli.py --input-file prompts.txt --category all --output results.csv
```
### 5. Smart Variation Examples
```python
base = "Car parked on city street"
# Natural language variations
variants = [
orchestrator.smart_variation(base, "make it golden hour"),
orchestrator.smart_variation(base, "convert to wet night scene"),
orchestrator.smart_variation(base, "cyberpunk style"),
orchestrator.smart_variation(base, "morning sunrise lighting")
]
```
---
## Advanced Features
### Custom Pipelines
Create custom transformation sequences:
```python
def add_fog(prompt):
return f"{prompt}, atmospheric fog, reduced visibility"
def add_motion_blur(prompt):
return f"{prompt}, motion blur effect, dynamic"
# Apply custom pipeline
result = orchestrator.create_custom_pipeline(
"Sports car on track",
transformations=[
lambda p: orchestrator.apply_lighting_modifier(p, 7),
add_fog,
add_motion_blur
]
)
```
### Filtering and Querying
```python
# Get all night modifiers
night_mods = orchestrator.get_modifiers_by_tags(["night"])
# Get all side-light modifiers
side_light = orchestrator.get_modifiers_by_tags(["side-light"])
# Combine multiple tags
dramatic = orchestrator.get_modifiers_by_tags(["golden-hour", "sunset", "blue-hour"])
```
### Comparison Sets for A/B Testing
```python
processor = BatchProcessor()
# Generate comparison pairs
comparisons = processor.generate_comparison_set(
"Product on pedestal",
modifier_pairs=[
(3, 7), # Hard light vs Golden hour
(4, 11), # Midday vs Night
(7, 18) # Golden hour dry vs Blue hour wet
]
)
# Each comparison contains variant_a and variant_b with full prompts
```
---
## CLI Command Reference
### List Commands
```bash
# List all modifiers
python cli.py --list-modifiers
# List all styles
python cli.py --list-styles
```
### Single Prompt Commands
```bash
# Apply specific modifier
python cli.py -p "Your prompt here" -m 7
# Apply category
python cli.py -p "Your prompt" -c dry
# Apply multiple specific modifiers
python cli.py -p "Your prompt" --modifiers 1,7,18
# Apply style
python cli.py -p "Your prompt" -s cyberpunk
# Use preset
python cli.py -p "Your prompt" --preset key_moments
# Smart variation
python cli.py -p "Your prompt" --smart "make it golden hour"
```
### Batch Commands
```bash
# Process file with all modifiers
python cli.py -i prompts.txt -c all -o results.json
# Process file with DRY only
python cli.py -i prompts.txt -c dry -f csv -o dry_only.csv
# Process with preset
python cli.py -i prompts.txt --preset commercial -o commercial.txt
```
### Output Options
```bash
# Print to stdout
python cli.py -p "Prompt" -m 7 --print-only
# Save to specific file
python cli.py -p "Prompt" -c all -o my_results.json
# Choose format explicitly
python cli.py -p "Prompt" -c all -f csv -o output.csv
```
---
## Export Formats
### JSON Format
```json
[
{
"base_prompt": "Sports car on highway",
"variations": [
{
"id": "lighting_7",
"type": "lighting",
"category": "dry",
"description": "golden hour, warm low-angle side-light, clear sky",
"prompt": "Sports car on highway, golden hour, warm low-angle side-light, clear sky"
}
]
}
]
```
### CSV Format
```csv
base_prompt,variation_id,type,category,description,prompt
"Sports car","lighting_7","lighting","dry","golden hour...","Sports car, golden hour..."
```
### TXT Format
```
BASE PROMPT: Sports car on highway
================================================================================
[lighting_7] LIGHTING - dry
Description: golden hour, warm low-angle side-light, clear sky
Prompt: Sports car on highway, golden hour, warm low-angle side-light, clear sky
--------------------------------------------------------------------------------
```
---
## Best Practices
### 1. Choose the Right Modifier Category
- **DRY modifiers (1-16)**: Universal, work with any subject
- **WET modifiers (17-20)**: Dramatic, best for urban/street scenes with reflections
### 2. Layering Transformations
```python
# Good: Layer lighting + style
base = orchestrator.apply_lighting_modifier("Base prompt", 7)
final = orchestrator.apply_style_transformation(base, "cyberpunk", maintain_composition=False)
# Better: Use batch generation
variations = orchestrator.generate_variation_batch(
"Base prompt",
modifier_ids=[7],
include_styles=["cyberpunk"]
)
```
### 3. Preset Selection
- **Commercial**: Product shots, crisp lighting (modifiers 3, 4, 7, 11)
- **Key Moments**: Portfolios, dramatic variety (1, 7, 8, 10, 18, 20)
- **Full Day Cycle**: Time-lapse visualization (all DRY modifiers)
- **Artistic**: Creative projects (golden hour + styles)
### 4. Batch Processing Efficiency
```python
# Efficient: Process all prompts with same config
processor.process_multiple_prompts(
prompts=large_prompt_list,
config=single_config,
output_filename="bulk_output"
)
# Less efficient: Loop with individual processing
```
---
## Contributing & Customization
### Adding Custom Modifiers
```python
from prompt_orchestrator import LightingModifier, ModifierCategory
custom_mod = LightingModifier(
id=21,
description="stormy weather, dramatic dark clouds, side-light",
category=ModifierCategory.DRY,
tags=["storm", "dramatic", "weather"]
)
orchestrator.modifiers[21] = custom_mod
```
### Adding Custom Styles
```python
orchestrator.custom_style_templates["my_style"] = \
"custom aesthetic with specific characteristics"
# Use it
result = orchestrator.apply_style_transformation(base, "my_style")
```
---
## Performance Notes
- **In-memory processing**: All operations are fast, no external API calls
- **Batch operations**: Optimized for processing hundreds of prompts
- **No dependencies**: Uses only Python standard library
---
## Troubleshooting
**Q: My prompts are too long**
```python
# Use preserve_base=False to replace instead of append
result = orchestrator.apply_lighting_modifier(base, 7, preserve_base=False)
```
**Q: I need specific lighting combinations**
```python
# Create custom pipeline
def custom_combo(p):
# Your custom logic
return modified_prompt
result = orchestrator.create_custom_pipeline(base, [custom_combo, ...])
```
**Q: Smart variation not working as expected**
```python
# Smart variation uses keyword matching - be specific
# Good: "make it golden hour with warm side light"
# Less good: "make it nice"
```
---
## License
MIT License - Free for commercial and personal use
## Support
For issues or feature requests, please provide:
1. Your base prompt
2. Desired modifier/transformation
3. Expected vs actual output
---
## Version History
- **v1.0** - Initial release with 20 modifiers, style system, batch processing, CLI
+538
View File
@@ -0,0 +1,538 @@
# Prompt Orchestration Nodes for ComfyUI
Advanced prompt manipulation system integrated into ComfyUI workflows. Control lighting, style, and variations with precision using 20 pre-defined modifiers and unlimited transformations.
---
## 🎯 What This Does
Brings professional prompt engineering capabilities directly into your ComfyUI workflows:
- **20 Lighting Modifiers**: Sunrise to moonlit night, dry and wet conditions
- **Style Transformations**: Cyberpunk, noir, minimalist, and custom styles
- **Smart Variation**: Natural language prompt modification
- **Batch Generation**: Multiple variations from single input
- **Preset Collections**: Optimized modifier sets for common use cases
---
## 📦 Available Nodes (9 Total)
All nodes appear under the **"Prompt Orchestration"** category.
### 1. Lighting Modifier (Single)
**Purpose**: Apply one lighting modifier to a prompt
**Inputs**:
- `text`: Your base prompt
- `modifier_id`: Number 1-20 (see modifier reference below)
- `preserve_base`: Append vs replace (default: True)
**Output**: Modified prompt with lighting applied
**Example Use**:
```
Input: "Sports car on highway"
Modifier: 7 (golden hour)
Output: "Sports car on highway, golden hour, warm low-angle side-light, clear sky"
```
### 2. Batch Variation Generator
**Purpose**: Generate multiple variations at once
**Inputs**:
- `text`: Base prompt
- `category`: "all", "dry", "wet", or "custom"
- `custom_ids`: Comma-separated IDs if using "custom" (e.g., "1,7,18")
**Outputs**: Up to 10 variation strings (var_1 through var_10)
**Example Use**:
Connect each output to a different CLIP Text Encode for batch image generation.
### 3. Style Transform
**Purpose**: Apply artistic style to prompt
**Inputs**:
- `text`: Base prompt
- `style`: Choose from 11 built-in styles or "custom"
- `maintain_composition`: Keep original composition (default: True)
- `custom_style`: Your custom style description (if style="custom")
**Output**: Style-transformed prompt
**Built-in Styles**:
- cyberpunk, noir, minimalist, pop_art_1960s
- bauhaus, art_deco, impressionist, futuristic
- vintage, brutalist
### 4. Smart Variation (NLP)
**Purpose**: Natural language prompt modification
**Inputs**:
- `text`: Base prompt
- `intent`: What you want (e.g., "make it golden hour", "add wet street")
**Output**: Intelligently modified prompt
**Example Intents**:
- "make it golden hour"
- "convert to wet night scene"
- "add cyberpunk style"
- "morning sunrise lighting"
### 5. Preset Collection
**Purpose**: Apply optimized modifier sets
**Inputs**:
- `text`: Base prompt
- `preset`: Choose from 5 presets
**Outputs**: 8 variations optimized for the use case
**Available Presets**:
- `key_moments`: Dramatic lighting variety (6 variations)
- `commercial`: Product photography optimized (4 variations)
- `artistic`: Creative with styles (7 variations)
- `full_day_cycle`: Complete day progression (16 variations)
- `wet_night`: Dramatic wet conditions (4 variations)
### 6. Modifier Info
**Purpose**: Display information about a modifier
**Inputs**:
- `modifier_id`: Number 1-20
**Outputs**:
- `description`: Full modifier description
- `category`: "dry" or "wet"
- `tags`: Comma-separated tags
**Use**: Connect to a text display to see what modifiers do.
### 7. Prompt Combiner
**Purpose**: Merge multiple text inputs
**Inputs**:
- `text_1` through `text_4`: Text pieces to combine
- `separator`: String to join with (default: ", ")
**Output**: Combined text
**Example**:
```
text_1: "Sports car"
text_2: "on mountain road"
text_3: "golden hour"
separator: ", "
Output: "Sports car, on mountain road, golden hour"
```
### 8. Random Modifier
**Purpose**: Apply random modifier from category
**Inputs**:
- `text`: Base prompt
- `category`: "all", "dry", or "wet"
- `seed`: Random seed for reproducibility
**Outputs**:
- `modified_prompt`: Result with random modifier
- `used_modifier_id`: Which modifier was selected
**Use**: Create diverse variations with randomness.
### 9. Multi-Style Layer
**Purpose**: Apply multiple styles sequentially
**Inputs**:
- `text`: Base prompt
- `style_1`, `style_2`, `style_3`: Up to 3 styles
**Output**: Prompt with layered styles
**Use**: Create complex aesthetic combinations.
---
## 🌟 The 20 Lighting Modifiers
### DRY Category (1-16): Clear Conditions
| ID | Time/Condition | Description |
|----|---------------|-------------|
| 1 | Sunrise | car-friendly side-light, clear sky |
| 2 | Early Morning | subtle back-light along road axis, clear sky |
| 3 | Late Morning | high-sun hard light for crisp body lines |
| 4 | Midday | crisp hard sunlight, clear sky |
| 5 | Early Afternoon | gentle side-light, clear sky |
| 6 | Late Afternoon | long shadow geometry, clear sky |
| 7 | **Golden Hour** ⭐ | warm low-angle side-light (most popular) |
| 8 | Sunset | soft rim-lit edges, clear sky |
| 9 | Twilight | residual glow, clear sky |
| 10 | Blue Hour | deep clear sky |
| 11 | Night | clear sky with clean highlights |
| 12 | Moonlit Night | clear sky |
| 13 | Early Morning | three-quarter back-light, clear sky |
| 14 | Late Afternoon | subtle back-light along road axis |
| 15 | Evening | car-friendly side-light, clear sky |
| 16 | Dawn | clean low-haze air, clear sky |
### WET Category (17-20): Dramatic Wet Conditions
| ID | Condition | Description |
|----|-----------|-------------|
| 17 | Predawn Blue Hour | wet road, subtle back-light |
| 18 | **Blue Hour Wet** ⭐ | wet road, car-friendly side-light (dramatic) |
| 19 | Twilight Wet | wet road, residual glow |
| 20 | Night Wet | wet street, warm interior lights glowing |
---
## 🔧 Common Workflow Patterns
### Pattern 1: Single Image with Lighting
```
[Text Input] → [Lighting Modifier] → [CLIP Text Encode] → [KSampler]
```
Set modifier_id to 7 for golden hour (most versatile).
### Pattern 2: Batch Variations
```
[Text Input] → [Batch Variation Generator] → [Multiple CLIP Text Encode nodes]
→ [KSamplers for each variation]
```
Connect each var_output to a separate CLIP encoder.
### Pattern 3: Style + Lighting Combo
```
[Text Input] → [Lighting Modifier] → [Style Transform] → [CLIP Text Encode]
```
Apply lighting first, then style for best results.
### Pattern 4: Smart Creative Workflow
```
[Text Input] → [Smart Variation] → [CLIP Text Encode]
```
Use natural language like "make it cyberpunk golden hour".
### Pattern 5: Preset Collection for Comparison
```
[Text Input] → [Preset Collection] → [8 separate render paths]
```
Generate 8 variations optimized for a specific use case.
### Pattern 6: Random Exploration
```
[Text Input] → [Random Modifier] → [CLIP Text Encode]
[Seed Control] ↓
[Display Used ID]
```
Generate different lighting each run, track which modifier was used.
---
## 💡 Usage Tips
### Best Practices
1. **Start with Golden Hour (7)**: Most universally flattering
2. **Wet modifiers (17-20)** for dramatic urban/automotive scenes
3. **Use presets** for quick, professional results
4. **Layer styles carefully**: Apply lighting before styles
5. **Batch testing**: Use Batch Variation Generator for exploration
### Modifier Selection Guide
**For Products**:
- Use modifiers: 3, 4, 7, 11
- Or use "commercial" preset
**For Automotive**:
- Morning/evening: 1, 2, 7, 8
- Dramatic: 18, 20 (wet conditions)
**For Architecture**:
- Full day: 1-16 (all DRY)
- Dramatic: 7, 8, 10, 18
**For Portraits**:
- Flattering: 7, 8, 15
- Dramatic: 10, 18
### Performance Tips
1. **Batch node outputs**: Can connect to batch processing nodes
2. **Seed control**: Use fixed seeds with Random Modifier for reproducibility
3. **Text preview**: Use Show Text nodes to preview before rendering
4. **Save presets**: Save workflow variations for different modifier sets
---
## 📋 Example Workflows
### Example 1: Golden Hour Car Photography
```
[Prompt: "luxury sports car mountain road"]
↓
[Lighting Modifier: modifier_id=7]
↓
[CLIP Text Encode]
↓
[KSampler]
```
**Result**: Car with warm golden hour lighting
### Example 2: Cyberpunk Style Variation
```
[Prompt: "city street night scene"]
↓
[Lighting Modifier: modifier_id=11 (night)]
↓
[Style Transform: style=cyberpunk]
↓
[CLIP Text Encode]
```
**Result**: Night scene with cyberpunk aesthetic
### Example 3: Multi-Variation Comparison
```
[Prompt: "product on pedestal"]
↓
[Preset Collection: preset=commercial]
↓
[var_1] → [CLIP] → [KSampler 1]
[var_2] → [CLIP] → [KSampler 2]
[var_3] → [CLIP] → [KSampler 3]
[var_4] → [CLIP] → [KSampler 4]
```
**Result**: 4 product shots with optimal lighting
### Example 4: Smart Natural Language
```
[Prompt: "architectural exterior"]
↓
[Smart Variation: intent="make it golden hour with dramatic shadows"]
↓
[CLIP Text Encode]
```
**Result**: Automatically applies golden hour + emphasis on shadows
### Example 5: Random Daily Inspiration
```
[Prompt: "creative scene"]
↓
[Random Modifier: category=all, seed=<daily seed>]
↓
[Modifier Info: <connect used_modifier_id>]
↓
[CLIP Text Encode]
```
**Result**: Different lighting each day, see which modifier was chosen
---
## 🔄 Integration with Other Nodes
### With Wildcard Nodes
```
[Wildcard: "__subject__"] → [Lighting Modifier] → [CLIP]
```
Combine dynamic subjects with lighting control.
### With Lora/Checkpoint Switching
```
[Text] → [Batch Variation] → [CLIP per variation]
→ [Different LoRAs per output]
```
Test same prompt with different lighting and models.
### With Conditioning Combine
```
[Base Prompt] → [Lighting Modifier] → [CLIP] ─┐
[Detail Prompt] → [CLIP] ────────────────────┬→ [Conditioning Combine]
```
Layer lighting-modified base with detailed conditioning.
---
## 🎨 Advanced Techniques
### Technique 1: Progressive Lighting
Use multiple Lighting Modifier nodes in sequence to build complex lighting:
```
[Base] → [Modifier 7: golden hour] → [Modifier 19: wet road] → [Result]
```
Creates golden hour with wet reflections.
### Technique 2: Style Layering
```
[Base] → [Style: noir] → [Style: minimalist] → [Multi-layered aesthetic]
```
Use Multi-Style Layer node or chain Style Transform nodes.
### Technique 3: Conditional Lighting
```
┌→ [Modifier 7] → [Day scene]
[Base] → [Switch] ──┤
└→ [Modifier 18] → [Night scene]
```
Use switch nodes to choose lighting based on conditions.
### Technique 4: Batch A/B Testing
```
[Prompt] → [Batch Variation: custom_ids="7,18"]
↓
[var_1: golden hour] → [Render]
[var_2: blue hour wet] → [Render]
```
Compare specific modifiers side-by-side.
---
## 📊 Node Reference Quick Chart
| Node | Input Type | Output Type | Best For |
|------|-----------|-------------|----------|
| Lighting Modifier | Single text | Single text | Precise control |
| Batch Variation | Single text | 10 texts | Exploration |
| Style Transform | Single text | Single text | Aesthetic change |
| Smart Variation | Single text | Single text | Quick natural edits |
| Preset Collection | Single text | 8 texts | Professional sets |
| Modifier Info | Number | 3 texts | Understanding modifiers |
| Prompt Combiner | 4 texts | Single text | Building prompts |
| Random Modifier | Text + seed | Text + number | Randomization |
| Multi-Style | Text + styles | Single text | Complex aesthetics |
---
## 🐛 Troubleshooting
### Issue: Node outputs empty text
**Cause**: Invalid modifier ID or missing inputs
**Solution**: Check modifier_id is 1-20, ensure text input is connected
### Issue: Batch outputs are all the same
**Cause**: Not enough modifiers in selected category
**Solution**: Use "all" category or specify more custom_ids
### Issue: Style not applying
**Cause**: Style name typo or "custom" selected without custom_style input
**Solution**: Double-check style dropdown selection
### Issue: Smart Variation not working as expected
**Cause**: Intent too vague or unrecognized keywords
**Solution**: Use specific terms like "golden hour", "wet", "night", "cyberpunk"
---
## 🎓 Learning Path
1. **Start Simple**: Try Lighting Modifier with modifier_id=7
2. **Explore**: Use Batch Variation with category="all"
3. **Style**: Add Style Transform node after Lighting Modifier
4. **Optimize**: Use Preset Collection for your use case
5. **Advanced**: Combine multiple nodes for complex effects
---
## 💾 Workflow JSON Examples
See the `example_workflows/` folder for:
- `basic_lighting.json` - Simple lighting modifier workflow
- `batch_comparison.json` - Multi-variation comparison
- `style_transform.json` - Style transformation workflow
- `commercial_product.json` - Product photography setup
- `random_exploration.json` - Random modifier with seed control
---
## 🔗 Related Resources
- **Core System Docs**: `../README.md`
- **Installation Guide**: `INSTALLATION.md`
- **Node Reference**: `NODE_REFERENCE.md`
- **Python API**: `../prompt_orchestrator.py`
---
## 📝 Version Info
- **Version**: 1.0.0
- **ComfyUI Compatibility**: Latest version
- **Python**: 3.7+
- **Dependencies**: None (stdlib only)
---
## 🚀 Quick Start Workflow
**Create your first workflow in 3 steps:**
1. **Add nodes**:
- Add Node → Prompt Orchestration → Lighting Modifier (Single)
- Connect your text input
2. **Configure**:
- Set modifier_id: 7 (golden hour)
- Keep preserve_base: True
3. **Connect**:
- Output → CLIP Text Encode → Your image generation pipeline
**Result**: Prompt with professional golden hour lighting!
---
## 📄 License
MIT License - Free for commercial and personal use
---
**Need Help?**
Check `INSTALLATION.md` for setup issues or the core `README.md` for modifier details.
**Ready to use?**
Start with the Lighting Modifier node and modifier #7 (golden hour)! 🌅
+22
View File
@@ -0,0 +1,22 @@
"""
Prompt Orchestration System - ComfyUI Custom Nodes
Integration package for advanced prompt manipulation in ComfyUI workflows
"""
from .prompt_orchestration_nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
# Version info
__version__ = "1.0.0"
__author__ = "Prompt Orchestration System"
__description__ = "Advanced prompt manipulation with 20 lighting modifiers and unlimited style transformations"
# Print loading confirmation
print(f"\n{'='*80}")
print(f"Prompt Orchestration Nodes v{__version__} loaded successfully!")
print(f" - 9 custom nodes available")
print(f" - 20 lighting modifiers (DRY/WET categories)")
print(f" - 10+ built-in style templates")
print(f" - Category: 'Prompt Orchestration'")
print(f"{'='*80}\n")
+311
View File
@@ -0,0 +1,311 @@
"""
Batch Processing Script for Image Prompt Variations
Automates generation of multiple prompt variations from input templates
"""
import json
from pathlib import Path
from typing import List, Dict, Optional
from prompt_orchestrator import PromptOrchestrator, ModifierCategory
class BatchProcessor:
"""Handles batch processing of image prompts with various automation features"""
def __init__(self, output_dir: str = "output"):
"""
Initialize batch processor.
Args:
output_dir: Directory to save output files
"""
self.orchestrator = PromptOrchestrator()
self.output_dir = Path(output_dir)
self.output_dir.mkdir(exist_ok=True)
def process_single_prompt(self, base_prompt: str, config: Dict) -> List[Dict]:
"""
Process a single prompt with specified configuration.
Args:
base_prompt: The base prompt to vary
config: Configuration dict with keys:
- lighting_ids: List of modifier IDs (or "all", "dry", "wet")
- styles: List of style names to apply
- export_format: Output format (txt, json, csv)
Returns:
List of variation dictionaries
"""
# Determine which lighting modifiers to use
if config.get("lighting_ids") == "all":
modifier_ids = list(range(1, 21))
elif config.get("lighting_ids") == "dry":
dry_mods = self.orchestrator.get_modifiers_by_category(ModifierCategory.DRY)
modifier_ids = [m.id for m in dry_mods]
elif config.get("lighting_ids") == "wet":
wet_mods = self.orchestrator.get_modifiers_by_category(ModifierCategory.WET)
modifier_ids = [m.id for m in wet_mods]
else:
modifier_ids = config.get("lighting_ids", [])
# Generate variations
variations = self.orchestrator.generate_variation_batch(
base_prompt=base_prompt,
modifier_ids=modifier_ids if modifier_ids else None,
include_styles=config.get("styles", None)
)
return variations
def process_multiple_prompts(self, prompts: List[str], config: Dict,
output_filename: str = "batch_output") -> str:
"""
Process multiple base prompts and save results.
Args:
prompts: List of base prompts
config: Configuration dictionary
output_filename: Name for output file (without extension)
Returns:
Path to output file
"""
all_results = []
for idx, prompt in enumerate(prompts, 1):
variations = self.process_single_prompt(prompt, config)
all_results.append({
"base_prompt_id": idx,
"base_prompt": prompt,
"variations": variations
})
# Export results
export_format = config.get("export_format", "json")
output_path = self.output_dir / f"{output_filename}.{export_format}"
if export_format == "json":
with open(output_path, "w", encoding="utf-8") as f:
json.dump(all_results, f, indent=2)
elif export_format == "txt":
with open(output_path, "w", encoding="utf-8") as f:
for result in all_results:
f.write(f"\n{'=' * 80}\n")
f.write(f"BASE PROMPT #{result['base_prompt_id']}: {result['base_prompt']}\n")
f.write(f"{'=' * 80}\n\n")
for var in result['variations']:
f.write(f"[{var['id']}] {var['type'].upper()} - {var['category']}\n")
f.write(f" Description: {var['description']}\n")
f.write(f" Prompt: {var['prompt']}\n\n")
elif export_format == "csv":
with open(output_path, "w", encoding="utf-8") as f:
f.write("base_prompt_id,base_prompt,variation_id,type,category,description,prompt\n")
for result in all_results:
for var in result['variations']:
f.write(f"{result['base_prompt_id']},\"{result['base_prompt']}\",")
f.write(f"{var['id']},{var['type']},{var['category']},")
f.write(f"\"{var['description']}\",\"{var['prompt']}\"\n")
return str(output_path)
def create_preset_collection(self, base_prompt: str, preset: str) -> List[Dict]:
"""
Apply a preset collection of variations.
Presets:
- "full_day_cycle": All DRY modifiers (complete day cycle)
- "wet_night": All WET modifiers (dramatic night scenes)
- "key_moments": Selected dramatic lighting moments
- "commercial": Best modifiers for commercial photography
Args:
base_prompt: The base prompt
preset: Preset name
Returns:
List of variations
"""
preset_configs = {
"full_day_cycle": {
"lighting_ids": "dry",
"styles": None
},
"wet_night": {
"lighting_ids": "wet",
"styles": None
},
"key_moments": {
"lighting_ids": [1, 7, 8, 10, 18, 20], # Sunrise, golden hour, sunset, blue hours, wet night
"styles": None
},
"commercial": {
"lighting_ids": [3, 4, 7, 11], # Crisp lighting for product shots
"styles": None
},
"artistic": {
"lighting_ids": [7, 8, 9, 10],
"styles": ["impressionist", "minimalist", "art_deco"]
}
}
if preset not in preset_configs:
raise ValueError(f"Unknown preset: {preset}. Available: {list(preset_configs.keys())}")
return self.process_single_prompt(base_prompt, preset_configs[preset])
def generate_comparison_set(self, base_prompt: str,
modifier_pairs: List[tuple]) -> List[Dict]:
"""
Generate side-by-side comparison prompts.
Args:
base_prompt: Base prompt
modifier_pairs: List of (modifier_id_1, modifier_id_2) tuples
Returns:
List of comparison variation sets
"""
comparisons = []
for mod_a, mod_b in modifier_pairs:
prompt_a = self.orchestrator.apply_lighting_modifier(base_prompt, mod_a)
prompt_b = self.orchestrator.apply_lighting_modifier(base_prompt, mod_b)
comparisons.append({
"comparison_type": f"modifier_{mod_a}_vs_{mod_b}",
"variant_a": {
"modifier_id": mod_a,
"description": self.orchestrator.modifiers[mod_a].description,
"prompt": prompt_a
},
"variant_b": {
"modifier_id": mod_b,
"description": self.orchestrator.modifiers[mod_b].description,
"prompt": prompt_b
}
})
return comparisons
def example_workflows():
"""Demonstrate various workflow scenarios"""
processor = BatchProcessor(output_dir="prompt_variations")
print("BATCH PROCESSING WORKFLOWS\n")
print("=" * 80)
# Workflow 1: Single prompt with all lighting variations
print("\n1. FULL LIGHTING CYCLE GENERATION")
print("-" * 80)
base_prompt_1 = "Luxury sedan in urban environment, architectural background"
config_full = {
"lighting_ids": "all",
"export_format": "json"
}
output_1 = processor.process_multiple_prompts(
prompts=[base_prompt_1],
config=config_full,
output_filename="full_lighting_cycle"
)
print(f"✓ Generated full lighting cycle → {output_1}")
# Workflow 2: Multiple prompts with preset
print("\n2. KEY MOMENTS PRESET (Multiple Prompts)")
print("-" * 80)
base_prompts = [
"Sports car on mountain road, scenic vista",
"Classic convertible on beach, sunset backdrop",
"Electric SUV in modern city, glass buildings"
]
variations = []
for prompt in base_prompts:
vars = processor.create_preset_collection(prompt, "key_moments")
variations.extend(vars)
print(f"✓ Generated {len(variations)} variations across {len(base_prompts)} base prompts")
# Workflow 3: Style transformations
print("\n3. STYLE TRANSFORMATION SET")
print("-" * 80)
base_prompt_3 = "Vintage motorcycle in garage, mechanical details"
config_styles = {
"lighting_ids": [7], # Golden hour only
"styles": ["cyberpunk", "art_deco", "minimalist", "noir"],
"export_format": "txt"
}
output_3 = processor.process_multiple_prompts(
prompts=[base_prompt_3],
config=config_styles,
output_filename="style_variations"
)
print(f"✓ Generated style variations → {output_3}")
# Workflow 4: Comparison set
print("\n4. COMPARISON SET GENERATION")
print("-" * 80)
base_prompt_4 = "Modern architecture, geometric forms"
comparison_pairs = [
(4, 7), # Midday vs Golden Hour
(11, 18), # Night dry vs Night wet
(1, 8) # Sunrise vs Sunset
]
comparisons = processor.generate_comparison_set(base_prompt_4, comparison_pairs)
# Save comparisons
output_4 = processor.output_dir / "comparisons.json"
with open(output_4, "w", encoding="utf-8") as f:
json.dump(comparisons, f, indent=2)
print(f"✓ Generated {len(comparisons)} comparison sets → {output_4}")
# Workflow 5: Commercial photography set
print("\n5. COMMERCIAL PHOTOGRAPHY PRESET")
print("-" * 80)
base_prompt_5 = "Product shot of smartwatch, clean background"
commercial_vars = processor.create_preset_collection(base_prompt_5, "commercial")
print(f"✓ Generated {len(commercial_vars)} commercial-grade variations")
print("\n" + "=" * 80)
print("All workflows completed successfully!")
print(f"Output directory: {processor.output_dir}")
def quick_generation_example():
"""Quick example for immediate use"""
orchestrator = PromptOrchestrator()
# Your base prompt
base = "Ferrari on coastal highway at sunset"
print("\nQUICK GENERATION EXAMPLES")
print("=" * 80)
# Generate 5 quick variations
variations = [
orchestrator.apply_lighting_modifier(base, 1), # Sunrise
orchestrator.apply_lighting_modifier(base, 7), # Golden hour
orchestrator.apply_lighting_modifier(base, 10), # Blue hour
orchestrator.apply_lighting_modifier(base, 18), # Blue hour wet
orchestrator.apply_style_transformation(base, "cyberpunk")
]
for i, var in enumerate(variations, 1):
print(f"\n{i}. {var}")
if __name__ == "__main__":
# Run example workflows
example_workflows()
# Quick generation
quick_generation_example()
+520
View File
@@ -0,0 +1,520 @@
"""
ComfyUI Custom Nodes - Image Prompt Orchestration System
Integrates the 20-modifier prompt orchestration system into ComfyUI workflows
"""
import sys
import os
from pathlib import Path
# Add parent directory to path to import orchestrator
current_dir = Path(__file__).parent
sys.path.insert(0, str(current_dir))
try:
from prompt_orchestrator import PromptOrchestrator, ModifierCategory
from batch_processor import BatchProcessor
except ImportError:
print("ERROR: Could not import prompt_orchestrator. Make sure it's in the same directory.")
raise
class PromptLightingModifierNode:
"""
Apply a single lighting modifier to a text prompt.
Choose from 20 pre-defined lighting conditions.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"modifier_id": ("INT", {
"default": 7,
"min": 1,
"max": 20,
"step": 1,
"display": "number"
}),
"preserve_base": ("BOOLEAN", {
"default": True
})
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("modified_prompt",)
FUNCTION = "apply_modifier"
CATEGORY = "Prompt Orchestration"
def __init__(self):
self.orchestrator = PromptOrchestrator()
def apply_modifier(self, text, modifier_id, preserve_base):
"""Apply lighting modifier to the input text"""
result = self.orchestrator.apply_lighting_modifier(
text,
modifier_id,
preserve_base=preserve_base
)
return (result,)
class PromptBatchVariationNode:
"""
Generate multiple prompt variations from a base prompt.
Outputs multiple strings for batch processing.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"category": (["all", "dry", "wet", "custom"],),
},
"optional": {
"custom_ids": ("STRING", {
"default": "1,7,18",
"multiline": False
})
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", "STRING",
"STRING", "STRING", "STRING", "STRING")
RETURN_NAMES = ("var_1", "var_2", "var_3", "var_4", "var_5",
"var_6", "var_7", "var_8", "var_9", "var_10")
FUNCTION = "generate_variations"
CATEGORY = "Prompt Orchestration"
OUTPUT_IS_LIST = (True, True, True, True, True, True, True, True, True, True)
def __init__(self):
self.orchestrator = PromptOrchestrator()
def generate_variations(self, text, category, custom_ids=""):
"""Generate multiple variations based on category"""
# Determine modifier IDs
if category == "all":
modifier_ids = list(range(1, 21))
elif category == "dry":
dry_mods = self.orchestrator.get_modifiers_by_category(ModifierCategory.DRY)
modifier_ids = [m.id for m in dry_mods]
elif category == "wet":
wet_mods = self.orchestrator.get_modifiers_by_category(ModifierCategory.WET)
modifier_ids = [m.id for m in wet_mods]
else: # custom
if custom_ids:
modifier_ids = [int(x.strip()) for x in custom_ids.split(",") if x.strip()]
else:
modifier_ids = [7] # Default to golden hour
# Generate variations
variations = self.orchestrator.generate_variation_batch(
text,
modifier_ids=modifier_ids
)
# Extract prompts
prompts = [v['prompt'] for v in variations]
# Pad to 10 outputs (ComfyUI requirement for fixed outputs)
while len(prompts) < 10:
prompts.append(text) # Use original if not enough variations
# Return as lists (OUTPUT_IS_LIST=True)
return tuple([prompts[i:i+1] for i in range(10)])
class PromptStyleTransformNode:
"""
Apply a style transformation to a prompt.
Choose from built-in styles or provide custom description.
"""
@classmethod
def INPUT_TYPES(cls):
styles = [
"custom",
"pop_art_1960s",
"bauhaus",
"art_deco",
"cyberpunk",
"minimalist",
"impressionist",
"noir",
"futuristic",
"vintage",
"brutalist"
]
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"style": (styles,),
"maintain_composition": ("BOOLEAN", {
"default": True
})
},
"optional": {
"custom_style": ("STRING", {
"default": "",
"multiline": False
})
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("styled_prompt",)
FUNCTION = "apply_style"
CATEGORY = "Prompt Orchestration"
def __init__(self):
self.orchestrator = PromptOrchestrator()
def apply_style(self, text, style, maintain_composition, custom_style=""):
"""Apply style transformation"""
if style == "custom" and custom_style:
style_to_use = custom_style
else:
style_to_use = style
result = self.orchestrator.apply_style_transformation(
text,
style_to_use,
maintain_composition=maintain_composition
)
return (result,)
class PromptSmartVariationNode:
"""
Smart variation using natural language intent.
Example: "make it golden hour" or "add cyberpunk style"
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"intent": ("STRING", {
"multiline": False,
"default": "make it golden hour"
})
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("smart_prompt",)
FUNCTION = "smart_vary"
CATEGORY = "Prompt Orchestration"
def __init__(self):
self.orchestrator = PromptOrchestrator()
def smart_vary(self, text, intent):
"""Apply smart variation based on natural language intent"""
result = self.orchestrator.smart_variation(text, intent)
return (result,)
class PromptPresetCollectionNode:
"""
Apply a preset collection of modifiers.
Optimized sets for common use cases.
"""
@classmethod
def INPUT_TYPES(cls):
presets = [
"key_moments",
"commercial",
"artistic",
"full_day_cycle",
"wet_night"
]
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"preset": (presets,)
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING",
"STRING", "STRING", "STRING")
RETURN_NAMES = ("var_1", "var_2", "var_3", "var_4",
"var_5", "var_6", "var_7", "var_8")
FUNCTION = "apply_preset"
CATEGORY = "Prompt Orchestration"
OUTPUT_IS_LIST = (True, True, True, True, True, True, True, True)
def __init__(self):
self.processor = BatchProcessor()
def apply_preset(self, text, preset):
"""Apply preset collection"""
variations = self.processor.create_preset_collection(text, preset)
prompts = [v['prompt'] for v in variations]
# Pad to 8 outputs
while len(prompts) < 8:
prompts.append(text)
# Return as lists
return tuple([prompts[i:i+1] for i in range(8)])
class PromptModifierInfoNode:
"""
Display information about a specific modifier.
Useful for understanding what each modifier does.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"modifier_id": ("INT", {
"default": 7,
"min": 1,
"max": 20,
"step": 1,
"display": "number"
})
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING")
RETURN_NAMES = ("description", "category", "tags")
FUNCTION = "get_info"
CATEGORY = "Prompt Orchestration"
def __init__(self):
self.orchestrator = PromptOrchestrator()
def get_info(self, modifier_id):
"""Get information about a modifier"""
if modifier_id in self.orchestrator.modifiers:
modifier = self.orchestrator.modifiers[modifier_id]
description = modifier.description
category = modifier.category.value
tags = ", ".join(modifier.tags)
else:
description = "Invalid modifier ID"
category = "unknown"
tags = ""
return (description, category, tags)
class PromptCombinerNode:
"""
Combine multiple text inputs with optional separator.
Useful for building complex prompts from parts.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text_1": ("STRING", {
"multiline": True,
"default": ""
})
},
"optional": {
"text_2": ("STRING", {
"multiline": True,
"default": ""
}),
"text_3": ("STRING", {
"multiline": True,
"default": ""
}),
"text_4": ("STRING", {
"multiline": True,
"default": ""
}),
"separator": ("STRING", {
"default": ", ",
"multiline": False
})
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("combined_text",)
FUNCTION = "combine"
CATEGORY = "Prompt Orchestration"
def combine(self, text_1, text_2="", text_3="", text_4="", separator=", "):
"""Combine multiple text inputs"""
texts = [t for t in [text_1, text_2, text_3, text_4] if t]
result = separator.join(texts)
return (result,)
class PromptRandomModifierNode:
"""
Apply a random modifier from a category.
Useful for generating diverse variations.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"category": (["all", "dry", "wet"],),
"seed": ("INT", {
"default": 0,
"min": 0,
"max": 0xffffffffffffffff
})
}
}
RETURN_TYPES = ("STRING", "INT")
RETURN_NAMES = ("modified_prompt", "used_modifier_id")
FUNCTION = "apply_random"
CATEGORY = "Prompt Orchestration"
def __init__(self):
self.orchestrator = PromptOrchestrator()
def apply_random(self, text, category, seed):
"""Apply a random modifier from the selected category"""
import random
random.seed(seed)
# Get modifiers from category
if category == "all":
modifier_ids = list(range(1, 21))
elif category == "dry":
dry_mods = self.orchestrator.get_modifiers_by_category(ModifierCategory.DRY)
modifier_ids = [m.id for m in dry_mods]
else: # wet
wet_mods = self.orchestrator.get_modifiers_by_category(ModifierCategory.WET)
modifier_ids = [m.id for m in wet_mods]
# Select random modifier
selected_id = random.choice(modifier_ids)
# Apply it
result = self.orchestrator.apply_lighting_modifier(text, selected_id)
return (result, selected_id)
class PromptMultiStyleNode:
"""
Apply multiple styles sequentially.
Creates layered style transformations.
"""
@classmethod
def INPUT_TYPES(cls):
styles = [
"none",
"pop_art_1960s",
"bauhaus",
"art_deco",
"cyberpunk",
"minimalist",
"impressionist",
"noir",
"futuristic",
"vintage",
"brutalist"
]
return {
"required": {
"text": ("STRING", {
"multiline": True,
"default": "Subject description here"
}),
"style_1": (styles,),
},
"optional": {
"style_2": (styles,),
"style_3": (styles,),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("multi_styled_prompt",)
FUNCTION = "apply_multi_style"
CATEGORY = "Prompt Orchestration"
def __init__(self):
self.orchestrator = PromptOrchestrator()
def apply_multi_style(self, text, style_1, style_2="none", style_3="none"):
"""Apply multiple styles sequentially"""
result = text
for style in [style_1, style_2, style_3]:
if style != "none":
result = self.orchestrator.apply_style_transformation(
result,
style,
maintain_composition=False
)
return (result,)
# Node class mappings for ComfyUI
NODE_CLASS_MAPPINGS = {
"PromptLightingModifier": PromptLightingModifierNode,
"PromptBatchVariation": PromptBatchVariationNode,
"PromptStyleTransform": PromptStyleTransformNode,
"PromptSmartVariation": PromptSmartVariationNode,
"PromptPresetCollection": PromptPresetCollectionNode,
"PromptModifierInfo": PromptModifierInfoNode,
"PromptCombiner": PromptCombinerNode,
"PromptRandomModifier": PromptRandomModifierNode,
"PromptMultiStyle": PromptMultiStyleNode,
}
# Display name mappings
NODE_DISPLAY_NAME_MAPPINGS = {
"PromptLightingModifier": "Lighting Modifier (Single)",
"PromptBatchVariation": "Batch Variation Generator",
"PromptStyleTransform": "Style Transform",
"PromptSmartVariation": "Smart Variation (NLP)",
"PromptPresetCollection": "Preset Collection",
"PromptModifierInfo": "Modifier Info",
"PromptCombiner": "Prompt Combiner",
"PromptRandomModifier": "Random Modifier",
"PromptMultiStyle": "Multi-Style Layer",
}
+377
View File
@@ -0,0 +1,377 @@
"""
Image Prompt Orchestration System
Automated text input manipulation for image generation variations
"""
from typing import List, Dict, Optional, Callable
from dataclasses import dataclass
from enum import Enum
class ModifierCategory(Enum):
"""Categories for lighting/environment modifiers"""
DRY = "dry"
WET = "wet"
@dataclass
class LightingModifier:
"""Represents a lighting/time-of-day modifier"""
id: int
description: str
category: ModifierCategory
tags: List[str]
class PromptOrchestrator:
"""
Main orchestration class for image prompt variation automation.
Handles text input manipulation and modifier application.
"""
# 20 plug-and-play USER_MODIFIERS (anchor-agnostic)
MODIFIERS = [
# DRY (universal, no location assumptions)
LightingModifier(1, "sunrise, car-friendly side-light, clear sky", ModifierCategory.DRY,
["sunrise", "morning", "side-light", "clear"]),
LightingModifier(2, "early morning, subtle back-light along the road axis, clear sky", ModifierCategory.DRY,
["early-morning", "back-light", "clear"]),
LightingModifier(3, "late morning, high-sun hard light for crisp body lines, clear sky", ModifierCategory.DRY,
["late-morning", "hard-light", "crisp", "clear"]),
LightingModifier(4, "midday, crisp hard sunlight, clear sky", ModifierCategory.DRY,
["midday", "hard-light", "clear"]),
LightingModifier(5, "early afternoon, gentle side-light, clear sky", ModifierCategory.DRY,
["afternoon", "side-light", "clear"]),
LightingModifier(6, "late afternoon, long shadow geometry, clear sky", ModifierCategory.DRY,
["afternoon", "shadows", "clear"]),
LightingModifier(7, "golden hour, warm low-angle side-light, clear sky", ModifierCategory.DRY,
["golden-hour", "warm", "side-light", "clear"]),
LightingModifier(8, "sunset, soft rim-lit edges, clear sky", ModifierCategory.DRY,
["sunset", "rim-light", "clear"]),
LightingModifier(9, "twilight, residual glow, clear sky", ModifierCategory.DRY,
["twilight", "glow", "clear"]),
LightingModifier(10, "blue hour, deep clear sky", ModifierCategory.DRY,
["blue-hour", "clear"]),
LightingModifier(11, "night, clear sky with clean highlights", ModifierCategory.DRY,
["night", "clear", "highlights"]),
LightingModifier(12, "moonlit night, clear sky", ModifierCategory.DRY,
["night", "moonlit", "clear"]),
LightingModifier(13, "early morning, three-quarter back-light, clear sky", ModifierCategory.DRY,
["morning", "back-light", "clear"]),
LightingModifier(14, "late afternoon, subtle back-light along the road axis, clear sky", ModifierCategory.DRY,
["afternoon", "back-light", "clear"]),
LightingModifier(15, "evening, car-friendly side-light, clear sky", ModifierCategory.DRY,
["evening", "side-light", "clear"]),
LightingModifier(16, "dawn, clean low-haze air, clear sky", ModifierCategory.DRY,
["dawn", "haze", "clear"]),
# WET (low-light only; triggers wet mode)
LightingModifier(17, "predawn blue hour, wet road, subtle back-light along the road axis", ModifierCategory.WET,
["predawn", "blue-hour", "wet", "back-light"]),
LightingModifier(18, "blue hour, wet road, car-friendly side-light", ModifierCategory.WET,
["blue-hour", "wet", "side-light"]),
LightingModifier(19, "twilight, wet road, residual glow, clear sky", ModifierCategory.WET,
["twilight", "wet", "glow"]),
LightingModifier(20, "night, wet street, clear sky (buildings visible → warm interior lights glowing)", ModifierCategory.WET,
["night", "wet", "interior-lights"]),
]
def __init__(self):
"""Initialize the orchestrator with default modifiers"""
self.modifiers = {m.id: m for m in self.MODIFIERS}
self.custom_style_templates = self._load_style_templates()
def _load_style_templates(self) -> Dict[str, str]:
"""Load predefined style transformation templates"""
return {
"pop_art_1960s": "1960s pop art style with bright colors, bold graphics, and commercial aesthetics",
"bauhaus": "Bauhaus style with geometric forms, primary colors, and modernist principles",
"art_deco": "Art Deco style with geometric patterns, luxurious details, and streamlined forms",
"cyberpunk": "cyberpunk aesthetic with neon lights, high-tech elements, and dystopian atmosphere",
"minimalist": "minimalist style with clean lines, negative space, and essential elements only",
"impressionist": "impressionist painting style with visible brushstrokes and emphasis on light",
"noir": "film noir style with dramatic shadows, high contrast, and moody atmosphere",
"futuristic": "futuristic design with sleek forms, advanced materials, and sci-fi elements",
"vintage": "vintage aesthetic with retro colors, grain, and nostalgic atmosphere",
"brutalist": "brutalist architecture style with raw concrete, geometric forms, and bold structure",
}
def apply_lighting_modifier(self, base_prompt: str, modifier_id: int,
preserve_base: bool = True) -> str:
"""
Apply a lighting modifier to a base prompt.
Args:
base_prompt: The original prompt text
modifier_id: ID of the modifier to apply (1-20)
preserve_base: If True, append modifier; if False, replace lighting info
Returns:
Modified prompt string
"""
if modifier_id not in self.modifiers:
raise ValueError(f"Invalid modifier_id: {modifier_id}. Must be 1-20.")
modifier = self.modifiers[modifier_id]
if preserve_base:
# Append the modifier to maintain original composition
return f"{base_prompt}, {modifier.description}"
else:
# Replace lighting-related content
cleaned_prompt = self._strip_lighting_keywords(base_prompt)
return f"{cleaned_prompt}, {modifier.description}"
def _strip_lighting_keywords(self, prompt: str) -> str:
"""Remove common lighting/time-of-day keywords from prompt"""
lighting_keywords = [
"sunrise", "sunset", "morning", "afternoon", "evening", "night", "twilight",
"blue hour", "golden hour", "dawn", "dusk", "midday", "noon",
"clear sky", "cloudy", "overcast", "back-light", "side-light", "rim-light",
"wet road", "wet street", "dry", "moonlit", "sunlit"
]
cleaned = prompt
for keyword in lighting_keywords:
# Case-insensitive removal
cleaned = cleaned.replace(keyword, "")
cleaned = cleaned.replace(keyword.title(), "")
cleaned = cleaned.replace(keyword.upper(), "")
# Clean up extra commas and spaces
cleaned = " ".join(cleaned.split())
cleaned = cleaned.replace(" ,", ",").replace(",,", ",")
cleaned = cleaned.strip(", ")
return cleaned
def apply_style_transformation(self, base_prompt: str, style: str,
maintain_composition: bool = True) -> str:
"""
Apply a style transformation to a prompt.
Args:
base_prompt: The original prompt text
style: Style name (from templates) or custom style description
maintain_composition: Whether to explicitly state composition preservation
Returns:
Style-transformed prompt string
"""
# Check if it's a predefined style
if style.lower().replace(" ", "_") in self.custom_style_templates:
style_desc = self.custom_style_templates[style.lower().replace(" ", "_")]
else:
style_desc = style
if maintain_composition:
return f"Transform to {style_desc} while maintaining the original composition. Base scene: {base_prompt}"
else:
return f"{base_prompt}, {style_desc}"
def generate_variation_batch(self, base_prompt: str,
modifier_ids: Optional[List[int]] = None,
include_styles: Optional[List[str]] = None) -> List[Dict[str, str]]:
"""
Generate a batch of prompt variations.
Args:
base_prompt: The original prompt text
modifier_ids: List of modifier IDs to apply (None = all 20)
include_styles: List of style transformations to include (None = none)
Returns:
List of dictionaries with 'id', 'type', 'description', and 'prompt' keys
"""
variations = []
# Generate lighting variations
if modifier_ids is None:
modifier_ids = list(range(1, 21))
for mod_id in modifier_ids:
modifier = self.modifiers[mod_id]
prompt = self.apply_lighting_modifier(base_prompt, mod_id)
variations.append({
"id": f"lighting_{mod_id}",
"type": "lighting",
"category": modifier.category.value,
"description": modifier.description,
"prompt": prompt
})
# Generate style variations
if include_styles:
for idx, style in enumerate(include_styles, 1):
prompt = self.apply_style_transformation(base_prompt, style)
variations.append({
"id": f"style_{idx}",
"type": "style",
"category": "transformation",
"description": style,
"prompt": prompt
})
return variations
def get_modifiers_by_category(self, category: ModifierCategory) -> List[LightingModifier]:
"""Get all modifiers of a specific category (DRY/WET)"""
return [m for m in self.modifiers.values() if m.category == category]
def get_modifiers_by_tags(self, tags: List[str]) -> List[LightingModifier]:
"""Get modifiers that match any of the specified tags"""
return [m for m in self.modifiers.values()
if any(tag in m.tags for tag in tags)]
def create_custom_pipeline(self, base_prompt: str,
transformations: List[Callable]) -> str:
"""
Apply a custom sequence of transformations to a prompt.
Args:
base_prompt: The original prompt text
transformations: List of transformation functions to apply in sequence
Returns:
Final transformed prompt string
"""
result = base_prompt
for transform in transformations:
result = transform(result)
return result
def export_variations(self, variations: List[Dict[str, str]],
format: str = "txt") -> str:
"""
Export variations to a formatted string.
Args:
variations: List of variation dictionaries
format: Export format ('txt', 'json', 'csv')
Returns:
Formatted string representation
"""
if format == "json":
import json
return json.dumps(variations, indent=2)
elif format == "csv":
lines = ["id,type,category,description,prompt"]
for v in variations:
# Escape commas in fields
line = f"{v['id']},{v['type']},{v['category']},\"{v['description']}\",\"{v['prompt']}\""
lines.append(line)
return "\n".join(lines)
else: # txt format
lines = []
for v in variations:
lines.append(f"[{v['id']}] {v['type'].upper()} - {v['category']}")
lines.append(f"Description: {v['description']}")
lines.append(f"Prompt: {v['prompt']}")
lines.append("-" * 80)
return "\n".join(lines)
def smart_variation(self, base_prompt: str, intent: str) -> str:
"""
Apply intelligent variation based on natural language intent.
Args:
base_prompt: The original prompt text
intent: Natural language description of desired variation
Returns:
Modified prompt string
"""
intent_lower = intent.lower()
# Detect time-of-day intents
time_mappings = {
"morning": [1, 2, 3],
"sunrise": [1],
"afternoon": [5, 6, 14],
"golden hour": [7],
"sunset": [8],
"evening": [15],
"night": [11, 12],
"twilight": [9],
"blue hour": [10, 17, 18],
"wet": [17, 18, 19, 20]
}
for keyword, mod_ids in time_mappings.items():
if keyword in intent_lower:
# Use first matching modifier
return self.apply_lighting_modifier(base_prompt, mod_ids[0])
# Detect style intents
if "style" in intent_lower or "aesthetic" in intent_lower:
for style_name in self.custom_style_templates.keys():
if style_name.replace("_", " ") in intent_lower:
return self.apply_style_transformation(base_prompt, style_name)
# Default: treat as custom style description
return self.apply_style_transformation(base_prompt, intent)
def demo_usage():
"""Demonstration of the orchestrator capabilities"""
orchestrator = PromptOrchestrator()
# Base prompt example
base_prompt = "Modern sports car on coastal highway, dramatic ocean backdrop"
print("=" * 80)
print("IMAGE PROMPT ORCHESTRATION SYSTEM - DEMO")
print("=" * 80)
print(f"\nBase Prompt: {base_prompt}\n")
# Example 1: Apply single lighting modifier
print("EXAMPLE 1: Applying Golden Hour Lighting (Modifier #7)")
print("-" * 80)
golden_hour = orchestrator.apply_lighting_modifier(base_prompt, 7)
print(golden_hour)
print()
# Example 2: Apply wet condition
print("EXAMPLE 2: Applying Blue Hour Wet Road (Modifier #18)")
print("-" * 80)
wet_night = orchestrator.apply_lighting_modifier(base_prompt, 18)
print(wet_night)
print()
# Example 3: Style transformation
print("EXAMPLE 3: Style Transformation - Cyberpunk")
print("-" * 80)
cyberpunk = orchestrator.apply_style_transformation(base_prompt, "cyberpunk")
print(cyberpunk)
print()
# Example 4: Generate batch variations
print("EXAMPLE 4: Batch Generation (First 5 Modifiers)")
print("-" * 80)
batch = orchestrator.generate_variation_batch(base_prompt, modifier_ids=[1, 2, 3, 4, 5])
for variation in batch:
print(f"[{variation['id']}] {variation['description']}")
print(f" → {variation['prompt'][:100]}...")
print()
# Example 5: Smart variation with natural language
print("EXAMPLE 5: Smart Variation with Natural Language")
print("-" * 80)
smart_result = orchestrator.smart_variation(base_prompt, "make it golden hour with warm lighting")
print(smart_result)
print()
# Example 6: Get modifiers by category
print("EXAMPLE 6: All WET Category Modifiers")
print("-" * 80)
wet_mods = orchestrator.get_modifiers_by_category(ModifierCategory.WET)
for mod in wet_mods:
print(f"#{mod.id}: {mod.description}")
print()
if __name__ == "__main__":
demo_usage()
+324
View File
@@ -0,0 +1,324 @@
#!/usr/bin/env python3
"""
Test Suite for ComfyUI Prompt Orchestration Nodes
Validates that all nodes work correctly before deployment
"""
import sys
from pathlib import Path
# Add current directory to path
sys.path.insert(0, str(Path(__file__).parent))
from prompt_orchestration_nodes import (
PromptLightingModifierNode,
PromptBatchVariationNode,
PromptStyleTransformNode,
PromptSmartVariationNode,
PromptPresetCollectionNode,
PromptModifierInfoNode,
PromptCombinerNode,
PromptRandomModifierNode,
PromptMultiStyleNode
)
def test_lighting_modifier():
"""Test single lighting modifier node"""
print("\n" + "="*80)
print("TEST 1: Lighting Modifier Node")
print("="*80)
node = PromptLightingModifierNode()
# Test with golden hour
result = node.apply_modifier(
text="Sports car on highway",
modifier_id=7,
preserve_base=True
)
print(f"Input: 'Sports car on highway'")
print(f"Modifier: 7 (golden hour)")
print(f"Output: {result[0]}")
# Verify output contains expected elements
assert "Sports car on highway" in result[0], "Base text missing"
assert "golden hour" in result[0], "Modifier text missing"
print("✅ PASSED")
return True
def test_batch_variation():
"""Test batch variation generator node"""
print("\n" + "="*80)
print("TEST 2: Batch Variation Generator")
print("="*80)
node = PromptBatchVariationNode()
# Test with custom IDs
results = node.generate_variations(
text="Modern building",
category="custom",
custom_ids="1,7,18"
)
print(f"Input: 'Modern building'")
print(f"Category: custom")
print(f"Custom IDs: 1,7,18")
print(f"Generated {len(results)} outputs")
# Check first few variations
for i in range(min(3, len(results))):
print(f" var_{i+1}: {results[i][0][:80]}...")
assert len(results) == 10, "Should return 10 outputs"
print("✅ PASSED")
return True
def test_style_transform():
"""Test style transformation node"""
print("\n" + "="*80)
print("TEST 3: Style Transform Node")
print("="*80)
node = PromptStyleTransformNode()
result = node.apply_style(
text="Vintage car in garage",
style="cyberpunk",
maintain_composition=True
)
print(f"Input: 'Vintage car in garage'")
print(f"Style: cyberpunk")
print(f"Output: {result[0][:100]}...")
assert "cyberpunk" in result[0].lower(), "Style not applied"
print("✅ PASSED")
return True
def test_smart_variation():
"""Test smart variation with natural language"""
print("\n" + "="*80)
print("TEST 4: Smart Variation Node")
print("="*80)
node = PromptSmartVariationNode()
result = node.smart_vary(
text="Car on street",
intent="make it golden hour"
)
print(f"Input: 'Car on street'")
print(f"Intent: 'make it golden hour'")
print(f"Output: {result[0]}")
assert "golden hour" in result[0].lower() or "warm" in result[0].lower(), "Intent not recognized"
print("✅ PASSED")
return True
def test_preset_collection():
"""Test preset collection node"""
print("\n" + "="*80)
print("TEST 5: Preset Collection Node")
print("="*80)
node = PromptPresetCollectionNode()
results = node.apply_preset(
text="Product on pedestal",
preset="commercial"
)
print(f"Input: 'Product on pedestal'")
print(f"Preset: commercial")
print(f"Generated {len(results)} outputs")
for i in range(min(4, len(results))):
print(f" var_{i+1}: {results[i][0][:60]}...")
assert len(results) == 8, "Should return 8 outputs"
print("✅ PASSED")
return True
def test_modifier_info():
"""Test modifier info node"""
print("\n" + "="*80)
print("TEST 6: Modifier Info Node")
print("="*80)
node = PromptModifierInfoNode()
desc, category, tags = node.get_info(modifier_id=7)
print(f"Modifier ID: 7")
print(f"Description: {desc}")
print(f"Category: {category}")
print(f"Tags: {tags}")
assert "golden hour" in desc.lower(), "Wrong modifier info"
assert category in ["dry", "wet"], "Invalid category"
print("✅ PASSED")
return True
def test_combiner():
"""Test prompt combiner node"""
print("\n" + "="*80)
print("TEST 7: Prompt Combiner Node")
print("="*80)
node = PromptCombinerNode()
result = node.combine(
text_1="Sports car",
text_2="on mountain road",
text_3="golden hour",
separator=", "
)
print(f"Input 1: 'Sports car'")
print(f"Input 2: 'on mountain road'")
print(f"Input 3: 'golden hour'")
print(f"Output: {result[0]}")
assert "Sports car" in result[0], "Missing text_1"
assert "mountain road" in result[0], "Missing text_2"
assert "golden hour" in result[0], "Missing text_3"
print("✅ PASSED")
return True
def test_random_modifier():
"""Test random modifier node"""
print("\n" + "="*80)
print("TEST 8: Random Modifier Node")
print("="*80)
node = PromptRandomModifierNode()
result, modifier_id = node.apply_random(
text="Architecture scene",
category="dry",
seed=42
)
print(f"Input: 'Architecture scene'")
print(f"Category: dry")
print(f"Seed: 42")
print(f"Selected Modifier: {modifier_id}")
print(f"Output: {result[:80]}...")
assert 1 <= modifier_id <= 16, "Invalid modifier ID for DRY category"
# Test reproducibility
result2, modifier_id2 = node.apply_random(
text="Architecture scene",
category="dry",
seed=42
)
assert modifier_id == modifier_id2, "Same seed should give same modifier"
print("✅ PASSED (reproducible)")
return True
def test_multi_style():
"""Test multi-style layer node"""
print("\n" + "="*80)
print("TEST 9: Multi-Style Layer Node")
print("="*80)
node = PromptMultiStyleNode()
result = node.apply_multi_style(
text="Urban scene",
style_1="cyberpunk",
style_2="noir",
style_3="none"
)
print(f"Input: 'Urban scene'")
print(f"Style 1: cyberpunk")
print(f"Style 2: noir")
print(f"Output: {result[0][:100]}...")
# Should contain elements of both styles
assert len(result[0]) > len("Urban scene"), "Styles not applied"
print("✅ PASSED")
return True
def run_all_tests():
"""Run all tests and report results"""
print("\n" + "█"*80)
print("COMFYUI PROMPT ORCHESTRATION NODES - TEST SUITE")
print("█"*80)
tests = [
("Lighting Modifier", test_lighting_modifier),
("Batch Variation", test_batch_variation),
("Style Transform", test_style_transform),
("Smart Variation", test_smart_variation),
("Preset Collection", test_preset_collection),
("Modifier Info", test_modifier_info),
("Prompt Combiner", test_combiner),
("Random Modifier", test_random_modifier),
("Multi-Style Layer", test_multi_style)
]
results = []
for test_name, test_func in tests:
try:
success = test_func()
results.append((test_name, True, None))
except Exception as e:
print(f"\n❌ FAILED: {e}")
results.append((test_name, False, str(e)))
# Summary
print("\n" + "█"*80)
print("TEST SUMMARY")
print("█"*80)
passed = sum(1 for _, success, _ in results if success)
total = len(results)
for test_name, success, error in results:
status = "✅ PASS" if success else "❌ FAIL"
print(f"{status} - {test_name}")
if error:
print(f" Error: {error}")
print("\n" + "="*80)
print(f"Results: {passed}/{total} tests passed")
print("="*80)
if passed == total:
print("\n🎉 ALL TESTS PASSED! Nodes are ready for ComfyUI.")
return True
else:
print(f"\n⚠️ {total - passed} test(s) failed. Please review errors above.")
return False
if __name__ == "__main__":
success = run_all_tests()
sys.exit(0 if success else 1)