yuoop commited on
Commit
3e078ca
·
verified ·
1 Parent(s): 087cef2

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +975 -979
index.js CHANGED
@@ -9,944 +9,940 @@ import Logger from './logger.js';
9
 
10
  // 配置常量
11
  const CONFIG = {
12
- MODELS: {
13
- 'grok-2': 'grok-latest',
14
- 'grok-2-imageGen': 'grok-latest',
15
- 'grok-2-search': 'grok-latest',
16
- "grok-3": "grok-3",
17
- "grok-3-search": "grok-3",
18
- "grok-3-imageGen": "grok-3",
19
- "grok-3-deepsearch": "grok-3",
20
- "grok-3-reasoning": "grok-3"
21
- },
22
- API: {
23
- IS_TEMP_CONVERSATION: false, // 假设默认不使用临时会话
24
- GROK2_CONCURRENCY_LEVEL: 4,
25
- BASE_URL: "https://grok.com",
26
- PICGO_KEY: process.env.PICGO_KEY || null, //想要流式生图的话需要填入这个PICGO图床的key
27
- TUMY_KEY: process.env.TUMY_KEY || null //想要流式生图的话需要填入这个TUMY图床的key 两个图床二选一,默认使用PICGO
28
- },
29
- SERVER: {
30
- PORT: process.env.PORT || 3000,
31
- BODY_LIMIT: '5mb'
32
- },
33
- RETRY: {
34
- MAX_ATTEMPTS: 2//重试次数
35
- },
36
- SHOW_THINKING: true,
37
- IS_THINKING: false,
38
- IS_IMG_GEN: false,
39
- IS_IMG_GEN2: false,
40
- ISSHOW_SEARCH_RESULTS: true,//是否显示搜索结果
41
- CHROME_PATH: process.env.CHROME_PATH || null
42
  };
43
  puppeteer.use(StealthPlugin())
44
 
45
  // 请求头配置
46
  const DEFAULT_HEADERS = {
47
- 'accept': '*/*',
48
- 'accept-language': 'zh-CN,zh;q=0.9',
49
- 'accept-encoding': 'gzip, deflate, br, zstd',
50
- 'content-type': 'text/plain;charset=UTF-8',
51
- 'Connection': 'keep-alive',
52
- 'origin': 'https://grok.com',
53
- 'priority': 'u=1, i',
54
- 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
55
- 'sec-ch-ua-mobile': '?0',
56
- 'sec-ch-ua-platform': '"Windows"',
57
- 'sec-fetch-dest': 'empty',
58
- 'sec-fetch-mode': 'cors',
59
- 'sec-fetch-site': 'same-origin',
60
- 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
61
- 'baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
62
  };
63
 
64
 
65
  async function initialization() {
66
- if (CONFIG.CHROME_PATH == null) {
67
- try {
68
- CONFIG.CHROME_PATH = puppeteer.executablePath();
69
- } catch (error) {
70
- CONFIG.CHROME_PATH = "/usr/bin/chromium";
71
- }
72
- }
73
- Logger.info(`CHROME_PATH: ${CONFIG.CHROME_PATH}`, 'Server');
74
- Logger.info("初始化完成", 'Server');
75
  }
76
 
