99 lines
3.1 KiB
Python
99 lines
3.1 KiB
Python
from flask import Flask, request, Response
|
|
import requests
|
|
from flask_cors import CORS
|
|
|
|
app = Flask(__name__)
|
|
# Enable CORS for all routes and all origins
|
|
CORS(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True)
|
|
|
|
ANTHROPIC_API_BASE = "https://api.anthropic.com"
|
|
|
|
|
|
@app.route(
|
|
"/v1/<path:subpath>", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]
|
|
)
|
|
def proxy(subpath):
|
|
# For preflight OPTIONS requests, return immediately with appropriate CORS headers
|
|
if request.method == "OPTIONS":
|
|
response = Response()
|
|
response.headers.add("Access-Control-Allow-Origin", "*")
|
|
response.headers.add("Access-Control-Allow-Headers", "*")
|
|
response.headers.add(
|
|
"Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"
|
|
)
|
|
response.headers.add("Access-Control-Allow-Credentials", "true")
|
|
response.headers.add("Access-Control-Max-Age", "3600")
|
|
return response
|
|
|
|
# Construct the target URL
|
|
target_url = f"{ANTHROPIC_API_BASE}/v1/{subpath}"
|
|
|
|
# Get the request headers
|
|
headers = {
|
|
key: value
|
|
for key, value in request.headers
|
|
if key.lower() not in ("host", "origin", "referer", "accept-encoding")
|
|
}
|
|
|
|
# Explicitly set Accept-Encoding to identity to avoid gzip compression
|
|
headers["Accept-Encoding"] = "identity"
|
|
|
|
# Convert Authorization: Bearer to x-api-key
|
|
auth_header = headers.get("Authorization")
|
|
if auth_header and auth_header.startswith("Bearer "):
|
|
token = auth_header.replace("Bearer ", "")
|
|
headers["x-api-key"] = token
|
|
# Remove the original Authorization header
|
|
if "Authorization" in headers:
|
|
del headers["Authorization"]
|
|
|
|
# Get the request data/params
|
|
data = request.get_data()
|
|
params = request.args
|
|
|
|
# Add Anthropic-specific headers
|
|
headers["Anthropic-Version"] = "2023-06-01"
|
|
|
|
# Forward the request to Anthropic API
|
|
resp = requests.request(
|
|
method=request.method,
|
|
url=target_url,
|
|
headers=headers,
|
|
data=data,
|
|
params=params,
|
|
stream=True, # Always use stream=True to handle both streaming and non-streaming responses
|
|
)
|
|
|
|
# Create a Flask response object
|
|
def generate():
|
|
for chunk in resp.iter_content(chunk_size=1024):
|
|
yield chunk
|
|
|
|
response = Response(
|
|
generate(),
|
|
status=resp.status_code,
|
|
content_type=resp.headers.get("Content-Type"),
|
|
)
|
|
|
|
# Copy only required headers from the Anthropic response to our response
|
|
excluded_headers = (
|
|
"transfer-encoding",
|
|
"content-length",
|
|
"connection",
|
|
"content-encoding",
|
|
"vary",
|
|
"accept-ranges",
|
|
)
|
|
for key, value in resp.headers.items():
|
|
if key.lower() not in excluded_headers:
|
|
response.headers[key] = value
|
|
|
|
# Add CORS headers to the response
|
|
response.headers.add("Access-Control-Allow-Origin", "*")
|
|
response.headers.add("Access-Control-Allow-Credentials", "true")
|
|
|
|
return response
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=5000)
|