Prompt Builder Documentation
Simplified GenericPromptBuilder Architecture
All category prompt builders use the unified GenericPromptBuilder class with automatic variable resolution and clean file structure.
How to Create a New Category Prompt
Step 1: Clear Cache and Read S3 Schema
ALWAYS clear metadata cache first:
docker exec metamock-backend python temp_scripts/clear_metadata_cache.py --force
Read actual S3 schema:
docker exec metamock-backend python config/utils/read_s3_schema.py YOUR_CATEGORY_NAME
This shows exact attribute names, option values, and available backgrounds.
Step 2: Create Category Module Structure
Create simple folder structure:
config/prompts/YOUR_CATEGORY/
├── __init__.py
├── main.py
└── descriptions.py
Step 3: Main Category File (main.py)
Use GenericPromptBuilder with templates:
"""
Category prompt template for image generation
Uses the generic prompt builder with category-specific templates
"""
from config.prompts.generic_prompt_builder import GenericPromptBuilder
def build_prompt(attributes):
"""
Build complete prompt using GenericPromptBuilder
Args:
attributes: Dictionary of selected attributes from S3 schema
Returns:
Complete formatted prompt string
"""
# Define prompt templates with dynamic variables
human_model_prompt = [
"A {demographics} carrying a {size_desc} {color_desc} {type_desc}.",
"The product is made of {material_desc} {handle_desc}.",
"{background_setting}.",
"Product-specific requirements here."
]
product_only_prompt = [
"Commercial product photography of a {size_desc} {color_desc} {type_desc}.",
"Made of {material_desc} {handle_desc}.",
"{background_setting}.",
"Product must be clean and pristine."
]
builder = GenericPromptBuilder("YOUR_CATEGORY", attributes, human_model_prompt, product_only_prompt)
return builder.generate()
Step 4: Descriptions File (descriptions.py)
Single file with all descriptions using if/elif pattern:
"""
All descriptions for YOUR_CATEGORY
Consolidated into a single file for clean imports
"""
def get_description(attribute_name: str, value: str) -> str:
"""Get description for any attribute"""
# Type descriptions
if attribute_name == 'type':
type_descriptions = {
# Use EXACT values from S3 schema
"actual_s3_value": "descriptive text for prompt",
"another_s3_value": "another description"
}
return type_descriptions.get(value.lower(), value.lower())
# Material descriptions
elif attribute_name == 'material':
material_descriptions = {
"canvas": "canvas material",
"cotton": "cotton fabric",
# ... more materials
}
return material_descriptions.get(value.lower(), value.lower())
# Size descriptions
elif attribute_name == 'size':
size_descriptions = {
"small": "small",
"medium": "medium-sized",
"large": "large"
}
return size_descriptions.get(value.lower(), value.lower())
# Background human descriptions
elif attribute_name == 'background_human':
background_human_descriptions = {
"casual street carrying": "Casual street scene with person carrying product naturally",
"shopping lifestyle scene": "Shopping center environment",
# ... more backgrounds
}
return background_human_descriptions.get(value.lower(), "lifestyle scene with person")
# Background no human descriptions
elif attribute_name == 'background_no_human':
background_no_human_descriptions = {
"plain studio": "clean white studio background with professional lighting",
"product table": "standing upright on a modern product display table",
# ... more backgrounds
}
return background_no_human_descriptions.get(value.lower(), "clean studio background")
# Fallback
return value.lower()
Step 5: Module Exports (init.py)
Use lazy imports to avoid circular dependencies:
"""
Category prompt generation module
"""
# Lazy imports to avoid circular dependencies
def __getattr__(name):
if name == 'build_prompt':
from .main import build_prompt
return build_prompt
elif name == 'CategoryBulkGenerator': # if needed
from .bulk import CategoryBulkGenerator
return CategoryBulkGenerator
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
How GenericPromptBuilder Works
Universal RANDOM Resolution
The system now provides universal __RANDOM__ attribute resolution:
Model Demographics (handled by model_utils.py):
- model_age: __RANDOM__ → "Teenager", "Young adult", "Middle aged", "Old"
- model_sex: __RANDOM__ → "Male", "Female"
- model_ethnicity: __RANDOM__ → "White", "Black", "Oriental", "South asian", "Middle Eastern", "Hispanic"
Category Attributes (handled by GenericPromptBuilder from S3 schema):
- Any attribute in the S3 schema (type, material, background_human, etc.) can use __RANDOM__
- Values are randomly selected from the attribute's available options
- Works with both multi_select and simple list formats in the schema
Frontend Integration:
// ModelSelector.tsx automatically sends __RANDOM__ when user selects "🎲 Select Random"
<MenuItem value="__RANDOM__" sx={{ fontWeight: 'bold' }}>
🎲 Select Random
</MenuItem>
Automatic Variable Resolution
The builder automatically resolves these variables:
- {demographics} - from config.utils.model_utils.format_model_demographics() (with RANDOM resolution)
- {background_setting} - from config.utils.background_utils.get_background_setting()
- {color_desc} - from config.utils.color_utils.build_color_description()
- {attribute_desc} - from config.prompts.{category}.descriptions.get_description() (values may be resolved from RANDOM)
Module Caching
First access loads and caches descriptions module:
# Cached automatically per category
module = importlib.import_module(f"config.prompts.{category}.descriptions")
Cache Invalidation
Clear specific category cache:
GenericPromptBuilder.clear_category_cache("bags")
Template Variable Guidelines
Required Variables
{color_desc}- Always include color{background_setting}- Always include background{demographics}- For human model templates only
Category-Specific Variables
Use {attribute_desc} pattern where attribute matches S3 schema names:
- {type_desc} - calls get_description('type', value)
- {material_desc} - calls get_description('material', value)
- {size_desc} - calls get_description('size', value)
Template Structure
Human Model Template:
[
"A {demographics} using/wearing/carrying a {product_description}.",
"Product details with {material_desc} and {feature_desc}.",
"{background_setting}.",
"Product-specific requirements."
]
Product Only Template:
[
"Commercial product photography of a {product_description}.",
"Made of {material_desc} with {feature_desc}.",
"{background_setting}.",
"Product must be clean and pristine."
]
S3 Schema Commands
# See all categories
docker exec metamock-backend python config/utils/read_s3_schema.py
# Get specific category
docker exec metamock-backend python config/utils/read_s3_schema.py CATEGORY_NAME
# Get attribute keys only
docker exec metamock-backend python config/utils/read_s3_schema.py CATEGORY_NAME --keys
Global Enhancement Architecture
Category prompts focus on product-specific details. Global requirements are added automatically by the API:
# In server/api/image_generation.py
def all_prompt_enhancer(prompt: str, attributes: dict = None) -> str:
enhancements = [
"High quality, clear details"
]
# Context-aware enhancements based on human model detection
if has_human_model(attributes):
enhancements.extend(["Natural anatomy, realistic proportions"])
else:
enhancements.extend([
"Single product, no duplicates",
"Professional product photography",
"Product only, no human elements"
])
return f"{prompt}. {'. '.join(enhancements)}."
Don't include in category prompts: - ❌ "Professional photography" - ❌ "High quality" - ❌ "Single product"
Do include: - ✅ Product-specific quality notes - ✅ Category-relevant requirements - ✅ Material and attribute descriptions
Testing and Debugging
Test the builder:
from config.prompts.YOUR_CATEGORY import build_prompt
attributes = {
"type": "actual_s3_value",
"material": "actual_s3_value",
"color_palette": "red",
"model_age": "Young adult", # triggers human model
"background": "actual_s3_background"
}
prompt = build_prompt(attributes)
print(prompt)
Debug variable resolution:
from config.prompts.generic_prompt_builder import GenericPromptBuilder
builder = GenericPromptBuilder("YOUR_CATEGORY", attributes, human_template, product_template)
variables = builder.extract_template_variables()
print("Template variables:", variables)
for var in variables:
resolved = builder.resolve_variable(var)
print(f"{var} -> {resolved}")
Common Mistakes
❌ Making up attribute names - Always use exact S3 schema names
❌ Forgetting descriptions.py - Every {attr_desc} needs corresponding if/elif in descriptions.py
❌ Wrong template variables - Must match S3 attribute names
❌ Adding global requirements - Let API handle quality/quantity
❌ Complex logic in templates - Keep templates simple, logic in descriptions.py
❌ Circular imports - Use lazy imports in __init__.py
Architecture Benefits
This simplified architecture provides:
✅ Single file descriptions - All descriptions in one descriptions.py file
✅ Clean imports - One import per category: config.prompts.{category}.descriptions
✅ No circular dependencies - Lazy imports and proper module structure
✅ Easy maintenance - Simple if/elif pattern for all attributes
✅ Consistent caching - Module-level caching with invalidation
✅ Template flexibility - Dynamic variable resolution for any attribute
Complete Example: bags
config/prompts/bags/
├── __init__.py (lazy imports)
├── main.py (uses GenericPromptBuilder)
└── descriptions.py (all descriptions in if/elif pattern)
The bags category demonstrates the complete pattern and serves as a reference implementation for all future categories.
This architecture eliminates the complexity of nested folder structures while maintaining clean separation of concerns and easy maintainability.