File size: 4,805 Bytes
8ba64a4
 
 
 
 
 
 
 
 
 
 
e85027d
8ba64a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e85027d
 
8ba64a4
 
e85027d
 
 
 
 
8ba64a4
e85027d
8ba64a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e85027d
 
 
8ba64a4
e85027d
 
 
8ba64a4
 
e85027d
8ba64a4
e85027d
 
 
8ba64a4
 
e85027d
8ba64a4
 
e85027d
 
 
8ba64a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e85027d
8ba64a4
 
e85027d
8ba64a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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 = []
            pil_images = None  # temporarily removed to save cost
            for url in request.img_urls:
                try:
                    # response = requests.get(url)
                    # response.raise_for_status()
                    # image = Image.open(BytesIO(response.content))
                    # pil_images.append(image)
                    pass
                except Exception as e:
                    # logger.error(f"Failed to download or process image from {url}: {exception_to_str(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
                    ]  # switch to OpenAI, and try again if max_attempts not reached
                    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
                    ]  # switch to anthropic, and try again if max_attempts not reached
                    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
                ]  # switch to OpenAI, and try again if max_attempts not reached
            if attempt == request.max_attempts:
                raise HTTPException(
                    status_code=500,
                    detail="Internal server error",
                    headers={"attempt": attempt},
                )

    return json_attributes, attempt