feat: Add Content Generation Engine v2 with quality scoring
Major improvements to AI content generation: ## New Components (app/services/ai/) - PromptLibrary: YAML-based prompt templates with inheritance - ContextEngine: Anti-repetition and best performers tracking - ContentGeneratorV2: Enhanced generation with dynamic parameters - PlatformAdapter: Platform-specific content adaptation - ContentValidator: AI-powered quality scoring (0-100) ## Prompt Library (app/prompts/) - 3 personalities: default, educational, promotional - 5 templates: tip_tech, product_post, service_post, thread, response - 4 platform configs: x, threads, instagram, facebook - Few-shot examples by category: ia, productividad, seguridad ## Database Changes - New table: content_memory (tracks generated content) - New columns in posts: quality_score, score_breakdown, generation_attempts ## New API Endpoints (/api/v2/generate/) - POST /generate - Generation with quality check - POST /generate/batch - Batch generation - POST /quality/evaluate - Evaluate content quality - GET /templates, /personalities, /platforms - List configs ## Celery Tasks - update_engagement_scores (every 6h) - cleanup_old_memory (monthly) - refresh_best_posts_yaml (weekly) ## Tests - Comprehensive tests for all AI engine components Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,9 +16,13 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.database import Base
|
||||
# Import all models for autogenerate support
|
||||
from app.models import (
|
||||
User, Product, Service, TipTemplate,
|
||||
Post, ContentCalendar, ImageTemplate, Interaction
|
||||
Post, ContentCalendar, ImageTemplate, Interaction,
|
||||
PostMetrics, AnalyticsReport, Lead, OdooSyncLog,
|
||||
ABTest, ABTestVariant, RecycledPost, ThreadSeries, ThreadPost,
|
||||
ContentMemory
|
||||
)
|
||||
|
||||
# this is the Alembic Config object
|
||||
|
||||
100
alembic/versions/20260128_add_content_memory_and_quality.py
Normal file
100
alembic/versions/20260128_add_content_memory_and_quality.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""Add content_memory table and quality columns to posts
|
||||
|
||||
Revision ID: 20260128001
|
||||
Revises:
|
||||
Create Date: 2026-01-28
|
||||
|
||||
This migration adds:
|
||||
1. New table 'content_memory' for tracking generated content
|
||||
2. New columns in 'posts' for quality scoring
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '20260128001'
|
||||
down_revision = None
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# === Create content_memory table ===
|
||||
op.create_table(
|
||||
'content_memory',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('post_id', sa.Integer(), nullable=False),
|
||||
|
||||
# Content analysis
|
||||
sa.Column('topics', postgresql.ARRAY(sa.String()), nullable=True),
|
||||
sa.Column('key_phrases', postgresql.ARRAY(sa.String()), nullable=True),
|
||||
sa.Column('hook_type', sa.String(50), nullable=True),
|
||||
sa.Column('content_summary', sa.Text(), nullable=True),
|
||||
sa.Column('content_embedding', postgresql.JSON(), nullable=True),
|
||||
|
||||
# Engagement metrics
|
||||
sa.Column('engagement_score', sa.Float(), nullable=True),
|
||||
sa.Column('engagement_breakdown', postgresql.JSON(), nullable=True),
|
||||
sa.Column('is_top_performer', sa.Boolean(), default=False),
|
||||
|
||||
# Quality score
|
||||
sa.Column('quality_score', sa.Integer(), nullable=True),
|
||||
sa.Column('quality_breakdown', postgresql.JSON(), nullable=True),
|
||||
|
||||
# Example usage tracking
|
||||
sa.Column('times_used_as_example', sa.Integer(), default=0),
|
||||
sa.Column('last_used_as_example', sa.DateTime(), nullable=True),
|
||||
|
||||
# Metadata
|
||||
sa.Column('platform', sa.String(20), nullable=True),
|
||||
sa.Column('content_type', sa.String(50), nullable=True),
|
||||
sa.Column('personality_used', sa.String(50), nullable=True),
|
||||
sa.Column('template_used', sa.String(50), nullable=True),
|
||||
|
||||
# Timestamps
|
||||
sa.Column('created_at', sa.DateTime(), server_default=sa.text('now()'), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), server_default=sa.text('now()'), nullable=True),
|
||||
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.ForeignKeyConstraint(['post_id'], ['posts.id'], ondelete='CASCADE'),
|
||||
)
|
||||
|
||||
# Create indexes for content_memory
|
||||
op.create_index('ix_content_memory_post_id', 'content_memory', ['post_id'], unique=True)
|
||||
op.create_index('ix_content_memory_engagement_score', 'content_memory', ['engagement_score'])
|
||||
op.create_index('ix_content_memory_is_top_performer', 'content_memory', ['is_top_performer'])
|
||||
op.create_index('ix_content_memory_hook_type', 'content_memory', ['hook_type'])
|
||||
op.create_index('ix_content_memory_platform', 'content_memory', ['platform'])
|
||||
op.create_index('ix_content_memory_content_type', 'content_memory', ['content_type'])
|
||||
op.create_index('ix_content_memory_created_at', 'content_memory', ['created_at'])
|
||||
|
||||
# === Add quality columns to posts table ===
|
||||
op.add_column('posts', sa.Column('quality_score', sa.Integer(), nullable=True))
|
||||
op.add_column('posts', sa.Column('score_breakdown', postgresql.JSON(), nullable=True))
|
||||
op.add_column('posts', sa.Column('generation_attempts', sa.Integer(), server_default='1', nullable=True))
|
||||
|
||||
# Create index for quality_score
|
||||
op.create_index('ix_posts_quality_score', 'posts', ['quality_score'])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# Remove indexes from posts
|
||||
op.drop_index('ix_posts_quality_score', table_name='posts')
|
||||
|
||||
# Remove columns from posts
|
||||
op.drop_column('posts', 'generation_attempts')
|
||||
op.drop_column('posts', 'score_breakdown')
|
||||
op.drop_column('posts', 'quality_score')
|
||||
|
||||
# Remove indexes from content_memory
|
||||
op.drop_index('ix_content_memory_created_at', table_name='content_memory')
|
||||
op.drop_index('ix_content_memory_content_type', table_name='content_memory')
|
||||
op.drop_index('ix_content_memory_platform', table_name='content_memory')
|
||||
op.drop_index('ix_content_memory_hook_type', table_name='content_memory')
|
||||
op.drop_index('ix_content_memory_is_top_performer', table_name='content_memory')
|
||||
op.drop_index('ix_content_memory_engagement_score', table_name='content_memory')
|
||||
op.drop_index('ix_content_memory_post_id', table_name='content_memory')
|
||||
|
||||
# Drop content_memory table
|
||||
op.drop_table('content_memory')
|
||||
Reference in New Issue
Block a user