77
  class AuthTokenManager {
78
- constructor() {
79
- this.tokenModelMap = {};
80
- this.expiredTokens = new Set();
81
- this.tokenStatusMap = {};
82
-
83
- // 定义模型请求频率限制和过期时间
84
- this.modelConfig = {
85
- "grok-2": {
86
- RequestFrequency: 30,
87
- ExpirationTime: 1 * 60 * 60 * 1000 // 1小时
88
- },
89
- "grok-3": {
90
- RequestFrequency: 20,
91
- ExpirationTime: 2 * 60 * 60 * 1000 // 2小时
92
- },
93
- "grok-3-deepsearch": {
94
- RequestFrequency: 10,
95
- ExpirationTime: 24 * 60 * 60 * 1000 // 24小时
96
- },
97
- "grok-3-reasoning": {
98
- RequestFrequency: 10,
99
- ExpirationTime: 24 * 60 * 60 * 1000 // 24小时
100
- }
101
- };
102
- this.tokenResetSwitch = false;
103
- this.tokenResetTimer = null;
104
- }
105
- async fetchGrokStats(token, modelName) {
106
- let requestKind = 'DEFAULT';
107
- if (modelName == 'grok-2' || modelName == 'grok-3') {
108
- requestKind = 'DEFAULT';
109
- } else if (modelName == 'grok-3-deepsearch') {
110
- requestKind = 'DEEPSEARCH';
111
- } else if (modelName == 'grok-3-reasoning') {
112
- requestKind = 'REASONING';
113
- }
114
- const response = await fetch('https://grok.com/rest/rate-limits', {
115
- method: 'POST',
116
- headers: {
117
- 'content-type': 'application/json',
118
- 'Cookie': token,
119
- },
120
- body: JSON.stringify({
121
- "requestKind": requestKind,
122
- "modelName": modelName == 'grok-2' ? 'grok-latest' : "grok-3"
123
- })
124
- });
125
-
126
- if (response.status != 200) {
127
- return 0;
128
- }
129
- const data = await response.json();
130
- return data.remainingQueries;
131
- }
132
- async addToken(token) {
133
- const sso = token.split("sso=")[1].split(";")[0];
134
-
135
- for (const model of Object.keys(this.modelConfig)) {
136
- if (!this.tokenModelMap[model]) {
137
- this.tokenModelMap[model] = [];
138
- }
139
- if (!this.tokenStatusMap[sso]) {
140
- this.tokenStatusMap[sso] = {};
141
- }
142
- const existingTokenEntry = this.tokenModelMap[model].find(entry => entry.token === token);
143
-
144
- if (!existingTokenEntry) {
145
- try {
146
- const remainingQueries = await this.fetchGrokStats(token, model);
147
-
148
- const modelRequestFrequency = this.modelConfig[model].RequestFrequency;
149
- const usedRequestCount = modelRequestFrequency - remainingQueries;
150
-
151
- if (usedRequestCount === modelRequestFrequency) {
152
- this.expiredTokens.add({
153
- token: token,
154
- model: model,
155
- expiredTime: Date.now()
156
- });
157
-
158
- if (!this.tokenStatusMap[sso][model]) {
159
- this.tokenStatusMap[sso][model] = {
160
- isValid: false,
161
- invalidatedTime: Date.now(),
162
- totalRequestCount: Math.max(0, usedRequestCount)
163
- };
164
- }
165
-
166
- if (!this.tokenResetSwitch) {
167
- this.startTokenResetProcess();
168
- this.tokenResetSwitch = true;
169
- }
170
- } else {
171
- this.tokenModelMap[model].push({
172
- token: token,
173
- RequestCount: Math.max(0, usedRequestCount),
174
- AddedTime: Date.now(),
175
- StartCallTime: null
176
- });
177
-
178
- if (!this.tokenStatusMap[sso][model]) {
179
- this.tokenStatusMap[sso][model] = {
180
- isValid: true,
181
- invalidatedTime: null,
182
- totalRequestCount: Math.max(0, usedRequestCount)
183
- };
184
- }
185
- }
186
- } catch (error) {
187
- this.tokenModelMap[model].push({
188
- token: token,
189
- RequestCount: 0,
190
- AddedTime: Date.now(),
191
- StartCallTime: null
192
- });
193
-
194
- if (!this.tokenStatusMap[sso][model]) {
195
- this.tokenStatusMap[sso][model] = {
196
- isValid: true,
197
- invalidatedTime: null,
198
- totalRequestCount: 0
199
- };
200
- }
201
-
202
- Logger.error(`获取模型 ${model} 的统计信息失败: ${error}`, 'TokenManager');
203
- }
204
- await Utils.delay(200);
205
- }
206
- }
207
- }
208
-
209
- setToken(token) {
210
- const models = Object.keys(this.modelConfig);
211
- this.tokenModelMap = models.reduce((map, model) => {
212
- map[model] = [{
213
- token,
214
- RequestCount: 0,
215
- AddedTime: Date.now(),
216
- StartCallTime: null
217
- }];
218
- return map;
219
- }, {});
220
- const sso = token.split("sso=")[1].split(";")[0];
221
- this.tokenStatusMap[sso] = models.reduce((statusMap, model) => {
222
- statusMap[model] = {
223
- isValid: true,
224
- invalidatedTime: null,
225
- totalRequestCount: 0
226
- };
227
- return statusMap;
228
- }, {});
229
- }
230
-
231
- async deleteToken(token) {
232
- try {
233
- const sso = token.split("sso=")[1].split(";")[0];
234
- await Promise.all([
235
- new Promise((resolve) => {
236
- this.tokenModelMap = Object.fromEntries(
237
- Object.entries(this.tokenModelMap).map(([model, entries]) => [
238
- model,
239
- entries.filter(entry => entry.token !== token)
240
- ])
241
- );
242
- resolve();
243
- }),
244
-
245
- new Promise((resolve) => {
246
- delete this.tokenStatusMap[sso];
247
- resolve();
248
- }),
249
- ]);
250
- Logger.info(`令牌已成功移除: ${token}`, 'TokenManager');
251
- return true;
252
- } catch (error) {
253
- Logger.error('令牌删除失败:', error);
254
- return false;
255
- }
256
- }
257
- getNextTokenForModel(modelId) {
258
- const normalizedModel = this.normalizeModelName(modelId);
259
-
260
- if (!this.tokenModelMap[normalizedModel] || this.tokenModelMap[normalizedModel].length === 0) {
261
- return null;
262
- }
263
- const tokenEntry = this.tokenModelMap[normalizedModel][0];
264
-
265
- if (tokenEntry) {
266
- if (tokenEntry.StartCallTime === null || tokenEntry.StartCallTime === undefined) {
267
- tokenEntry.StartCallTime = Date.now();
268
- }
269
- if (!this.tokenResetSwitch) {
270
- this.startTokenResetProcess();
271
- this.tokenResetSwitch = true;
272
- }
273
- tokenEntry.RequestCount++;
274
-
275
- if (tokenEntry.RequestCount > this.modelConfig[normalizedModel].RequestFrequency) {
276
- this.removeTokenFromModel(normalizedModel, tokenEntry.token);
277
- const nextTokenEntry = this.tokenModelMap[normalizedModel][0];
278
- return nextTokenEntry ? nextTokenEntry.token : null;
279
- }
280
- const sso = tokenEntry.token.split("sso=")[1].split(";")[0];
281
- if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][normalizedModel]) {
282
- if (tokenEntry.RequestCount === this.modelConfig[normalizedModel].RequestFrequency) {
283
- this.tokenStatusMap[sso][normalizedModel].isValid = false;
284
- this.tokenStatusMap[sso][normalizedModel].invalidatedTime = Date.now();
285
- }
286
- this.tokenStatusMap[sso][normalizedModel].totalRequestCount++;
287
- }
288
- return tokenEntry.token;
289
- }
290
-
291
- return null;
292
- }
293
-
294
- removeTokenFromModel(modelId, token) {
295
- const normalizedModel = this.normalizeModelName(modelId);
296
-
297
- if (!this.tokenModelMap[normalizedModel]) {
298
- Logger.error(`模型 ${normalizedModel} 不存在`, 'TokenManager');
299
- return false;
300
- }
301
-
302
- const modelTokens = this.tokenModelMap[normalizedModel];
303
- const tokenIndex = modelTokens.findIndex(entry => entry.token === token);
304
-
305
- if (tokenIndex !== -1) {
306
- const removedTokenEntry = modelTokens.splice(tokenIndex, 1)[0];
307
- this.expiredTokens.add({
308
- token: removedTokenEntry.token,
309
- model: normalizedModel,
310
- expiredTime: Date.now()
311
- });
312
-
313
- if (!this.tokenResetSwitch) {
314
- this.startTokenResetProcess();
315
- this.tokenResetSwitch = true;
316
- }
317
- Logger.info(`模型${modelId}的令牌已失效,已成功移除令牌: ${token}`, 'TokenManager');
318
- return true;
319
- }
320
-
321
- Logger.error(`在模型 ${normalizedModel} 中未找到 token: ${token}`, 'TokenManager');
322
- return false;
323
- }
324
-
325
- getExpiredTokens() {
326
- return Array.from(this.expiredTokens);
327
- }
328
-
329
- normalizeModelName(model) {
330
- if (model.startsWith('grok-') && !model.includes('deepsearch') && !model.includes('reasoning')) {
331
- return model.split('-').slice(0, 2).join('-');
332
- }
333
- return model;
334
- }
335
-
336
- getTokenCountForModel(modelId) {
337
- const normalizedModel = this.normalizeModelName(modelId);
338
- return this.tokenModelMap[normalizedModel]?.length || 0;
339
- }
340
-
341
- getRemainingTokenRequestCapacity() {
342
- const remainingCapacityMap = {};
343
-
344
- Object.keys(this.modelConfig).forEach(model => {
345
- const modelTokens = this.tokenModelMap[model] || [];
346
-
347
- const modelRequestFrequency = this.modelConfig[model].RequestFrequency;
348
-
349
- const totalUsedRequests = modelTokens.reduce((sum, tokenEntry) => {
350
- return sum + (tokenEntry.RequestCount || 0);
351
- }, 0);
352
-
353
- // 计算剩余可用请求数量
354
- const remainingCapacity = (modelTokens.length * modelRequestFrequency) - totalUsedRequests;
355
- remainingCapacityMap[model] = Math.max(0, remainingCapacity);
356
- });
357
-
358
- return remainingCapacityMap;
359
- }
360
-
361
- getTokenArrayForModel(modelId) {
362
- const normalizedModel = this.normalizeModelName(modelId);
363
- return this.tokenModelMap[normalizedModel] || [];
364
- }
365
-
366
- startTokenResetProcess() {
367
- if (this.tokenResetTimer) {
368
- clearInterval(this.tokenResetTimer);
369
- }
370
-
371
- this.tokenResetTimer = setInterval(() => {
372
- const now = Date.now();
373
-
374
- this.expiredTokens.forEach(expiredTokenInfo => {
375
- const { token, model, expiredTime } = expiredTokenInfo;
376
- const expirationTime = this.modelConfig[model].ExpirationTime;
377
- if (now - expiredTime >= expirationTime) {
378
- if (!this.tokenModelMap[model].some(entry => entry.token === token)) {
379
- this.tokenModelMap[model].push({
380
- token: token,
381
- RequestCount: 0,
382
- AddedTime: now,
383
- StartCallTime: null
384
- });
385
- }
386
- const sso = token.split("sso=")[1].split(";")[0];
387
-
388
- if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][model]) {
389
- this.tokenStatusMap[sso][model].isValid = true;
390
- this.tokenStatusMap[sso][model].invalidatedTime = null;
391
- this.tokenStatusMap[sso][model].totalRequestCount = 0;
392
- }
393
-
394
- this.expiredTokens.delete(expiredTokenInfo);
395
- }
396
- });
397
-
398
- Object.keys(this.modelConfig).forEach(model => {
399
- if (!this.tokenModelMap[model]) return;
400
-
401
- const processedTokens = this.tokenModelMap[model].map(tokenEntry => {
402
- if (!tokenEntry.StartCallTime) return tokenEntry;
403
-
404
- const expirationTime = this.modelConfig[model].ExpirationTime;
405
- if (now - tokenEntry.StartCallTime >= expirationTime) {
406
- const sso = tokenEntry.token.split("sso=")[1].split(";")[0];
407
- if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][model]) {
408
- this.tokenStatusMap[sso][model].isValid = true;
409
- this.tokenStatusMap[sso][model].invalidatedTime = null;
410
- this.tokenStatusMap[sso][model].totalRequestCount = 0;
411
- }
412
-
413
- return {
414
- ...tokenEntry,
415
- RequestCount: 0,
416
- StartCallTime: null
417
- };
418
- }
419
-
420
- return tokenEntry;
421
- });
422
-
423
- this.tokenModelMap[model] = processedTokens;
424
- });
425
- }, 1 * 60 * 60 * 1000);
426
- }
427
-
428
- getAllTokens() {
429
- const allTokens = new Set();
430
- Object.values(this.tokenModelMap).forEach(modelTokens => {
431
- modelTokens.forEach(entry => allTokens.add(entry.token));
432
- });
433
- return Array.from(allTokens);
434
- }
435
-
436
- getTokenStatusMap() {
437
- return this.tokenStatusMap;
438
- }
439
  }
440
 
441
 
442
  class Utils {
443
- static delay(time) {
444
- return new Promise(function (resolve) {
445
- setTimeout(resolve, time)
446
- });
447
- }
448
- static async organizeSearchResults(searchResults) {
449
- // 确保传入的是有效的搜索结果对象
450
- if (!searchResults || !searchResults.results) {
451
- return '';
452
- }
453
-
454
- const results = searchResults.results;
455
- const formattedResults = results.map((result, index) => {
456
- // 处理可能为空的字段
457
- const title = result.title || '未知标题';
458
- const url = result.url || '#';
459
- const preview = result.preview || '无预览内容';
460
-
461
- return `\r\n<details><summary>资料[${index}]: ${title}</summary>\r\n${preview}\r\n\n[Link](${url})\r\n</details>`;
462
- });
463
- return formattedResults.join('\n\n');
464
- }
465
- static async createAuthHeaders(model, authorization) {
466
- return `sso=${authorization};ssp_rw=${authorization}`; // 直接使用authorization
467
- }
468
  }
469
 
