-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathapi-d1.js
4653 lines (3987 loc) · 157 KB
/
api-d1.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// ctrl+f 搜索 自定义 ,修改你需要自定义的配置
// ==================== 常量定义 ====================
const API_BASE_URL = "https://api.siliconflow.cn"; // 可自定义修改为您的API地址 openai格式
const API_ENDPOINTS = {
chat: "/v1/chat/completions",
embeddings: "/v1/embeddings",
images: "/v1/images/generations",
models: "/v1/models",
audio: "/v1/audio/transcriptions",
userInfo: "/v1/user/info",
rerank: "/v1/rerank",
};
const KV_KEYS = {
TOKENS: "tokens",
STATS: "stats",
PASSWORD: "admin_password",
SESSION_SECRET: "session_secret",
};
// 存储类型 - 自定义修改为您想使用的存储类型: "kv" 或 "d1"
const STORAGE_TYPE = "d1";
// API鉴权密钥 - 自定义修改为您的密钥,用于验证API请求
const API_KEY = "sk-yourCustomApiKey123456789";
// 默认管理员密码 - 自定义修改为更安全的密码
const DEFAULT_ADMIN_PASSWORD = "xxx";
// ==================== 内存数据存储 ====================
// 存储API令牌列表
let tokens = [];
// 改进的数据锁机制
let dataLock = {
locked: false,
owner: null,
timestamp: 0,
timeout: 20000, // 锁超时时间,20秒
};
// 请求统计数据 - 分钟级
let requestTimestamps = [];
let tokenCounts = [];
// 请求统计数据 - 天级
let requestTimestampsDay = [];
let tokenCountsDay = [];
// 上次保存统计数据的时间
let lastStatsSave = Date.now();
// 记录最后处理的日期(用于按自然日重置每日统计)
let lastProcessedDate = null;
// 设置日志级别
let logLevel = "debug"; // debug, info, warn, error
// 全局统计变量
let lastKVSaveTime = Date.now();
let pendingUpdates = 0;
// const KV_SAVE_INTERVAL = 300000; // 每5分钟保存一次 - 已弃用,改为实时保存
// const MAX_PENDING_UPDATES = 20; // 积累20次更新后强制保存 - 已弃用,改为实时保存
// ==================== 缓存配置 ====================
const CACHE_TTL = {
TOKENS: 30 * 1000, // 令牌缓存半分钟
STATS: 30 * 1000, // 统计数据缓存半分钟
};
// 缓存对象
const cache = {
tokens: {
data: null,
timestamp: 0,
},
stats: {
data: null,
timestamp: 0,
},
};
// ==================== 日志类 ===================
class Logger {
static debug(message, ...args) {
if (logLevel === "debug") {
console.debug("[DEBUG] " + message, ...args);
}
}
static info(message, ...args) {
if (logLevel === "debug" || logLevel === "info") {
console.info("[INFO] " + message, ...args);
}
}
static warn(message, ...args) {
if (logLevel === "debug" || logLevel === "info" || logLevel === "warn") {
console.warn("[WARN] " + message, ...args);
}
}
static error(message, ...args) {
console.error("[ERROR] " + message, ...args);
}
}
// ==================== 数据锁定管理 ====================
function acquireDataLock(operationId = null) {
const now = Date.now();
// 检查是否有锁超时,自动释放
if (dataLock.locked && now - dataLock.timestamp > dataLock.timeout) {
Logger.warn(`检测到锁超时,自动释放。上一个持有者: ${dataLock.owner || "未知"}, 持有时间: ${(now - dataLock.timestamp) / 1000}秒`);
releaseDataLock(true);
}
// 尝试获取锁
if (!dataLock.locked) {
dataLock.locked = true;
dataLock.owner = operationId || `op-${now}-${Math.random().toString(36).substring(2, 7)}`;
dataLock.timestamp = now;
return dataLock.owner; // 返回锁ID而不是布尔值
}
return false;
}
function releaseDataLock(force = false, lockId = null) {
// 如果锁已经是释放状态,直接返回成功
if (!dataLock.locked) {
return true;
}
// 只有锁的拥有者或强制释放才能解锁
if (force || !lockId || dataLock.owner === lockId) {
dataLock.locked = false;
dataLock.owner = null;
return true;
}
Logger.warn(`尝试释放非自己持有的锁被拒绝。请求者: ${lockId}, 持有者: ${dataLock.owner}`);
return false;
}
// ==================== 令牌管理函数 ====================
async function loadTokensFromKV(env, forceRefresh = false) {
try {
// 检查缓存是否有效,除非强制刷新
const now = Date.now();
if (!forceRefresh && cache.tokens.data && now - cache.tokens.timestamp < CACHE_TTL.TOKENS) {
tokens = cache.tokens.data;
Logger.debug(`使用缓存中的令牌数据,共${tokens.length}个令牌`);
return true;
}
// 根据存储类型选择加载方式
if (STORAGE_TYPE === "d1" && env.DB) {
// 从D1加载数据
try {
const result = await env.DB.prepare("SELECT data FROM tokens ORDER BY id DESC LIMIT 1").first();
if (result && result.data) {
tokens = JSON.parse(result.data);
// 更新缓存
cache.tokens.data = tokens;
cache.tokens.timestamp = now;
Logger.info(`已从D1加载${tokens.length}个令牌${forceRefresh ? " (强制刷新)" : ""}`);
} else {
tokens = [];
cache.tokens.data = [];
cache.tokens.timestamp = now;
Logger.info("D1中没有令牌数据,初始化为空数组");
}
return true;
} catch (dbError) {
Logger.error("从D1加载令牌失败:", dbError);
// 如果D1失败,尝试从KV加载作为备份
Logger.info("尝试从KV加载令牌作为备份...");
}
}
// 从KV加载数据(作为备份或默认方式)
const data = await env.API_TOKENS.get(KV_KEYS.TOKENS, { type: "json" });
if (data) {
tokens = data;
// 更新缓存
cache.tokens.data = data;
cache.tokens.timestamp = now;
Logger.info(`已从KV加载${tokens.length}个令牌${forceRefresh ? " (强制刷新)" : ""}`);
} else {
tokens = [];
cache.tokens.data = [];
cache.tokens.timestamp = now;
Logger.info("KV中没有令牌数据,初始化为空数组");
}
return true;
} catch (error) {
Logger.error("加载令牌失败:", error);
return false;
}
}
async function saveTokensToKV(env) {
if (!env) return false;
try {
// 获取数据锁,防止并发写入
await acquireDataLock();
// 根据存储类型选择保存方式
if (STORAGE_TYPE === "d1" && env.DB) {
try {
// 保存到D1数据库
const tokensJson = JSON.stringify(tokens);
// 先插入新记录
await env.DB.prepare("INSERT INTO tokens (data) VALUES (?)").bind(tokensJson).run();
// 然后删除旧记录,只保留最新的5条记录
await env.DB.prepare("DELETE FROM tokens WHERE id NOT IN (SELECT id FROM tokens ORDER BY id DESC LIMIT 5)").run();
// 更新缓存
cache.tokens.data = [...tokens];
cache.tokens.timestamp = Date.now();
Logger.info(`已保存${tokens.length}个令牌到D1并更新缓存,并清理了旧记录`);
releaseDataLock();
return true;
} catch (dbError) {
Logger.error("保存令牌到D1失败:", dbError);
// 如果D1失败,尝试保存到KV作为备份
Logger.info("尝试保存令牌到KV作为备份...");
}
}
// 保存到KV(作为备份或默认方式)
await env.API_TOKENS.put(KV_KEYS.TOKENS, JSON.stringify(tokens));
// 更新缓存
cache.tokens.data = [...tokens];
cache.tokens.timestamp = Date.now();
Logger.info(`已保存${tokens.length}个令牌到KV并更新缓存`);
releaseDataLock();
return true;
} catch (error) {
releaseDataLock();
Logger.error("保存令牌失败:", error);
return false;
}
}
// 获取北京时间字符串
function getBJTimeString() {
const date = new Date();
const bjTime = new Date(date.getTime() + 8 * 60 * 60 * 1000);
return bjTime.toISOString().replace("T", " ").substring(0, 19);
}
/**
* 估算文本的token数量
* @param {string} text - 要估算的文本
* @param {boolean} isChatMessage - 是否为聊天消息
* @param {string} textType - 文本类型: "normal", "image_prompt", "completion", "code"
* @returns {number} - 估算的token数量
*/
function estimateTokenCount(text, isChatMessage = false, textType = "normal") {
if (!text) return 0;
// 计算中文字符数和总字符数
const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
// 计算代码块
const codeBlocks = text.match(/```[\s\S]*?```/g) || [];
let codeChars = 0;
for (const block of codeBlocks) {
codeChars += block.length;
}
// 计算ASCII符号字符(标点、数字等)
const symbolChars = (text.match(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\d]/g) || []).length;
// 计算空格、换行等空白字符
const whitespaceChars = (text.match(/\s/g) || []).length;
const totalChars = text.length;
const nonChineseChars = totalChars - chineseChars;
let estimatedTokens = 0;
// 根据文本类型使用不同的估算逻辑
switch (textType) {
case "image_prompt":
// 图片提示词通常需要更详细的描述,token使用效率更高
// 中文字符约0.6个token/字,非中文字符约0.2个token/字(5字/token)
estimatedTokens = Math.ceil(chineseChars * 0.6) + Math.ceil(nonChineseChars / 5);
break;
case "code":
// 代码文本估算
// 代码通常token效率高
estimatedTokens = Math.ceil(chineseChars * 0.7) + Math.ceil((nonChineseChars - codeChars) / 4) + Math.ceil(codeChars / 6);
break;
case "completion":
// 模型输出的完成内容
// 通常模型生成的文本token效率较高,倾向于符合tokenizer的常用词
// 中文约0.65 token/字,非中文约0.3 token/字
estimatedTokens = Math.ceil(chineseChars * 0.65) + Math.ceil(nonChineseChars / 3.5);
// 调整:根据特殊字符比例进一步优化
if (symbolChars > totalChars * 0.3) {
// 大量标点的文本token率更高
estimatedTokens = Math.ceil(estimatedTokens * 1.1);
}
break;
case "normal":
default:
// 普通文本
// 基础估算:中文字符约0.7个token/字,非中文字符约0.25个token/字(4字/token)
let baseEstimate = Math.ceil(chineseChars * 0.7) + Math.ceil((nonChineseChars - codeChars) / 4);
// 代码部分单独计算
if (codeChars > 0) {
baseEstimate += Math.ceil(codeChars / 5.5);
}
// 调整空白字符的影响
if (whitespaceChars > totalChars * 0.2) {
// 大量空白字符的文本,token效率更高
baseEstimate = Math.ceil(baseEstimate * 0.95);
}
estimatedTokens = baseEstimate;
break;
}
// 添加消息格式开销
if (isChatMessage) {
// 基础消息格式开销
let messageOverhead = 4;
// 根据文本长度调整,长消息格式开销相对较小
if (totalChars > 1000) {
messageOverhead = 3;
} else if (totalChars < 20) {
// 极短消息可能有更高的格式开销比例
messageOverhead = 5;
}
estimatedTokens += messageOverhead;
}
return Math.max(1, Math.round(estimatedTokens)); // 确保至少返回1个token
}
// 添加令牌到KV
async function addTokenToKV(env, tokenInput) {
const lockId = acquireDataLock("add-token");
if (!lockId) {
return { success: false, message: "系统正忙,请稍后再试" };
}
try {
// 加载现有令牌
await loadTokensFromKV(env);
// 处理输入,支持多行和逗号分隔
const tokenLines = tokenInput.split(/[\n,]+/).map((line) => line.trim());
const validTokens = tokenLines.filter((token) => token.length > 0);
if (validTokens.length === 0) {
releaseDataLock(false, lockId);
return { success: false, message: "未提供有效的令牌" };
}
let addedCount = 0;
let duplicateCount = 0;
for (const token of validTokens) {
// 检查令牌是否已存在
const tokenExists = tokens.some((t) => t.key === token);
if (!tokenExists) {
// 添加新令牌
tokens.push({
key: token,
enabled: true,
addedAt: getBJTimeString(),
lastUsed: null,
usageCount: 0,
errorCount: 0,
consecutiveErrors: 0,
balance: null,
lastChecked: null,
});
addedCount++;
} else {
duplicateCount++;
}
}
// 保存更新后的令牌列表
await saveTokensToKV(env);
releaseDataLock(false, lockId);
let message = `成功添加了${addedCount}个令牌`;
if (duplicateCount > 0) {
message += `,${duplicateCount}个令牌已存在`;
}
return {
success: true,
message: message,
addedCount,
duplicateCount,
};
} catch (error) {
Logger.error("添加令牌失败:", error);
releaseDataLock(false, lockId);
return { success: false, message: "添加令牌失败: " + error.message };
}
}
// 从KV删除令牌
async function removeTokenFromKV(env, tokenToRemove, skipLock = false) {
let lockId = null;
if (!skipLock) {
lockId = acquireDataLock("remove-token");
if (!lockId) {
return { success: false, message: "系统正忙,请稍后再试" };
}
}
try {
// 加载现有令牌
if (!skipLock) {
await loadTokensFromKV(env);
}
// 处理输入,支持多行和逗号分隔
const tokenLines = tokenToRemove.split(/[\n,]+/).map((line) => line.trim());
const validTokens = tokenLines.filter((token) => token.length > 0);
if (validTokens.length === 0) {
if (!skipLock) releaseDataLock(false, lockId);
return { success: false, message: "未提供有效的令牌" };
}
const initialCount = tokens.length;
tokens = tokens.filter((token) => !validTokens.includes(token.key));
const removedCount = initialCount - tokens.length;
// 保存更新后的令牌列表
await saveTokensToKV(env);
if (!skipLock) releaseDataLock(false, lockId);
return {
success: true,
message: `成功删除了${removedCount}个令牌`,
removedCount,
};
} catch (error) {
Logger.error("删除令牌失败:", error);
if (!skipLock) releaseDataLock(false, lockId);
return { success: false, message: "删除令牌失败: " + error.message };
}
}
// 切换令牌状态
async function toggleTokenStatus(env, tokenKey) {
const lockId = acquireDataLock("toggle-token");
if (!lockId) {
return { success: false, message: "系统正忙,请稍后再试" };
}
try {
// 加载现有令牌
await loadTokensFromKV(env);
// 查找令牌
const tokenIndex = tokens.findIndex((t) => t.key === tokenKey);
if (tokenIndex === -1) {
releaseDataLock(false, lockId);
return { success: false, message: "未找到指定的令牌" };
}
// 切换状态
tokens[tokenIndex].enabled = !tokens[tokenIndex].enabled;
const newStatus = tokens[tokenIndex].enabled ? "启用" : "禁用";
// 如果是启用令牌,重置连续错误计数
if (tokens[tokenIndex].enabled) {
tokens[tokenIndex].consecutiveErrors = 0;
Logger.info(`手动启用令牌 ${obfuscateKey(tokenKey)},已重置连续错误计数`);
}
// 保存更新后的令牌列表
await saveTokensToKV(env);
releaseDataLock(false, lockId);
return {
success: true,
message: `已将令牌状态切换为${newStatus}${tokens[tokenIndex].enabled ? "并重置错误计数" : ""}`,
enabled: tokens[tokenIndex].enabled,
};
} catch (error) {
Logger.error("切换令牌状态失败:", error);
releaseDataLock(false, lockId);
return { success: false, message: "切换令牌状态失败: " + error.message };
}
}
// ==================== 令牌选择策略 ====================
// 初始化令牌统计
function initializeTokenStats() {
return {
totalRequests: 0,
successfulRequests: 0,
failedRequests: 0,
totalTokens: 0,
lastUsed: null,
};
}
// 获取下一个令牌(简单轮询)
function getNextToken() {
// 过滤出启用状态的令牌
const enabledTokens = tokens.filter((token) => token.enabled);
if (enabledTokens.length === 0) {
return null;
}
// 找出最近最少使用的令牌
enabledTokens.sort((a, b) => {
if (!a.lastUsed) return -1;
if (!b.lastUsed) return 1;
return new Date(a.lastUsed) - new Date(b.lastUsed);
});
return enabledTokens[0];
}
// 智能选择令牌(考虑成功率和使用量)
function getSmartToken() {
// 过滤出启用状态的令牌
const enabledTokens = tokens.filter((token) => token.enabled);
if (enabledTokens.length === 0) {
return null;
}
// 计算每个令牌的分数
// 分数 = (成功请求率 * 0.7) + (1 - 相对使用量 * 0.3)
enabledTokens.forEach((token) => {
const totalReq = token.usageCount || 0;
const errorRate = totalReq > 0 ? (token.errorCount || 0) / totalReq : 0;
const successRate = 1 - errorRate;
// 找出使用量最大的令牌作为基准
const maxUsage = Math.max(...enabledTokens.map((t) => t.usageCount || 0));
const relativeUsage = maxUsage > 0 ? (token.usageCount || 0) / maxUsage : 0;
// 计算总分
token.score = successRate * 0.7 + (1 - relativeUsage) * 0.3;
// 连续错误降低分数
if (token.consecutiveErrors > 0) {
token.score = token.score * Math.pow(0.8, token.consecutiveErrors);
}
});
// 按分数降序排序
enabledTokens.sort((a, b) => b.score - a.score);
return enabledTokens[0];
}
// 根据请求路径选择令牌
function selectTokenForRequest(requestPath) {
// 过滤出启用状态的令牌
const enabledTokens = tokens.filter((token) => token.enabled);
// 如果没有启用的令牌,直接返回null
if (enabledTokens.length === 0) {
Logger.warn("没有可用的启用状态令牌");
return null;
}
// 根据不同的请求路径选择不同的令牌选择策略
let selectedToken = null;
if (requestPath.includes(API_ENDPOINTS.images)) {
// 对于图像请求使用简单轮询
selectedToken = getNextToken();
} else {
// 对于其他请求使用智能选择
selectedToken = getSmartToken();
}
// 如果选择失败,尝试使用另一种策略
if (!selectedToken && requestPath.includes(API_ENDPOINTS.images)) {
Logger.debug("图像请求轮询选择失败,尝试智能选择");
selectedToken = getSmartToken();
} else if (!selectedToken) {
Logger.debug("智能选择失败,尝试轮询选择");
selectedToken = getNextToken();
}
if (selectedToken) {
Logger.debug(`已选择令牌: ${obfuscateKey(selectedToken.key)}`);
} else {
Logger.warn("无法选择可用令牌");
}
return selectedToken;
}
// ==================== 统计数据管理 ====================
// 清理旧的请求数据
function cleanupOldRequestData() {
const now = Date.now();
const ONE_MINUTE = 60 * 1000;
const ONE_DAY = 24 * 60 * 60 * 1000;
try {
// 清理分钟级数据
let minuteCleanupCount = 0;
// 确保数组长度一致
if (requestTimestamps.length !== tokenCounts.length) {
const minLength = Math.min(requestTimestamps.length, tokenCounts.length);
requestTimestamps.length = minLength;
tokenCounts.length = minLength;
Logger.warn(`分钟级统计数据长度不一致,已调整为${minLength}`);
}
// 清理过期数据
for (let i = requestTimestamps.length - 1; i >= 0; i--) {
if (now - requestTimestamps[i] > ONE_MINUTE) {
requestTimestamps.splice(0, i + 1);
tokenCounts.splice(0, i + 1);
minuteCleanupCount = i + 1;
break;
}
}
if (minuteCleanupCount > 0) {
Logger.debug(`清理了${minuteCleanupCount}条分钟级统计数据`);
}
// 清理天级数据
let dayCleanupCount = 0;
// 确保数组长度一致
if (requestTimestampsDay.length !== tokenCountsDay.length) {
const minDayLength = Math.min(requestTimestampsDay.length, tokenCountsDay.length);
requestTimestampsDay.length = minDayLength;
tokenCountsDay.length = minDayLength;
Logger.warn(`天级统计数据长度不一致,已调整为${minDayLength}`);
}
// 获取当前日期(按北京时间)
const bjTimeString = getBJTimeString(); // 使用现有函数获取北京时间
const beijingDateStr = bjTimeString.substring(0, 10); // 截取YYYY-MM-DD部分
// 重置逻辑:如果有上一次的日期记录且与当前日期不同,则重置天级数据
if (lastProcessedDate && lastProcessedDate !== beijingDateStr) {
Logger.info(`日期已变更:${lastProcessedDate} -> ${beijingDateStr},重置每日统计数据`);
requestTimestampsDay = [];
tokenCountsDay = [];
dayCleanupCount = "全部重置";
}
// 记录当前处理的日期
lastProcessedDate = beijingDateStr;
// 原有的清理逻辑(清理超过24小时的数据)
for (let i = requestTimestampsDay.length - 1; i >= 0; i--) {
if (now - requestTimestampsDay[i] > ONE_DAY) {
requestTimestampsDay.splice(0, i + 1);
tokenCountsDay.splice(0, i + 1);
if (dayCleanupCount !== "全部重置") {
dayCleanupCount = i + 1;
}
break;
}
}
if (dayCleanupCount) {
Logger.debug(`清理了${dayCleanupCount}条天级统计数据`);
}
} catch (error) {
Logger.error("清理统计数据时出错:", error);
}
}
// 从KV加载统计数据
async function loadStatsFromKV(env, forceRefresh = false) {
try {
// 检查缓存是否有效,除非强制刷新
const now = Date.now();
if (!forceRefresh && cache.stats.data && now - cache.stats.timestamp < CACHE_TTL.STATS) {
const cachedStats = cache.stats.data;
requestTimestamps = cachedStats.requestTimestamps || [];
tokenCounts = cachedStats.tokenCounts || [];
requestTimestampsDay = cachedStats.requestTimestampsDay || [];
tokenCountsDay = cachedStats.tokenCountsDay || [];
lastProcessedDate = cachedStats.lastProcessedDate || null;
// 清理缓存中的旧数据
cleanupOldRequestData();
Logger.debug("使用缓存中的统计数据");
return true;
}
// 根据存储类型选择加载方式
if (STORAGE_TYPE === "d1" && env.DB) {
// 从D1加载数据
try {
const result = await env.DB.prepare("SELECT * FROM stats ORDER BY id DESC LIMIT 1").first();
if (result) {
requestTimestamps = JSON.parse(result.request_timestamps) || [];
tokenCounts = JSON.parse(result.token_counts) || [];
requestTimestampsDay = JSON.parse(result.request_timestamps_day) || [];
tokenCountsDay = JSON.parse(result.token_counts_day) || [];
lastProcessedDate = result.last_processed_date || null;
// 更新缓存
cache.stats.data = {
requestTimestamps: [...requestTimestamps],
tokenCounts: [...tokenCounts],
requestTimestampsDay: [...requestTimestampsDay],
tokenCountsDay: [...tokenCountsDay],
lastProcessedDate: lastProcessedDate,
};
cache.stats.timestamp = now;
// 清理旧数据
cleanupOldRequestData();
Logger.info(`已从D1加载请求统计数据${forceRefresh ? " (强制刷新)" : ""}`);
return true;
} else {
// 初始化空数据
requestTimestamps = [];
tokenCounts = [];
requestTimestampsDay = [];
tokenCountsDay = [];
lastProcessedDate = getBJTimeString().substring(0, 10);
// 更新缓存
cache.stats.data = {
requestTimestamps: [],
tokenCounts: [],
requestTimestampsDay: [],
tokenCountsDay: [],
lastProcessedDate: lastProcessedDate,
};
cache.stats.timestamp = now;
Logger.info("D1中没有请求统计数据,初始化为空");
return true;
}
} catch (dbError) {
Logger.error("从D1加载统计数据失败:", dbError);
// 如果D1失败,尝试从KV加载作为备份
Logger.info("尝试从KV加载统计数据作为备份...");
}
}
// 从KV加载数据(作为备份或默认方式)
const data = await env.API_TOKENS.get(KV_KEYS.STATS, { type: "json" });
if (data) {
requestTimestamps = data.requestTimestamps || [];
tokenCounts = data.tokenCounts || [];
requestTimestampsDay = data.requestTimestampsDay || [];
tokenCountsDay = data.tokenCountsDay || [];
lastProcessedDate = data.lastProcessedDate || null;
// 更新缓存
cache.stats.data = {
requestTimestamps: [...requestTimestamps],
tokenCounts: [...tokenCounts],
requestTimestampsDay: [...requestTimestampsDay],
tokenCountsDay: [...tokenCountsDay],
lastProcessedDate: lastProcessedDate,
};
cache.stats.timestamp = now;
// 清理旧数据
cleanupOldRequestData();
Logger.info(`已从KV加载请求统计数据${forceRefresh ? " (强制刷新)" : ""}`);
} else {
requestTimestamps = [];
tokenCounts = [];
requestTimestampsDay = [];
tokenCountsDay = [];
// 初始化最后处理日期
lastProcessedDate = getBJTimeString().substring(0, 10); // 使用现有函数,截取YYYY-MM-DD部分
// 更新缓存为空数据
cache.stats.data = {
requestTimestamps: [],
tokenCounts: [],
requestTimestampsDay: [],
tokenCountsDay: [],
lastProcessedDate: lastProcessedDate,
};
cache.stats.timestamp = now;
Logger.info("KV中没有请求统计数据,初始化为空");
}
return true;
} catch (error) {
Logger.error("加载统计数据失败:", error);
requestTimestamps = [];
tokenCounts = [];
requestTimestampsDay = [];
tokenCountsDay = [];
return false;
}
}
// 保存统计数据到KV
async function saveStatsToKV(env, forceSave = false) {
if (!env) return false;
// 只在强制保存或每隔10分钟保存一次,以减少写入
const now = Date.now();
const SAVE_INTERVAL = 10 * 60 * 1000; // 10分钟
if (!forceSave && now - lastStatsSave < SAVE_INTERVAL) {
return false;
}
try {
// 获取数据锁,防止并发写入
const lockId = await acquireDataLock("save-stats");
if (!lockId) {
Logger.warn("保存统计数据时无法获取锁,稍后重试");
return false;
}
const statsData = {
requestTimestamps,
tokenCounts,
requestTimestampsDay,
tokenCountsDay,
lastProcessedDate,
lastUpdated: new Date().toISOString(),
};
// 根据存储类型选择保存方式
if (STORAGE_TYPE === "d1" && env.DB) {
try {
// 保存到D1数据库
await env.DB.prepare("INSERT INTO stats (request_timestamps, token_counts, request_timestamps_day, token_counts_day, last_processed_date) VALUES (?, ?, ?, ?, ?)")
.bind(JSON.stringify(requestTimestamps), JSON.stringify(tokenCounts), JSON.stringify(requestTimestampsDay), JSON.stringify(tokenCountsDay), lastProcessedDate)
.run();
// 删除旧记录,只保留最新的10条记录
await env.DB.prepare("DELETE FROM stats WHERE id NOT IN (SELECT id FROM stats ORDER BY id DESC LIMIT 10)").run();
// 更新缓存
cache.stats.data = {
requestTimestamps: [...requestTimestamps],
tokenCounts: [...tokenCounts],
requestTimestampsDay: [...requestTimestampsDay],
tokenCountsDay: [...tokenCountsDay],
lastProcessedDate: lastProcessedDate,
};
cache.stats.timestamp = now;
lastStatsSave = now;
Logger.info("已保存请求统计数据到D1并更新缓存,并清理了旧记录");
releaseDataLock(false, lockId);
return true;
} catch (dbError) {
Logger.error("保存请求统计数据到D1失败:", dbError);
// 如果D1失败,尝试保存到KV作为备份
Logger.info("尝试保存统计数据到KV作为备份...");
}
}
// 保存到KV(作为备份或默认方式)
await env.API_TOKENS.put(KV_KEYS.STATS, JSON.stringify(statsData));
// 更新缓存
cache.stats.data = {
requestTimestamps: [...requestTimestamps],
tokenCounts: [...tokenCounts],
requestTimestampsDay: [...requestTimestampsDay],
tokenCountsDay: [...tokenCountsDay],
lastProcessedDate: lastProcessedDate,
};
cache.stats.timestamp = now;
lastStatsSave = now;
Logger.info("已保存请求统计数据到KV并更新缓存");
releaseDataLock(false, lockId);
return true;
} catch (error) {
releaseDataLock(true); // 强制释放锁,防止死锁
Logger.error("保存请求统计数据失败:", error);
return false;
}
}
// 更新令牌统计
async function updateTokenStats(token, success, tokenCount = 0, env = null) {
if (!token) return;
// 确保tokenCount是有效数字
tokenCount = typeof tokenCount === "number" && !isNaN(tokenCount) ? tokenCount : 0;
// 更新令牌使用记录
const tokenIndex = tokens.findIndex((t) => t.key === token.key);
if (tokenIndex !== -1) {
tokens[tokenIndex].lastUsed = getBJTimeString();
tokens[tokenIndex].usageCount = (tokens[tokenIndex].usageCount || 0) + 1;
// 更新令牌的token使用量统计
tokens[tokenIndex].totalTokens = (tokens[tokenIndex].totalTokens || 0) + tokenCount;
if (success) {
tokens[tokenIndex].consecutiveErrors = 0;
tokens[tokenIndex].successCount = (tokens[tokenIndex].successCount || 0) + 1;
} else {
tokens[tokenIndex].errorCount = (tokens[tokenIndex].errorCount || 0) + 1;
tokens[tokenIndex].consecutiveErrors = (tokens[tokenIndex].consecutiveErrors || 0) + 1;
tokens[tokenIndex].lastErrorTime = new Date().toISOString(); // 记录最后错误时间
// 注意:令牌禁用逻辑已移至handleApiRequest函数中,确保在达到最大重试次数后才禁用令牌
}
}
// 更新全局请求统计
const now = Date.now();
pendingUpdates++;
// 添加分钟级别的统计
requestTimestamps.push(now);
tokenCounts.push(tokenCount);
// 添加天级别的统计
requestTimestampsDay.push(now);
tokenCountsDay.push(tokenCount);
// 清理旧数据
cleanupOldRequestData();
// 判断是否需要保存到KV
const shouldSave = env ? true : false; // 只要有环境变量,就立即保存到KV
// 如果需要保存到KV
if (shouldSave) {
await saveTokensToKV(env);
await saveStatsToKV(env, true);
}
}
// 获取请求统计信息
function getRequestStats() {
// 先清理旧数据
cleanupOldRequestData();
const now = Date.now();
// 分钟级统计计算,
const rpm = requestTimestamps.length; // 分钟请求数
let tpm = 0;
for (const count of tokenCounts) {
tpm += count || 0;
}
// 天级统计计算,
const rpd = requestTimestampsDay.length; // 天请求数
let tpd = 0;
for (const count of tokenCountsDay) {
tpd += count || 0;
}
// 计算活跃令牌数和禁用令牌数
const activeTokens = tokens.filter((token) => token.enabled).length;
const disabledTokens = tokens.length - activeTokens;
// 添加更多有用的统计信息
const tokenDetails = tokens.map((token) => ({
key: obfuscateKey(token.key),
enabled: token.enabled,
usageCount: token.usageCount || 0,
errorCount: token.errorCount || 0,
successCount: token.successCount || 0,
totalTokens: token.totalTokens || 0,
consecutiveErrors: token.consecutiveErrors || 0,
lastUsed: token.lastUsed || null,
}));