|
from io import BytesIO |
|
|
|
import requests |
|
from fastapi import HTTPException |
|
from PIL import Image |
|
|
|
from app.config import get_settings |
|
from app.core.errors import BadRequestError, VendorError |
|
from app.schemas.requests import ExtractionRequest |
|
from app.schemas.responses import APIResponse |
|
from app.services.factory import AIServiceFactory |
|
from app.utils.logger import exception_to_str, setup_logger |
|
|
|
logger = setup_logger(__name__) |
|
settings = get_settings() |
|
|
|
|
|
async def handle_extract(request: ExtractionRequest): |
|
request.max_attempts = max(request.max_attempts, 1) |
|
request.max_attempts = min(request.max_attempts, 5) |
|
|
|
for attempt in range(1, request.max_attempts + 1): |
|
try: |
|
logger.info(f"Attempt: {attempt}") |
|
if request.ai_model in settings.OPENAI_MODELS: |
|
ai_vendor = "openai" |
|
elif request.ai_model in settings.ANTHROPIC_MODELS: |
|
ai_vendor = "anthropic" |
|
else: |
|
raise ValueError( |
|
f"Invalid AI model: {request.ai_model}, only support {settings.SUPPORTED_MODELS}" |
|
) |
|
service = AIServiceFactory.get_service(ai_vendor) |
|
|
|
|
|
pil_images = None |
|
for url in request.img_urls: |
|
try: |
|
|
|
|
|
|
|
|
|
pass |
|
except Exception as e: |
|
|
|
raise HTTPException( |
|
status_code=400, |
|
detail=f"Failed to process image from {url}", |
|
headers={"attempt": attempt}, |
|
) |
|
|
|
json_attributes = await service.extract_attributes_with_validation( |
|
request.attributes, |
|
request.ai_model, |
|
request.img_urls, |
|
request.product_taxonomy, |
|
request.product_data, |
|
pil_images=pil_images, |
|
) |
|
break |
|
except BadRequestError as e: |
|
logger.error( |
|
f"Bad request error: {exception_to_str(e)}", |
|
) |
|
raise HTTPException( |
|
status_code=400, |
|
detail=exception_to_str(e), |
|
headers={"attempt": attempt}, |
|
) |
|
except ValueError as e: |
|
logger.error(f"Value error: {exception_to_str(e)}") |
|
raise HTTPException( |
|
status_code=400, |
|
detail=exception_to_str(e), |
|
headers={"attempt": attempt}, |
|
) |
|
except VendorError as e: |
|
logger.error(f"Vendor error: {exception_to_str(e)}") |
|
if attempt == request.max_attempts: |
|
raise HTTPException( |
|
status_code=500, |
|
detail=exception_to_str(e), |
|
headers={"attempt": attempt}, |
|
) |
|
else: |
|
if request.ai_model in settings.ANTHROPIC_MODELS: |
|
request.ai_model = settings.OPENAI_MODELS[ |
|
0 |
|
] |
|
logger.info( |
|
f"Switching from anthropic to {request.ai_model} for attempt {attempt + 1}" |
|
) |
|
elif request.ai_model in settings.OPENAI_MODELS: |
|
request.ai_model = settings.ANTHROPIC_MODELS[ |
|
0 |
|
] |
|
logger.info( |
|
f"Switching from OpenAI to {request.ai_model} for attempt {attempt + 1}" |
|
) |
|
|
|
except HTTPException as e: |
|
logger.error(f"HTTP exception: {exception_to_str(e)}") |
|
raise e |
|
except Exception as e: |
|
logger.error("Exception: ", exception_to_str(e)) |
|
if ( |
|
"overload" in str(e).lower() |
|
and request.ai_model in settings.ANTHROPIC_MODELS |
|
): |
|
request.ai_model = settings.OPENAI_MODELS[ |
|
0 |
|
] |
|
if attempt == request.max_attempts: |
|
raise HTTPException( |
|
status_code=500, |
|
detail="Internal server error", |
|
headers={"attempt": attempt}, |
|
) |
|
|
|
return json_attributes, attempt |
|
|