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