更智能的提醒
Browse files- package-lock.json +8 -1
- src/api/client.js +15 -2
- src/auth/token_manager.js +10 -3
- src/server/index.js +26 -2
package-lock.json
CHANGED
|
@@ -1,11 +1,18 @@
|
|
| 1 |
{
|
| 2 |
-
"name": "
|
|
|
|
| 3 |
"lockfileVersion": 3,
|
| 4 |
"requires": true,
|
| 5 |
"packages": {
|
| 6 |
"": {
|
|
|
|
|
|
|
|
|
|
| 7 |
"dependencies": {
|
| 8 |
"express": "^5.1.0"
|
|
|
|
|
|
|
|
|
|
| 9 |
}
|
| 10 |
},
|
| 11 |
"node_modules/accepts": {
|
|
|
|
| 1 |
{
|
| 2 |
+
"name": "antigravity-to-openai",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
"lockfileVersion": 3,
|
| 5 |
"requires": true,
|
| 6 |
"packages": {
|
| 7 |
"": {
|
| 8 |
+
"name": "antigravity-to-openai",
|
| 9 |
+
"version": "1.0.0",
|
| 10 |
+
"license": "MIT",
|
| 11 |
"dependencies": {
|
| 12 |
"express": "^5.1.0"
|
| 13 |
+
},
|
| 14 |
+
"engines": {
|
| 15 |
+
"node": ">=18.0.0"
|
| 16 |
}
|
| 17 |
},
|
| 18 |
"node_modules/accepts": {
|
src/api/client.js
CHANGED
|
@@ -3,6 +3,11 @@ import config from '../config/config.js';
|
|
| 3 |
|
| 4 |
export async function generateAssistantResponse(requestBody, callback) {
|
| 5 |
const token = await tokenManager.getToken();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
const url = config.api.url;
|
| 7 |
|
| 8 |
const response = await fetch(url, {
|
|
@@ -10,7 +15,7 @@ export async function generateAssistantResponse(requestBody, callback) {
|
|
| 10 |
headers: {
|
| 11 |
'Host': config.api.host,
|
| 12 |
'User-Agent': config.api.userAgent,
|
| 13 |
-
'Authorization': `Bearer ${token}`,
|
| 14 |
'Content-Type': 'application/json',
|
| 15 |
'Accept-Encoding': 'gzip'
|
| 16 |
},
|
|
@@ -19,6 +24,10 @@ export async function generateAssistantResponse(requestBody, callback) {
|
|
| 19 |
|
| 20 |
if (!response.ok) {
|
| 21 |
const errorText = await response.text();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
throw new Error(`API请求失败 (${response.status}): ${errorText}`);
|
| 23 |
}
|
| 24 |
|
|
@@ -85,12 +94,16 @@ export async function generateAssistantResponse(requestBody, callback) {
|
|
| 85 |
export async function getAvailableModels() {
|
| 86 |
const token = await tokenManager.getToken();
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
const response = await fetch(config.api.modelsUrl, {
|
| 89 |
method: 'POST',
|
| 90 |
headers: {
|
| 91 |
'Host': config.api.host,
|
| 92 |
'User-Agent': config.api.userAgent,
|
| 93 |
-
'Authorization': `Bearer ${token}`,
|
| 94 |
'Content-Type': 'application/json',
|
| 95 |
'Accept-Encoding': 'gzip'
|
| 96 |
},
|
|
|
|
| 3 |
|
| 4 |
export async function generateAssistantResponse(requestBody, callback) {
|
| 5 |
const token = await tokenManager.getToken();
|
| 6 |
+
|
| 7 |
+
if (!token) {
|
| 8 |
+
throw new Error('没有可用的token,请运行 npm run login 获取token');
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
const url = config.api.url;
|
| 12 |
|
| 13 |
const response = await fetch(url, {
|
|
|
|
| 15 |
headers: {
|
| 16 |
'Host': config.api.host,
|
| 17 |
'User-Agent': config.api.userAgent,
|
| 18 |
+
'Authorization': `Bearer ${token.access_token}`,
|
| 19 |
'Content-Type': 'application/json',
|
| 20 |
'Accept-Encoding': 'gzip'
|
| 21 |
},
|
|
|
|
| 24 |
|
| 25 |
if (!response.ok) {
|
| 26 |
const errorText = await response.text();
|
| 27 |
+
if (response.status === 403) {
|
| 28 |
+
tokenManager.disableCurrentToken(token);
|
| 29 |
+
throw new Error(`该账号没有使用权限,已自动禁用。错误详情: ${errorText}`);
|
| 30 |
+
}
|
| 31 |
throw new Error(`API请求失败 (${response.status}): ${errorText}`);
|
| 32 |
}
|
| 33 |
|
|
|
|
| 94 |
export async function getAvailableModels() {
|
| 95 |
const token = await tokenManager.getToken();
|
| 96 |
|
| 97 |
+
if (!token) {
|
| 98 |
+
throw new Error('没有可用的token,请运行 npm run login 获取token');
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
const response = await fetch(config.api.modelsUrl, {
|
| 102 |
method: 'POST',
|
| 103 |
headers: {
|
| 104 |
'Host': config.api.host,
|
| 105 |
'User-Agent': config.api.userAgent,
|
| 106 |
+
'Authorization': `Bearer ${token.access_token}`,
|
| 107 |
'Content-Type': 'application/json',
|
| 108 |
'Accept-Encoding': 'gzip'
|
| 109 |
},
|
src/auth/token_manager.js
CHANGED
|
@@ -87,6 +87,7 @@ class TokenManager {
|
|
| 87 |
}
|
| 88 |
|
| 89 |
disableToken(token) {
|
|
|
|
| 90 |
token.enable = false;
|
| 91 |
this.saveToFile();
|
| 92 |
this.loadTokens();
|
|
@@ -102,9 +103,8 @@ class TokenManager {
|
|
| 102 |
if (this.isExpired(token)) {
|
| 103 |
await this.refreshToken(token);
|
| 104 |
}
|
| 105 |
-
const accessToken = token.access_token;
|
| 106 |
this.currentIndex = (this.currentIndex + 1) % this.tokens.length;
|
| 107 |
-
return
|
| 108 |
} catch (error) {
|
| 109 |
if (error.statusCode === 403) {
|
| 110 |
log.warn(`Token ${this.currentIndex} 刷新失败(403),禁用并尝试下一个`);
|
|
@@ -120,6 +120,13 @@ class TokenManager {
|
|
| 120 |
return null;
|
| 121 |
}
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
async handleRequestError(error, currentAccessToken) {
|
| 124 |
if (error.statusCode === 403) {
|
| 125 |
log.warn('请求遇到403错误,尝试刷新token');
|
|
@@ -128,7 +135,7 @@ class TokenManager {
|
|
| 128 |
try {
|
| 129 |
await this.refreshToken(currentToken);
|
| 130 |
log.info('Token刷新成功,返回新token');
|
| 131 |
-
return currentToken
|
| 132 |
} catch (refreshError) {
|
| 133 |
if (refreshError.statusCode === 403) {
|
| 134 |
log.warn('刷新token也遇到403,禁用并切换到下一个');
|
|
|
|
| 87 |
}
|
| 88 |
|
| 89 |
disableToken(token) {
|
| 90 |
+
log.warn(`禁用token`)
|
| 91 |
token.enable = false;
|
| 92 |
this.saveToFile();
|
| 93 |
this.loadTokens();
|
|
|
|
| 103 |
if (this.isExpired(token)) {
|
| 104 |
await this.refreshToken(token);
|
| 105 |
}
|
|
|
|
| 106 |
this.currentIndex = (this.currentIndex + 1) % this.tokens.length;
|
| 107 |
+
return token;
|
| 108 |
} catch (error) {
|
| 109 |
if (error.statusCode === 403) {
|
| 110 |
log.warn(`Token ${this.currentIndex} 刷新失败(403),禁用并尝试下一个`);
|
|
|
|
| 120 |
return null;
|
| 121 |
}
|
| 122 |
|
| 123 |
+
disableCurrentToken(token) {
|
| 124 |
+
const found = this.tokens.find(t => t.access_token === token.access_token);
|
| 125 |
+
if (found) {
|
| 126 |
+
this.disableToken(found);
|
| 127 |
+
}
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
async handleRequestError(error, currentAccessToken) {
|
| 131 |
if (error.statusCode === 403) {
|
| 132 |
log.warn('请求遇到403错误,尝试刷新token');
|
|
|
|
| 135 |
try {
|
| 136 |
await this.refreshToken(currentToken);
|
| 137 |
log.info('Token刷新成功,返回新token');
|
| 138 |
+
return currentToken;
|
| 139 |
} catch (refreshError) {
|
| 140 |
if (refreshError.statusCode === 403) {
|
| 141 |
log.warn('刷新token也遇到403,禁用并切换到下一个');
|
src/server/index.js
CHANGED
|
@@ -49,8 +49,8 @@ app.get('/v1/models', async (req, res) => {
|
|
| 49 |
});
|
| 50 |
|
| 51 |
app.post('/v1/chat/completions', async (req, res) => {
|
|
|
|
| 52 |
try {
|
| 53 |
-
const { messages, model, stream = true, tools, ...params} = req.body;
|
| 54 |
|
| 55 |
if (!messages) {
|
| 56 |
return res.status(400).json({ error: 'messages is required' });
|
|
@@ -129,7 +129,31 @@ app.post('/v1/chat/completions', async (req, res) => {
|
|
| 129 |
} catch (error) {
|
| 130 |
logger.error('生成响应失败:', error.message);
|
| 131 |
if (!res.headersSent) {
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
}
|
| 134 |
}
|
| 135 |
});
|
|
|
|
| 49 |
});
|
| 50 |
|
| 51 |
app.post('/v1/chat/completions', async (req, res) => {
|
| 52 |
+
const { messages, model, stream = true, tools, ...params} = req.body;
|
| 53 |
try {
|
|
|
|
| 54 |
|
| 55 |
if (!messages) {
|
| 56 |
return res.status(400).json({ error: 'messages is required' });
|
|
|
|
| 129 |
} catch (error) {
|
| 130 |
logger.error('生成响应失败:', error.message);
|
| 131 |
if (!res.headersSent) {
|
| 132 |
+
if (stream) {
|
| 133 |
+
res.setHeader('Content-Type', 'text/event-stream');
|
| 134 |
+
res.setHeader('Cache-Control', 'no-cache');
|
| 135 |
+
res.setHeader('Connection', 'keep-alive');
|
| 136 |
+
const id = `chatcmpl-${Date.now()}`;
|
| 137 |
+
const created = Math.floor(Date.now() / 1000);
|
| 138 |
+
res.write(`data: ${JSON.stringify({
|
| 139 |
+
id,
|
| 140 |
+
object: 'chat.completion.chunk',
|
| 141 |
+
created,
|
| 142 |
+
model,
|
| 143 |
+
choices: [{ index: 0, delta: { content: `错误: ${error.message}` }, finish_reason: null }]
|
| 144 |
+
})}\n\n`);
|
| 145 |
+
res.write(`data: ${JSON.stringify({
|
| 146 |
+
id,
|
| 147 |
+
object: 'chat.completion.chunk',
|
| 148 |
+
created,
|
| 149 |
+
model,
|
| 150 |
+
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }]
|
| 151 |
+
})}\n\n`);
|
| 152 |
+
res.write('data: [DONE]\n\n');
|
| 153 |
+
res.end();
|
| 154 |
+
} else {
|
| 155 |
+
res.status(500).json({ error: error.message });
|
| 156 |
+
}
|
| 157 |
}
|
| 158 |
}
|
| 159 |
});
|