470
  class GrokApiClient {
471
- constructor(modelId) {
472
- if (!CONFIG.MODELS[modelId]) {
473
- throw new Error(`不支持的模型: ${modelId}`);
474
- }
475
- this.modelId = CONFIG.MODELS[modelId];
476
- }
477
-
478
- processMessageContent(content) {
479
- if (typeof content === 'string') return content;
480
- return null;
481
- }
482
- // 获取图片类型
483
- getImageType(base64String) {
484
- let mimeType = 'image/jpeg';
485
- if (base64String.includes('data:image')) {
486
- const matches = base64String.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+);base64,/);
487
- if (matches) {
488
- mimeType = matches[1];
489
- }
490
- }
491
- const extension = mimeType.split('/')[1];
492
- const fileName = `image.${extension}`;
493
-
494
- return {
495
- mimeType: mimeType,
496
- fileName: fileName
497
- };
498
- }
499
-
500
- async uploadBase64Image(base64Data, url) {
501
- try {
502
- // 处理 base64 数据
503
- let imageBuffer;
504
- if (base64Data.includes('data:image')) {
505
- imageBuffer = base64Data.split(',')[1];
506
- } else {
507
- imageBuffer = base64Data
508
- }
509
- const { mimeType, fileName } = this.getImageType(base64Data);
510
- let uploadData = {
511
- rpc: "uploadFile",
512
- req: {
513
- fileName: fileName,
514
- fileMimeType: mimeType,
515
- content: imageBuffer
516
- }
517
- };
518
- Logger.info("发送图片请求", 'Server');
519
- // 发送请求
520
- const response = await fetch(url, {
521
- method: 'POST',
522
- headers: {
523
- ...CONFIG.DEFAULT_HEADERS,
524
- "cookie": CONFIG.API.SIGNATURE_COOKIE
525
- },
526
- body: JSON.stringify(uploadData)
527
- });
528
-
529
- if (!response.ok) {
530
- Logger.error(`上传图片失败,状态码:${response.status},原因:${response.error}`, 'Server');
531
- return '';
532
- }
533
-
534
- const result = await response.json();
535
- Logger.info('上传图片成功:', result, 'Server');
536
- return result.fileMetadataId;
537
-
538
- } catch (error) {
539
- Logger.error(error, 'Server');
540
- return '';
541
- }
542
- }
543
-
544
- async prepareChatRequest(request) {
545
- if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY && request.stream) {
546
- throw new Error(`该模型流式输出需要配置PICGO或者TUMY图床密钥!`);
547
- }
548
-
549
- // 处理画图模型的消息限制
550
- let todoMessages = request.messages;
551
- if (request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') {
552
- const lastMessage = todoMessages[todoMessages.length - 1];
553
- if (lastMessage.role !== 'user') {
554
- throw new Error('画图模型的最后一条消息必须是用户消息!');
555
- }
556
- todoMessages = [lastMessage];
557
- }
558
-
559
- const fileAttachments = [];
560
- let messages = '';
561
- let lastRole = null;
562
- let lastContent = '';
563
- const search = request.model === 'grok-2-search' || request.model === 'grok-3-search';
564
-
565
- // 移除<details>
566
- <summary>💭 <b>思考过程 (点击展开)</b></summary>
567
- 标签及其内容和base64图片
568
- const removeThinkTags = (text) => {
569
- text = text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
570
- text = text.replace(/!$$image$$$data:.*?base64,.*?$/g, '[图片]');
571
- return text;
572
- };
573
-
574
- const processImageUrl = async (content) => {
575
- if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
576
- const imageResponse = await this.uploadBase64Image(
577
- content.image_url.url,
578
- `${CONFIG.API.BASE_URL}/api/rpc`
579
- );
580
- return imageResponse;
581
- }
582
- return null;
583
- };
584
-
585
- const processContent = async (content) => {
586
- if (Array.isArray(content)) {
587
- let textContent = '';
588
- for (const item of content) {
589
- if (item.type === 'image_url') {
590
- textContent += (textContent ? '\n' : '') + "[图片]";
591
- } else if (item.type === 'text') {
592
- textContent += (textContent ? '\n' : '') + removeThinkTags(item.text);
593
- }
594
- }
595
- return textContent;
596
- } else if (typeof content === 'object' && content !== null) {
597
- if (content.type === 'image_url') {
598
- return "[图片]";
599
- } else if (content.type === 'text') {
600
- return removeThinkTags(content.text);
601
- }
602
- }
603
- return removeThinkTags(this.processMessageContent(content));
604
- };
605
-
606
- for (const current of todoMessages) {
607
- const role = current.role === 'assistant' ? 'assistant' : 'user';
608
- const isLastMessage = current === todoMessages[todoMessages.length - 1];
609
-
610
- // 处理图片附件
611
- if (isLastMessage && current.content) {
612
- if (Array.isArray(current.content)) {
613
- for (const item of current.content) {
614
- if (item.type === 'image_url') {
615
- const processedImage = await processImageUrl(item);
616
- if (processedImage) fileAttachments.push(processedImage);
617
- }
618
- }
619
- } else if (current.content.type === 'image_url') {
620
- const processedImage = await processImageUrl(current.content);
621
- if (processedImage) fileAttachments.push(processedImage);
622
- }
623
- }
624
-
625
- // 处理文本内容
626
- const textContent = await processContent(current.content);
627
-
628
- if (textContent || (isLastMessage && fileAttachments.length > 0)) {
629
- if (role === lastRole && textContent) {
630
- lastContent += '\n' + textContent;
631
- messages = messages.substring(0, messages.lastIndexOf(`${role.toUpperCase()}: `)) +
632
- `${role.toUpperCase()}: ${lastContent}\n`;
633
- } else {
634
- messages += `${role.toUpperCase()}: ${textContent || '[图片]'}\n`;
635
- lastContent = textContent;
636
- lastRole = role;
637
- }
638
- }
639
- }
640
-
641
- return {
642
- temporary: CONFIG.API.IS_TEMP_CONVERSATION,
643
- modelName: this.modelId,
644
- message: messages.trim(),
645
- fileAttachments: fileAttachments.slice(0, 4),
646
- imageAttachments: [],
647
- disableSearch: false,
648
- enableImageGeneration: true,
649
- returnImageBytes: false,
650
- returnRawGrokInXaiRequest: false,
651
- enableImageStreaming: false,
652
- imageGenerationCount: 1,
653
- forceConcise: false,
654
- toolOverrides: {
655
- imageGen: request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen',
656
- webSearch: search,
657
- xSearch: search,
658
- xMediaSearch: search,
659
- trendsSearch: search,
660
- xPostAnalyze: search
661
- },
662
- enableSideBySide: true,
663
- isPreset: false,
664
- sendFinalMetadata: true,
665
- customInstructions: "",
666
- deepsearchPreset: request.model === 'grok-3-deepsearch' ? "default" : "",
667
- isReasoning: request.model === 'grok-3-reasoning'
668
- };
669
- }
670
  }
671
 
672
  class MessageProcessor {
673
- static createChatResponse(message, model, isStream = false) {
674
- const baseResponse = {
675
- id: `chatcmpl-${uuidv4()}`,
676
- created: Math.floor(Date.now() / 1000),
677
- model: model
678
- };
679
-
680
- if (isStream) {
681
- return {
682
- ...baseResponse,
683
- object: 'chat.completion.chunk',
684
- choices: [{
685
- index: 0,
686
- delta: {
687
- content: message
688
- }
689
- }]
690
- };
691
- }
692
-
693
- return {
694
- ...baseResponse,
695
- object: 'chat.completion',
696
- choices: [{
697
- index: 0,
698
- message: {
699
- role: 'assistant',
700
- content: message
701
- },
702
- finish_reason: 'stop'
703
- }],
704
- usage: null
705
- };
706
- }
707
  }
708
  async function processModelResponse(response, model) {
709
- let result = { token: null, imageUrl: null }
710
- if (CONFIG.IS_IMG_GEN) {
711
- if (response?.cachedImageGenerationResponse && !CONFIG.IS_IMG_GEN2) {
712
- result.imageUrl = response.cachedImageGenerationResponse.imageUrl;
713
- }
714
- return result;
715
- }
716
-
717
- //非生图模型的处理
718
- switch (model) {
719
- case 'grok-2':
720
- result.token = response?.token;
721
- return result;
722
- case 'grok-2-search':
723
- case 'grok-3-search':
724
- if (response?.webSearchResults && CONFIG.ISSHOW_SEARCH_RESULTS) {
725
- result.token = `\r\n<think>${await Utils.organizeSearchResults(response.webSearchResults)}
726
  </details>
727
  \r\n`;
728
- } else {
729
- result.token = response?.token;
730
- }
731
- return result;
732
- case 'grok-3':
733
- result.token = response?.token;
734
- return result;
735
- case 'grok-3-deepsearch':
736
- if (response?.messageTag === "final") {
737
- result.token = response?.token;
738
- }
739
- return result;
740
- case 'grok-3-reasoning':
741
- if (response?.isThinking && !CONFIG.SHOW_THINKING) return result;
742
-
743
- if (response?.isThinking && !CONFIG.IS_THINKING) {
744
- result.token = "<details>
745
- <summary>💭 <b>思考过程 (点击展开)</b></summary>
746
- " + response?.token;
747
- CONFIG.IS_THINKING = true;
748
- } else if (!response.isThinking && CONFIG.IS_THINKING) {
749
- result.token = "
750
- </details>
751
- " + response?.token;
752
- CONFIG.IS_THINKING = false;
753
- } else {
754
- result.token = response?.token;
755
- }
756
- return result;
757
- }
758
- return result;
759
  }
760
 
761
  async function handleResponse(response, model, res, isStream) {
762
- try {
763
- const stream = response.body;
764
- let buffer = '';
765
- let fullResponse = '';
766
- const dataPromises = [];
767
- if (isStream) {
768
- res.setHeader('Content-Type', 'text/event-stream');
769
- res.setHeader('Cache-Control', 'no-cache');
770
- res.setHeader('Connection', 'keep-alive');
771
- }
772
- CONFIG.IS_THINKING = false;
773
- CONFIG.IS_IMG_GEN = false;
774
- CONFIG.IS_IMG_GEN2 = false;
775
- Logger.info("开始处理流式响应", 'Server');
776
-
777
- return new Promise((resolve, reject) => {
778
- stream.on('data', async (chunk) => {
779
- buffer += chunk.toString();
780
- const lines = buffer.split('\n');
781
- buffer = lines.pop() || '';
782
-
783
- for (const line of lines) {
784
- if (!line.trim()) continue;
785
- try {
786
- const linejosn = JSON.parse(line.trim());
787
- if (linejosn?.error) {
788
- Logger.error(JSON.stringify(linejosn, null, 2), 'Server');
789
- stream.destroy();
790
- reject(new Error(linejosn.error.message || "API Error"));
791
- return;
792
- }
793
- let response = linejosn?.result?.response;
794
- if (!response) continue;
795
- if (response?.doImgGen || response?.imageAttachmentInfo) {
796
- CONFIG.IS_IMG_GEN = true;
797
- }
798
- const processPromise = (async () => {
799
- const result = await processModelResponse(response, model);
800
-
801
- if (result.token) {
802
- if (isStream) {
803
- res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(result.token, model, true))}\n\n`);
804
- } else {
805
- fullResponse += result.token;
806
- }
807
- }
808
- if (result.imageUrl) {
809
- CONFIG.IS_IMG_GEN2 = true;
810
- const dataImage = await handleImageResponse(result.imageUrl);
811
- if (isStream) {
812
- res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(dataImage, model, true))}\n\n`);
813
- } else {
814
- res.json(MessageProcessor.createChatResponse(dataImage, model));
815
- }
816
- }
817
- })();
818
- dataPromises.push(processPromise);
819
- } catch (error) {
820
- Logger.error(error, 'Server');
821
- continue;
822
- }
823
- }
824
- });
825
-
826
- stream.on('end', async () => {
827
- try {
828
- await Promise.all(dataPromises);
829
- if (isStream) {
830
- res.write('data: [DONE]\n\n');
831
- res.end();
832
- } else {
833
- if (!CONFIG.IS_IMG_GEN2) {
834
- res.json(MessageProcessor.createChatResponse(fullResponse, model));
835
- }
836
- }
837
- resolve();
838
- } catch (error) {
839
- Logger.error(error, 'Server');
840
- reject(error);
841
- }
842
- });
843
-
844
- stream.on('error', (error) => {
845
- Logger.error(error, 'Server');
846
- reject(error);
847
- });
848
- });
849
- } catch (error) {
850
- Logger.error(error, 'Server');
851
- throw new Error(error);
852
- }
853
  }
