Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -3,14 +3,19 @@ import time
|
|
3 |
import json
|
4 |
import asyncio
|
5 |
import aiohttp
|
|
|
6 |
from aiohttp import web
|
7 |
|
|
|
|
|
|
|
|
|
8 |
PROJECT_ID = os.getenv('PROJECT_ID')
|
9 |
CLIENT_ID = os.getenv('CLIENT_ID')
|
10 |
CLIENT_SECRET = os.getenv('CLIENT_SECRET')
|
11 |
REFRESH_TOKEN = os.getenv('REFRESH_TOKEN')
|
12 |
API_KEY = os.getenv('API_KEY')
|
13 |
-
MODEL = 'claude-3-5-sonnet@20240620'
|
14 |
|
15 |
TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token'
|
16 |
|
@@ -24,13 +29,16 @@ async def get_access_token():
|
|
24 |
now = time.time()
|
25 |
|
26 |
if token_cache['access_token'] and now < token_cache['expiry'] - 120:
|
|
|
27 |
return token_cache['access_token']
|
28 |
|
29 |
if token_cache['refresh_promise']:
|
|
|
30 |
await token_cache['refresh_promise']
|
31 |
return token_cache['access_token']
|
32 |
|
33 |
async def refresh_token():
|
|
|
34 |
async with aiohttp.ClientSession() as session:
|
35 |
async with session.post(TOKEN_URL, json={
|
36 |
'client_id': CLIENT_ID,
|
@@ -41,6 +49,7 @@ async def get_access_token():
|
|
41 |
data = await response.json()
|
42 |
token_cache['access_token'] = data['access_token']
|
43 |
token_cache['expiry'] = now + data['expires_in']
|
|
|
44 |
|
45 |
token_cache['refresh_promise'] = refresh_token()
|
46 |
await token_cache['refresh_promise']
|
@@ -49,10 +58,14 @@ async def get_access_token():
|
|
49 |
|
50 |
def get_location():
|
51 |
current_seconds = time.localtime().tm_sec
|
52 |
-
|
|
|
|
|
53 |
|
54 |
def construct_api_url(location, model):
|
55 |
-
|
|
|
|
|
56 |
|
57 |
def format_model_name(model):
|
58 |
if model == 'claude-3-5-sonnet-20240620':
|
@@ -60,32 +73,33 @@ def format_model_name(model):
|
|
60 |
return model
|
61 |
|
62 |
async def handle_request(request):
|
|
|
|
|
63 |
if request.method == 'OPTIONS':
|
|
|
64 |
return handle_options()
|
65 |
|
66 |
api_key = request.headers.get('x-api-key')
|
67 |
if api_key != API_KEY:
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
error_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, x-api-key, anthropic-version, model'
|
82 |
-
return error_response
|
83 |
|
84 |
access_token = await get_access_token()
|
85 |
location = get_location()
|
86 |
|
87 |
-
|
88 |
-
|
89 |
|
90 |
if 'anthropic_version' in request_body:
|
91 |
del request_body['anthropic_version']
|
@@ -101,11 +115,13 @@ async def handle_request(request):
|
|
101 |
'Content-Type': 'application/json; charset=utf-8'
|
102 |
}
|
103 |
|
|
|
104 |
async with aiohttp.ClientSession() as session:
|
105 |
async with session.post(api_url, json=request_body, headers=headers) as response:
|
106 |
response_body = await response.read()
|
107 |
response_headers = response.headers
|
108 |
response_status = response.status
|
|
|
109 |
|
110 |
modified_response = web.Response(
|
111 |
body=response_body,
|
@@ -116,9 +132,29 @@ async def handle_request(request):
|
|
116 |
modified_response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
|
117 |
modified_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, x-api-key, anthropic-version, model'
|
118 |
|
|
|
119 |
return modified_response
|
120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
def handle_options():
|
|
|
122 |
headers = {
|
123 |
'Access-Control-Allow-Origin': '*',
|
124 |
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
@@ -130,4 +166,5 @@ app = web.Application()
|
|
130 |
app.router.add_route('*', '/ai/v1/messages', handle_request)
|
131 |
|
132 |
if __name__ == '__main__':
|
|
|
133 |
web.run_app(app, port=8080)
|
|
|
3 |
import json
|
4 |
import asyncio
|
5 |
import aiohttp
|
6 |
+
import logging
|
7 |
from aiohttp import web
|
8 |
|
9 |
+
# 设置日志
|
10 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
11 |
+
logger = logging.getLogger(__name__)
|
12 |
+
|
13 |
PROJECT_ID = os.getenv('PROJECT_ID')
|
14 |
CLIENT_ID = os.getenv('CLIENT_ID')
|
15 |
CLIENT_SECRET = os.getenv('CLIENT_SECRET')
|
16 |
REFRESH_TOKEN = os.getenv('REFRESH_TOKEN')
|
17 |
API_KEY = os.getenv('API_KEY')
|
18 |
+
MODEL = 'claude-3-5-sonnet@20240620'
|
19 |
|
20 |
TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token'
|
21 |
|
|
|
29 |
now = time.time()
|
30 |
|
31 |
if token_cache['access_token'] and now < token_cache['expiry'] - 120:
|
32 |
+
logger.info("Using cached access token")
|
33 |
return token_cache['access_token']
|
34 |
|
35 |
if token_cache['refresh_promise']:
|
36 |
+
logger.info("Waiting for ongoing token refresh")
|
37 |
await token_cache['refresh_promise']
|
38 |
return token_cache['access_token']
|
39 |
|
40 |
async def refresh_token():
|
41 |
+
logger.info("Refreshing access token")
|
42 |
async with aiohttp.ClientSession() as session:
|
43 |
async with session.post(TOKEN_URL, json={
|
44 |
'client_id': CLIENT_ID,
|
|
|
49 |
data = await response.json()
|
50 |
token_cache['access_token'] = data['access_token']
|
51 |
token_cache['expiry'] = now + data['expires_in']
|
52 |
+
logger.info("Access token refreshed successfully")
|
53 |
|
54 |
token_cache['refresh_promise'] = refresh_token()
|
55 |
await token_cache['refresh_promise']
|
|
|
58 |
|
59 |
def get_location():
|
60 |
current_seconds = time.localtime().tm_sec
|
61 |
+
location = 'europe-west1' if current_seconds < 30 else 'us-east5'
|
62 |
+
logger.info(f"Selected location: {location}")
|
63 |
+
return location
|
64 |
|
65 |
def construct_api_url(location, model):
|
66 |
+
url = f'https://{location}-aiplatform.googleapis.com/v1/projects/{PROJECT_ID}/locations/{location}/publishers/anthropic/models/{model}:streamRawPredict'
|
67 |
+
logger.info(f"Constructed API URL: {url}")
|
68 |
+
return url
|
69 |
|
70 |
def format_model_name(model):
|
71 |
if model == 'claude-3-5-sonnet-20240620':
|
|
|
73 |
return model
|
74 |
|
75 |
async def handle_request(request):
|
76 |
+
logger.info(f"Received {request.method} request to {request.path}")
|
77 |
+
|
78 |
if request.method == 'OPTIONS':
|
79 |
+
logger.info("Handling OPTIONS request")
|
80 |
return handle_options()
|
81 |
|
82 |
api_key = request.headers.get('x-api-key')
|
83 |
if api_key != API_KEY:
|
84 |
+
logger.warning("Invalid API key")
|
85 |
+
return create_error_response('Your API key does not have permission to use the specified resource.', 403)
|
86 |
+
|
87 |
+
try:
|
88 |
+
request_body = await request.json()
|
89 |
+
logger.info(f"Received request body: {json.dumps(request_body)}")
|
90 |
+
except json.JSONDecodeError:
|
91 |
+
logger.error("Invalid JSON in request body")
|
92 |
+
return create_error_response('Invalid JSON in request body', 400)
|
93 |
+
|
94 |
+
if not request_body:
|
95 |
+
logger.error("Empty request body")
|
96 |
+
return create_error_response('Empty request body', 400)
|
|
|
|
|
97 |
|
98 |
access_token = await get_access_token()
|
99 |
location = get_location()
|
100 |
|
101 |
+
model = format_model_name(request_body.get('model', MODEL))
|
102 |
+
logger.info(f"Using model: {model}")
|
103 |
|
104 |
if 'anthropic_version' in request_body:
|
105 |
del request_body['anthropic_version']
|
|
|
115 |
'Content-Type': 'application/json; charset=utf-8'
|
116 |
}
|
117 |
|
118 |
+
logger.info(f"Sending request to Anthropic API: {api_url}")
|
119 |
async with aiohttp.ClientSession() as session:
|
120 |
async with session.post(api_url, json=request_body, headers=headers) as response:
|
121 |
response_body = await response.read()
|
122 |
response_headers = response.headers
|
123 |
response_status = response.status
|
124 |
+
logger.info(f"Received response from Anthropic API. Status: {response_status}")
|
125 |
|
126 |
modified_response = web.Response(
|
127 |
body=response_body,
|
|
|
132 |
modified_response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
|
133 |
modified_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, x-api-key, anthropic-version, model'
|
134 |
|
135 |
+
logger.info("Sending response back to client")
|
136 |
return modified_response
|
137 |
|
138 |
+
def create_error_response(message, status_code):
|
139 |
+
logger.error(f"Creating error response: {message} (Status: {status_code})")
|
140 |
+
response = web.Response(
|
141 |
+
text=json.dumps({
|
142 |
+
'type': 'error',
|
143 |
+
'error': {
|
144 |
+
'type': 'request_error',
|
145 |
+
'message': message
|
146 |
+
}
|
147 |
+
}),
|
148 |
+
status=status_code,
|
149 |
+
content_type='application/json'
|
150 |
+
)
|
151 |
+
response.headers['Access-Control-Allow-Origin'] = '*'
|
152 |
+
response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS, DELETE, HEAD'
|
153 |
+
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, x-api-key, anthropic-version, model'
|
154 |
+
return response
|
155 |
+
|
156 |
def handle_options():
|
157 |
+
logger.info("Handling OPTIONS request")
|
158 |
headers = {
|
159 |
'Access-Control-Allow-Origin': '*',
|
160 |
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
|
166 |
app.router.add_route('*', '/ai/v1/messages', handle_request)
|
167 |
|
168 |
if __name__ == '__main__':
|
169 |
+
logger.info("Starting server on port 8080")
|
170 |
web.run_app(app, port=8080)
|