aicodeswitch 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/database.js +139 -15
- package/dist/server/main.js +1 -0
- package/dist/server/proxy-server.js +60 -10
- package/dist/ui/assets/index-CvBDcTGi.js +391 -0
- package/dist/ui/assets/index-vy5mPtJs.css +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-D6QkIxk8.js +0 -391
- package/dist/ui/assets/index-g31C4Oez.css +0 -1
package/dist/server/database.js
CHANGED
|
@@ -100,6 +100,46 @@ class DatabaseManager {
|
|
|
100
100
|
console.log('[DB] Migration completed: model_limits column added');
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
|
+
// 检查rules表是否有token相关字段
|
|
104
|
+
const rulesColumns = this.db.pragma('table_info(rules)');
|
|
105
|
+
const hasTokenLimit = rulesColumns.some((col) => col.name === 'token_limit');
|
|
106
|
+
const hasTotalTokensUsed = rulesColumns.some((col) => col.name === 'total_tokens_used');
|
|
107
|
+
const hasResetInterval = rulesColumns.some((col) => col.name === 'reset_interval');
|
|
108
|
+
const hasLastResetAt = rulesColumns.some((col) => col.name === 'last_reset_at');
|
|
109
|
+
if (!hasTokenLimit) {
|
|
110
|
+
console.log('[DB] Running migration: Adding token_limit column to rules table');
|
|
111
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN token_limit INTEGER;');
|
|
112
|
+
console.log('[DB] Migration completed: token_limit column added');
|
|
113
|
+
}
|
|
114
|
+
if (!hasTotalTokensUsed) {
|
|
115
|
+
console.log('[DB] Running migration: Adding total_tokens_used column to rules table');
|
|
116
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN total_tokens_used INTEGER DEFAULT 0;');
|
|
117
|
+
console.log('[DB] Migration completed: total_tokens_used column added');
|
|
118
|
+
}
|
|
119
|
+
if (!hasResetInterval) {
|
|
120
|
+
console.log('[DB] Running migration: Adding reset_interval column to rules table');
|
|
121
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN reset_interval INTEGER;');
|
|
122
|
+
console.log('[DB] Migration completed: reset_interval column added');
|
|
123
|
+
}
|
|
124
|
+
if (!hasLastResetAt) {
|
|
125
|
+
console.log('[DB] Running migration: Adding last_reset_at column to rules table');
|
|
126
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN last_reset_at INTEGER;');
|
|
127
|
+
console.log('[DB] Migration completed: last_reset_at column added');
|
|
128
|
+
}
|
|
129
|
+
// 检查rules表是否有timeout字段
|
|
130
|
+
const hasRuleTimeout = rulesColumns.some((col) => col.name === 'timeout');
|
|
131
|
+
if (!hasRuleTimeout) {
|
|
132
|
+
console.log('[DB] Running migration: Adding timeout column to rules table');
|
|
133
|
+
this.db.exec('ALTER TABLE rules ADD COLUMN timeout INTEGER;');
|
|
134
|
+
console.log('[DB] Migration completed: timeout column added to rules');
|
|
135
|
+
}
|
|
136
|
+
// 检查api_services表是否有timeout字段,如果有则移除
|
|
137
|
+
const hasServiceTimeout = columns.some((col) => col.name === 'timeout');
|
|
138
|
+
if (hasServiceTimeout) {
|
|
139
|
+
console.log('[DB] Running migration: Removing timeout column from api_services table');
|
|
140
|
+
yield this.migrateRemoveServiceTimeout();
|
|
141
|
+
console.log('[DB] Migration completed: timeout column removed from api_services');
|
|
142
|
+
}
|
|
103
143
|
});
|
|
104
144
|
}
|
|
105
145
|
migrateMaxOutputTokensToModelLimits() {
|
|
@@ -138,6 +178,35 @@ class DatabaseManager {
|
|
|
138
178
|
console.log('[DB] Migration completed: Replaced max_output_tokens with model_limits');
|
|
139
179
|
});
|
|
140
180
|
}
|
|
181
|
+
migrateRemoveServiceTimeout() {
|
|
182
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
183
|
+
// SQLite 不支持直接删除列,需要重建表
|
|
184
|
+
this.db.pragma('foreign_keys = OFF');
|
|
185
|
+
this.db.exec(`
|
|
186
|
+
CREATE TABLE api_services_new (
|
|
187
|
+
id TEXT PRIMARY KEY,
|
|
188
|
+
vendor_id TEXT NOT NULL,
|
|
189
|
+
name TEXT NOT NULL,
|
|
190
|
+
api_url TEXT NOT NULL,
|
|
191
|
+
api_key TEXT NOT NULL,
|
|
192
|
+
source_type TEXT,
|
|
193
|
+
supported_models TEXT,
|
|
194
|
+
model_limits TEXT,
|
|
195
|
+
created_at INTEGER NOT NULL,
|
|
196
|
+
updated_at INTEGER NOT NULL,
|
|
197
|
+
FOREIGN KEY (vendor_id) REFERENCES vendors(id) ON DELETE CASCADE
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
INSERT INTO api_services_new (id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, created_at, updated_at)
|
|
201
|
+
SELECT id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, created_at, updated_at
|
|
202
|
+
FROM api_services;
|
|
203
|
+
|
|
204
|
+
DROP TABLE api_services;
|
|
205
|
+
ALTER TABLE api_services_new RENAME TO api_services;
|
|
206
|
+
`);
|
|
207
|
+
this.db.pragma('foreign_keys = ON');
|
|
208
|
+
});
|
|
209
|
+
}
|
|
141
210
|
createTables() {
|
|
142
211
|
this.db.exec(`
|
|
143
212
|
CREATE TABLE IF NOT EXISTS vendors (
|
|
@@ -154,7 +223,6 @@ class DatabaseManager {
|
|
|
154
223
|
name TEXT NOT NULL,
|
|
155
224
|
api_url TEXT NOT NULL,
|
|
156
225
|
api_key TEXT NOT NULL,
|
|
157
|
-
timeout INTEGER,
|
|
158
226
|
source_type TEXT,
|
|
159
227
|
supported_models TEXT,
|
|
160
228
|
created_at INTEGER NOT NULL,
|
|
@@ -180,6 +248,11 @@ class DatabaseManager {
|
|
|
180
248
|
target_model TEXT,
|
|
181
249
|
replaced_model TEXT,
|
|
182
250
|
sort_order INTEGER DEFAULT 0,
|
|
251
|
+
timeout INTEGER,
|
|
252
|
+
token_limit INTEGER,
|
|
253
|
+
total_tokens_used INTEGER DEFAULT 0,
|
|
254
|
+
reset_interval INTEGER,
|
|
255
|
+
last_reset_at INTEGER,
|
|
183
256
|
created_at INTEGER NOT NULL,
|
|
184
257
|
updated_at INTEGER NOT NULL,
|
|
185
258
|
FOREIGN KEY (route_id) REFERENCES routes(id) ON DELETE CASCADE,
|
|
@@ -252,7 +325,6 @@ class DatabaseManager {
|
|
|
252
325
|
name: row.name,
|
|
253
326
|
apiUrl: row.api_url,
|
|
254
327
|
apiKey: row.api_key,
|
|
255
|
-
timeout: row.timeout,
|
|
256
328
|
sourceType: row.source_type,
|
|
257
329
|
supportedModels: row.supported_models ? row.supported_models.split(',').map((model) => model.trim()).filter((model) => model.length > 0) : undefined,
|
|
258
330
|
modelLimits: row.model_limits ? JSON.parse(row.model_limits) : undefined,
|
|
@@ -269,18 +341,18 @@ class DatabaseManager {
|
|
|
269
341
|
const id = crypto_1.default.randomUUID();
|
|
270
342
|
const now = Date.now();
|
|
271
343
|
this.db
|
|
272
|
-
.prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key,
|
|
273
|
-
.run(id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.
|
|
344
|
+
.prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
345
|
+
.run(id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, now, now);
|
|
274
346
|
return Object.assign(Object.assign({}, service), { id, createdAt: now, updatedAt: now });
|
|
275
347
|
}
|
|
276
348
|
updateAPIService(id, service) {
|
|
277
349
|
const now = Date.now();
|
|
278
350
|
const result = this.db
|
|
279
|
-
.prepare('UPDATE api_services SET name = ?, api_url = ?, api_key = ?,
|
|
280
|
-
.run(service.name, service.apiUrl, service.apiKey, service.
|
|
351
|
+
.prepare('UPDATE api_services SET name = ?, api_url = ?, api_key = ?, source_type = ?, supported_models = ?, model_limits = ?, updated_at = ? WHERE id = ?')
|
|
352
|
+
.run(service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, now, id);
|
|
281
353
|
// 调试日志: 记录更新操作
|
|
282
354
|
if (result.changes > 0 && process.env.NODE_ENV === 'development') {
|
|
283
|
-
console.log(`[DB] Updated service ${id}: ${service.name} -> ${service.apiUrl}
|
|
355
|
+
console.log(`[DB] Updated service ${id}: ${service.name} -> ${service.apiUrl}`);
|
|
284
356
|
}
|
|
285
357
|
return result.changes > 0;
|
|
286
358
|
}
|
|
@@ -348,6 +420,11 @@ class DatabaseManager {
|
|
|
348
420
|
targetModel: row.target_model,
|
|
349
421
|
replacedModel: row.replaced_model,
|
|
350
422
|
sortOrder: row.sort_order,
|
|
423
|
+
timeout: row.timeout,
|
|
424
|
+
tokenLimit: row.token_limit,
|
|
425
|
+
totalTokensUsed: row.total_tokens_used,
|
|
426
|
+
resetInterval: row.reset_interval,
|
|
427
|
+
lastResetAt: row.last_reset_at,
|
|
351
428
|
createdAt: row.created_at,
|
|
352
429
|
updatedAt: row.updated_at,
|
|
353
430
|
}));
|
|
@@ -356,21 +433,68 @@ class DatabaseManager {
|
|
|
356
433
|
const id = crypto_1.default.randomUUID();
|
|
357
434
|
const now = Date.now();
|
|
358
435
|
this.db
|
|
359
|
-
.prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
360
|
-
.run(id, route.routeId, route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, now, now);
|
|
436
|
+
.prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, timeout, token_limit, total_tokens_used, reset_interval, last_reset_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
437
|
+
.run(id, route.routeId, route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, route.timeout || null, route.tokenLimit || null, route.totalTokensUsed || 0, route.resetInterval || null, route.lastResetAt || null, now, now);
|
|
361
438
|
return Object.assign(Object.assign({}, route), { id, createdAt: now, updatedAt: now });
|
|
362
439
|
}
|
|
363
440
|
updateRule(id, route) {
|
|
364
441
|
const now = Date.now();
|
|
365
442
|
const result = this.db
|
|
366
|
-
.prepare('UPDATE rules SET content_type = ?, target_service_id = ?, target_model = ?, replaced_model = ?, sort_order = ?, updated_at = ? WHERE id = ?')
|
|
367
|
-
.run(route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, now, id);
|
|
443
|
+
.prepare('UPDATE rules SET content_type = ?, target_service_id = ?, target_model = ?, replaced_model = ?, sort_order = ?, timeout = ?, token_limit = ?, reset_interval = ?, updated_at = ? WHERE id = ?')
|
|
444
|
+
.run(route.contentType, route.targetServiceId, route.targetModel || null, route.replacedModel || null, route.sortOrder || 0, route.timeout !== undefined ? route.timeout : null, route.tokenLimit !== undefined ? route.tokenLimit : null, route.resetInterval !== undefined ? route.resetInterval : null, now, id);
|
|
368
445
|
return result.changes > 0;
|
|
369
446
|
}
|
|
370
447
|
deleteRule(id) {
|
|
371
448
|
const result = this.db.prepare('DELETE FROM rules WHERE id = ?').run(id);
|
|
372
449
|
return result.changes > 0;
|
|
373
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* 增加规则的token使用量
|
|
453
|
+
* @param ruleId 规则ID
|
|
454
|
+
* @param tokensUsed 使用的token数量
|
|
455
|
+
* @returns 是否成功
|
|
456
|
+
*/
|
|
457
|
+
incrementRuleTokenUsage(ruleId, tokensUsed) {
|
|
458
|
+
const result = this.db
|
|
459
|
+
.prepare('UPDATE rules SET total_tokens_used = total_tokens_used + ? WHERE id = ?')
|
|
460
|
+
.run(tokensUsed, ruleId);
|
|
461
|
+
return result.changes > 0;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* 重置规则的token使用量
|
|
465
|
+
* @param ruleId 规则ID
|
|
466
|
+
* @returns 是否成功
|
|
467
|
+
*/
|
|
468
|
+
resetRuleTokenUsage(ruleId) {
|
|
469
|
+
const now = Date.now();
|
|
470
|
+
const result = this.db
|
|
471
|
+
.prepare('UPDATE rules SET total_tokens_used = 0, last_reset_at = ? WHERE id = ?')
|
|
472
|
+
.run(now, ruleId);
|
|
473
|
+
return result.changes > 0;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* 检查并重置到期的规则
|
|
477
|
+
* 如果规则设置了reset_interval且已经到了重置时间,则自动重置token使用量
|
|
478
|
+
* @param ruleId 规则ID
|
|
479
|
+
* @returns 是否进行了重置
|
|
480
|
+
*/
|
|
481
|
+
checkAndResetRuleIfNeeded(ruleId) {
|
|
482
|
+
const rule = this.db
|
|
483
|
+
.prepare('SELECT reset_interval, last_reset_at FROM rules WHERE id = ?')
|
|
484
|
+
.get(ruleId);
|
|
485
|
+
if (!rule || !rule.reset_interval) {
|
|
486
|
+
return false; // 没有设置重置间隔
|
|
487
|
+
}
|
|
488
|
+
const now = Date.now();
|
|
489
|
+
const resetIntervalMs = rule.reset_interval * 60 * 60 * 1000; // 小时转毫秒
|
|
490
|
+
const lastResetAt = rule.last_reset_at || 0;
|
|
491
|
+
// 检查是否已经到了重置时间
|
|
492
|
+
if (now - lastResetAt >= resetIntervalMs) {
|
|
493
|
+
this.resetRuleTokenUsage(ruleId);
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
374
498
|
// Log operations
|
|
375
499
|
addLog(log) {
|
|
376
500
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -625,8 +749,8 @@ class DatabaseManager {
|
|
|
625
749
|
// Import API services
|
|
626
750
|
for (const service of importData.apiServices) {
|
|
627
751
|
this.db
|
|
628
|
-
.prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key,
|
|
629
|
-
.run(service.id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.
|
|
752
|
+
.prepare('INSERT INTO api_services (id, vendor_id, name, api_url, api_key, source_type, supported_models, model_limits, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
753
|
+
.run(service.id, service.vendorId, service.name, service.apiUrl, service.apiKey, service.sourceType || null, service.supportedModels ? service.supportedModels.join(',') : null, service.modelLimits ? JSON.stringify(service.modelLimits) : null, service.createdAt, service.updatedAt);
|
|
630
754
|
}
|
|
631
755
|
// Import routes
|
|
632
756
|
for (const route of importData.routes) {
|
|
@@ -637,8 +761,8 @@ class DatabaseManager {
|
|
|
637
761
|
// Import rules
|
|
638
762
|
for (const rule of importData.rules) {
|
|
639
763
|
this.db
|
|
640
|
-
.prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
641
|
-
.run(rule.id, rule.routeId, rule.contentType || 'default', rule.targetServiceId, rule.targetModel || null, rule.replacedModel || null, rule.sortOrder || 0, rule.createdAt, rule.updatedAt);
|
|
764
|
+
.prepare('INSERT INTO rules (id, route_id, content_type, target_service_id, target_model, replaced_model, sort_order, timeout, token_limit, total_tokens_used, reset_interval, last_reset_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)')
|
|
765
|
+
.run(rule.id, rule.routeId, rule.contentType || 'default', rule.targetServiceId, rule.targetModel || null, rule.replacedModel || null, rule.sortOrder || 0, rule.timeout || null, rule.tokenLimit || null, rule.totalTokensUsed || 0, rule.resetInterval || null, rule.lastResetAt || null, rule.createdAt, rule.updatedAt);
|
|
642
766
|
}
|
|
643
767
|
// Update config
|
|
644
768
|
this.updateConfig(importData.config);
|
package/dist/server/main.js
CHANGED
|
@@ -282,6 +282,7 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
282
282
|
app.post('/api/rules', (req, res) => res.json(dbManager.createRule(req.body)));
|
|
283
283
|
app.put('/api/rules/:id', (req, res) => res.json(dbManager.updateRule(req.params.id, req.body)));
|
|
284
284
|
app.delete('/api/rules/:id', (req, res) => res.json(dbManager.deleteRule(req.params.id)));
|
|
285
|
+
app.put('/api/rules/:id/reset-tokens', (req, res) => res.json(dbManager.resetRuleTokenUsage(req.params.id)));
|
|
285
286
|
app.get('/api/logs', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
286
287
|
const rawLimit = typeof req.query.limit === 'string' ? parseInt(req.query.limit, 10) : NaN;
|
|
287
288
|
const rawOffset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : NaN;
|
|
@@ -476,32 +476,53 @@ class ProxyServer {
|
|
|
476
476
|
const modelMappingRules = rules.filter(rule => rule.contentType === 'model-mapping' &&
|
|
477
477
|
rule.replacedModel &&
|
|
478
478
|
requestModel.includes(rule.replacedModel));
|
|
479
|
-
//
|
|
479
|
+
// 过滤黑名单和token限制
|
|
480
480
|
for (const rule of modelMappingRules) {
|
|
481
481
|
const isBlacklisted = yield this.dbManager.isServiceBlacklisted(rule.targetServiceId, routeId, rule.contentType);
|
|
482
|
-
if (
|
|
483
|
-
|
|
482
|
+
if (isBlacklisted) {
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
// 检查并重置到期的规则
|
|
486
|
+
this.dbManager.checkAndResetRuleIfNeeded(rule.id);
|
|
487
|
+
// 检查token限制
|
|
488
|
+
if (rule.tokenLimit && rule.totalTokensUsed !== undefined && rule.totalTokensUsed >= rule.tokenLimit) {
|
|
489
|
+
continue; // 跳过超限规则
|
|
484
490
|
}
|
|
491
|
+
return rule;
|
|
485
492
|
}
|
|
486
493
|
}
|
|
487
494
|
// 2. 查找其他内容类型的规则
|
|
488
495
|
const contentType = this.determineContentType(req);
|
|
489
496
|
const contentTypeRules = rules.filter(rule => rule.contentType === contentType);
|
|
490
|
-
//
|
|
497
|
+
// 过滤黑名单和token限制
|
|
491
498
|
for (const rule of contentTypeRules) {
|
|
492
499
|
const isBlacklisted = yield this.dbManager.isServiceBlacklisted(rule.targetServiceId, routeId, contentType);
|
|
493
|
-
if (
|
|
494
|
-
|
|
500
|
+
if (isBlacklisted) {
|
|
501
|
+
continue;
|
|
495
502
|
}
|
|
503
|
+
// 检查并重置到期的规则
|
|
504
|
+
this.dbManager.checkAndResetRuleIfNeeded(rule.id);
|
|
505
|
+
// 检查token限制
|
|
506
|
+
if (rule.tokenLimit && rule.totalTokensUsed !== undefined && rule.totalTokensUsed >= rule.tokenLimit) {
|
|
507
|
+
continue; // 跳过超限规则
|
|
508
|
+
}
|
|
509
|
+
return rule;
|
|
496
510
|
}
|
|
497
511
|
// 3. 最后返回 default 规则
|
|
498
512
|
const defaultRules = rules.filter(rule => rule.contentType === 'default');
|
|
499
|
-
//
|
|
513
|
+
// 过滤黑名单和token限制
|
|
500
514
|
for (const rule of defaultRules) {
|
|
501
515
|
const isBlacklisted = yield this.dbManager.isServiceBlacklisted(rule.targetServiceId, routeId, 'default');
|
|
502
|
-
if (
|
|
503
|
-
|
|
516
|
+
if (isBlacklisted) {
|
|
517
|
+
continue;
|
|
504
518
|
}
|
|
519
|
+
// 检查并重置到期的规则
|
|
520
|
+
this.dbManager.checkAndResetRuleIfNeeded(rule.id);
|
|
521
|
+
// 检查token限制
|
|
522
|
+
if (rule.tokenLimit && rule.totalTokensUsed !== undefined && rule.totalTokensUsed >= rule.tokenLimit) {
|
|
523
|
+
continue; // 跳过超限规则
|
|
524
|
+
}
|
|
525
|
+
return rule;
|
|
505
526
|
}
|
|
506
527
|
return undefined;
|
|
507
528
|
});
|
|
@@ -527,6 +548,23 @@ class ProxyServer {
|
|
|
527
548
|
// 3. Default rules
|
|
528
549
|
const defaultRules = rules.filter(rule => rule.contentType === 'default');
|
|
529
550
|
candidates.push(...defaultRules);
|
|
551
|
+
// 4. 检查并重置到期的规则
|
|
552
|
+
candidates.forEach(rule => {
|
|
553
|
+
this.dbManager.checkAndResetRuleIfNeeded(rule.id);
|
|
554
|
+
});
|
|
555
|
+
// 5. 过滤掉超过token限制的规则(仅在有多个候选规则时)
|
|
556
|
+
if (candidates.length > 1) {
|
|
557
|
+
const filteredCandidates = candidates.filter(rule => {
|
|
558
|
+
if (rule.tokenLimit && rule.totalTokensUsed !== undefined) {
|
|
559
|
+
return rule.totalTokensUsed < rule.tokenLimit;
|
|
560
|
+
}
|
|
561
|
+
return true; // 没有设置限制的规则总是可用
|
|
562
|
+
});
|
|
563
|
+
// 如果过滤后还有规则,使用过滤后的结果
|
|
564
|
+
if (filteredCandidates.length > 0) {
|
|
565
|
+
return filteredCandidates;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
530
568
|
return candidates;
|
|
531
569
|
}
|
|
532
570
|
determineContentType(req) {
|
|
@@ -1008,6 +1046,10 @@ class ProxyServer {
|
|
|
1008
1046
|
var _a, _b;
|
|
1009
1047
|
if (logged || !((_a = this.config) === null || _a === void 0 ? void 0 : _a.enableLogging))
|
|
1010
1048
|
return;
|
|
1049
|
+
// 只记录来自编程工具的请求
|
|
1050
|
+
if (!SUPPORTED_TARGETS.some(target => req.path.startsWith(`/${target}/`))) {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1011
1053
|
logged = true;
|
|
1012
1054
|
// 获取供应商信息
|
|
1013
1055
|
const vendors = this.dbManager.getVendors();
|
|
@@ -1026,6 +1068,7 @@ class ProxyServer {
|
|
|
1026
1068
|
usage: usageForLog,
|
|
1027
1069
|
error,
|
|
1028
1070
|
// 新增字段
|
|
1071
|
+
ruleId: rule.id,
|
|
1029
1072
|
targetType,
|
|
1030
1073
|
targetServiceId: service.id,
|
|
1031
1074
|
targetServiceName: service.name,
|
|
@@ -1038,6 +1081,13 @@ class ProxyServer {
|
|
|
1038
1081
|
streamChunks: streamChunksForLog,
|
|
1039
1082
|
upstreamRequest: upstreamRequestForLog,
|
|
1040
1083
|
});
|
|
1084
|
+
// 更新规则的token使用量(只在成功请求时更新)
|
|
1085
|
+
if (usageForLog && statusCode < 400) {
|
|
1086
|
+
const totalTokens = (usageForLog.inputTokens || 0) + (usageForLog.outputTokens || 0);
|
|
1087
|
+
if (totalTokens > 0) {
|
|
1088
|
+
this.dbManager.incrementRuleTokenUsage(rule.id, totalTokens);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1041
1091
|
});
|
|
1042
1092
|
try {
|
|
1043
1093
|
if (targetType === 'claude-code') {
|
|
@@ -1083,7 +1133,7 @@ class ProxyServer {
|
|
|
1083
1133
|
method: req.method,
|
|
1084
1134
|
url: `${service.apiUrl}${mappedPath}`,
|
|
1085
1135
|
headers: this.buildUpstreamHeaders(req, service, sourceType, streamRequested),
|
|
1086
|
-
timeout:
|
|
1136
|
+
timeout: rule.timeout || 3000000, // 默认300秒
|
|
1087
1137
|
validateStatus: () => true,
|
|
1088
1138
|
responseType: streamRequested ? 'stream' : 'json',
|
|
1089
1139
|
};
|