854
 
855
  async function handleImageResponse(imageUrl) {
856
- const MAX_RETRIES = 2;
857
- let retryCount = 0;
858
- let imageBase64Response;
859
-
860
- while (retryCount < MAX_RETRIES) {
861
- try {
862
- imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
863
- method: 'GET',
864
- headers: {
865
- ...DEFAULT_HEADERS,
866
- "cookie": CONFIG.API.SIGNATURE_COOKIE
867
- }
868
- });
869
-
870
- if (imageBase64Response.ok) break;
871
- retryCount++;
872
- if (retryCount === MAX_RETRIES) {
873
- throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
874
- }
875
- await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
876
-
877
- } catch (error) {
878
- Logger.error(error, 'Server');
879
- retryCount++;
880
- if (retryCount === MAX_RETRIES) {
881
- throw error;
882
- }
883
- await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
884
- }
885
- }
886
-
887
-
888
- const arrayBuffer = await imageBase64Response.arrayBuffer();
889
- const imageBuffer = Buffer.from(arrayBuffer);
890
-
891
- if (!CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY) {
892
- const base64Image = imageBuffer.toString('base64');
893
- const imageContentType = imageBase64Response.headers.get('content-type');
894
- return `![image](data:${imageContentType};base64,${base64Image})`
895
- }
896
-
897
- Logger.info("开始上传图床", 'Server');
898
- const formData = new FormData();
899
- if (CONFIG.API.PICGO_KEY) {
900
- formData.append('source', imageBuffer, {
901
- filename: image-${Date.now()}.jpg,
902
- contentType: 'image/jpeg'
903
- });
904
- const formDataHeaders = formData.getHeaders();
905
- const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
906
- method: "POST",
907
- headers: {
908
- ...formDataHeaders,
909
- "Content-Type": "multipart/form-data",
910
- "X-API-Key": CONFIG.API.PICGO_KEY
911
- },
912
- body: formData
913
- });
914
- if (!responseURL.ok) {
915
- return "生图失败,请查看PICGO图床密钥是否设置正确"
916
- } else {
917
- Logger.info("生图成功", 'Server');
918
- const result = await responseURL.json();
919
- return ![image](${result.image.url})
920
- }
921
- } else if (CONFIG.API.TUMY_KEY) {
922
- const formData = new FormData();
923
- formData.append('file', imageBuffer, {
924
- filename: image-${Date.now()}.jpg,
925
- contentType: 'image/jpeg'
926
- });
927
- const formDataHeaders = formData.getHeaders();
928
- const responseURL = await fetch("https://tu.my/api/v1/upload", {
929
- method: "POST",
930
- headers: {
931
- ...formDataHeaders,
932
- "Accept": "application/json",
933
- 'Authorization': Bearer ${CONFIG.API.TUMY_KEY}
934
- },
935
- body: formData
936
- });
937
- if (!responseURL.ok) {
938
- return "生图失败,请查看TUMY图床密钥是否设置正确"
939
- } else {
940
- try {
941
- const result = await responseURL.json();
942
- Logger.info("生图成功", 'Server');
943
- return ![image](${result.data.links.url})
944
- } catch (error) {
945
- Logger.error(error, 'Server');
946
- return "生图失败,请查看TUMY图床密钥是否设置正确"
947
- }
948
- }
949
- }
950
  }
951
 
952
  const tokenManager = new AuthTokenManager();
@@ -958,99 +954,99 @@ app.use(Logger.requestLogger);
958
  app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
959
  app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
960
  app.use(cors({
961
- origin: '*',
962
- methods: ['GET', 'POST', 'OPTIONS'],
963
- allowedHeaders: ['Content-Type', 'Authorization']
964
  }));
965
 
966
  app.get('/hf/v1/models', (req, res) => {
967
- res.json({
968
- object: "list",
969
- data: Object.keys(tokenManager.tokenModelMap).map((model, index) => ({
970
- id: model,
971
- object: "model",
972
- created: Math.floor(Date.now() / 1000),
973
- owned_by: "grok",
974
- }))
975
- });
976
  });
977
 
978
  app.post('/hf/v1/chat/completions', async (req, res) => {
979
- try {
980
- const authorization = req.headers.authorization?.replace('Bearer ', '');
981
- if (!authorization) {
982
- return res.status(401).json({ error: 'Authorization header is missing' });
983
- }
984
 
985
- const { model, stream } = req.body;
986
- const grokClient = new GrokApiClient(model);
987
- const requestPayload = await grokClient.prepareChatRequest(req.body);
988
-
989
- CONFIG.API.SIGNATURE_COOKIE = await Utils.createAuthHeaders(model, authorization);
990
-
991
- Logger.info(`当前令牌: ${JSON.stringify(CONFIG.API.SIGNATURE_COOKIE, null, 2)}`, 'Server');
992
- Logger.info(`当前可用模型的全部可用数量: ${JSON.stringify(tokenManager.getRemainingTokenRequestCapacity(), null, 2)}`, 'Server');
993
-
994
- const response = await fetch(`${CONFIG.API.BASE_URL}/rest/app-chat/conversations/new`, {
995
- method: 'POST',
996
- headers: {
997
- "accept": "text/event-stream",
998
- "baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
999
- "content-type": "text/plain;charset=UTF-8",
1000
- "Connection": "keep-alive",
1001
- "cookie": CONFIG.API.SIGNATURE_COOKIE
1002
- },
1003
- body: JSON.stringify(requestPayload)
1004
- });
1005
 
1006
- if (response.ok) {
1007
- Logger.info(`请求成功`, 'Server');
1008
- try {
1009
- await handleResponse(response, model, res, stream);
1010
- Logger.info(`请求结束`, 'Server');
1011
- return;
1012
- } catch (error) {
1013
- Logger.error(error, 'Server');
1014
- res.status(500).json({
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1015
  error: {
1016
- message: error.message || error,
1017
- type: 'server_error'
1018
  }
1019
  });
1020
  }
1021
- } else {
1022
- Logger.error(`请求失败,状态码: ${response.status}`, 'Server');
1023
- let errorMessage = `请求失败,状态码: ${response.status}`;
1024
- try {
1025
- const errorBody = await response.json();
1026
- errorMessage += `, 错误信息: ${JSON.stringify(errorBody)}`;
1027
- } catch (e) {
1028
- Logger.warn("无法解析错误信息");
1029
- }
1030
- res.status(response.status).json({
1031
  error: {
1032
- message: errorMessage,
1033
- type: 'api_error'
1034
  }
1035
  });
1036
  }
1037
-
1038
- } catch (error) {
1039
- Logger.error(error, 'ChatAPI');
1040
- res.status(500).json({
1041
- error: {
1042
- message: error.message || error,
1043
- type: 'server_error'
1044
- }
1045
- });
1046
- }
1047
- 展开
1048
  });
1049
 
1050
  app.use((req, res) => {
1051
- res.status(200).send('api运行正常');
1052
  });
1053
 
1054
  app.listen(CONFIG.SERVER.PORT, () => {
1055
- Logger.info(服务器已启动,监听端口: ${CONFIG.SERVER.PORT}, 'Server');
1056
  });
 
9
 
10
  // 配置常量
11
  const CONFIG = {
12
+ MODELS: {
13
+ 'grok-2': 'grok-latest',
14
+ 'grok-2-imageGen': 'grok-latest',
15
+ 'grok-2-search': 'grok-latest',
16
+ "grok-3": "grok-3",
17
+ "grok-3-search": "grok-3",
18
+ "grok-3-imageGen": "grok-3",
19
+ "grok-3-deepsearch": "grok-3",
20
+ "grok-3-reasoning": "grok-3"
21
+ },
22
+ API: {
23
+ IS_TEMP_CONVERSATION: false, // 假设默认不使用临时会话
24
+ GROK2_CONCURRENCY_LEVEL: 4,
25
+ BASE_URL: "https://grok.com",
26
+ PICGO_KEY: process.env.PICGO_KEY || null, //想要流式生图的话需要填入这个PICGO图床的key
27
+ TUMY_KEY: process.env.TUMY_KEY || null //想要流式生图的话需要填入这个TUMY图床的key 两个图床二选一,默认使用PICGO
28
+ },
29
+ SERVER: {
30
+ PORT: process.env.PORT || 3000,
31
+ BODY_LIMIT: '5mb'
32
+ },
33
+ RETRY: {
34
+ MAX_ATTEMPTS: 2//重试次数
35
+ },
36
+ SHOW_THINKING: true,
37
+ IS_THINKING: false,
38
+ IS_IMG_GEN: false,
39
+ IS_IMG_GEN2: false,
40
+ ISSHOW_SEARCH_RESULTS: true,//是否显示搜索结果
41
+ CHROME_PATH: process.env.CHROME_PATH || null
42
  };
