Add files via upload
This commit is contained in:
+213
@@ -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.
|
||||
@@ -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
|
||||
@@ -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._
|
||||
@@ -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
|
||||
@@ -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
@@ -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")
|
||||
@@ -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()
|
||||
@@ -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",
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
Reference in New Issue
Block a user