|
|
""" |
|
|
Current Occasions – AI-driven trending context for ad generation. |
|
|
Uses the LLM to infer relevant occasions from the current date (holidays, |
|
|
cultural moments, seasonal themes). No fallbacks; results are cached per day. |
|
|
""" |
|
|
|
|
|
from datetime import date |
|
|
from typing import List, Dict, Any |
|
|
|
|
|
|
|
|
_OCCASIONS_CACHE: Dict[str, List[Dict]] = {} |
|
|
|
|
|
|
|
|
def _normalize_ai_occasions(raw: Any) -> List[Dict]: |
|
|
"""Parse and normalize LLM output to our occasion shape.""" |
|
|
if not raw: |
|
|
return [] |
|
|
items = raw.get("occasions") if isinstance(raw, dict) else raw |
|
|
if not isinstance(items, list): |
|
|
return [] |
|
|
normalized: List[Dict] = [] |
|
|
for o in items: |
|
|
if not isinstance(o, dict): |
|
|
continue |
|
|
title = o.get("title") or o.get("name") |
|
|
summary = o.get("summary") or o.get("description") |
|
|
if not title or not summary: |
|
|
continue |
|
|
normalized.append({ |
|
|
"title": str(title).strip(), |
|
|
"summary": str(summary).strip(), |
|
|
"category": str(o.get("category", "Occasion")).strip() or "Occasion", |
|
|
"relevance_window": str(o.get("relevance_window", "week")).strip() or "week", |
|
|
}) |
|
|
return normalized |
|
|
|
|
|
|
|
|
async def get_current_occasions(today: date | None = None) -> List[Dict]: |
|
|
""" |
|
|
Return current occasions for ad trending context. |
|
|
Results are cached per day. |
|
|
|
|
|
Returns: |
|
|
List of dicts: title, summary, category, relevance_window (empty if AI fails). |
|
|
""" |
|
|
if today is None: |
|
|
today = date.today() |
|
|
cache_key = today.isoformat() |
|
|
|
|
|
if cache_key in _OCCASIONS_CACHE: |
|
|
return _OCCASIONS_CACHE[cache_key] |
|
|
|
|
|
try: |
|
|
from services.llm import llm_service |
|
|
except Exception: |
|
|
_OCCASIONS_CACHE[cache_key] = [] |
|
|
return [] |
|
|
|
|
|
system_prompt = """You are an expert in global holidays, cultural moments, and seasonal themes used for advertising and marketing. |
|
|
Given a date, list 4–6 occasions that are relevant RIGHT NOW or in the next few days for that date. Include: |
|
|
- Official holidays (US, India, global where relevant) |
|
|
- Cultural/seasonal moments (e.g. Valentine's Week with daily themes like Rose Day, Teddy Day; Black Friday; back-to-school) |
|
|
- Awareness days/weeks/months (e.g. Black History Month, Earth Day) |
|
|
- Shopping or behavior moments (Singles' Day, Prime Day, etc.) |
|
|
Be specific to the exact date when it matters (e.g. "Teddy Day" on Feb 10, "Valentine's Day" on Feb 14). |
|
|
Respond with valid JSON only, in this exact shape (no extra fields): |
|
|
{"occasions": [{"title": "...", "summary": "One sentence on why it matters for ads.", "category": "Occasion", "relevance_window": "day"|"week"|"month"}]} |
|
|
Use relevance_window: "day" for single-day events, "week" for a week-long moment, "month" for month-long themes.""" |
|
|
|
|
|
user_prompt = f"""Today's date is {today.isoformat()} ({today.strftime('%A, %B %d, %Y')}). |
|
|
List 4–6 current or upcoming occasions that are relevant for advertising and marketing on or around this date. Consider global and regional relevance. Output JSON only.""" |
|
|
|
|
|
try: |
|
|
response = await llm_service.generate_json( |
|
|
prompt=user_prompt, |
|
|
system_prompt=system_prompt, |
|
|
temperature=0.3, |
|
|
) |
|
|
occasions = _normalize_ai_occasions(response) |
|
|
except Exception as e: |
|
|
print(f"⚠️ AI occasions failed: {e}") |
|
|
occasions = [] |
|
|
|
|
|
_OCCASIONS_CACHE[cache_key] = occasions |
|
|
return occasions |
|
|
|
|
|
|
|
|
def clear_occasions_cache() -> None: |
|
|
"""Clear the occasions cache (e.g. for tests).""" |
|
|
global _OCCASIONS_CACHE |
|
|
_OCCASIONS_CACHE = {} |
|
|
|