43
  puppeteer.use(StealthPlugin())
44
 
45
  // 请求头配置
46
  const DEFAULT_HEADERS = {
47
+ 'accept': '*/*',
48
+ 'accept-language': 'zh-CN,zh;q=0.9',
49
+ 'accept-encoding': 'gzip, deflate, br, zstd',
50
+ 'content-type': 'text/plain;charset=UTF-8',
51
+ 'Connection': 'keep-alive',
52
+ 'origin': 'https://grok.com',
53
+ 'priority': 'u=1, i',
54
+ 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
55
+ 'sec-ch-ua-mobile': '?0',
56
+ 'sec-ch-ua-platform': '"Windows"',
57
+ 'sec-fetch-dest': 'empty',
58
+ 'sec-fetch-mode': 'cors',
59
+ 'sec-fetch-site': 'same-origin',
60
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
61
+ 'baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
62
  };
63
 
64
 
65
  async function initialization() {
66
+ if (CONFIG.CHROME_PATH == null) {
67
+ try {
68
+ CONFIG.CHROME_PATH = puppeteer.executablePath();
69
+ } catch (error) {
70
+ CONFIG.CHROME_PATH = "/usr/bin/chromium";
71
+ }
72
+ }
73
+ Logger.info(`CHROME_PATH: ${CONFIG.CHROME_PATH}`, 'Server');
74
+ Logger.info("初始化完成", 'Server');
75
  }
76
 
77
  class AuthTokenManager {
78
+ constructor() {
79
+ this.tokenModelMap = {};
80
+ this.expiredTokens = new Set();
81
+ this.tokenStatusMap = {};
82
+
83
+ // 定义模型请求频率限制和过期时间
84
+ this.modelConfig = {
85
+ "grok-2": {
86
+ RequestFrequency: 30,
87
+ ExpirationTime: 1 * 60 * 60 * 1000 // 1小时
88
+ },
89
+ "grok-3": {
90
+ RequestFrequency: 20,
91
+ ExpirationTime: 2 * 60 * 60 * 1000 // 2小时
92
+ },
93
+ "grok-3-deepsearch": {
94
+ RequestFrequency: 10,
95
+ ExpirationTime: 24 * 60 * 60 * 1000 // 24小时
96
+ },
97
+ "grok-3-reasoning": {
98
+ RequestFrequency: 10,
99
+ ExpirationTime: 24 * 60 * 60 * 1000 // 24小时
100
+ }
101
+ };
102
+ this.tokenResetSwitch = false;
103
+ this.tokenResetTimer = null;
104
+ }
105
+ async fetchGrokStats(token, modelName) {
106
+ let requestKind = 'DEFAULT';
107
+ if (modelName == 'grok-2' || modelName == 'grok-3') {
108
+ requestKind = 'DEFAULT';
109
+ } else if (modelName == 'grok-3-deepsearch') {
110
+ requestKind = 'DEEPSEARCH';
111
+ } else if (modelName == 'grok-3-reasoning') {
112
+ requestKind = 'REASONING';
113
+ }
114
+ const response = await fetch('https://grok.com/rest/rate-limits', {
115
+ method: 'POST',
116
+ headers: {
117
+ 'content-type': 'application/json',
118
+ 'Cookie': token,
119
+ },
120
+ body: JSON.stringify({
121
+ "requestKind": requestKind,
122
+ "modelName": modelName == 'grok-2' ? 'grok-latest' : "grok-3"
123
+ })
124
+ });
125
+
126
+ if (response.status != 200) {
127
+ return 0;
128
+ }
129
+ const data = await response.json();
130
+ return data.remainingQueries;
131
+ }
132
+ async addToken(token) {
133
+ const sso = token.split("sso=")[1].split(";")[0];
134
+
135
+ for (const model of Object.keys(this.modelConfig)) {
136
+ if (!this.tokenModelMap[model]) {
137
+ this.tokenModelMap[model] = [];
138
+ }
139
+ if (!this.tokenStatusMap[sso]) {
140
+ this.tokenStatusMap[sso] = {};
141
+ }
142
+ const existingTokenEntry = this.tokenModelMap[model].find(entry => entry.token === token);
143
+
144
+ if (!existingTokenEntry) {
145
+ try {
146
+ const remainingQueries = await this.fetchGrokStats(token, model);
147
+
148
+ const modelRequestFrequency = this.modelConfig[model].RequestFrequency;
149
+ const usedRequestCount = modelRequestFrequency - remainingQueries;
150
+
151
+ if (usedRequestCount === modelRequestFrequency) {
152
+ this.expiredTokens.add({
153
+ token: token,
154
+ model: model,
155
+ expiredTime: Date.now()
156
+ });
157
+
158
+ if (!this.tokenStatusMap[sso][model]) {
159
+ this.tokenStatusMap[sso][model] = {
160
+ isValid: false,
161
+ invalidatedTime: Date.now(),
162
+ totalRequestCount: Math.max(0, usedRequestCount)
163
+ };
164
+ }
165
+
166
+ if (!this.tokenResetSwitch) {
167
+ this.startTokenResetProcess();
168
+ this.tokenResetSwitch = true;
169
+ }
170
+ } else {
171
+ this.tokenModelMap[model].push({
172
+ token: token,
173
+ RequestCount: Math.max(0, usedRequestCount),
174
+ AddedTime: Date.now(),
175
+ StartCallTime: null
176
+ });
177
+
178
+ if (!this.tokenStatusMap[sso][model]) {
179
+ this.tokenStatusMap[sso][model] = {
180
+ isValid: true,
181
+ invalidatedTime: null,
182
+ totalRequestCount: Math.max(0, usedRequestCount)
183
+ };
184
+ }
185
+ }
186
+ } catch (error) {
187
+ this.tokenModelMap[model].push({
188
+ token: token,
189
+ RequestCount: 0,
190
+ AddedTime: Date.now(),
191
+ StartCallTime: null
192
+ });
193
+
194
+ if (!this.tokenStatusMap[sso][model]) {
195
+ this.tokenStatusMap[sso][model] = {
196
+ isValid: true,
197
+ invalidatedTime: null,
198
+ totalRequestCount: 0
199
+ };
200
+ }
201
+
202
+ Logger.error(`获取模型 ${model} 的统计信息失败: ${error}`, 'TokenManager');
203
+ }
204
+ await Utils.delay(200);
205
+ }
206
+ }
207
+ }
208
+
209
+ setToken(token) {
210
+ const models = Object.keys(this.modelConfig);
211
+ this.tokenModelMap = models.reduce((map, model) => {
212
+ map[model] = [{
213
+ token,
214
+ RequestCount: 0,
215
+ AddedTime: Date.now(),
216
+ StartCallTime: null
217
+ }];
218
+ return map;
219
+ }, {});
220
+ const sso = token.split("sso=")[1].split(";")[0];
221
+ this.tokenStatusMap[sso] = models.reduce((statusMap, model) => {
222
+ statusMap[model] = {
223
+ isValid: true,
224
+ invalidatedTime: null,
225
+ totalRequestCount: 0
226
+ };
227
+ return statusMap;
228
+ }, {});
229
+ }
230
+
231
+ async deleteToken(token) {
232
+ try {
233
+ const sso = token.split("sso=")[1].split(";")[0];
234
+ await Promise.all([
235
+ new Promise((resolve) => {
236
+ this.tokenModelMap = Object.fromEntries(
237
+ Object.entries(this.tokenModelMap).map(([model, entries]) => [
238
+ model,
239
+ entries.filter(entry => entry.token !== token)
240
+ ])
241
+ );
242
+ resolve();
243
+ }),
244
+
245
+ new Promise((resolve) => {
246
+ delete this.tokenStatusMap[sso];
247
+ resolve();
248
+ }),
249
+ ]);
250
+ Logger.info(`令牌已成功移除: ${token}`, 'TokenManager');
251
+ return true;
252
+ } catch (error) {
253
+ Logger.error('令牌删除失败:', error);
254
+ return false;
255
+ }
256
+ }
257
+ getNextTokenForModel(modelId) {
258
+ const normalizedModel = this.normalizeModelName(modelId);
259
+
260
+ if (!this.tokenModelMap[normalizedModel] || this.tokenModelMap[normalizedModel].length === 0) {
261
+ return null;
262
+ }
263
+ const tokenEntry = this.tokenModelMap[normalizedModel][0];
264
+
265
+ if (tokenEntry) {
266
+ if (tokenEntry.StartCallTime === null || tokenEntry.StartCallTime === undefined) {
267
+ tokenEntry.StartCallTime = Date.now();
268
+ }
269
+ if (!this.tokenResetSwitch) {
270
+ this.startTokenResetProcess();
271
+ this.tokenResetSwitch = true;
272
+ }
273
+ tokenEntry.RequestCount++;
274
+
275
+ if (tokenEntry.RequestCount > this.modelConfig[normalizedModel].RequestFrequency) {
276
+ this.removeTokenFromModel(normalizedModel, tokenEntry.token);
277
+ const nextTokenEntry = this.tokenModelMap[normalizedModel][0];
278
+ return nextTokenEntry ? nextTokenEntry.token : null;
279
+ }
280
+ const sso = tokenEntry.token.split("sso=")[1].split(";")[0];
281
+ if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][normalizedModel]) {
282
+ if (tokenEntry.RequestCount === this.modelConfig[normalizedModel].RequestFrequency) {
283
+ this.tokenStatusMap[sso][normalizedModel].isValid = false;
284
+ this.tokenStatusMap[sso][normalizedModel].invalidatedTime = Date.now();
285
+ }
286
+ this.tokenStatusMap[sso][normalizedModel].totalRequestCount++;
287
+ }
288
+ return tokenEntry.token;
289
+ }
290
+
291
+ return null;
292
+ }
293
+
294
+ removeTokenFromModel(modelId, token) {
295
+ const normalizedModel = this.normalizeModelName(modelId);
296
+
297
+ if (!this.tokenModelMap[normalizedModel]) {
298
+ Logger.error(`模型 ${normalizedModel} 不存在`, 'TokenManager');
299
+ return false;
300
+ }
301
+
302
+ const modelTokens = this.tokenModelMap[normalizedModel];
303
+ const tokenIndex = modelTokens.findIndex(entry => entry.token === token);
304
+
305
+ if (tokenIndex !== -1) {
306
+ const removedTokenEntry = modelTokens.splice(tokenIndex, 1)[0];
307
+ this.expiredTokens.add({
308
+ token: removedTokenEntry.token,
309
+ model: normalizedModel,
310
+ expiredTime: Date.now()
311
+ });
312
+
313
+ if (!this.tokenResetSwitch) {
314
+ this.startTokenResetProcess();
315
+ this.tokenResetSwitch = true;
316
+ }
317
+ Logger.info(`模型${modelId}的令牌已失效,已成功移除令牌: ${token}`, 'TokenManager');
318
+ return true;
319
+ }
320
+
321
+ Logger.error(`在模型 ${normalizedModel} 中未找到 token: ${token}`, 'TokenManager');
322
+ return false;
323
+ }
324
+
325
+ getExpiredTokens() {
326
+ return Array.from(this.expiredTokens);
327
+ }
328
+
329
+ normalizeModelName(model) {
330
+ if (model.startsWith('grok-') && !model.includes('deepsearch') && !model.includes('reasoning')) {
331
+ return model.split('-').slice(0, 2).join('-');
332
+ }
333
+ return model;
334
+ }
335
+
336
+ getTokenCountForModel(modelId) {
337
+ const normalizedModel = this.normalizeModelName(modelId);
338
+ return this.tokenModelMap[normalizedModel]?.length || 0;
339
+ }
340
+
341
+ getRemainingTokenRequestCapacity() {
342
+ const remainingCapacityMap = {};
343
+
344
+ Object.keys(this.modelConfig).forEach(model => {
345
+ const modelTokens = this.tokenModelMap[model] || [];
346
+
347
+ const modelRequestFrequency = this.modelConfig[model].RequestFrequency;
348
+
349
+ const totalUsedRequests = modelTokens.reduce((sum, tokenEntry) => {
350
+ return sum + (tokenEntry.RequestCount || 0);
351
+ }, 0);
352
+
353
+ // 计算剩余可用请求数量
354
+ const remainingCapacity = (modelTokens.length * modelRequestFrequency) - totalUsedRequests;
355
+ remainingCapacityMap[model] = Math.max(0, remainingCapacity);
356
+ });
357
+
358
+ return remainingCapacityMap;
359
+ }
360
+
361
+ getTokenArrayForModel(modelId) {
362
+ const normalizedModel = this.normalizeModelName(modelId);
363
+ return this.tokenModelMap[normalizedModel] || [];
364
+ }
365
+
366
+ startTokenResetProcess() {
367
+ if (this.tokenResetTimer) {
368
+ clearInterval(this.tokenResetTimer);
369
+ }
370
+
371
+ this.tokenResetTimer = setInterval(() => {
372
+ const now = Date.now();
373
+
374
+ this.expiredTokens.forEach(expiredTokenInfo => {
375
+ const { token, model, expiredTime } = expiredTokenInfo;
376
+ const expirationTime = this.modelConfig[model].ExpirationTime;
377
+ if (now - expiredTime >= expirationTime) {
378
+ if (!this.tokenModelMap[model].some(entry => entry.token === token)) {
379
+ this.tokenModelMap[model].push({
380
+ token: token,
381
+ RequestCount: 0,
382
+ AddedTime: now,
383
+ StartCallTime: null
384
+ });
385
+ }
386
+ const sso = token.split("sso=")[1].split(";")[0];
387
+
388
+ if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][model]) {
389
+ this.tokenStatusMap[sso][model].isValid = true;
390
+ this.tokenStatusMap[sso][model].invalidatedTime = null;
391
+ this.tokenStatusMap[sso][model].totalRequestCount = 0;
392
+ }
393
+
394
+ this.expiredTokens.delete(expiredTokenInfo);
395
+ }
396
+ });
397
+
398
+ Object.keys(this.modelConfig).forEach(model => {
399
+ if (!this.tokenModelMap[model]) return;
400
+
401
+ const processedTokens = this.tokenModelMap[model].map(tokenEntry => {
402
+ if (!tokenEntry.StartCallTime) return tokenEntry;
403
+
404
+ const expirationTime = this.modelConfig[model].ExpirationTime;
405
+ if (now - tokenEntry.StartCallTime >= expirationTime) {
406
+ const sso = tokenEntry.token.split("sso=")[1].split(";")[0];
407
+ if (this.tokenStatusMap[sso] && this.tokenStatusMap[sso][model]) {
408
+ this.tokenStatusMap[sso][model].isValid = true;
409
+ this.tokenStatusMap[sso][model].invalidatedTime = null;
410
+ this.tokenStatusMap[sso][model].totalRequestCount = 0;
411
+ }
412
+
413
+ return {
414
+ ...tokenEntry,
415
+ RequestCount: 0,
416
+ StartCallTime: null
417
+ };
418
+ }
419
+
420
+ return tokenEntry;
421
+ });
422
+
423
+ this.tokenModelMap[model] = processedTokens;
424
+ });
425
+ }, 1 * 60 * 60 * 1000);
426
+ }
427
+
428
+ getAllTokens() {
429
+ const allTokens = new Set();
430
+ Object.values(this.tokenModelMap).forEach(modelTokens => {
431
+ modelTokens.forEach(entry => allTokens.add(entry.token));
432
+ });
433
+ return Array.from(allTokens);
434
+ }
435
+
436
+ getTokenStatusMap() {
437
+ return this.tokenStatusMap;
438
+ }
439
  }
