smgc commited on
Commit
04f086f
1 Parent(s): 24d0b35

Rename app.py to app.js

Browse files
Files changed (2) hide show
  1. app.js +191 -0
  2. app.py +0 -113
app.js ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const http = require('http');
2
+ const url = require('url');
3
+ const fetch = require('node-fetch');
4
+
5
+ const MODEL = 'claude-3-5-sonnet@20240620';
6
+
7
+ const PROJECT_ID = process.env.PROJECT_ID;
8
+ const CLIENT_ID = process.env.CLIENT_ID;
9
+ const CLIENT_SECRET = process.env.CLIENT_SECRET;
10
+ const REFRESH_TOKEN = process.env.REFRESH_TOKEN;
11
+ const API_KEY = process.env.API_KEY;
12
+
13
+ const TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token';
14
+
15
+ let tokenCache = {
16
+ accessToken: '',
17
+ expiry: 0,
18
+ refreshPromise: null
19
+ };
20
+
21
+ async function getAccessToken() {
22
+ const now = Date.now() / 1000;
23
+
24
+ if (tokenCache.accessToken && now < tokenCache.expiry - 120) {
25
+ return tokenCache.accessToken;
26
+ }
27
+
28
+ if (tokenCache.refreshPromise) {
29
+ await tokenCache.refreshPromise;
30
+ return tokenCache.accessToken;
31
+ }
32
+
33
+ tokenCache.refreshPromise = (async () => {
34
+ try {
35
+ const response = await fetch(TOKEN_URL, {
36
+ method: 'POST',
37
+ headers: {
38
+ 'Content-Type': 'application/json'
39
+ },
40
+ body: JSON.stringify({
41
+ client_id: CLIENT_ID,
42
+ client_secret: CLIENT_SECRET,
43
+ refresh_token: REFRESH_TOKEN,
44
+ grant_type: 'refresh_token'
45
+ })
46
+ });
47
+
48
+ const data = await response.json();
49
+ tokenCache.accessToken = data.access_token;
50
+ tokenCache.expiry = now + data.expires_in;
51
+ } finally {
52
+ tokenCache.refreshPromise = null;
53
+ }
54
+ })();
55
+
56
+ await tokenCache.refreshPromise;
57
+ return tokenCache.accessToken;
58
+ }
59
+
60
+ function getLocation() {
61
+ const currentSeconds = new Date().getSeconds();
62
+ return currentSeconds < 30 ? 'europe-west1' : 'us-east5';
63
+ }
64
+
65
+ function constructApiUrl(location) {
66
+ return `https://${location}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${location}/publishers/anthropic/models/${MODEL}:streamRawPredict`;
67
+ }
68
+
69
+ async function handleRequest(request) {
70
+ if (request.method === 'OPTIONS') {
71
+ return handleOptions();
72
+ }
73
+
74
+ const apiKey = request.headers['x-api-key'];
75
+ if (apiKey !== API_KEY) {
76
+ const errorResponse = {
77
+ status: 403,
78
+ headers: {
79
+ 'Content-Type': 'application/json',
80
+ 'Access-Control-Allow-Origin': '*',
81
+ 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE, HEAD',
82
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key, anthropic-version, model'
83
+ },
84
+ body: JSON.stringify({
85
+ type: "error",
86
+ error: {
87
+ type: "permission_error",
88
+ message: "Your API key does not have permission to use the specified resource."
89
+ }
90
+ })
91
+ };
92
+
93
+ return errorResponse;
94
+ }
95
+
96
+ const accessToken = await getAccessToken();
97
+ const location = getLocation();
98
+ const apiUrl = constructApiUrl(location);
99
+
100
+ let requestBody = JSON.parse(request.body);
101
+
102
+ if (requestBody.anthropic_version) {
103
+ delete requestBody.anthropic_version;
104
+ }
105
+
106
+ if (requestBody.model) {
107
+ delete requestBody.model;
108
+ }
109
+
110
+ requestBody.anthropic_version = "vertex-2023-10-16";
111
+
112
+ const modifiedHeaders = {
113
+ ...request.headers,
114
+ 'Authorization': `Bearer ${accessToken}`,
115
+ 'Content-Type': 'application/json; charset=utf-8'
116
+ };
117
+ delete modifiedHeaders['anthropic-version'];
118
+
119
+ const modifiedRequest = {
120
+ headers: modifiedHeaders,
121
+ method: request.method,
122
+ body: JSON.stringify(requestBody),
123
+ redirect: 'follow'
124
+ };
125
+
126
+ const response = await fetch(apiUrl, modifiedRequest);
127
+ const responseBody = await response.text();
128
+ const modifiedResponse = {
129
+ status: response.status,
130
+ statusText: response.statusText,
131
+ headers: {
132
+ ...response.headers,
133
+ 'Access-Control-Allow-Origin': '*',
134
+ 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
135
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key, anthropic-version, model'
136
+ },
137
+ body: responseBody
138
+ };
139
+
140
+ return modifiedResponse;
141
+ }
142
+
143
+ function handleOptions() {
144
+ const headers = {
145
+ 'Access-Control-Allow-Origin': '*',
146
+ 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
147
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key, anthropic-version, model'
148
+ };
149
+
150
+ return {
151
+ status: 204,
152
+ headers: headers
153
+ };
154
+ }
155
+
156
+ const server = http.createServer(async (req, res) => {
157
+ const parsedUrl = url.parse(req.url, true);
158
+ if (parsedUrl.pathname === '/ai/v1/messages') {
159
+ const request = {
160
+ method: req.method,
161
+ headers: req.headers,
162
+ body: await getRequestBody(req)
163
+ };
164
+
165
+ const response = await handleRequest(request);
166
+ res.writeHead(response.status, response.statusText, response.headers);
167
+ res.end(response.body);
168
+ } else {
169
+ res.writeHead(404, { 'Content-Type': 'application/json' });
170
+ res.end(JSON.stringify({ error: 'Not Found' }));
171
+ }
172
+ });
173
+
174
+ function getRequestBody(req) {
175
+ return new Promise((resolve, reject) => {
176
+ let body = '';
177
+ req.on('data', chunk => {
178
+ body += chunk.toString();
179
+ });
180
+ req.on('end', () => {
181
+ resolve(body);
182
+ });
183
+ req.on('error', err => {
184
+ reject(err);
185
+ });
186
+ });
187
+ }
188
+
189
+ server.listen(8080, () => {
190
+ console.log('Server is running on port 8080');
191
+ });
app.py DELETED
@@ -1,113 +0,0 @@
1
- from flask import Flask, request, jsonify, Response, make_response
2
- import requests
3
- import threading
4
- import time
5
- import os
6
-
7
- app = Flask(__name__)
8
-
9
- MODEL = 'claude-3-5-sonnet@20240620'
10
- PROJECT_ID = os.getenv('PROJECT_ID')
11
- CLIENT_ID = os.getenv('CLIENT_ID')
12
- CLIENT_SECRET = os.getenv('CLIENT_SECRET')
13
- REFRESH_TOKEN = os.getenv('REFRESH_TOKEN')
14
- API_KEY = os.getenv('API_KEY')
15
- TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token'
16
-
17
- token_cache = {
18
- 'access_token': '',
19
- 'expiry': 0,
20
- 'refresh_promise': None
21
- }
22
-
23
- 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
- token_cache['refresh_promise'].join()
31
- return token_cache['access_token']
32
-
33
- def refresh_token():
34
- try:
35
- response = requests.post(TOKEN_URL, json={
36
- 'client_id': CLIENT_ID,
37
- 'client_secret': CLIENT_SECRET,
38
- 'refresh_token': REFRESH_TOKEN,
39
- 'grant_type': 'refresh_token'
40
- })
41
- data = response.json()
42
- token_cache['access_token'] = data['access_token']
43
- token_cache['expiry'] = now + data['expires_in']
44
- finally:
45
- token_cache['refresh_promise'] = None
46
-
47
- token_cache['refresh_promise'] = threading.Thread(target=refresh_token)
48
- token_cache['refresh_promise'].start()
49
- token_cache['refresh_promise'].join()
50
- return token_cache['access_token']
51
-
52
- def get_location():
53
- current_seconds = time.localtime().tm_sec
54
- return 'europe-west1' if current_seconds < 30 else 'us-east5'
55
-
56
- def construct_api_url(location):
57
- return f'https://{location}-aiplatform.googleapis.com/v1/projects/{PROJECT_ID}/locations/{location}/publishers/anthropic/models/{MODEL}:streamRawPredict'
58
-
59
- @app.route('/ai/v1/messages', methods=['POST', 'OPTIONS'])
60
- def handle_request():
61
- if request.method == 'OPTIONS':
62
- return handle_options()
63
-
64
- api_key = request.headers.get('x-api-key')
65
- if api_key != API_KEY:
66
- error_response = make_response(jsonify({
67
- 'type': 'error',
68
- 'error': {
69
- 'type': 'permission_error',
70
- 'message': 'Your API key does not have permission to use the specified resource.'
71
- }
72
- }), 403)
73
- error_response.headers['Access-Control-Allow-Origin'] = '*'
74
- error_response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS, DELETE, HEAD'
75
- error_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, x-api-key, anthropic-version, model'
76
- return error_response
77
-
78
- access_token = get_access_token()
79
- location = get_location()
80
- api_url = construct_api_url(location)
81
-
82
- request_body = request.json
83
-
84
- if 'anthropic_version' in request_body:
85
- del request_body['anthropic_version']
86
- if 'model' in request_body:
87
- del request_body['model']
88
-
89
- request_body['anthropic_version'] = 'vertex-2023-10-16'
90
-
91
- headers = {
92
- 'Authorization': f'Bearer {access_token}',
93
- 'Content-Type': 'application/json; charset=utf-8'
94
- }
95
-
96
- response = requests.post(api_url, headers=headers, json=request_body)
97
- modified_response = make_response(response.content, response.status_code)
98
- modified_response.headers['Access-Control-Allow-Origin'] = '*'
99
- modified_response.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
100
- modified_response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, x-api-key, anthropic-version, model'
101
-
102
- return modified_response
103
-
104
- def handle_options():
105
- headers = {
106
- 'Access-Control-Allow-Origin': '*',
107
- 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
108
- 'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key, anthropic-version, model'
109
- }
110
- return '', 204, headers
111
-
112
- if __name__ == '__main__':
113
- app.run(port=8080)