- Updated AI prompts to include metadata context, cluster roles, and product attributes for improved content generation. - Enhanced GenerateContentFunction to incorporate taxonomy and keyword objects for richer context. - Introduced new metadata fields in frontend components for better content organization and filtering. - Added cluster match, taxonomy match, and relevance score to LinkResults for improved link management. - Implemented metadata completeness scoring and recommended actions in AnalysisPreview for better content optimization. - Updated API services to support new metadata structures and site progress tracking.
945 lines
32 KiB
Python
945 lines
32 KiB
Python
"""
|
||
Prompt Registry - Centralized prompt management with override hierarchy
|
||
Supports: task-level overrides → DB prompts → default fallbacks
|
||
"""
|
||
import logging
|
||
from typing import Dict, Any, Optional
|
||
from django.db import models
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class PromptRegistry:
|
||
"""
|
||
Centralized prompt registry with hierarchical resolution:
|
||
1. Task-level prompt_override (if exists)
|
||
2. DB prompt for (account, function)
|
||
3. Default fallback from registry
|
||
"""
|
||
|
||
# Default prompts stored in registry
|
||
DEFAULT_PROMPTS = {
|
||
'clustering': """You are a semantic strategist and SEO architecture engine. Your task is to analyze the provided keyword list and group them into meaningful, intent-driven topic clusters that reflect how real users search, think, and act online.
|
||
|
||
Return a single JSON object with a "clusters" array. Each cluster must follow this structure:
|
||
|
||
{
|
||
"name": "[Descriptive cluster name — natural, SEO-relevant, clearly expressing the topic]",
|
||
"description": "[1–2 concise sentences explaining what this cluster covers and why these keywords belong together]",
|
||
"keywords": ["keyword 1", "keyword 2", "keyword 3", "..."]
|
||
}
|
||
|
||
CLUSTERING STRATEGY:
|
||
|
||
1. Keyword-first, structure-follows:
|
||
- Do NOT rely on assumed categories or existing content structures.
|
||
- Begin purely from the meaning, intent, and behavioral connection between keywords.
|
||
|
||
2. Use multi-dimensional grouping logic:
|
||
- Group keywords by these behavioral dimensions:
|
||
• Search Intent → informational, commercial, transactional, navigational
|
||
• Use-Case or Problem → what the user is trying to achieve or solve
|
||
• Function or Feature → how something works or what it does
|
||
• Persona or Audience → who the content or product serves
|
||
• Context → location, time, season, platform, or device
|
||
- Combine 2–3 dimensions naturally where they make sense.
|
||
|
||
3. Model real search behavior:
|
||
- Favor clusters that form natural user journeys such as:
|
||
• Problem ➝ Solution
|
||
• General ➝ Specific
|
||
• Product ➝ Use-case
|
||
• Buyer ➝ Benefit
|
||
• Tool ➝ Function
|
||
• Task ➝ Method
|
||
- Each cluster should feel like a real topic hub users would explore in depth.
|
||
|
||
4. Avoid superficial groupings:
|
||
- Do not cluster keywords just because they share words.
|
||
- Do not force-fit outliers or unrelated keywords.
|
||
- Exclude keywords that don't logically connect to any cluster.
|
||
|
||
5. Quality rules:
|
||
- Each cluster should include between 3–10 strongly related keywords.
|
||
- Never duplicate a keyword across multiple clusters.
|
||
- Prioritize semantic strength, search intent, and usefulness for SEO-driven content structure.
|
||
- It's better to output fewer, high-quality clusters than many weak or shallow ones.
|
||
|
||
INPUT FORMAT:
|
||
{
|
||
"keywords": [IGNY8_KEYWORDS]
|
||
}
|
||
|
||
OUTPUT FORMAT:
|
||
Return ONLY the final JSON object in this format:
|
||
{
|
||
"clusters": [
|
||
{
|
||
"name": "...",
|
||
"description": "...",
|
||
"keywords": ["...", "...", "..."]
|
||
}
|
||
]
|
||
}
|
||
|
||
Do not include any explanations, text, or commentary outside the JSON output.
|
||
""",
|
||
|
||
'ideas': """Generate SEO-optimized, high-quality content ideas and outlines for each keyword cluster.
|
||
Input:
|
||
Clusters: [IGNY8_CLUSTERS]
|
||
Keywords: [IGNY8_CLUSTER_KEYWORDS]
|
||
|
||
Output: JSON with "ideas" array.
|
||
Each cluster → 1 cluster_hub + 2–4 supporting ideas.
|
||
Each idea must include:
|
||
title, description, content_type, content_structure, cluster_id, estimated_word_count (1500–2200), and covered_keywords.
|
||
|
||
Outline Rules:
|
||
|
||
Intro: 1 hook (30–40 words) + 2 intro paragraphs (50–60 words each).
|
||
|
||
5–8 H2 sections, each with 2–3 H3s.
|
||
|
||
Each H2 ≈ 250–300 words, mixed content (paragraphs, lists, tables, blockquotes).
|
||
|
||
Vary section format and tone; no bullets or lists at start.
|
||
|
||
Tables have columns; blockquotes = expert POV or data insight.
|
||
|
||
Use depth, examples, and real context.
|
||
|
||
Avoid repetitive structure.
|
||
|
||
Tone: Professional editorial flow. No generic phrasing. Use varied sentence openings and realistic examples.
|
||
|
||
Output JSON Example:
|
||
|
||
{
|
||
"ideas": [
|
||
{
|
||
"title": "Best Organic Cotton Duvet Covers for All Seasons",
|
||
"description": {
|
||
"introduction": {
|
||
"hook": "Transform your sleep with organic cotton that blends comfort and sustainability.",
|
||
"paragraphs": [
|
||
{"content_type": "paragraph", "details": "Overview of organic cotton's rise in bedding industry."},
|
||
{"content_type": "paragraph", "details": "Why consumers prefer organic bedding over synthetic alternatives."}
|
||
]
|
||
},
|
||
"H2": [
|
||
{
|
||
"heading": "Why Choose Organic Cotton for Bedding?",
|
||
"subsections": [
|
||
{"subheading": "Health and Skin Benefits", "content_type": "paragraph", "details": "Discuss hypoallergenic and chemical-free aspects."},
|
||
{"subheading": "Environmental Sustainability", "content_type": "list", "details": "Eco benefits like low water use, no pesticides."},
|
||
{"subheading": "Long-Term Cost Savings", "content_type": "table", "details": "Compare durability and pricing over time."}
|
||
]
|
||
}
|
||
]
|
||
},
|
||
"content_type": "post",
|
||
"content_structure": "review",
|
||
"cluster_id": 12,
|
||
"estimated_word_count": 1800,
|
||
"covered_keywords": "organic duvet covers, eco-friendly bedding, sustainable sheets"
|
||
}
|
||
]
|
||
}""",
|
||
|
||
'content_generation': """You are an editorial content strategist. Your task is to generate a complete JSON response object that includes all the fields listed below, based on the provided content idea, keyword cluster, keyword list, and metadata context.
|
||
|
||
Only the `content` field should contain HTML inside JSON object.
|
||
|
||
==================
|
||
Generate a complete JSON response object matching this structure:
|
||
==================
|
||
|
||
{
|
||
"title": "[Blog title using the primary keyword — full sentence case]",
|
||
"meta_title": "[Meta title under 60 characters — natural, optimized, and compelling]",
|
||
"meta_description": "[Meta description under 160 characters — clear and enticing summary]",
|
||
"content": "[HTML content — full editorial structure with <p>, <h2>, <h3>, <ul>, <ol>, <table>]",
|
||
"word_count": [Exact integer — word count of HTML body only],
|
||
"primary_keyword": "[Single primary keyword used in title and first paragraph]",
|
||
"secondary_keywords": [
|
||
"[Keyword 1]",
|
||
"[Keyword 2]",
|
||
"[Keyword 3]"
|
||
],
|
||
"tags": [
|
||
"[2–4 word lowercase tag 1]",
|
||
"[2–4 word lowercase tag 2]",
|
||
"[2–4 word lowercase tag 3]",
|
||
"[2–4 word lowercase tag 4]",
|
||
"[2–4 word lowercase tag 5]"
|
||
],
|
||
"categories": [
|
||
"[Parent Category > Child Category]",
|
||
"[Optional Second Category > Optional Subcategory]"
|
||
]
|
||
}
|
||
|
||
===========================
|
||
CONTENT FLOW RULES
|
||
===========================
|
||
|
||
**INTRODUCTION:**
|
||
- Start with 1 italicized hook (30–40 words)
|
||
- Follow with 2 narrative paragraphs (each 50–60 words; 2–3 sentences max)
|
||
- No headings allowed in intro
|
||
|
||
**H2 SECTIONS (5–8 total):**
|
||
Each section should be 250–300 words and follow this format:
|
||
1. Two narrative paragraphs (80–120 words each, 2–3 sentences)
|
||
2. One list or table (must come *after* a paragraph)
|
||
3. Optional closing paragraph (40–60 words)
|
||
4. Insert 2–3 subsections naturally after main paragraphs
|
||
|
||
**Formatting Rules:**
|
||
- Vary use of unordered lists, ordered lists, and tables across sections
|
||
- Never begin any section or sub-section with a list or table
|
||
|
||
===========================
|
||
KEYWORD & SEO RULES
|
||
===========================
|
||
|
||
- **Primary keyword** must appear in:
|
||
- The title
|
||
- First paragraph of the introduction
|
||
- At least 2 H2 headings
|
||
|
||
- **Secondary keywords** must be used naturally, not forced
|
||
|
||
- **Tone & style guidelines:**
|
||
- No robotic or passive voice
|
||
- Avoid generic intros like "In today's world…"
|
||
- Don't repeat heading in opening sentence
|
||
- Vary sentence structure and length
|
||
|
||
===========================
|
||
STAGE 3: METADATA CONTEXT (NEW)
|
||
===========================
|
||
|
||
**Cluster Role:**
|
||
[IGNY8_CLUSTER_ROLE]
|
||
- If role is "hub": Create comprehensive, authoritative content that serves as the main resource for this topic cluster. Include overview sections, key concepts, and links to related topics.
|
||
- If role is "supporting": Create detailed, focused content that supports the hub page. Dive deep into specific aspects, use cases, or subtopics.
|
||
- If role is "attribute": Create content focused on specific attributes, features, or specifications. Include detailed comparisons, specifications, or attribute-focused information.
|
||
|
||
**Taxonomy Context:**
|
||
[IGNY8_TAXONOMY]
|
||
- Use taxonomy information to structure categories and tags appropriately.
|
||
- Align content with taxonomy hierarchy and relationships.
|
||
- Ensure content fits within the defined taxonomy structure.
|
||
|
||
**Product/Service Attributes:**
|
||
[IGNY8_ATTRIBUTES]
|
||
- If attributes are provided (e.g., product specs, service modifiers), incorporate them naturally into the content.
|
||
- For product content: Include specifications, features, dimensions, materials, etc. as relevant.
|
||
- For service content: Include service tiers, pricing modifiers, availability, etc. as relevant.
|
||
- Present attributes in a user-friendly format (tables, lists, or integrated into narrative).
|
||
|
||
===========================
|
||
INPUT VARIABLES
|
||
===========================
|
||
|
||
CONTENT IDEA DETAILS:
|
||
[IGNY8_IDEA]
|
||
|
||
KEYWORD CLUSTER:
|
||
[IGNY8_CLUSTER]
|
||
|
||
ASSOCIATED KEYWORDS:
|
||
[IGNY8_KEYWORDS]
|
||
|
||
===========================
|
||
OUTPUT FORMAT
|
||
===========================
|
||
|
||
Return ONLY the final JSON object.
|
||
Do NOT include any comments, formatting, or explanations.""",
|
||
|
||
'site_structure_generation': """You are a senior UX architect and information designer. Use the business brief, objectives, style references, and existing site info to propose a complete multi-page marketing website structure.
|
||
|
||
INPUT CONTEXT
|
||
==============
|
||
BUSINESS BRIEF:
|
||
[IGNY8_BUSINESS_BRIEF]
|
||
|
||
PRIMARY OBJECTIVES:
|
||
[IGNY8_OBJECTIVES]
|
||
|
||
STYLE & BRAND NOTES:
|
||
[IGNY8_STYLE]
|
||
|
||
SITE INFO / CURRENT STRUCTURE:
|
||
[IGNY8_SITE_INFO]
|
||
|
||
OUTPUT REQUIREMENTS
|
||
====================
|
||
Return ONE JSON object with the following keys:
|
||
|
||
{
|
||
"site": {
|
||
"name": "...",
|
||
"primary_navigation": ["home", "services", "about", "contact"],
|
||
"secondary_navigation": ["blog", "faq"],
|
||
"hero_message": "High level value statement",
|
||
"tone": "voice + tone summary"
|
||
},
|
||
"pages": [
|
||
{
|
||
"slug": "home",
|
||
"title": "Home",
|
||
"type": "home | about | services | products | blog | contact | custom",
|
||
"status": "draft",
|
||
"objective": "Explain the core brand promise and primary CTA",
|
||
"primary_cta": "Book a strategy call",
|
||
"seo": {
|
||
"meta_title": "...",
|
||
"meta_description": "..."
|
||
},
|
||
"blocks": [
|
||
{
|
||
"type": "hero | features | services | stats | testimonials | faq | contact | custom",
|
||
"heading": "Section headline",
|
||
"subheading": "Support copy",
|
||
"layout": "full-width | two-column | cards | carousel",
|
||
"content": [
|
||
"Bullet or short paragraph describing what to render in this block"
|
||
]
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
|
||
RULES
|
||
=====
|
||
- Include 5–8 pages covering the complete buyer journey (awareness → evaluation → conversion → trust).
|
||
- Every page must have at least 3 blocks with concrete guidance (no placeholders like "Lorem ipsum").
|
||
- Use consistent slug naming, all lowercase with hyphens.
|
||
- Type must match the allowed enum and reflect page intent.
|
||
- Ensure the navigation arrays align with the page list.
|
||
- Focus on practical descriptions that an engineering team can hand off directly to the Site Builder.
|
||
|
||
Return ONLY valid JSON. No commentary, explanations, or Markdown.
|
||
""",
|
||
|
||
'image_prompt_extraction': """Extract image prompts from the following article content.
|
||
|
||
ARTICLE TITLE: {title}
|
||
|
||
ARTICLE CONTENT:
|
||
{content}
|
||
|
||
Extract image prompts for:
|
||
1. Featured Image: One main image that represents the article topic
|
||
2. In-Article Images: Up to {max_images} images that would be useful within the article content
|
||
|
||
Return a JSON object with this structure:
|
||
{{
|
||
"featured_prompt": "Detailed description of the featured image",
|
||
"in_article_prompts": [
|
||
"Description of first in-article image",
|
||
"Description of second in-article image",
|
||
...
|
||
]
|
||
}}
|
||
|
||
Make sure each prompt is detailed enough for image generation, describing the visual elements, style, mood, and composition.""",
|
||
|
||
'image_prompt_template': 'Create a high-quality {image_type} image to use as a featured photo for a blog post titled "{post_title}". The image should visually represent the theme, mood, and subject implied by the image prompt: {image_prompt}. Focus on a realistic, well-composed scene that naturally communicates the topic without text or logos. Use balanced lighting, pleasing composition, and photographic detail suitable for lifestyle or editorial web content. Avoid adding any visible or readable text, brand names, or illustrative effects. **And make sure image is not blurry.**',
|
||
|
||
'negative_prompt': 'text, watermark, logo, overlay, title, caption, writing on walls, writing on objects, UI, infographic elements, post title',
|
||
|
||
'optimize_content': """You are an expert content optimizer specializing in SEO, readability, and engagement.
|
||
|
||
Your task is to optimize the provided content to improve its SEO score, readability, and engagement metrics.
|
||
|
||
CURRENT CONTENT:
|
||
Title: {CONTENT_TITLE}
|
||
Word Count: {WORD_COUNT}
|
||
Source: {SOURCE}
|
||
Primary Keyword: {PRIMARY_KEYWORD}
|
||
Internal Links: {INTERNAL_LINKS_COUNT}
|
||
|
||
CURRENT META DATA:
|
||
Meta Title: {META_TITLE}
|
||
Meta Description: {META_DESCRIPTION}
|
||
|
||
CURRENT SCORES:
|
||
{CURRENT_SCORES}
|
||
|
||
HTML CONTENT:
|
||
{HTML_CONTENT}
|
||
|
||
OPTIMIZATION REQUIREMENTS:
|
||
|
||
1. SEO Optimization:
|
||
- Ensure meta title is 30-60 characters (if provided)
|
||
- Ensure meta description is 120-160 characters (if provided)
|
||
- Optimize primary keyword usage (natural, not keyword stuffing)
|
||
- Improve heading structure (H1, H2, H3 hierarchy)
|
||
- Add internal links where relevant (maintain existing links)
|
||
|
||
2. Readability:
|
||
- Average sentence length: 15-20 words
|
||
- Use clear, concise language
|
||
- Break up long paragraphs
|
||
- Use bullet points and lists where appropriate
|
||
- Ensure proper paragraph structure
|
||
|
||
3. Engagement:
|
||
- Add compelling headings
|
||
- Include relevant images placeholders (alt text)
|
||
- Use engaging language
|
||
- Create clear call-to-action sections
|
||
- Improve content flow and structure
|
||
|
||
OUTPUT FORMAT:
|
||
Return ONLY a JSON object in this format:
|
||
{{
|
||
"html_content": "[Optimized HTML content]",
|
||
"meta_title": "[Optimized meta title, 30-60 chars]",
|
||
"meta_description": "[Optimized meta description, 120-160 chars]",
|
||
"optimization_notes": "[Brief notes on what was optimized]"
|
||
}}
|
||
|
||
Do not include any explanations, text, or commentary outside the JSON output.
|
||
""",
|
||
|
||
# Phase 8: Universal Content Types
|
||
'product_generation': """You are a product content specialist. Generate comprehensive product content that includes detailed descriptions, features, specifications, pricing, and benefits.
|
||
|
||
INPUT:
|
||
Product Name: [IGNY8_PRODUCT_NAME]
|
||
Product Description: [IGNY8_PRODUCT_DESCRIPTION]
|
||
Product Features: [IGNY8_PRODUCT_FEATURES]
|
||
Target Audience: [IGNY8_TARGET_AUDIENCE]
|
||
Primary Keyword: [IGNY8_PRIMARY_KEYWORD]
|
||
|
||
OUTPUT FORMAT:
|
||
Return ONLY a JSON object in this format:
|
||
{
|
||
"title": "[Product name and key benefit]",
|
||
"meta_title": "[SEO-optimized meta title, 30-60 chars]",
|
||
"meta_description": "[Compelling meta description, 120-160 chars]",
|
||
"html_content": "[Complete HTML product page content]",
|
||
"word_count": [Integer word count],
|
||
"primary_keyword": "[Primary keyword]",
|
||
"secondary_keywords": ["keyword1", "keyword2", "keyword3"],
|
||
"tags": ["tag1", "tag2", "tag3"],
|
||
"categories": ["Category > Subcategory"],
|
||
"json_blocks": [
|
||
{
|
||
"type": "product_overview",
|
||
"heading": "Product Overview",
|
||
"content": "Detailed product description"
|
||
},
|
||
{
|
||
"type": "features",
|
||
"heading": "Key Features",
|
||
"items": ["Feature 1", "Feature 2", "Feature 3"]
|
||
},
|
||
{
|
||
"type": "specifications",
|
||
"heading": "Specifications",
|
||
"data": {"Spec 1": "Value 1", "Spec 2": "Value 2"}
|
||
},
|
||
{
|
||
"type": "pricing",
|
||
"heading": "Pricing",
|
||
"content": "Pricing information"
|
||
},
|
||
{
|
||
"type": "benefits",
|
||
"heading": "Benefits",
|
||
"items": ["Benefit 1", "Benefit 2", "Benefit 3"]
|
||
}
|
||
],
|
||
"structure_data": {
|
||
"product_type": "[Product type]",
|
||
"price_range": "[Price range]",
|
||
"target_market": "[Target market]"
|
||
}
|
||
}
|
||
|
||
CONTENT REQUIREMENTS:
|
||
- Include compelling product overview
|
||
- List key features with benefits
|
||
- Provide detailed specifications
|
||
- Include pricing information (if available)
|
||
- Highlight unique selling points
|
||
- Use SEO-optimized headings
|
||
- Include call-to-action sections
|
||
- Ensure natural keyword usage
|
||
""",
|
||
|
||
'service_generation': """You are a service page content specialist. Generate comprehensive service page content that explains services, benefits, process, and pricing.
|
||
|
||
INPUT:
|
||
Service Name: [IGNY8_SERVICE_NAME]
|
||
Service Description: [IGNY8_SERVICE_DESCRIPTION]
|
||
Service Benefits: [IGNY8_SERVICE_BENEFITS]
|
||
Target Audience: [IGNY8_TARGET_AUDIENCE]
|
||
Primary Keyword: [IGNY8_PRIMARY_KEYWORD]
|
||
|
||
OUTPUT FORMAT:
|
||
Return ONLY a JSON object in this format:
|
||
{
|
||
"title": "[Service name and value proposition]",
|
||
"meta_title": "[SEO-optimized meta title, 30-60 chars]",
|
||
"meta_description": "[Compelling meta description, 120-160 chars]",
|
||
"html_content": "[Complete HTML service page content]",
|
||
"word_count": [Integer word count],
|
||
"primary_keyword": "[Primary keyword]",
|
||
"secondary_keywords": ["keyword1", "keyword2", "keyword3"],
|
||
"tags": ["tag1", "tag2", "tag3"],
|
||
"categories": ["Category > Subcategory"],
|
||
"json_blocks": [
|
||
{
|
||
"type": "service_overview",
|
||
"heading": "Service Overview",
|
||
"content": "Detailed service description"
|
||
},
|
||
{
|
||
"type": "benefits",
|
||
"heading": "Benefits",
|
||
"items": ["Benefit 1", "Benefit 2", "Benefit 3"]
|
||
},
|
||
{
|
||
"type": "process",
|
||
"heading": "Our Process",
|
||
"steps": ["Step 1", "Step 2", "Step 3"]
|
||
},
|
||
{
|
||
"type": "pricing",
|
||
"heading": "Pricing",
|
||
"content": "Pricing information"
|
||
},
|
||
{
|
||
"type": "faq",
|
||
"heading": "Frequently Asked Questions",
|
||
"items": [{"question": "Q1", "answer": "A1"}]
|
||
}
|
||
],
|
||
"structure_data": {
|
||
"service_type": "[Service type]",
|
||
"duration": "[Service duration]",
|
||
"target_market": "[Target market]"
|
||
}
|
||
}
|
||
|
||
CONTENT REQUIREMENTS:
|
||
- Clear service overview and value proposition
|
||
- Detailed benefits and outcomes
|
||
- Step-by-step process explanation
|
||
- Pricing information (if available)
|
||
- FAQ section addressing common questions
|
||
- Include testimonials or case studies (if applicable)
|
||
- Use SEO-optimized headings
|
||
- Include call-to-action sections
|
||
""",
|
||
|
||
'generate_page_content': """You are a Site Builder content specialist. Generate structured page content optimized for website pages with JSON blocks format.
|
||
|
||
Your task is to generate content that will be rendered as a modern website page with structured blocks (hero, features, testimonials, text, CTA, etc.).
|
||
|
||
INPUT DATA:
|
||
----------
|
||
Page Title: [IGNY8_PAGE_TITLE]
|
||
Page Slug: [IGNY8_PAGE_SLUG]
|
||
Page Type: [IGNY8_PAGE_TYPE] (home, products, blog, contact, about, services, custom)
|
||
Site Name: [IGNY8_SITE_NAME]
|
||
Site Description: [IGNY8_SITE_DESCRIPTION]
|
||
Existing Block Hints: [IGNY8_EXISTING_BLOCKS]
|
||
Structure Hints: [IGNY8_STRUCTURE_HINTS]
|
||
|
||
OUTPUT FORMAT:
|
||
--------------
|
||
Return ONLY a JSON object in this exact structure:
|
||
|
||
{
|
||
"title": "[Page title - SEO optimized, natural]",
|
||
"html_content": "[Full HTML content for fallback/SEO - complete article]",
|
||
"word_count": [Integer - word count of HTML content],
|
||
"blocks": [
|
||
{
|
||
"type": "hero",
|
||
"data": {
|
||
"heading": "[Compelling hero headline]",
|
||
"subheading": "[Supporting subheadline]",
|
||
"content": "[Brief hero description - 1-2 sentences]",
|
||
"buttonText": "[CTA button text]",
|
||
"buttonLink": "[CTA link URL]"
|
||
}
|
||
},
|
||
{
|
||
"type": "text",
|
||
"data": {
|
||
"heading": "[Section heading]",
|
||
"content": "[Rich text content with paragraphs, lists, etc.]"
|
||
}
|
||
},
|
||
{
|
||
"type": "features",
|
||
"data": {
|
||
"heading": "[Features section heading]",
|
||
"content": [
|
||
"[Feature 1: Description]",
|
||
"[Feature 2: Description]",
|
||
"[Feature 3: Description]"
|
||
]
|
||
}
|
||
},
|
||
{
|
||
"type": "testimonials",
|
||
"data": {
|
||
"heading": "[Testimonials heading]",
|
||
"subheading": "[Optional subheading]",
|
||
"content": [
|
||
"[Testimonial quote 1]",
|
||
"[Testimonial quote 2]",
|
||
"[Testimonial quote 3]"
|
||
]
|
||
}
|
||
},
|
||
{
|
||
"type": "cta",
|
||
"data": {
|
||
"heading": "[CTA heading]",
|
||
"subheading": "[CTA subheading]",
|
||
"content": "[CTA description]",
|
||
"buttonText": "[Button text]",
|
||
"buttonLink": "[Button link]"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
|
||
BLOCK TYPE GUIDELINES:
|
||
----------------------
|
||
Based on page type, use appropriate blocks:
|
||
|
||
**Home Page:**
|
||
- Start with "hero" block (compelling headline + CTA)
|
||
- Follow with "features" or "text" blocks
|
||
- Include "testimonials" block
|
||
- End with "cta" block
|
||
|
||
**Products Page:**
|
||
- Start with "text" block (product overview)
|
||
- Use "features" or "grid" blocks for product listings
|
||
- Include "text" blocks for product details
|
||
|
||
**Blog Page:**
|
||
- Use "text" blocks for article content
|
||
- Can include "quote" blocks for highlights
|
||
- Structure as readable article format
|
||
|
||
**Contact Page:**
|
||
- Start with "text" block (contact info)
|
||
- Use "form" block structure hints
|
||
- Include "text" blocks for location/hours
|
||
|
||
**About Page:**
|
||
- Start with "hero" or "text" block
|
||
- Use "features" for team/values
|
||
- Include "stats" block if applicable
|
||
- End with "text" block
|
||
|
||
**Services Page:**
|
||
- Start with "text" block (service overview)
|
||
- Use "features" for service offerings
|
||
- Include "text" blocks for details
|
||
|
||
CONTENT REQUIREMENTS:
|
||
---------------------
|
||
1. **Hero Block** (for home/about pages):
|
||
- Compelling headline (8-12 words)
|
||
- Clear value proposition
|
||
- Strong CTA button
|
||
|
||
2. **Text Blocks**:
|
||
- Natural, engaging copy
|
||
- SEO-optimized headings
|
||
- Varied content (paragraphs, lists, emphasis)
|
||
|
||
3. **Features Blocks**:
|
||
- 3-6 features
|
||
- Clear benefit statements
|
||
- Action-oriented language
|
||
|
||
4. **Testimonials Blocks**:
|
||
- 3-5 authentic-sounding testimonials
|
||
- Specific, believable quotes
|
||
- Varied lengths
|
||
|
||
5. **CTA Blocks**:
|
||
- Clear value proposition
|
||
- Strong action words
|
||
- Compelling button text
|
||
|
||
6. **HTML Content** (for SEO):
|
||
- Complete article version of all blocks
|
||
- Proper HTML structure
|
||
- SEO-optimized with headings, paragraphs, lists
|
||
- 800-1500 words total
|
||
|
||
TONE & STYLE:
|
||
-------------
|
||
- Professional but approachable
|
||
- Clear and concise
|
||
- Benefit-focused
|
||
- Action-oriented
|
||
- Natural keyword usage (not forced)
|
||
- No generic phrases or placeholder text
|
||
|
||
IMPORTANT:
|
||
----------
|
||
- Return ONLY the JSON object
|
||
- Do NOT include markdown formatting
|
||
- Do NOT include explanations or comments
|
||
- Ensure all blocks have proper "type" and "data" structure
|
||
- HTML content should be complete and standalone
|
||
- Blocks should be optimized for the specific page type""",
|
||
|
||
'taxonomy_generation': """You are a taxonomy and categorization specialist. Generate comprehensive taxonomy page content that organizes and explains categories, tags, and hierarchical structures.
|
||
|
||
INPUT:
|
||
Taxonomy Name: [IGNY8_TAXONOMY_NAME]
|
||
Taxonomy Description: [IGNY8_TAXONOMY_DESCRIPTION]
|
||
Taxonomy Items: [IGNY8_TAXONOMY_ITEMS]
|
||
Primary Keyword: [IGNY8_PRIMARY_KEYWORD]
|
||
|
||
OUTPUT FORMAT:
|
||
Return ONLY a JSON object in this format:
|
||
{
|
||
"title": "[Taxonomy name and purpose]",
|
||
"meta_title": "[SEO-optimized meta title, 30-60 chars]",
|
||
"meta_description": "[Compelling meta description, 120-160 chars]",
|
||
"html_content": "[Complete HTML taxonomy page content]",
|
||
"word_count": [Integer word count],
|
||
"primary_keyword": "[Primary keyword]",
|
||
"secondary_keywords": ["keyword1", "keyword2", "keyword3"],
|
||
"tags": ["tag1", "tag2", "tag3"],
|
||
"categories": ["Category > Subcategory"],
|
||
"json_blocks": [
|
||
{
|
||
"type": "taxonomy_overview",
|
||
"heading": "Taxonomy Overview",
|
||
"content": "Detailed taxonomy description"
|
||
},
|
||
{
|
||
"type": "categories",
|
||
"heading": "Categories",
|
||
"items": [
|
||
{
|
||
"name": "Category 1",
|
||
"description": "Category description",
|
||
"subcategories": ["Subcat 1", "Subcat 2"]
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"type": "tags",
|
||
"heading": "Tags",
|
||
"items": ["Tag 1", "Tag 2", "Tag 3"]
|
||
},
|
||
{
|
||
"type": "hierarchy",
|
||
"heading": "Taxonomy Hierarchy",
|
||
"structure": {"Level 1": {"Level 2": ["Level 3"]}}
|
||
}
|
||
],
|
||
"structure_data": {
|
||
"taxonomy_type": "[Taxonomy type]",
|
||
"item_count": [Integer],
|
||
"hierarchy_levels": [Integer]
|
||
}
|
||
}
|
||
|
||
CONTENT REQUIREMENTS:
|
||
- Clear taxonomy overview and purpose
|
||
- Organized category structure
|
||
- Tag organization and relationships
|
||
- Hierarchical structure visualization
|
||
- SEO-optimized headings
|
||
- Include navigation and organization benefits
|
||
- Use clear, descriptive language
|
||
""",
|
||
}
|
||
|
||
# Mapping from function names to prompt types
|
||
FUNCTION_TO_PROMPT_TYPE = {
|
||
'auto_cluster': 'clustering',
|
||
'generate_ideas': 'ideas',
|
||
'generate_content': 'content_generation',
|
||
'generate_images': 'image_prompt_extraction',
|
||
'extract_image_prompts': 'image_prompt_extraction',
|
||
'generate_image_prompts': 'image_prompt_extraction',
|
||
'generate_site_structure': 'site_structure_generation',
|
||
'generate_page_content': 'generate_page_content', # Site Builder specific
|
||
'optimize_content': 'optimize_content',
|
||
# Phase 8: Universal Content Types
|
||
'generate_product_content': 'product_generation',
|
||
'generate_service_page': 'service_generation',
|
||
'generate_taxonomy': 'taxonomy_generation',
|
||
}
|
||
|
||
@classmethod
|
||
def get_prompt(
|
||
cls,
|
||
function_name: str,
|
||
account: Optional[Any] = None,
|
||
task: Optional[Any] = None,
|
||
context: Optional[Dict[str, Any]] = None
|
||
) -> str:
|
||
"""
|
||
Get prompt for a function with hierarchical resolution.
|
||
|
||
Priority:
|
||
1. task.prompt_override (if task provided and has override)
|
||
2. DB prompt for (account, function)
|
||
3. Default fallback from registry
|
||
|
||
Args:
|
||
function_name: AI function name (e.g., 'auto_cluster', 'generate_ideas')
|
||
account: Account object (optional)
|
||
task: Task object with optional prompt_override (optional)
|
||
context: Additional context for prompt rendering (optional)
|
||
|
||
Returns:
|
||
Prompt string ready for formatting
|
||
"""
|
||
# Step 1: Check task-level override
|
||
if task and hasattr(task, 'prompt_override') and task.prompt_override:
|
||
logger.info(f"Using task-level prompt override for {function_name}")
|
||
prompt = task.prompt_override
|
||
return cls._render_prompt(prompt, context or {})
|
||
|
||
# Step 2: Get prompt type
|
||
prompt_type = cls.FUNCTION_TO_PROMPT_TYPE.get(function_name, function_name)
|
||
|
||
# Step 3: Try DB prompt
|
||
if account:
|
||
try:
|
||
from igny8_core.modules.system.models import AIPrompt
|
||
db_prompt = AIPrompt.objects.get(
|
||
account=account,
|
||
prompt_type=prompt_type,
|
||
is_active=True
|
||
)
|
||
logger.info(f"Using DB prompt for {function_name} (account {account.id})")
|
||
prompt = db_prompt.prompt_value
|
||
return cls._render_prompt(prompt, context or {})
|
||
except Exception as e:
|
||
logger.debug(f"No DB prompt found for {function_name}: {e}")
|
||
|
||
# Step 4: Use default fallback
|
||
prompt = cls.DEFAULT_PROMPTS.get(prompt_type, '')
|
||
if not prompt:
|
||
logger.warning(f"No default prompt found for {prompt_type}, using empty string")
|
||
|
||
return cls._render_prompt(prompt, context or {})
|
||
|
||
@classmethod
|
||
def _render_prompt(cls, prompt_template: str, context: Dict[str, Any]) -> str:
|
||
"""
|
||
Render prompt template with context variables.
|
||
Supports both .format() style ({variable}) and placeholder replacement ([IGNY8_*]).
|
||
|
||
Args:
|
||
prompt_template: Prompt template string
|
||
context: Context variables for rendering
|
||
|
||
Returns:
|
||
Rendered prompt string
|
||
"""
|
||
if not context:
|
||
return prompt_template
|
||
|
||
rendered = prompt_template
|
||
|
||
# Step 1: Replace [IGNY8_*] placeholders first (always do this)
|
||
for key, value in context.items():
|
||
placeholder = f'[IGNY8_{key.upper()}]'
|
||
if placeholder in rendered:
|
||
rendered = rendered.replace(placeholder, str(value))
|
||
logger.debug(f"Replaced placeholder {placeholder} with {len(str(value))} characters")
|
||
|
||
# Step 2: Try .format() style for {variable} placeholders (if any remain)
|
||
# Normalize context keys - convert UPPER to lowercase for .format()
|
||
normalized_context = {}
|
||
for key, value in context.items():
|
||
# Try both original key and lowercase version
|
||
normalized_context[key] = value
|
||
normalized_context[key.lower()] = value
|
||
|
||
# Only try .format() if there are {variable} placeholders
|
||
if '{' in rendered and '}' in rendered:
|
||
try:
|
||
rendered = rendered.format(**normalized_context)
|
||
except (KeyError, ValueError) as e:
|
||
# If .format() fails, log warning but keep the [IGNY8_*] replacements
|
||
logger.warning(f"Failed to format prompt with .format(): {e}. Using [IGNY8_*] replacements only.")
|
||
|
||
return rendered
|
||
|
||
@classmethod
|
||
def get_image_prompt_template(cls, account: Optional[Any] = None) -> str:
|
||
"""
|
||
Get image prompt template.
|
||
Returns template string (not rendered) - caller should format with .format()
|
||
"""
|
||
prompt_type = 'image_prompt_template'
|
||
|
||
# Try DB prompt
|
||
if account:
|
||
try:
|
||
from igny8_core.modules.system.models import AIPrompt
|
||
db_prompt = AIPrompt.objects.get(
|
||
account=account,
|
||
prompt_type=prompt_type,
|
||
is_active=True
|
||
)
|
||
return db_prompt.prompt_value
|
||
except Exception:
|
||
pass
|
||
|
||
# Use default
|
||
return cls.DEFAULT_PROMPTS.get(prompt_type, '')
|
||
|
||
@classmethod
|
||
def get_negative_prompt(cls, account: Optional[Any] = None) -> str:
|
||
"""
|
||
Get negative prompt.
|
||
Returns template string (not rendered).
|
||
"""
|
||
prompt_type = 'negative_prompt'
|
||
|
||
# Try DB prompt
|
||
if account:
|
||
try:
|
||
from igny8_core.modules.system.models import AIPrompt
|
||
db_prompt = AIPrompt.objects.get(
|
||
account=account,
|
||
prompt_type=prompt_type,
|
||
is_active=True
|
||
)
|
||
return db_prompt.prompt_value
|
||
except Exception:
|
||
pass
|
||
|
||
# Use default
|
||
return cls.DEFAULT_PROMPTS.get(prompt_type, '')
|
||
|
||
|
||
# Convenience function for backward compatibility
|
||
def get_prompt(function_name: str, account=None, task=None, context=None) -> str:
|
||
"""Get prompt using registry"""
|
||
return PromptRegistry.get_prompt(function_name, account=account, task=task, context=context)
|
||
|