r1sh1's picture
Update app.py
cc5ef85 verified
Raw History Blame Contribute Delete
7.51 kB
import os
from fastapi import FastAPI, UploadFile, File, Form
from fastapi.middleware.cors import CORSMiddleware
import google.generativeai as genai
from PIL import Image
import io
import json
import hashlib
from google.api_core import exceptions as google_exceptions
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
print("Initializing backend...")
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY")
MODEL_LOADED = False
if GEMINI_API_KEY:
try:
genai.configure(api_key=GEMINI_API_KEY)
# gemini-flash-latest is usually the safest bet for active Free Tier quota
candidate_models = [
'gemini-flash-latest',
'gemini-2.0-flash',
'gemini-pro-latest'
]
model = None
for model_name in candidate_models:
try:
print(f"Attempting to load model: {model_name}...")
model = genai.GenerativeModel(model_name)
# Verify it exists by a dummy call or just trust it if it constructs
MODEL_LOADED = True
print(f"Successfully initialized {model_name}.")
break
except Exception as inner_e:
print(f"Failed to load {model_name}: {inner_e}")
if not model:
print("No suitable Gemini model found among candidates.")
except Exception as e:
print(f"General configuration error: {e}")
else:
print("Warning: GEMINI_API_KEY environment variable not set. Running in simulation mode.")
@app.get("/")
def read_root():
return {"status": "online", "engine": "Creative Intelligence Engine", "active": True}
@app.get("/api/health")
def health_check():
return {
"status": "ok",
"model_loaded": MODEL_LOADED,
"api_key_configured": bool(GEMINI_API_KEY),
"message": "Neural Engine analysis is operational."
}
prompt_template = """
You are a neuromarketing expert AI analyzing an ad creative image critically with emphasis on copy, CTA, best time and platform etc..
Evaluate this image strictly on these 5 cognitive parameters. Give a score between 0.10 and 0.98.
Also provide a VERY brief 1-sentence analytical reason based exactly on what is visibly in the image.
The parameters are:
- visual_saliency (How effectively it draws the eye with contrast, colors, shapes)
- cognitive_ease (Clean layout vs clutter. Higher score = easier to process)
- emotional_arousal (Does it trigger a mood via faces, colors, or atmosphere)
- value_recognition (Speed of identifying the core reward/benefit/CTA)
- memory_encoding (Distinctiveness of brand motifs, logos, or striking imagery)
Return strictly valid JSON containing a single "scores" object, a "reasons" object, and an "insights" array.
Format:
{
"scores": {
"visual_saliency": 0.42,
"cognitive_ease": 0.28,
"emotional_arousal": 0.35,
"value_recognition": 0.48,
"memory_encoding": 0.22
},
"reasons": {
"visual_saliency": "The high contrast between the bright red text and black background immediately grabs attention.",
"cognitive_ease": "...",
"emotional_arousal": "...",
"value_recognition": "...",
"memory_encoding": "..."
},
"insights": [
"Overall synthesis of the creative's strength."
]
}
"""
@app.post("/api/analyze")
async def analyze_creative(
creative: UploadFile = File(None),
description: str = Form("Creative"),
name: str = Form("creative"),
id: str = Form("creative")
):
source = "Cloud VLM (Neural Proxy)"
if creative and MODEL_LOADED:
try:
image_data = await creative.read()
image = Image.open(io.BytesIO(image_data))
if image.mode != "RGB":
image = image.convert("RGB")
response = model.generate_content([prompt_template, image])
text_response = response.text
# Extract JSON block safely
json_str = text_response
if "```json" in text_response:
json_str = text_response.split("```json")[1].split("```")[0].strip()
elif "```" in text_response:
json_str = text_response.split("```")[1].split("```")[0].strip()
data = json.loads(json_str)
scores = data.get("scores", {})
reasons = data.get("reasons", {})
insights = data.get("insights", ["Analysis completed successfully."])
except google_exceptions.ResourceExhausted as e:
print(f"Quota Exceeded (429): {e}")
scores = {
"visual_saliency": 0.40, "cognitive_ease": 0.35,
"emotional_arousal": 0.30, "value_recognition": 0.40, "memory_encoding": 0.35
}
reasons = {k: "Analysis paused due to capacity constraints." for k in scores}
insights = ["Neural Engine is currently at peak capacity. Analysis is temporarily paused to ensure data fidelity. Please try again in a few moments."]
source = "Cloud VLM (Quota Reached)"
except Exception as e:
print(f"API Error during analysis: {e}")
scores = {
"visual_saliency": 0.32, "cognitive_ease": 0.28,
"emotional_arousal": 0.10, "value_recognition": 0.45, "memory_encoding": 0.24
}
reasons = {k: "Neural processing temporarily restricted." for k in scores}
insights = ["The Neural Engine encountered an unexpected synchronization error. Please retry the analysis."]
source = f"HF Space (Sync Error)"
else:
# Deterministic fallback based on file name
seed = name + description
h = int(hashlib.md5(seed.encode()).hexdigest(), 16)
scores = {
"visual_saliency": 0.10 + ((h % 31) / 100),
"cognitive_ease": 0.10 + (((h // 2) % 31) / 100),
"emotional_arousal": 0.10 + (((h // 5) % 31) / 100),
"value_recognition": 0.10 + (((h // 11) % 31) / 100),
"memory_encoding": 0.10 + (((h // 7) % 31) / 100),
}
reasons = {
"visual_saliency": "Mock reason based on deterministic simulation.",
"cognitive_ease": "The layout is simulated to have this ease.",
"emotional_arousal": "Colors simulated from fallback logic.",
"value_recognition": "Simulated CTA recognition.",
"memory_encoding": "Simulated branding elements."
}
insights = ["Running in simulation mode. No valid GEMINI_API_KEY provided."]
source = "HF Space (Simulation Fallback)"
# Calculate timeline metrics from current scores (averaging for consistency)
avg_score = sum(scores.values()) / len(scores) if scores else 0.5
timeline = {
"attention": round(avg_score * 100 + (h % 10 if 'h' in locals() else 5), 1),
"emotion": round(avg_score * 95 + (h % 15 if 'h' in locals() else 2), 1),
"memory": round(avg_score * 98 + (h % 12 if 'h' in locals() else 4), 1)
}
# Clamp timeline scores to 100
timeline = {k: min(v, 99.0) for k, v in timeline.items()}
return {
"id": id,
"name": name,
"scores": scores,
"reasons": reasons,
"timeline": timeline,
"insights": insights,
"source": source
}