440
 
441
 
442
  class Utils {
443
+ static delay(time) {
444
+ return new Promise(function (resolve) {
445
+ setTimeout(resolve, time)
446
+ });
447
+ }
448
+ static async organizeSearchResults(searchResults) {
449
+ // 确保传入的是有效的搜索结果对象
450
+ if (!searchResults || !searchResults.results) {
451
+ return '';
452
+ }
453
+
454
+ const results = searchResults.results;
455
+ const formattedResults = results.map((result, index) => {
456
+ // 处理可能为空的字段
457
+ const title = result.title || '未知标题';
458
+ const url = result.url || '#';
459
+ const preview = result.preview || '无预览内容';
460
+
461
+ return `\r\n<details><summary>资料[${index}]: ${title}</summary>\r\n${preview}\r\n\n[Link](${url})\r\n</details>`;
462
+ });
463
+ return formattedResults.join('\n\n');
464
+ }
465
+ static async createAuthHeaders(model, authorization) {
466
+ return `sso=${authorization};ssp_rw=${authorization}`; // 直接使用authorization
467
+ }
468
  }
469
 
470
  class GrokApiClient {
471
+ constructor(modelId) {
472
+ if (!CONFIG.MODELS[modelId]) {
473
+ throw new Error(`不支持的模型: ${modelId}`);
474
+ }
475
+ this.modelId = CONFIG.MODELS[modelId];
476
+ }
477
+
478
+ processMessageContent(content) {
479
+ if (typeof content === 'string') return content;
480
+ return null;
481
+ }
482
+ // 获取图片类型
483
+ getImageType(base64String) {
484
+ let mimeType = 'image/jpeg';
485
+ if (base64String.includes('data:image')) {
486
+ const matches = base64String.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+);base64,/);
487
+ if (matches) {
488
+ mimeType = matches[1];
489
+ }
490
+ }
491
+ const extension = mimeType.split('/')[1];
492
+ const fileName = `image.${extension}`;
493
+
494
+ return {
495
+ mimeType: mimeType,
496
+ fileName: fileName
497
+ };
498
+ }
499
+
500
+ async uploadBase64Image(base64Data, url) {
501
+ try {
502
+ // 处理 base64 数据
503
+ let imageBuffer;
504
+ if (base64Data.includes('data:image')) {
505
+ imageBuffer = base64Data.split(',')[1];
506
+ } else {
507
+ imageBuffer = base64Data
508
+ }
509
+ const { mimeType, fileName } = this.getImageType(base64Data);
510
+ let uploadData = {
511
+ rpc: "uploadFile",
512
+ req: {
513
+ fileName: fileName,
514
+ fileMimeType: mimeType,
515
+ content: imageBuffer
516
+ }
517
+ };
518
+ Logger.info("发送图片请求", 'Server');
519
+ // 发送请求
520
+ const response = await fetch(url, {
521
+ method: 'POST',
522
+ headers: {
523
+ ...CONFIG.DEFAULT_HEADERS,
524
+ "cookie": CONFIG.API.SIGNATURE_COOKIE
525
+ },
526
+ body: JSON.stringify(uploadData)
527
+ });
528
+
529
+ if (!response.ok) {
530
+ Logger.error(`上传图片失败,状态码:${response.status},原因:${response.error}`, 'Server');
531
+ return '';
532
+ }
533
+
534
+ const result = await response.json();
535
+ Logger.info('上传图片成功:', result, 'Server');
536
+ return result.fileMetadataId;
537
+
538
+ } catch (error) {
539
+ Logger.error(error, 'Server');
540
+ return '';
541
+ }
542
+ }
543
+
544
+ async prepareChatRequest(request) {
545
+ if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY && request.stream) {
546
+ throw new Error(`该模型流式输出需要配置PICGO或者TUMY图床密钥!`);
547
+ }
548
+
549
+ // 处理画图模型的消息限制
550
+ let todoMessages = request.messages;
551
+ if (request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') {
552
+ const lastMessage = todoMessages[todoMessages.length - 1];
553
+ if (lastMessage.role !== 'user') {
554
+ throw new Error('画图模型的最后一条消息必须是用户消息!');
555
+ }
556
+ todoMessages = [lastMessage];
557
+ }
558
+
559
+ const fileAttachments = [];
560
+ let messages = '';
561
+ let lastRole = null;
562
+ let lastContent = '';
563
+ const search = request.model === 'grok-2-search' || request.model === 'grok-3-search';
564
+
565
+ // 移除<details>
566
+ <summary>💭 <b>思考过程 (点击展开)</b></summary>
567
+ 标签及其内容和base64图片
568
+ const removeThinkTags = (text) => {
569
+ text = text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
570
+ text = text.replace(/!$$image$$$data:.*?base64,.*?$/g, '[图片]');
571
+ return text;
572
+ };
573
+
574
+ const processImageUrl = async (content) => {
575
+ if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
576
+ const imageResponse = await this.uploadBase64Image(
577
+ content.image_url.url,
578
+ `${CONFIG.API.BASE_URL}/api/rpc`
579
+ );
580
+ return imageResponse;
581
+ }
582
+ return null;
583
+ };
584
+
585
+ const processContent = async (content) => {
586
+ if (Array.isArray(content)) {
587
+ let textContent = '';
588
+ for (const item of content) {
589
+ if (item.type === 'image_url') {
590
+ textContent += (textContent ? '\n' : '') + "[图片]";
591
+ } else if (item.type === 'text') {
592
+ textContent += (textContent ? '\n' : '') + removeThinkTags(item.text);
593
+ }
594
+ }
595
+ return textContent;
596
+ } else if (typeof content === 'object' && content !== null) {
597
+ if (content.type === 'image_url') {
598
+ return "[图片]";
599
+ } else if (content.type === 'text') {
600
+ return removeThinkTags(content.text);
601
+ }
602
+ }
603
+ return removeThinkTags(this.processMessageContent(content));
604
+ };
605
+
606
+ for (const current of todoMessages) {
607
+ const role = current.role === 'assistant' ? 'assistant' : 'user';
608
+ const isLastMessage = current === todoMessages[todoMessages.length - 1];
609
+
610
+ // 处理图片附件
611
+ if (isLastMessage && current.content) {
612
+ if (Array.isArray(current.content)) {
613
+ for (const item of current.content) {
614
+ if (item.type === 'image_url') {
615
+ const processedImage = await processImageUrl(item);
616
+ if (processedImage) fileAttachments.push(processedImage);
617
+ }
618
+ }
619
+ } else if (current.content.type === 'image_url') {
620
+ const processedImage = await processImageUrl(current.content);
621
+ if (processedImage) fileAttachments.push(processedImage);
622
+ }
623
+ }
624
+
625
+ // 处理文本内容
626
+ const textContent = await processContent(current.content);
627
+
628
+ if (textContent || (isLastMessage && fileAttachments.length > 0)) {
629
+ if (role === lastRole && textContent) {
630
+ lastContent += '\n' + textContent;
631
+ messages = messages.substring(0, messages.lastIndexOf(`${role.toUpperCase()}: `)) +
632
+ `${role.toUpperCase()}: ${lastContent}\n`;
633
+ } else {
634
+ messages += `${role.toUpperCase()}: ${textContent || '[图片]'}\n`;
635
+ lastContent = textContent;
636
+ lastRole = role;
637
+ }
638
+ }
639
+ }
640
+
641
+ return {
642
+ temporary: CONFIG.API.IS_TEMP_CONVERSATION,
643
+ modelName: this.modelId,
644
+ message: messages.trim(),
645
+ fileAttachments: fileAttachments.slice(0, 4),
646
+ imageAttachments: [],
647
+ disableSearch: false,
648
+ enableImageGeneration: true,
649
+ returnImageBytes: false,
650
+ returnRawGrokInXaiRequest: false,
651
+ enableImageStreaming: false,
652
+ imageGenerationCount: 1,
653
+ forceConcise: false,
654
+ toolOverrides: {
655
+ imageGen: request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen',
656
+ webSearch: search,
657
+ xSearch: search,
658
+ xMediaSearch: search,
659
+ trendsSearch: search,
660
+ xPostAnalyze: search
661
+ },
662
+ enableSideBySide: true,
663
+ isPreset: false,
664
+ sendFinalMetadata: true,
665
+ customInstructions: "",
666
+ deepsearchPreset: request.model === 'grok-3-deepsearch' ? "default" : "",
667
+ isReasoning: request.model === 'grok-3-reasoning'
668
+ };
669
+ }
670
  }
