Spaces:
Sleeping
Sleeping
| """Database endpoints: stats, list ads, get/delete ad, edit copy.""" | |
| from typing import Optional | |
| from fastapi import APIRouter, HTTPException, Depends | |
| from api.schemas import DbStatsResponse, EditAdCopyRequest | |
| from services.database import db_service | |
| from services.auth_dependency import get_current_user | |
| router = APIRouter(tags=["database"]) | |
| async def get_database_stats(username: str = Depends(get_current_user)): | |
| """Get statistics about stored ad creatives for the current user.""" | |
| return await db_service.get_stats(username=username) | |
| async def list_stored_ads( | |
| niche: Optional[str] = None, | |
| generation_method: Optional[str] = None, | |
| limit: int = 50, | |
| offset: int = 0, | |
| username: str = Depends(get_current_user), | |
| ): | |
| """List ad creatives for the current user with optional filters and pagination.""" | |
| ads, total = await db_service.list_ad_creatives( | |
| username=username, | |
| niche=niche, | |
| generation_method=generation_method, | |
| limit=limit, | |
| offset=offset, | |
| ) | |
| return { | |
| "total": total, | |
| "limit": limit, | |
| "offset": offset, | |
| "ads": [ | |
| { | |
| "id": str(ad.get("id", "")), | |
| "niche": ad.get("niche", ""), | |
| "title": ad.get("title"), | |
| "headline": ad.get("headline", ""), | |
| "primary_text": ad.get("primary_text"), | |
| "description": ad.get("description"), | |
| "body_story": ad.get("body_story"), | |
| "cta": ad.get("cta", ""), | |
| "psychological_angle": ad.get("psychological_angle", ""), | |
| "image_url": ad.get("image_url"), | |
| "r2_url": ad.get("r2_url"), | |
| "image_filename": ad.get("image_filename"), | |
| "image_model": ad.get("image_model"), | |
| "angle_key": ad.get("angle_key"), | |
| "concept_key": ad.get("concept_key"), | |
| "generation_method": ad.get("generation_method", "standard"), | |
| "created_at": ad.get("created_at"), | |
| } | |
| for ad in ads | |
| ], | |
| } | |
| async def get_stored_ad(ad_id: str): | |
| """Get a specific ad creative by ID.""" | |
| ad = await db_service.get_ad_creative(ad_id) | |
| if not ad: | |
| raise HTTPException(status_code=404, detail=f"Ad '{ad_id}' not found") | |
| return { | |
| "id": str(ad.get("id", "")), | |
| "niche": ad.get("niche", ""), | |
| "title": ad.get("title"), | |
| "headline": ad.get("headline", ""), | |
| "primary_text": ad.get("primary_text"), | |
| "description": ad.get("description"), | |
| "body_story": ad.get("body_story"), | |
| "cta": ad.get("cta", ""), | |
| "psychological_angle": ad.get("psychological_angle", ""), | |
| "why_it_works": ad.get("why_it_works"), | |
| "image_url": ad.get("image_url"), | |
| "image_filename": ad.get("image_filename"), | |
| "image_model": ad.get("image_model"), | |
| "image_seed": ad.get("image_seed"), | |
| "r2_url": ad.get("r2_url"), | |
| "angle_key": ad.get("angle_key"), | |
| "angle_name": ad.get("angle_name"), | |
| "angle_trigger": ad.get("angle_trigger"), | |
| "angle_category": ad.get("angle_category"), | |
| "concept_key": ad.get("concept_key"), | |
| "concept_name": ad.get("concept_name"), | |
| "concept_structure": ad.get("concept_structure"), | |
| "concept_visual": ad.get("concept_visual"), | |
| "concept_category": ad.get("concept_category"), | |
| "generation_method": ad.get("generation_method", "standard"), | |
| "metadata": ad.get("metadata"), | |
| "created_at": ad.get("created_at"), | |
| "updated_at": ad.get("updated_at"), | |
| } | |
| async def delete_stored_ad(ad_id: str, username: str = Depends(get_current_user)): | |
| """Delete an ad creative. Users can only delete their own ads.""" | |
| success = await db_service.delete_ad_creative(ad_id, username=username) | |
| if not success: | |
| raise HTTPException(status_code=404, detail=f"Ad '{ad_id}' not found or could not be deleted") | |
| return {"success": True, "deleted_id": ad_id} | |
| async def edit_ad_copy( | |
| request: EditAdCopyRequest, | |
| username: str = Depends(get_current_user), | |
| ): | |
| """ | |
| Edit ad copy fields. Modes: manual (direct update) or ai (AI-improved version). | |
| """ | |
| from services.llm import LLMService | |
| ad = await db_service.get_ad_creative(request.ad_id) | |
| if not ad: | |
| raise HTTPException(status_code=404, detail=f"Ad '{request.ad_id}' not found") | |
| if ad.get("username") != username: | |
| raise HTTPException(status_code=403, detail="You can only edit your own ads") | |
| if request.mode == "manual": | |
| success = await db_service.update_ad_creative( | |
| ad_id=request.ad_id, | |
| username=username, | |
| **{request.field: request.value}, | |
| ) | |
| if not success: | |
| raise HTTPException(status_code=500, detail="Failed to update ad") | |
| return {"edited_value": request.value, "success": True} | |
| llm_service = LLMService() | |
| field_labels = { | |
| "title": "title", | |
| "headline": "headline", | |
| "primary_text": "primary text", | |
| "description": "description", | |
| "body_story": "body story", | |
| "cta": "call to action", | |
| } | |
| field_label = field_labels.get(request.field, request.field) | |
| current_value = request.value | |
| niche = ad.get("niche", "general") | |
| system_prompt = f"""You are an expert copywriter specializing in high-converting ad copy for {niche.replace('_', ' ')}. | |
| Your task is to improve the {field_label} while maintaining its core message and emotional impact. | |
| Keep the same tone and style, but make it more compelling, clear, and effective.""" | |
| user_prompt = f"""Current {field_label}:\n{current_value}\n\n""" | |
| if request.user_suggestion: | |
| user_prompt += f"User's suggestion: {request.user_suggestion}\n\n" | |
| user_prompt += f"""Please provide an improved version of this {field_label} that: | |
| 1. Maintains the core message and emotional impact | |
| 2. Is more compelling and engaging | |
| 3. Follows best practices for {field_label} in ad copy | |
| 4. {"Incorporates the user's suggestion" if request.user_suggestion else "Is optimized for conversion"} | |
| Return ONLY the improved {field_label} text, without any explanations or additional text.""" | |
| try: | |
| edited_value = await llm_service.generate( | |
| prompt=user_prompt, | |
| system_prompt=system_prompt, | |
| temperature=0.7, | |
| ) | |
| edited_value = edited_value.strip().strip('"').strip("'") | |
| return {"edited_value": edited_value, "success": True} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Failed to generate AI edit: {str(e)}") | |