| 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 |
|
|