KiWA001 commited on
Commit
b544391
·
1 Parent(s): 45ac16b

feat: standard openai error handling

Browse files
Files changed (3) hide show
  1. error_handling.py +51 -0
  2. main.py +20 -0
  3. v1_router.py +21 -8
error_handling.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import Request
2
+ from fastapi.responses import JSONResponse
3
+
4
+ def openai_error(message: str, code: str, type: str = "invalid_request_error", status_code: int = 400):
5
+ """
6
+ Return an OpenAI-formatted error response.
7
+ """
8
+ return JSONResponse(
9
+ status_code=status_code,
10
+ content={
11
+ "error": {
12
+ "message": message,
13
+ "type": type,
14
+ "param": None,
15
+ "code": code
16
+ }
17
+ }
18
+ )
19
+
20
+ # Common Errors
21
+ def error_invalid_api_key():
22
+ return openai_error(
23
+ "Incorrect API key provided.",
24
+ "invalid_api_key",
25
+ "authentication_error",
26
+ 401
27
+ )
28
+
29
+ def error_quota_exceeded():
30
+ return openai_error(
31
+ "You have exceeded your current quota, please check your plan and billing details.",
32
+ "insufficient_quota",
33
+ "insufficient_quota",
34
+ 429
35
+ )
36
+
37
+ def error_model_not_found(model_name: str):
38
+ return openai_error(
39
+ f"The model '{model_name}' does not exist",
40
+ "model_not_found",
41
+ "invalid_request_error",
42
+ 404
43
+ )
44
+
45
+ def error_server(message: str):
46
+ return openai_error(
47
+ message,
48
+ "internal_server_error",
49
+ "server_error",
50
+ 500
51
+ )
main.py CHANGED
@@ -69,6 +69,26 @@ app = FastAPI(
69
  # Mount static files (for CSS/JS if needed later)
70
  app.mount("/static", StaticFiles(directory="static"), name="static")
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  # CORS middleware
73
  app.add_middleware(
74
  CORSMiddleware,
 
69
  # Mount static files (for CSS/JS if needed later)
70
  app.mount("/static", StaticFiles(directory="static"), name="static")
71
 
72
+ # Error Handling (Global)
73
+ from error_handling import openai_error
74
+
75
+ @app.exception_handler(HTTPException)
76
+ async def http_exception_handler(request: Request, exc: HTTPException):
77
+ """
78
+ Override default 404/401/500 to return OpenAI-style JSON.
79
+ """
80
+ code = "invalid_request_error"
81
+ if exc.status_code == 401: code = "invalid_api_key"
82
+ if exc.status_code == 429: code = "insufficient_quota"
83
+ if exc.status_code == 404: code = "model_not_found"
84
+ if exc.status_code == 500: code = "internal_server_error"
85
+
86
+ return openai_error(
87
+ message=exc.detail,
88
+ code=code,
89
+ status_code=exc.status_code
90
+ )
91
+
92
  # CORS middleware
93
  app.add_middleware(
94
  CORSMiddleware,
v1_router.py CHANGED
@@ -8,6 +8,13 @@ from config import DEMO_API_KEY
8
  from db import get_supabase
9
  from services import engine
10
  from utils import calculate_usage
 
 
 
 
 
 
 
11
 
12
  # Initialize Router
13
  router = APIRouter()
@@ -77,7 +84,7 @@ async def verify_api_key(
77
  token = x_api_key
78
 
79
  if not token:
80
- raise HTTPException(status_code=401, detail="Missing API Key")
81
 
82
  # 1. Check Demo Key
83
  if token == DEMO_API_KEY:
@@ -86,15 +93,14 @@ async def verify_api_key(
86
  # 2. Check Database
87
  supabase = get_supabase()
88
  if not supabase:
89
- # Fallback if DB is down but key matches verified format? No, safer to reject.
90
- raise HTTPException(status_code=503, detail="Auth service unavailable")
91
 
92
  try:
93
  # Check if key exists and is active
94
  res = supabase.table("api_keys").select("*").eq("token", token).execute()
95
 
96
  if not res.data:
97
- raise HTTPException(status_code=401, detail="Invalid API Key")
98
 
99
  key_data = res.data[0]
100
 
@@ -102,18 +108,19 @@ async def verify_api_key(
102
  raise HTTPException(status_code=403, detail="API Key is inactive")
103
 
104
  # Check limits
105
- # Note: We check limit BEFORE processing, but update usage AFTER (bg task)
106
  current_usage = key_data.get("usage_tokens", 0)
107
  limit = key_data.get("limit_tokens", 0)
108
 
109
  if limit > 0 and current_usage >= limit:
110
- raise HTTPException(status_code=429, detail="Quota exceeded")
111
 
112
  return key_data
113
 
 
 
114
  except Exception as e:
115
  print(f"Auth Error: {e}")
116
- raise HTTPException(status_code=500, detail="Auth Error")
117
 
118
  # --- Background Task for Usage Update ---
119
 
@@ -208,5 +215,11 @@ async def chat_completions(
208
  usage=UsageInfo(**usage)
209
  )
210
 
 
 
 
 
 
 
211
  except Exception as e:
212
- raise HTTPException(status_code=500, detail=str(e))
 
8
  from db import get_supabase
9
  from services import engine
10
  from utils import calculate_usage
11
+ from error_handling import (
12
+ openai_error,
13
+ error_invalid_api_key,
14
+ error_quota_exceeded,
15
+ error_model_not_found,
16
+ error_server
17
+ )
18
 
19
  # Initialize Router
20
  router = APIRouter()
 
84
  token = x_api_key
85
 
86
  if not token:
87
+ raise HTTPException(status_code=401, detail="Incorrect API Key")
88
 
89
  # 1. Check Demo Key
90
  if token == DEMO_API_KEY:
 
93
  # 2. Check Database
94
  supabase = get_supabase()
95
  if not supabase:
96
+ raise HTTPException(status_code=503, detail="Service unavailable")
 
97
 
98
  try:
99
  # Check if key exists and is active
100
  res = supabase.table("api_keys").select("*").eq("token", token).execute()
101
 
102
  if not res.data:
103
+ raise HTTPException(status_code=401, detail="Incorrect API key provided")
104
 
105
  key_data = res.data[0]
106
 
 
108
  raise HTTPException(status_code=403, detail="API Key is inactive")
109
 
110
  # Check limits
 
111
  current_usage = key_data.get("usage_tokens", 0)
112
  limit = key_data.get("limit_tokens", 0)
113
 
114
  if limit > 0 and current_usage >= limit:
115
+ raise HTTPException(status_code=429, detail="You have exceeded your current quota")
116
 
117
  return key_data
118
 
119
+ except HTTPException:
120
+ raise
121
  except Exception as e:
122
  print(f"Auth Error: {e}")
123
+ raise HTTPException(status_code=500, detail="Internal server error")
124
 
125
  # --- Background Task for Usage Update ---
126
 
 
215
  usage=UsageInfo(**usage)
216
  )
217
 
218
+ except ValueError as e:
219
+ # Invalid model or params
220
+ # We need to return the JSON response object, but we are inside an async endpoint.
221
+ # Direct return works!
222
+ return error_model_not_found(request.model) if "model" in str(e) else openai_error(str(e), "invalid_request_error")
223
+
224
  except Exception as e:
225
+ return error_server(str(e))