671
 
672
  class MessageProcessor {
673
+ static createChatResponse(message, model, isStream = false) {
674
+ const baseResponse = {
675
+ id: `chatcmpl-${uuidv4()}`,
676
+ created: Math.floor(Date.now() / 1000),
677
+ model: model
678
+ };
679
+
680
+ if (isStream) {
681
+ return {
682
+ ...baseResponse,
683
+ object: 'chat.completion.chunk',
684
+ choices: [{
685
+ index: 0,
686
+ delta: {
687
+ content: message
688
+ }
689
+ }]
690
+ };
691
+ }
692
+
693
+ return {
694
+ ...baseResponse,
695
+ object: 'chat.completion',
696
+ choices: [{
697
+ index: 0,
698
+ message: {
699
+ role: 'assistant',
700
+ content: message
701
+ },
702
+ finish_reason: 'stop'
703
+ }],
704
+ usage: null
705
+ };
706
+ }
707
  }
708
  async function processModelResponse(response, model) {
709
+ let result = { token: null, imageUrl: null }
710
+ if (CONFIG.IS_IMG_GEN) {
711
+ if (response?.cachedImageGenerationResponse && !CONFIG.IS_IMG_GEN2) {
712
+ result.imageUrl = response.cachedImageGenerationResponse.imageUrl;
713
+ }
714
+ return result;
715
+ }
716
+
717
+ //非生图模型的处理
718
+ switch (model) {
719
+ case 'grok-2':
720
+ result.token = response?.token;
721
+ return result;
722
+ case 'grok-2-search':
723
+ case 'grok-3-search':
724
+ if (response?.webSearchResults && CONFIG.ISSHOW_SEARCH_RESULTS) {
725
+ result.token = `\r\n<think>${await Utils.organizeSearchResults(response.webSearchResults)}
726
  </details>
727
  \r\n`;
728
+ } else {
729
+ result.token = response?.token;
730
+ }
731
+ return result;
732
+ case 'grok-3':
733
+ result.token = response?.token;
734
+ return result;
735
+ case 'grok-3-deepsearch':
736
+ if (response?.messageTag === "final") {
737
+ result.token = response?.token;
738
+ }
739
+ return result;
740
+ case 'grok-3-reasoning':
741
+ if (response?.isThinking && !CONFIG.SHOW_THINKING) return result;
742
+
743
+ if (response?.isThinking && !CONFIG.IS_THINKING) {
744
+ result.token = "<details><summary>💭 <b>思考过程 (点击展开)</b></summary>" + response?.token;
745
+ CONFIG.IS_THINKING = true;
746
+ } else if (!response.isThinking && CONFIG.IS_THINKING) {
747
+ result.token = "</details>" + response?.token;
748
+ CONFIG.IS_THINKING = false;
749
+ } else {
750
+ result.token = response?.token;
751
+ }
752
+ return result;
753
+ }
754
+ return result;
 
 
 
 
755
  }
756
 
