File size: 7,726 Bytes
a5784e9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
import time
from typing import Any, List, Optional

from fastapi import HTTPException


class AIStudioProxyError(Exception):
    """Base exception for AIStudio Proxy errors."""
    def __init__(
        self,
        message: str,
        req_id: Optional[str] = None,
        http_status: int = 500,
        retry_after: Optional[int] = None,
        **kwargs: Any
    ):
        self.message = message
        self.req_id = req_id
        self.http_status = http_status
        self.retry_after = retry_after
        self.timestamp = time.time()
        self.context = kwargs

        # Format message with req_id if present for string representation
        super().__init__(self.__str__())

    def __str__(self) -> str:
        if self.req_id:
            return f"[{self.req_id}] {self.message}"
        return self.message

    def __repr__(self) -> str:
        return f"<{self.__class__.__name__} message='{self.message}' req_id='{self.req_id}' http_status={self.http_status} context={self.context}>"

    def to_http_exception(self) -> HTTPException:
        headers = {}
        if self.retry_after is not None:
            headers["Retry-After"] = str(self.retry_after)

        return HTTPException(
            status_code=self.http_status,
            detail=str(self),
            headers=headers if headers else None
        )

# Browser Errors
class BrowserError(AIStudioProxyError):
    def __init__(self, message: str, http_status: int = 503, retry_after: int = 30, **kwargs):
        super().__init__(message, http_status=http_status, retry_after=retry_after, **kwargs)

class BrowserInitError(BrowserError):
    pass

class PageNotReadyError(BrowserError):
    pass

class BrowserCrashedError(BrowserError):
    def __init__(self, message: str = "Browser crashed unexpectedly", **kwargs):
        super().__init__(message, **kwargs)

class SelectorNotFoundError(BrowserError):
    def __init__(self, selector: str, message: str = "Selector not found", **kwargs):
        super().__init__(f"{message}: {selector}", selector=selector, **kwargs)

# Model Errors
class ModelError(AIStudioProxyError):
    def __init__(self, message: str, http_status: int = 422, **kwargs):
        super().__init__(message, http_status=http_status, **kwargs)

class InvalidModelError(ModelError):
    def __init__(self, model_id: str, available_models: Optional[List[str]] = None, message: str = "Invalid model", **kwargs):
        msg = f"{message}: {model_id}"
        if available_models:
            msg += f" (Available: {', '.join(available_models)})"
        super().__init__(msg, model_id=model_id, available_models=available_models, **kwargs)

class ModelSwitchError(ModelError):
    def __init__(self, target_model: str, current_model: str, message: str = "Failed to switch model", **kwargs):
        super().__init__(f"{message} from {current_model} to {target_model}", target_model=target_model, current_model=current_model, **kwargs)

class ModelListError(ModelError):
    pass

# Client Errors
class ClientDisconnectedError(Exception):
    """Client disconnected exception (kept simple for backward compatibility but with extra fields)."""
    def __init__(self, message: str = "Client disconnected", stage: str = "", req_id: Optional[str] = None, http_status: int = 499):
        self.message = message
        self.stage = stage
        self.req_id = req_id
        self.http_status = http_status
        super().__init__(message)

# Validation Errors
class ValidationError(AIStudioProxyError):
    def __init__(self, message: str, http_status: int = 400, **kwargs):
        super().__init__(message, http_status=http_status, **kwargs)

class MissingParameterError(ValidationError):
    def __init__(self, parameter: str, message: str = "Missing parameter", **kwargs):
        super().__init__(f"{message}: {parameter}", parameter=parameter, **kwargs)

class InvalidParameterError(ValidationError):
    def __init__(self, parameter: str, value: Any, reason: str, message: str = "Invalid parameter", **kwargs):
        super().__init__(f"{message} {parameter}={value}: {reason}", parameter=parameter, value=value, reason=reason, **kwargs)

# Stream Errors
class StreamError(AIStudioProxyError):
    def __init__(self, message: str, http_status: int = 502, **kwargs):
        super().__init__(message, http_status=http_status, **kwargs)

class ProxyConnectionError(StreamError):
    def __init__(self, proxy_url: str, message: str = "Failed to connect to proxy", **kwargs):
        super().__init__(f"{message}: {proxy_url}", proxy_url=proxy_url, **kwargs)

class StreamTimeoutError(StreamError):
    def __init__(self, timeout_seconds: float, message: str = "Stream timed out", **kwargs):
        super().__init__(f"{message} after {timeout_seconds}s", timeout_seconds=timeout_seconds, **kwargs)

# Resource Errors
class ResourceError(AIStudioProxyError):
    def __init__(self, message: str, http_status: int = 503, retry_after: int = 60, **kwargs):
        super().__init__(message, http_status=http_status, retry_after=retry_after, **kwargs)

class QueueFullError(ResourceError):
    def __init__(self, queue_size: int, message: str = "Queue full", **kwargs):
        super().__init__(f"{message} (size: {queue_size})", queue_size=queue_size, **kwargs)

# Upstream Errors
class UpstreamError(AIStudioProxyError):
    def __init__(self, message: str, http_status: int = 502, retry_after: int = 10, **kwargs):
        super().__init__(message, http_status=http_status, retry_after=retry_after, **kwargs)

class AIStudioError(UpstreamError):
    def __init__(self, error_message: str, status_code: int, message: str = "AI Studio error", **kwargs):
        super().__init__(f"{message}: {error_message} (Status: {status_code})", ai_studio_status=status_code, error_message=error_message, **kwargs)

class QuotaExceededError(UpstreamError):
    def __init__(self, message: str = "Quota exceeded", retry_after: int = 3600, **kwargs):
        super().__init__(message, retry_after=retry_after, **kwargs)

class EmptyResponseError(UpstreamError):
    def __init__(self, message: str = "Received empty response", **kwargs):
        super().__init__(message, **kwargs)

class QuotaExceededRetry(Exception):
    pass

# Timeout Errors
class TimeoutError(AIStudioProxyError):
    def __init__(self, message: str, http_status: int = 504, **kwargs):
        super().__init__(message, http_status=http_status, **kwargs)

class ResponseTimeoutError(TimeoutError):
    def __init__(self, timeout_seconds: float, message: str = "Response timed out", **kwargs):
        super().__init__(f"{message} after {timeout_seconds}s", timeout_seconds=timeout_seconds, **kwargs)

class ProcessingTimeoutError(TimeoutError):
    def __init__(self, timeout_seconds: Optional[float] = None, message: str = "Processing timeout", **kwargs):
        msg = message
        if timeout_seconds:
            msg += f" after {timeout_seconds}s"
        super().__init__(msg, timeout_seconds=timeout_seconds, **kwargs)

# Configuration Errors
class ConfigurationError(AIStudioProxyError):
    def __init__(self, message: str, http_status: int = 500, **kwargs):
        super().__init__(message, http_status=http_status, **kwargs)

class MissingConfigError(ConfigurationError):
    def __init__(self, config_key: str, message: str = "Missing configuration", **kwargs):
        super().__init__(f"{message}: {config_key}", config_key=config_key, **kwargs)

class InvalidConfigError(ConfigurationError):
    def __init__(self, config_key: str, value: Any, reason: str, message: str = "Invalid configuration", **kwargs):
        super().__init__(f"{message} {config_key}={value}: {reason}", config_key=config_key, value=value, reason=reason, **kwargs)