Skip to content

[BUG] Orchestrator AI prompts overwritten by static YAML config #48

@dhirmadi

Description

@dhirmadi

Bug Report

Severity: High (affects all orchestrator-mode tenants with custom AI prompts)
Component: publisher_v2/src/publisher_v2/services/ai.py
Discovered: 2025-12-30 during integration testing for lbd.shibari.photo


Problem

Custom AI prompts (system_prompt, role_prompt) sent by the orchestrator are ignored. The Publisher always uses the static YAML defaults instead of tenant-specific prompts.

Root Cause

In CaptionGeneratorOpenAI.__init__() (ai.py lines 209-231), the orchestrator prompts are loaded first, then overwritten by static config:

# Step 1: Load from orchestrator config ✅
self.system_prompt = config.system_prompt   # Tenant's custom prompt
self.role_prompt = config.role_prompt

# Step 2: OVERWRITE with static YAML ❌ (wrong order!)
static_prompts = get_static_config().ai_prompts
if static_prompts.caption.system:
    self.system_prompt = static_prompts.caption.system   # ← Overwrites tenant prompt!
if static_prompts.caption.role:
    self.role_prompt = static_prompts.caption.role       # ← Overwrites tenant prompt!

Evidence

Orchestrator sends custom prompts for lbd.shibari.photo:

"ai": {
    "system_prompt": "Using the image analysis, write a tiny caption between 100-150 characters. 1. Wrap the message in ✨ sparkles ✨. 2. Start with a theme: Memory Monday, Titty Tuesday...",
    "role_prompt": "You are a classy, fun, and playful 30-year-old woman posting on FetLife. Your style is 'short and sweet'—sophisticated yet flirty..."
}

But Publisher uses static defaults from config/static/ai_prompts.yaml:

caption:
  system: >
    You are a senior social media copywriter. Write authentic, concise, platform-aware captions.
  role: "Write a caption for:"

Expected Behavior

Orchestrator-provided prompts (tenant-specific) should take precedence over static YAML defaults. Static config should only be used as a fallback when the orchestrator doesn't provide prompts.

Proposed Fix

Change the conditional logic to use static config as fallback, not override:

# Load from orchestrator config first
self.system_prompt = config.system_prompt
self.role_prompt = config.role_prompt

# Only use static config as FALLBACK if orchestrator didn't provide prompts
static_prompts = get_static_config().ai_prompts
if not self.system_prompt and static_prompts.caption.system:
    self.system_prompt = static_prompts.caption.system
if not self.role_prompt and static_prompts.caption.role:
    self.role_prompt = static_prompts.caption.role

Same pattern needed for:

  • sd_caption_system_prompt (lines 228-229)
  • sd_caption_role_prompt (lines 230-231)

Affected Code Locations

File Lines Issue
publisher_v2/src/publisher_v2/services/ai.py 224-227 Caption prompts overwritten
publisher_v2/src/publisher_v2/services/ai.py 228-231 SD caption prompts overwritten

Acceptance Criteria

  • Orchestrator-provided system_prompt takes precedence over static YAML
  • Orchestrator-provided role_prompt takes precedence over static YAML
  • Same for sd_caption_system_prompt and sd_caption_role_prompt
  • Static YAML prompts are only used when orchestrator provides null/empty
  • Add unit test to verify prompt precedence

Testing Notes

To reproduce:

  1. Configure a tenant in orchestrator with custom ai.system_prompt and ai.role_prompt
  2. Run Publisher in orchestrator mode
  3. Observe generated captions use generic "social media copywriter" style instead of custom prompts

Related: Feature 022 (Orchestrator Schema V2 Integration)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingmust-fix

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions