Add Anthropic-compatible HTTP error handling
Browse files
main.py
CHANGED
|
@@ -4,8 +4,9 @@ Full compatibility with Anthropic Messages API + Interleaved Thinking
|
|
| 4 |
Supports: /v1/messages, /anthropic/v1/messages, /api/v1/messages
|
| 5 |
Optimized for: 2 vCPU, 16GB RAM
|
| 6 |
"""
|
| 7 |
-
from fastapi import FastAPI, HTTPException, Header, Request
|
| 8 |
-
from fastapi.responses import StreamingResponse, HTMLResponse, FileResponse
|
|
|
|
| 9 |
from fastapi.staticfiles import StaticFiles
|
| 10 |
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
from pydantic import BaseModel, Field
|
|
@@ -68,6 +69,63 @@ app.add_middleware(
|
|
| 68 |
)
|
| 69 |
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
# ============== Anthropic API Models ==============
|
| 72 |
|
| 73 |
class TextBlock(BaseModel):
|
|
|
|
| 4 |
Supports: /v1/messages, /anthropic/v1/messages, /api/v1/messages
|
| 5 |
Optimized for: 2 vCPU, 16GB RAM
|
| 6 |
"""
|
| 7 |
+
from fastapi import FastAPI, HTTPException, Header, Request, status
|
| 8 |
+
from fastapi.responses import StreamingResponse, HTMLResponse, FileResponse, JSONResponse
|
| 9 |
+
from fastapi.exceptions import RequestValidationError
|
| 10 |
from fastapi.staticfiles import StaticFiles
|
| 11 |
from fastapi.middleware.cors import CORSMiddleware
|
| 12 |
from pydantic import BaseModel, Field
|
|
|
|
| 69 |
)
|
| 70 |
|
| 71 |
|
| 72 |
+
# ============== Anthropic Error Handling ==============
|
| 73 |
+
|
| 74 |
+
class AnthropicError(BaseModel):
|
| 75 |
+
type: str
|
| 76 |
+
message: str
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
class AnthropicErrorResponse(BaseModel):
|
| 80 |
+
type: str = "error"
|
| 81 |
+
error: AnthropicError
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def create_error_response(status_code: int, error_type: str, message: str) -> JSONResponse:
|
| 85 |
+
"""Create Anthropic-compatible error response."""
|
| 86 |
+
return JSONResponse(
|
| 87 |
+
status_code=status_code,
|
| 88 |
+
content={
|
| 89 |
+
"type": "error",
|
| 90 |
+
"error": {
|
| 91 |
+
"type": error_type,
|
| 92 |
+
"message": message
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
@app.exception_handler(RequestValidationError)
|
| 99 |
+
async def validation_exception_handler(request: Request, exc: RequestValidationError):
|
| 100 |
+
"""Handle validation errors (400 - invalid_request_error)."""
|
| 101 |
+
errors = exc.errors()
|
| 102 |
+
message = "; ".join([f"{e['loc'][-1]}: {e['msg']}" for e in errors])
|
| 103 |
+
return create_error_response(400, "invalid_request_error", message)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
@app.exception_handler(HTTPException)
|
| 107 |
+
async def http_exception_handler(request: Request, exc: HTTPException):
|
| 108 |
+
"""Handle HTTP exceptions with Anthropic error format."""
|
| 109 |
+
error_mapping = {
|
| 110 |
+
400: "invalid_request_error",
|
| 111 |
+
401: "authentication_error",
|
| 112 |
+
403: "permission_error",
|
| 113 |
+
404: "not_found_error",
|
| 114 |
+
413: "request_too_large",
|
| 115 |
+
429: "rate_limit_error",
|
| 116 |
+
500: "api_error",
|
| 117 |
+
529: "overloaded_error"
|
| 118 |
+
}
|
| 119 |
+
error_type = error_mapping.get(exc.status_code, "api_error")
|
| 120 |
+
return create_error_response(exc.status_code, error_type, str(exc.detail))
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
@app.exception_handler(Exception)
|
| 124 |
+
async def general_exception_handler(request: Request, exc: Exception):
|
| 125 |
+
"""Handle unexpected errors (500 - api_error)."""
|
| 126 |
+
return create_error_response(500, "api_error", f"An unexpected error occurred: {str(exc)}")
|
| 127 |
+
|
| 128 |
+
|
| 129 |
# ============== Anthropic API Models ==============
|
| 130 |
|
| 131 |
class TextBlock(BaseModel):
|