import os
import asyncio
import json
import traceback
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import HTMLResponse, JSONResponse
from dotenv import load_dotenv
# Import Agents
from agents.visual_analyst import VisualAnalyst
from agents.memory_agent import MemoryAgent
from agents.writer_agent import WriterAgent
load_dotenv()
app = FastAPI()
# Initialize Agents
try:
visual_agent = VisualAnalyst()
memory_agent = MemoryAgent()
writer_agent = WriterAgent()
# Try seeding database, but don't crash if it fails (optional robustness)
try:
memory_agent.seed_database()
except Exception as e:
print(f"⚠️ Memory Agent Seed Warning: {e}")
print("✅ All Agents Online")
except Exception as e:
print(f"❌ Agent Startup Failed: {e}")
# We continue, but endpoints might fail if agents aren't ready.
@app.get("/", response_class=HTMLResponse)
async def read_root():
try:
with open("dashboard.html", "r") as f:
return f.read()
except FileNotFoundError:
return "
Error: dashboard.html not found
"
@app.post("/generate-catalog")
async def generate_catalog(file: UploadFile = File(...)):
file_path = None
try:
# 1. Save Temp File
os.makedirs("uploads", exist_ok=True)
file_path = f"uploads/{file.filename}"
with open(file_path, "wb") as f:
f.write(await file.read())
# 2. Run AI Pipeline (Sequential)
print("▶️ Starting Visual Analysis...")
visual_data = await visual_agent.analyze_image(file_path)
print("▶️ Retrieving Keywords...")
query = f"{visual_data.get('main_color', '')} {visual_data.get('product_type', 'product')}"
seo_keywords = memory_agent.retrieve_keywords(query)
# 2b. AI Fallback: Generate keywords if Pinecone returns empty
if not seo_keywords:
print("🤖 Database empty. Using AI Fallback for SEO keywords.")
try:
fallback_prompt = (
f"The internal keyword database is empty for this product. "
f"Based on these visual features: {json.dumps(visual_data)}, "
f"generate a list of 10 high-converting e-commerce SEO tags "
f"and return them as a JSON array of strings. Return ONLY the JSON array."
)
fallback_response = visual_agent.client.models.generate_content(
model="gemini-2.5-flash",
contents=fallback_prompt
)
import re
match = re.search(r'\[.*\]', fallback_response.text, re.DOTALL)
if match:
seo_keywords = json.loads(match.group(0))
else:
seo_keywords = [tag.strip() for tag in fallback_response.text.split(",") if tag.strip()]
print(f"✅ AI Fallback generated {len(seo_keywords)} keywords.")
except Exception as fallback_err:
print(f"⚠️ AI Fallback also failed: {fallback_err}")
seo_keywords = []
print("▶️ Writing Listing...")
listing = writer_agent.write_listing(visual_data, seo_keywords)
# 3. Construct Final Payload
final_data = {
"visual_data": visual_data,
"seo_keywords": seo_keywords,
"listing": listing
}
return JSONResponse(content=final_data)
except Exception as e:
error_details = traceback.format_exc()
print(f"❌ Error in generate-catalog: {e}")
print(error_details)
return JSONResponse(
content={
"error": "An internal server error occurred.",
"type": type(e).__name__
},
status_code=500
)
finally:
# Cleanup
if file_path and os.path.exists(file_path):
try:
os.remove(file_path)
except Exception as cleanup_error:
print(f"⚠️ Cleanup Warning: {cleanup_error}")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)