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.