from fastapi import FastAPI, File, UploadFile, HTTPException, Header, Form, BackgroundTasks from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware from typing import Optional import os import asyncio from models.color import extract_color_palette from models.classifier import get_classifier from models.material import detect_material_clip from models.embeddings import get_image_embedding from models.attribute_parser import generate_caption from llm_utils import generate_description # same as before, calls Seller Assistant app = FastAPI(title="Advanced Product Intelligence Service") app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) @app.post("/api/v1/product-intelligence") async def product_intelligence( file: UploadFile = File(...), generate_seo: Optional[bool] = Form(False), generate_caption: Optional[bool] = Form(False), # BLIP caption api_key: Optional[str] = Header(None) ): contents = await file.read() # Run all models in parallel (some can be run concurrently) tasks = [ extract_color_palette(contents), asyncio.to_thread(lambda: get_classifier().predict(contents)), asyncio.to_thread(lambda: detect_material_clip(contents)), asyncio.to_thread(get_image_embedding, contents) ] if generate_caption: tasks.append(asyncio.to_thread(generate_caption, contents)) results = await asyncio.gather(*tasks) palette_info, classifier_output, material_info, embedding, caption = results[0], results[1], results[2], results[3], results[4] if generate_caption else None response = { "colors": palette_info, "category": classifier_output["category"], "category_confidence": classifier_output["category_confidence"], "attributes": classifier_output["attributes"], "material": material_info["primary_material"], "material_confidence": material_info["confidence"], "material_all_scores": material_info["all_scores"], "embedding": embedding, # 512-dim vector "suggested_tags": [classifier_output["category"], material_info["primary_material"]] + palette_info["all_hex"][:2] } if generate_caption and caption: response["caption"] = caption if generate_seo: if not api_key: response["seo_title"] = f"Premium {material_info['primary_material']} {classifier_output['category']}" response["seo_description"] = f"High-quality {material_info['primary_material']} {classifier_output['category']} with {', '.join(palette_info['all_names'][:2])} tones." else: seo = await generate_description( classifier_output["category"], material_info["primary_material"], palette_info["all_hex"], api_key ) response["seo_title"] = seo.get("title", "") response["seo_description"] = seo.get("description", "") return JSONResponse(content=response) @app.get("/health") async def health(): return {"status": "advanced intelligence ready"}