757
  async function handleResponse(response, model, res, isStream) {
758
+ try {
759
+ const stream = response.body;
760
+ let buffer = '';
761
+ let fullResponse = '';
762
+ const dataPromises = [];
763
+ if (isStream) {
764
+ res.setHeader('Content-Type', 'text/event-stream');
765
+ res.setHeader('Cache-Control', 'no-cache');
766
+ res.setHeader('Connection', 'keep-alive');
767
+ }
768
+ CONFIG.IS_THINKING = false;
769
+ CONFIG.IS_IMG_GEN = false;
770
+ CONFIG.IS_IMG_GEN2 = false;
771
+ Logger.info("开始处理流式响应", 'Server');
772
+
773
+ return new Promise((resolve, reject) => {
774
+ stream.on('data', async (chunk) => {
775
+ buffer += chunk.toString();
776
+ const lines = buffer.split('\n');
777
+ buffer = lines.pop() || '';
778
+
779
+ for (const line of lines) {
780
+ if (!line.trim()) continue;
781
+ try {
782
+ const linejosn = JSON.parse(line.trim());
783
+ if (linejosn?.error) {
784
+ Logger.error(JSON.stringify(linejosn, null, 2), 'Server');
785
+ stream.destroy();
786
+ reject(new Error(linejosn.error.message || "API Error"));
787
+ return;
788
+ }
789
+ let response = linejosn?.result?.response;
790
+ if (!response) continue;
791
+ if (response?.doImgGen || response?.imageAttachmentInfo) {
792
+ CONFIG.IS_IMG_GEN = true;
793
+ }
794
+ const processPromise = (async () => {
795
+ const result = await processModelResponse(response, model);
796
+
797
+ if (result.token) {
798
+ if (isStream) {
799
+ res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(result.token, model, true))}\n\n`);
800
+ } else {
801
+ fullResponse += result.token;
802
+ }
803
+ }
804
+ if (result.imageUrl) {
805
+ CONFIG.IS_IMG_GEN2 = true;
806
+ const dataImage = await handleImageResponse(result.imageUrl);
807
+ if (isStream) {
808
+ res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(dataImage, model, true))}\n\n`);
809
+ } else {
810
+ res.json(MessageProcessor.createChatResponse(dataImage, model));
811
+ }
812
+ }
813
+ })();
814
+ dataPromises.push(processPromise);
815
+ } catch (error) {
816
+ Logger.error(error, 'Server');
817
+ continue;
818
+ }
819
+ }
820
+ });
821
+
822
+ stream.on('end', async () => {
823
+ try {
824
+ await Promise.all(dataPromises);
825
+ if (isStream) {
826
+ res.write('data: [DONE]\n\n');
827
+ res.end();
828
+ } else {
829
+ if (!CONFIG.IS_IMG_GEN2) {
830
+ res.json(MessageProcessor.createChatResponse(fullResponse, model));
831
+ }
832
+ }
833
+ resolve();
834
+ } catch (error) {
835
+ Logger.error(error, 'Server');
836
+ reject(error);
837
+ }
838
+ });
839
+
840
+ stream.on('error', (error) => {
841
+ Logger.error(error, 'Server');
842
+ reject(error);
843
+ });
844
+ });
845
+ } catch (error) {
846
+ Logger.error(error, 'Server');
847
+ throw new Error(error);
848
+ }
849
  }
850
 
851
  async function handleImageResponse(imageUrl) {
852
+ const MAX_RETRIES = 2;
853
+ let retryCount = 0;
854
+ let imageBase64Response;
855
+
856
+ while (retryCount < MAX_RETRIES) {
857
+ try {
858
+ imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
859
+ method: 'GET',
860
+ headers: {
861
+ ...DEFAULT_HEADERS,
862
+ "cookie": CONFIG.API.SIGNATURE_COOKIE
863
+ }
864
+ });
865
+
866
+ if (imageBase64Response.ok) break;
867
+ retryCount++;
868
+ if (retryCount === MAX_RETRIES) {
869
+ throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
870
+ }
871
+ await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
872
+
873
+ } catch (error) {
874
+ Logger.error(error, 'Server');
875
+ retryCount++;
876
+ if (retryCount === MAX_RETRIES) {
877
+ throw error;
878
+ }
879
+ await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
880
+ }
881
+ }
882
+
883
+
884
+ const arrayBuffer = await imageBase64Response.arrayBuffer();
885
+ const imageBuffer = Buffer.from(arrayBuffer);
886
+
887
+ if (!CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY) {
888
+ const base64Image = imageBuffer.toString('base64');
889
+ const imageContentType = imageBase64Response.headers.get('content-type');
890
+ return `![image](data:${imageContentType};base64,${base64Image})`
891
+ }
892
+
893
+ Logger.info("开始上传图床", 'Server');
894
+ const formData = new FormData();
895
+ if (CONFIG.API.PICGO_KEY) {
896
+ formData.append('source', imageBuffer, {
897
+ filename: `image-${Date.now()}.jpg`,
898
+ contentType: 'image/jpeg'
899
+ });
900
+ const formDataHeaders = formData.getHeaders();
901
+ const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
902
+ method: "POST",
903
+ headers: {
904
+ ...formDataHeaders,
905
+ "Content-Type": "multipart/form-data",
906
+ "X-API-Key": CONFIG.API.PICGO_KEY
907
+ },
908
+ body: formData
909
+ });
910
+ if (!responseURL.ok) {
911
+ return "生图失败,请查看PICGO图床密钥是否设置正确"
912
+ } else {
913
+ Logger.info("生图成功", 'Server');
914
+ const result = await responseURL.json();
915
+ return `![image](${result.image.url})`
916
+ }
917
+ } else if (CONFIG.API.TUMY_KEY) {
918
+ const formData = new FormData();
919
+ formData.append('file', imageBuffer, {
920
+ filename: `image-${Date.now()}.jpg`,
921
+ contentType: 'image/jpeg'
922
+ });
923
+ const formDataHeaders = formData.getHeaders();
924
+ const responseURL = await fetch("https://tu.my/api/v1/upload", {
925
+ method: "POST",
926
+ headers: {
927
+ ...formDataHeaders,
928
+ "Accept": "application/json",
929
+ 'Authorization': `Bearer ${CONFIG.API.TUMY_KEY}`
930
+ },
931
+ body: formData
932
+ });
933
+ if (!responseURL.ok) {
934
+ return "生图失败,请查看TUMY图床密钥是否设置正确"
935
+ } else {
936
+ try {
937
+ const result = await responseURL.json();
938
+ Logger.info("生图成功", 'Server');
939
+ return `![image](${result.data.links.url})`
940
+ } catch (error) {
941
+ Logger.error(error, 'Server');
942
+ return "生图失败,请查看TUMY图床密钥是否设置正确"
943
+ }
944
+ }
945
+ }
946
  }
947
 
948
  const tokenManager = new AuthTokenManager();
 
954
  app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
955
  app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
956
  app.use(cors({
957
+ origin: '*',
958
+ methods: ['GET', 'POST', 'OPTIONS'],
959
+ allowedHeaders: ['Content-Type', 'Authorization']
960
  }));
961
 
962
  app.get('/hf/v1/models', (req, res) => {
963
+ res.json({
964
+ object: "list",
965
+ data: Object.keys(tokenManager.tokenModelMap).map((model, index) => ({
966
+ id: model,
967
+ object: "model",
968
+ created: Math.floor(Date.now() / 1000),
969
+ owned_by: "grok",
970
+ }))
971
+ });
972
  });
973
 
974
  app.post('/hf/v1/chat/completions', async (req, res) => {
975
+ try {
976
+ const authorization = req.headers.authorization?.replace('Bearer ', '');
977
+ if (!authorization) {
978
+ return res.status(401).json({ error: 'Authorization header is missing' });
979
+ }
980
 
981
+ const { model, stream } = req.body;
982
+ const grokClient = new GrokApiClient(model);
983
+ const requestPayload = await grokClient.prepareChatRequest(req.body);
984
+
985
+ CONFIG.API.SIGNATURE_COOKIE = await Utils.createAuthHeaders(model, authorization);
986
+
987
+ Logger.info(`当前令牌: ${JSON.stringify(CONFIG.API.SIGNATURE_COOKIE, null, 2)}`, 'Server');
988
+ Logger.info(`当前可用模型的全部可用数量: ${JSON.stringify(tokenManager.getRemainingTokenRequestCapacity(), null, 2)}`, 'Server');
989
+
990
+ const response = await fetch(`${CONFIG.API.BASE_URL}/rest/app-chat/conversations/new`, {
991
+ method: 'POST',
992
+ headers: {
993
+ "accept": "text/event-stream",
994
+ "baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
995
+ "content-type": "text/plain;charset=UTF-8",
996
+ "Connection": "keep-alive",
997
+ "cookie": CONFIG.API.SIGNATURE_COOKIE
998
+ },
999
+ body: JSON.stringify(requestPayload)
1000
+ });
1001
 
1002
+ if (response.ok) {
1003
+ Logger.info(`请求成功`, 'Server');
1004
+ try {
1005
+ await handleResponse(response, model, res, stream);
1006
+ Logger.info(`请求结束`, 'Server');
1007
+ return;
1008
+ } catch (error) {
1009
+ Logger.error(error, 'Server');
1010
+ res.status(500).json({
1011
+ error: {
1012
+ message: error.message || error,
1013
+ type: 'server_error'
1014
+ }
1015
+ });
1016
+ }
1017
+ } else {
1018
+ Logger.error(`请求失败,状态码: ${response.status}`, 'Server');
1019
+ let errorMessage = `请求失败,状态码: ${response.status}`;
1020
+ try {
1021
+ const errorBody = await response.json();
1022
+ errorMessage += `, 错误信息: ${JSON.stringify(errorBody)}`;
1023
+ } catch (e) {
1024
+ Logger.warn("无法解析错误信息");
1025
+ }
1026
+ res.status(response.status).json({
1027
  error: {
1028
+ message: errorMessage,
1029
+ type: 'api_error'
1030
  }
1031
  });
1032
  }
1033
+
1034
+ } catch (error) {
1035
+ Logger.error(error, 'ChatAPI');
1036
+ res.status(500).json({
 
 
 
 
 
 
1037
  error: {
1038
+ message: error.message || error,
1039
+ type: 'server_error'
1040
  }
1041
  });
1042
  }
1043
+ 展开
 
 
 
 
 
 
 
 
 
 
1044
  });
1045
 
1046
  app.use((req, res) => {
1047
+ res.status(200).send('api运行正常');
1048
  });
1049
 
1050
  app.listen(CONFIG.SERVER.PORT, () => {
1051
+ Logger.info(`服务器已��动,监听端口: ${CONFIG.SERVER.PORT}`, 'Server');
1052
  });