vertex2api / app.js
smgc's picture
Update app.js
391caf4 verified
raw
history blame
No virus
5.76 kB
const http = require('http');
const url = require('url');
const fetch = require('node-fetch');
const MODEL = 'claude-3-5-sonnet@20240620';
const PROJECT_ID = process.env.PROJECT_ID;
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const REFRESH_TOKEN = process.env.REFRESH_TOKEN;
const API_KEY = process.env.API_KEY;
const TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token';
let tokenCache = {
accessToken: '',
expiry: 0,
refreshPromise: null
};
async function getAccessToken() {
const now = Date.now() / 1000;
if (tokenCache.accessToken && now < tokenCache.expiry - 120) {
return tokenCache.accessToken;
}
if (tokenCache.refreshPromise) {
await tokenCache.refreshPromise;
return tokenCache.accessToken;
}
tokenCache.refreshPromise = (async () => {
try {
const response = await fetch(TOKEN_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
refresh_token: REFRESH_TOKEN,
grant_type: 'refresh_token'
})
});
const data = await response.json();
tokenCache.accessToken = data.access_token;
tokenCache.expiry = now + data.expires_in;
} finally {
tokenCache.refreshPromise = null;
}
})();
await tokenCache.refreshPromise;
return tokenCache.accessToken;
}
function getLocation() {
const currentSeconds = new Date().getSeconds();
return currentSeconds < 30 ? 'europe-west1' : 'us-east5';
}
function constructApiUrl(location) {
const url = `https://${location}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${location}/publishers/anthropic/models/${MODEL}:streamRawPredict`;
console.log('Constructed URL:', url); // 打印URL以验证其正确性
return url;
}
async function handleRequest(request) {
try {
if (request.method === 'OPTIONS') {
return handleOptions();
}
const apiKey = request.headers['x-api-key'];
if (apiKey !== API_KEY) {
return handleErrorResponse(403, {
type: "error",
error: {
type: "permission_error",
message: "Your API key does not have permission to use the specified resource."
}
});
}
const accessToken = await getAccessToken();
const location = getLocation();
const apiUrl = constructApiUrl(location);
let requestBody = JSON.parse(request.body);
if (requestBody.anthropic_version) {
delete requestBody.anthropic_version;
}
if (requestBody.model) {
delete requestBody.model;
}
requestBody.anthropic_version = "vertex-2023-10-16";
const modifiedHeaders = {
...request.headers,
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json; charset=utf-8'
};
delete modifiedHeaders['anthropic-version'];
const modifiedRequest = {
headers: modifiedHeaders,
method: request.method,
body: JSON.stringify(requestBody),
redirect: 'manual'
};
const response = await fetch(apiUrl, modifiedRequest);
if (response.status === 301 || response.status === 302) {
const location = response.headers.get('location');
console.log('Redirected to:', location);
const redirectedResponse = await fetch(location, modifiedRequest);
return handleResponse(redirectedResponse);
}
return handleResponse(response);
} catch (error) {
console.error('Error handling request:', error);
return handleErrorResponse(500, {
type: "error",
error: {
type: "internal_error",
message: "Internal server error."
}
});
}
}
function handleResponse(response) {
return {
status: response.status,
headers: {
...response.headers,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key, anthropic-version, model'
},
body: response.body
};
}
function handleErrorResponse(status, body) {
return {
status: status,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE, HEAD',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key, anthropic-version, model'
},
body: JSON.stringify(body)
};
}
function handleOptions() {
const headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization, x-api-key, anthropic-version, model'
};
return {
status: 204,
headers: headers
};
}
const server = http.createServer(async (req, res) => {
const parsedUrl = url.parse(req.url, true);
if (parsedUrl.pathname === '/ai/v1/messages') {
const request = {
method: req.method,
headers: req.headers,
body: await getRequestBody(req)
};
const response = await handleRequest(request);
res.writeHead(response.status, response.headers);
res.end(response.body);
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not Found' }));
}
});
function getRequestBody(req) {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
resolve(body);
});
req.on('error', err => {
reject(err);
});
});
}
server.listen(8080, () => {
console.log('Server is running on port 8080');
});