This commit is contained in:
IGNY8 VPS (Salman)
2025-11-26 19:14:30 +00:00
parent f88aae78b1
commit 94a8aee0e2
12 changed files with 889 additions and 53 deletions

View File

@@ -524,16 +524,20 @@ class AIEngine:
'generate_image_prompts': 'image_prompt_extraction',
'generate_images': 'image_generation',
'generate_site_structure': 'site_structure_generation',
'generate_page_content': 'content_generation', # Site Builder page content
}
return mapping.get(function_name, function_name)
def _get_estimated_amount(self, function_name, data, payload):
"""Get estimated amount for credit calculation (before operation)"""
if function_name == 'generate_content':
# Estimate word count from task or default
if isinstance(data, dict):
return data.get('estimated_word_count', 1000)
return 1000 # Default estimate
if function_name == 'generate_content' or function_name == 'generate_page_content':
# Estimate word count - tasks don't have word_count field, use default
# For generate_content, data is a list of Task objects
# For generate_page_content, data is a PageBlueprint object
if isinstance(data, list) and len(data) > 0:
# Multiple tasks - estimate 1000 words per task
return len(data) * 1000
return 1000 # Default estimate for single item
elif function_name == 'generate_images':
# Count images to generate
if isinstance(payload, dict):
@@ -550,16 +554,24 @@ class AIEngine:
def _get_actual_amount(self, function_name, save_result, parsed, data):
"""Get actual amount for credit calculation (after operation)"""
if function_name == 'generate_content':
if function_name == 'generate_content' or function_name == 'generate_page_content':
# Get actual word count from saved content
if isinstance(save_result, dict):
word_count = save_result.get('word_count')
if word_count:
if word_count and word_count > 0:
return word_count
# Fallback: estimate from parsed content
if isinstance(parsed, dict) and 'content' in parsed:
content = parsed['content']
return len(content.split()) if isinstance(content, str) else 1000
# Fallback: estimate from html_content if available
if isinstance(parsed, dict) and 'html_content' in parsed:
html_content = parsed['html_content']
if isinstance(html_content, str):
# Strip HTML tags for word count
import re
text = re.sub(r'<[^>]+>', '', html_content)
return len(text.split())
return 1000
elif function_name == 'generate_images':
# Count successfully generated images

View File

@@ -65,7 +65,7 @@ class GenerateContentFunction(BaseAIFunction):
# STAGE 3: Preload relationships - taxonomy_term instead of taxonomy
tasks = list(queryset.select_related(
'account', 'site', 'sector', 'cluster', 'taxonomy_term'
).prefetch_related('keywords'))
))
if not tasks:
raise ValueError("No tasks found")
@@ -106,11 +106,10 @@ class GenerateContentFunction(BaseAIFunction):
if task.taxonomy_term.taxonomy_type:
taxonomy_data += f"Type: {task.taxonomy_term.get_taxonomy_type_display()}\n"
# STAGE 3: Build keywords context (from keywords M2M)
# STAGE 3: Build keywords context (from keywords TextField)
keywords_data = ''
if task.keywords.exists():
keyword_list = [kw.keyword for kw in task.keywords.all()]
keywords_data = "Keywords: " + ", ".join(keyword_list) + "\n"
if task.keywords:
keywords_data = f"Keywords: {task.keywords}\n"
# Get prompt from registry with context
prompt = PromptRegistry.get_prompt(
@@ -192,6 +191,7 @@ class GenerateContentFunction(BaseAIFunction):
# Core fields
title=title,
content_html=content_html or '',
word_count=word_count,
cluster=task.cluster,
content_type=task.content_type,
content_structure=task.content_structure,

View File

@@ -63,7 +63,7 @@ class GenerateImagePromptsFunction(BaseAIFunction):
if account:
queryset = queryset.filter(account=account)
contents = list(queryset.select_related('task', 'account', 'site', 'sector'))
contents = list(queryset.select_related('account', 'site', 'sector', 'cluster'))
if not contents:
raise ValueError("No content records found")
@@ -203,7 +203,7 @@ class GenerateImagePromptsFunction(BaseAIFunction):
"""Extract title, intro paragraphs, and H2 headings from content HTML"""
from bs4 import BeautifulSoup
html_content = content.html_content or ''
html_content = content.content_html or ''
soup = BeautifulSoup(html_content, 'html.parser')
# Extract title

View File

@@ -544,42 +544,42 @@ 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": {
"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": {
"data": {{
"heading": "[Section heading]",
"content": "[Rich text content with paragraphs, lists, etc.]"
}
},
{
}}
}},
{{
"type": "features",
"data": {
"data": {{
"heading": "[Features section heading]",
"content": [
"[Feature 1: Description]",
"[Feature 2: Description]",
"[Feature 3: Description]"
]
}
},
{
}}
}},
{{
"type": "testimonials",
"data": {
"data": {{
"heading": "[Testimonials heading]",
"subheading": "[Optional subheading]",
"content": [
@@ -587,20 +587,20 @@ Return ONLY a JSON object in this exact structure:
"[Testimonial quote 2]",
"[Testimonial quote 3]"
]
}
},
{
}}
}},
{{
"type": "cta",
"data": {
"data": {{
"heading": "[CTA heading]",
"subheading": "[CTA subheading]",
"content": "[CTA description]",
"buttonText": "[Button text]",
"buttonLink": "[Button link]"
}
}
}}
}}
]
}
}}
BLOCK TYPE GUIDELINES:
----------------------
@@ -699,7 +699,7 @@ 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]",
@@ -710,39 +710,39 @@ Return ONLY a JSON object in this format:
"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": {{"Level 1": {{"Level 2": ["Level 3"]}}}}
}}
],
"structure_data": {
"structure_data": {{
"taxonomy_type": "[Taxonomy type]",
"item_count": [Integer],
"hierarchy_levels": [Integer]
}
}
}}
}}
CONTENT REQUIREMENTS:
- Clear taxonomy overview and purpose
@@ -865,7 +865,7 @@ CONTENT REQUIREMENTS:
if '{' in rendered and '}' in rendered:
try:
rendered = rendered.format(**normalized_context)
except (KeyError, ValueError) as e:
except (KeyError, ValueError, IndexError) 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.")