removeed ifeadea generaetion fucntion
This commit is contained in:
@@ -1,303 +0,0 @@
|
||||
"""
|
||||
Generate Ideas AI Function
|
||||
Extracted from modules/planner/tasks.py
|
||||
"""
|
||||
import logging
|
||||
import json
|
||||
from typing import Dict, List, Any
|
||||
from django.db import transaction
|
||||
from igny8_core.ai.base import BaseAIFunction
|
||||
from igny8_core.modules.planner.models import Clusters, ContentIdeas
|
||||
from igny8_core.ai.ai_core import AICore
|
||||
from igny8_core.ai.validators import validate_cluster_exists
|
||||
from igny8_core.ai.tracker import ConsoleStepTracker
|
||||
from igny8_core.ai.prompts import PromptRegistry
|
||||
from igny8_core.ai.settings import get_model_config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GenerateIdeasFunction(BaseAIFunction):
|
||||
"""Generate content ideas from clusters using AI"""
|
||||
|
||||
def get_name(self) -> str:
|
||||
return 'generate_ideas'
|
||||
|
||||
def get_metadata(self) -> Dict:
|
||||
return {
|
||||
'display_name': 'Generate Ideas',
|
||||
'description': 'Generate SEO-optimized content ideas from keyword clusters',
|
||||
'phases': {
|
||||
'INIT': 'Initializing idea generation...',
|
||||
'PREP': 'Loading clusters...',
|
||||
'AI_CALL': 'Generating ideas with AI...',
|
||||
'PARSE': 'Parsing idea data...',
|
||||
'SAVE': 'Saving ideas...',
|
||||
'DONE': 'Ideas generated!'
|
||||
}
|
||||
}
|
||||
|
||||
def get_max_items(self) -> int:
|
||||
# No limit - return None
|
||||
return None
|
||||
|
||||
def validate(self, payload: dict, account=None) -> Dict:
|
||||
"""Validate cluster IDs"""
|
||||
result = super().validate(payload, account)
|
||||
if not result['valid']:
|
||||
return result
|
||||
|
||||
# Check cluster exists
|
||||
cluster_ids = payload.get('ids', [])
|
||||
if cluster_ids:
|
||||
cluster_id = cluster_ids[0] # For single cluster idea generation
|
||||
cluster_result = validate_cluster_exists(cluster_id, account)
|
||||
if not cluster_result['valid']:
|
||||
return cluster_result
|
||||
|
||||
# Removed plan limits check
|
||||
|
||||
return {'valid': True}
|
||||
|
||||
def prepare(self, payload: dict, account=None) -> Dict:
|
||||
"""Load cluster with keywords"""
|
||||
cluster_ids = payload.get('ids', [])
|
||||
if not cluster_ids:
|
||||
raise ValueError("No cluster IDs provided")
|
||||
|
||||
cluster_id = cluster_ids[0] # Single cluster for now
|
||||
|
||||
queryset = Clusters.objects.filter(id=cluster_id)
|
||||
if account:
|
||||
queryset = queryset.filter(account=account)
|
||||
|
||||
cluster = queryset.select_related('sector', 'account', 'site').prefetch_related('keywords').first()
|
||||
|
||||
if not cluster:
|
||||
raise ValueError("Cluster not found")
|
||||
|
||||
# Get keywords for this cluster
|
||||
from igny8_core.modules.planner.models import Keywords
|
||||
keywords = Keywords.objects.filter(cluster=cluster).values_list('keyword', flat=True)
|
||||
|
||||
# Format cluster data for AI
|
||||
cluster_data = [{
|
||||
'id': cluster.id,
|
||||
'name': cluster.name,
|
||||
'description': cluster.description or '',
|
||||
'keywords': list(keywords),
|
||||
}]
|
||||
|
||||
return {
|
||||
'cluster': cluster,
|
||||
'cluster_data': cluster_data,
|
||||
'account': account or cluster.account
|
||||
}
|
||||
|
||||
def build_prompt(self, data: Dict, account=None) -> str:
|
||||
"""Build ideas generation prompt using registry"""
|
||||
cluster_data = data['cluster_data']
|
||||
account = account or data.get('account')
|
||||
|
||||
# Format clusters text
|
||||
clusters_text = '\n'.join([
|
||||
f"Cluster ID: {c.get('id', '')} | Name: {c.get('name', '')} | Description: {c.get('description', '')}"
|
||||
for c in cluster_data
|
||||
])
|
||||
|
||||
# Format cluster keywords
|
||||
cluster_keywords_text = '\n'.join([
|
||||
f"Cluster ID: {c.get('id', '')} | Name: {c.get('name', '')} | Keywords: {', '.join(c.get('keywords', []))}"
|
||||
for c in cluster_data
|
||||
])
|
||||
|
||||
# Get prompt from registry with context
|
||||
prompt = PromptRegistry.get_prompt(
|
||||
function_name='generate_ideas',
|
||||
account=account,
|
||||
context={
|
||||
'CLUSTERS': clusters_text,
|
||||
'CLUSTER_KEYWORDS': cluster_keywords_text,
|
||||
}
|
||||
)
|
||||
|
||||
return prompt
|
||||
|
||||
def parse_response(self, response: str, step_tracker=None) -> List[Dict]:
|
||||
"""Parse AI response into idea data"""
|
||||
ai_core = AICore(account=self.account if hasattr(self, 'account') else None)
|
||||
json_data = ai_core.extract_json(response)
|
||||
|
||||
if not json_data or 'ideas' not in json_data:
|
||||
error_msg = f"Failed to parse ideas response: {response[:200]}..."
|
||||
logger.error(error_msg)
|
||||
raise ValueError(error_msg)
|
||||
|
||||
return json_data.get('ideas', [])
|
||||
|
||||
def save_output(
|
||||
self,
|
||||
parsed: List[Dict],
|
||||
original_data: Dict,
|
||||
account=None,
|
||||
progress_tracker=None,
|
||||
step_tracker=None
|
||||
) -> Dict:
|
||||
"""Save ideas to database"""
|
||||
cluster = original_data['cluster']
|
||||
account = account or original_data.get('account')
|
||||
|
||||
if not account:
|
||||
raise ValueError("Account is required for idea creation")
|
||||
|
||||
ideas_created = 0
|
||||
|
||||
with transaction.atomic():
|
||||
for idea_data in parsed:
|
||||
# Handle description - might be dict or string
|
||||
description = idea_data.get('description', '')
|
||||
if isinstance(description, dict):
|
||||
description = json.dumps(description)
|
||||
elif not isinstance(description, str):
|
||||
description = str(description)
|
||||
|
||||
# Handle target_keywords
|
||||
target_keywords = idea_data.get('covered_keywords', '') or idea_data.get('target_keywords', '')
|
||||
|
||||
# Create ContentIdeas record
|
||||
ContentIdeas.objects.create(
|
||||
idea_title=idea_data.get('title', 'Untitled Idea'),
|
||||
description=description,
|
||||
content_type=idea_data.get('content_type', 'blog_post'),
|
||||
content_structure=idea_data.get('content_structure', 'supporting_page'),
|
||||
target_keywords=target_keywords,
|
||||
keyword_cluster=cluster,
|
||||
estimated_word_count=idea_data.get('estimated_word_count', 1500),
|
||||
status='new',
|
||||
account=account,
|
||||
site=cluster.site,
|
||||
sector=cluster.sector,
|
||||
)
|
||||
ideas_created += 1
|
||||
|
||||
return {
|
||||
'count': ideas_created,
|
||||
'ideas_created': ideas_created
|
||||
}
|
||||
|
||||
|
||||
def generate_ideas_core(cluster_id: int, account_id: int = None, progress_callback=None):
|
||||
"""
|
||||
Core logic for generating ideas (legacy function signature for backward compatibility).
|
||||
Can be called with or without Celery.
|
||||
|
||||
Args:
|
||||
cluster_id: Cluster ID to generate idea for
|
||||
account_id: Account ID for account isolation
|
||||
progress_callback: Optional function to call for progress updates
|
||||
|
||||
Returns:
|
||||
Dict with 'success', 'idea_created', 'message', etc.
|
||||
"""
|
||||
import sys
|
||||
print("=" * 80, flush=True, file=sys.stdout)
|
||||
print("[GENERATE IDEAS CORE] Function started", flush=True, file=sys.stdout)
|
||||
print(f"[GENERATE IDEAS CORE] cluster_id: {cluster_id}", flush=True, file=sys.stdout)
|
||||
print(f"[GENERATE IDEAS CORE] account_id: {account_id}", flush=True, file=sys.stdout)
|
||||
print("=" * 80, flush=True, file=sys.stdout)
|
||||
|
||||
tracker = ConsoleStepTracker('generate_ideas')
|
||||
tracker.init("Task started")
|
||||
|
||||
try:
|
||||
from igny8_core.auth.models import Account
|
||||
|
||||
account = None
|
||||
if account_id:
|
||||
account = Account.objects.get(id=account_id)
|
||||
|
||||
tracker.prep("Loading account and cluster data...")
|
||||
|
||||
# Use the new function class
|
||||
fn = GenerateIdeasFunction()
|
||||
# Store account for use in methods
|
||||
fn.account = account
|
||||
|
||||
# Prepare payload
|
||||
payload = {'ids': [cluster_id]}
|
||||
|
||||
# Validate
|
||||
tracker.prep("Validating input...")
|
||||
validated = fn.validate(payload, account)
|
||||
if not validated['valid']:
|
||||
tracker.error('ValidationError', validated['error'])
|
||||
return {'success': False, 'error': validated['error']}
|
||||
|
||||
# Prepare data
|
||||
tracker.prep("Loading cluster with keywords...")
|
||||
data = fn.prepare(payload, account)
|
||||
|
||||
# Build prompt
|
||||
tracker.prep("Building prompt...")
|
||||
prompt = fn.build_prompt(data, account)
|
||||
|
||||
# Get model config from settings
|
||||
model_config = get_model_config('generate_ideas')
|
||||
|
||||
# Call AI using centralized request handler
|
||||
ai_core = AICore(account=account)
|
||||
result = ai_core.run_ai_request(
|
||||
prompt=prompt,
|
||||
model=model_config.get('model'),
|
||||
max_tokens=model_config.get('max_tokens'),
|
||||
temperature=model_config.get('temperature'),
|
||||
response_format=model_config.get('response_format'),
|
||||
function_name='generate_ideas',
|
||||
tracker=tracker
|
||||
)
|
||||
|
||||
if result.get('error'):
|
||||
return {'success': False, 'error': result['error']}
|
||||
|
||||
# Parse response
|
||||
tracker.parse("Parsing AI response...")
|
||||
ideas_data = fn.parse_response(result['content'])
|
||||
|
||||
if not ideas_data:
|
||||
tracker.error('ParseError', 'No ideas generated by AI')
|
||||
return {'success': False, 'error': 'No ideas generated by AI'}
|
||||
|
||||
tracker.parse(f"Parsed {len(ideas_data)} idea(s)")
|
||||
|
||||
# Take first idea
|
||||
idea_data = ideas_data[0]
|
||||
|
||||
# Save output
|
||||
tracker.save("Saving idea to database...")
|
||||
save_result = fn.save_output(ideas_data, data, account)
|
||||
tracker.save(f"Saved {save_result['ideas_created']} idea(s)")
|
||||
|
||||
tracker.done(f"Idea '{idea_data.get('title', 'Untitled')}' created successfully")
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'idea_created': save_result['ideas_created'],
|
||||
'message': f"Idea '{idea_data.get('title', 'Untitled')}' created"
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
import sys
|
||||
import traceback
|
||||
error_msg = str(e)
|
||||
error_type = type(e).__name__
|
||||
|
||||
print("=" * 80, flush=True, file=sys.stdout)
|
||||
print(f"[GENERATE IDEAS CORE] ERROR: {error_type}: {error_msg}", flush=True, file=sys.stdout)
|
||||
print("[GENERATE IDEAS CORE] Full traceback:", flush=True, file=sys.stdout)
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
print("=" * 80, flush=True, file=sys.stdout)
|
||||
|
||||
tracker.error('Exception', error_msg, e)
|
||||
logger.error(f"Error in generate_ideas_core: {error_msg}", exc_info=True)
|
||||
return {'success': False, 'error': error_msg, 'error_type': error_type}
|
||||
|
||||
Reference in New Issue
Block a user