yxmiler commited on
Commit
c5dd0cf
·
verified ·
1 Parent(s): b1ffcce

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +450 -322
index.js CHANGED
@@ -3,22 +3,29 @@ import fetch from 'node-fetch';
3
  import FormData from 'form-data';
4
  import dotenv from 'dotenv';
5
  import cors from 'cors';
6
- import puppeteer from 'puppeteer';
 
7
  import { v4 as uuidv4 } from 'uuid';
 
8
 
9
  dotenv.config();
 
10
  // 配置常量
11
  const CONFIG = {
12
  MODELS: {
13
- 'grok-latest': 'grok-latest',
14
- 'grok-latest-image': 'grok-latest',
15
- 'grok-latest-search': 'grok-latest'
 
 
 
 
16
  },
17
  API: {
18
  BASE_URL: "https://grok.com",
19
  API_KEY: process.env.API_KEY || "sk-123456",
20
- SSO_TOKEN: null,//登录时才有的认证cookie,这里暂时用不到,之后可能需要
21
  SIGNATURE_COOKIE: null,
 
22
  PICGO_KEY: process.env.PICGO_KEY || null //想要生图的话需要填入这个PICGO图床的key
23
  },
24
  SERVER: {
@@ -26,13 +33,16 @@ const CONFIG = {
26
  BODY_LIMIT: '5mb'
27
  },
28
  RETRY: {
29
- MAX_ATTEMPTS: 1,//重试次数
30
- DELAY_BASE: 1000 // 基础延迟时间(毫秒)
31
  },
 
 
 
 
32
  ISSHOW_SEARCH_RESULTS: process.env.ISSHOW_SEARCH_RESULTS === 'true',//是否显示搜索结果,默认关闭
33
  CHROME_PATH: process.env.CHROME_PATH || "/usr/bin/chromium"//chrome路径
34
  };
35
-
36
  // 请求头配置
37
  const DEFAULT_HEADERS = {
38
  'accept': '*/*',
@@ -52,9 +62,133 @@ const DEFAULT_HEADERS = {
52
  'baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
53
  };
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  class Utils {
56
  static async extractGrokHeaders() {
57
- console.log("开始提取头信息");
58
  try {
59
  // 启动浏览器
60
  const browser = await puppeteer.launch({
@@ -69,8 +203,10 @@ class Utils {
69
  });
70
 
71
  const page = await browser.newPage();
72
- await page.goto('https://grok.com/', { waitUntil: 'networkidle0' });
73
-
 
 
74
  // 获取所有 Cookies
75
  const cookies = await page.cookies();
76
  const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
@@ -85,80 +221,32 @@ class Utils {
85
  // 关闭浏览器
86
  await browser.close();
87
  // 打印并返回提取的头信息
88
- console.log('提取的头信息:', JSON.stringify(extractedHeaders, null, 2));
89
  return extractedHeaders;
90
 
91
  } catch (error) {
92
- console.error('获取头信息出错:', error);
93
  return null;
94
  }
95
  }
96
  static async get_signature() {
97
- if (CONFIG.API.SIGNATURE_COOKIE) {
98
- return CONFIG.API.SIGNATURE_COOKIE;
99
  }
100
- console.log("刷新认证信息");
101
  let retryCount = 0;
102
- while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
103
  let headers = await Utils.extractGrokHeaders();
104
  if (headers) {
105
- console.log("获取认证信息成功");
106
- CONFIG.API.SIGNATURE_COOKIE = { cookie: `x-anonuserid=${headers["x-anonuserid"]}; x-challenge=${headers["x-challenge"]}; x-signature=${headers["x-signature"]}` };
107
- return CONFIG.API.SIGNATURE_COOKIE;
108
  }
109
  retryCount++;
110
  if (retryCount >= CONFIG.RETRY.MAX_ATTEMPTS) {
111
  throw new Error(`获取认证信息失败!`);
112
  }
113
- await new Promise(resolve => setTimeout(resolve, CONFIG.RETRY.DELAY_BASE * retryCount));
114
- }
115
- }
116
- static async handleError(error, res) {
117
- // 如果是500错误且提供了原始请求函数,尝试重新获取签名并重试
118
- if (String(error).includes("status: 500")) {
119
- try {
120
- await Utils.get_signature();
121
- if (CONFIG.API.SIGNATURE_COOKIE) {
122
- return res.status(500).json({
123
- error: {
124
- message: `${retryError.message}已重新刷新认证信息,请重新对话`,
125
- type: 'server_error',
126
- param: null,
127
- code: error.code || null
128
- }
129
- });
130
- } else {
131
- return res.status(500).json({
132
- error: {
133
- message: "认证信息获取失败",
134
- type: 'server_error',
135
- param: null,
136
- code: null
137
- }
138
- });
139
- }
140
- } catch (retryError) {
141
- console.error('重试失败:', retryError);
142
- return res.status(500).json({
143
- error: {
144
- message: `${retryError.message},认证信息获取失败`,
145
- type: 'server_error',
146
- param: null,
147
- code: retryError.code || null
148
- }
149
- });
150
- }
151
  }
152
-
153
- // 其他错误直接返回
154
- res.status(500).json({
155
- error: {
156
- message: error.message,
157
- type: 'server_error',
158
- param: null,
159
- code: error.code || null
160
- }
161
- });
162
  }
163
  static async organizeSearchResults(searchResults) {
164
  // 确保传入的是有效的搜索结果对象
@@ -177,6 +265,11 @@ class Utils {
177
  });
178
  return formattedResults.join('\n\n');
179
  }
 
 
 
 
 
180
  }
181
 
182
 
@@ -228,7 +321,7 @@ class GrokApiClient {
228
  content: imageBuffer
229
  }
230
  };
231
- console.log("发送图片请求");
232
  // 发送请求
233
  const response = await fetch(url, {
234
  method: 'POST',
@@ -240,29 +333,26 @@ class GrokApiClient {
240
  });
241
 
242
  if (!response.ok) {
243
- console.error(`上传图片失败,状态码:${response.status},原因:${response.error}`);
244
  return '';
245
  }
246
 
247
  const result = await response.json();
248
- console.log('上传图片成功:', result);
249
  return result.fileMetadataId;
250
 
251
  } catch (error) {
252
- console.error('上传图片失败:', error);
253
  return '';
254
  }
255
  }
256
 
257
  async prepareChatRequest(request) {
258
-
259
- if (request.model === 'grok-latest-image' && !CONFIG.API.PICGO_KEY) {
260
  throw new Error(`该模型需要配置PICGO图床密钥!`);
261
  }
262
  var todoMessages = request.messages;
263
- if (request.model === 'grok-latest-image' || request.model === 'grok-latest-search') {
264
- todoMessages = Array.isArray(todoMessages) ? [todoMessages[todoMessages.length - 1]] : [todoMessages];;
265
- }
266
  let fileAttachments = [];
267
  let messages = '';
268
  let lastRole = null;
@@ -283,52 +373,43 @@ class GrokApiClient {
283
  for (const current of todoMessages) {
284
  const role = current.role === 'assistant' ? 'assistant' : 'user';
285
  let textContent = '';
286
- // 处理消息内容
287
  if (Array.isArray(current.content)) {
288
- // 处理数组内的所有内容
289
  for (const item of current.content) {
290
  if (item.type === 'image_url') {
291
- // 如果是图片且是最后一条消息,则处理图片
292
  if (current === todoMessages[todoMessages.length - 1]) {
293
  const processedImage = await processImageUrl(item);
294
  if (processedImage) fileAttachments.push(processedImage);
295
  }
296
- textContent += (textContent ? '\n' : '') + "[图片]";//图片占位符
297
  } else if (item.type === 'text') {
298
  textContent += (textContent ? '\n' : '') + item.text;
299
  }
300
  }
301
  } else if (typeof current.content === 'object' && current.content !== null) {
302
- // 处理单个对象内容
303
  if (current.content.type === 'image_url') {
304
- // 如果是图片且是最后一条消息,则处理图片
305
  if (current === todoMessages[todoMessages.length - 1]) {
306
  const processedImage = await processImageUrl(current.content);
307
  if (processedImage) fileAttachments.push(processedImage);
308
  }
309
- textContent += (textContent ? '\n' : '') + "[图片]";//图片占位符
310
  } else if (current.content.type === 'text') {
311
  textContent = current.content.text;
312
  }
313
  } else {
314
- // 处理普通文本内容
315
  textContent = this.processMessageContent(current.content);
316
  }
317
- // 添加文本内容��消息字符串
318
  if (textContent) {
319
  if (role === lastRole) {
320
- // 如果角色相同,合并消息内容
321
  lastContent += '\n' + textContent;
322
  messages = messages.substring(0, messages.lastIndexOf(`${role.toUpperCase()}: `)) +
323
  `${role.toUpperCase()}: ${lastContent}\n`;
324
  } else {
325
- // 如果角色不同,添加新的消息
326
  messages += `${role.toUpperCase()}: ${textContent}\n`;
327
  lastContent = textContent;
328
  lastRole = role;
329
  }
330
  } else if (current === todoMessages[todoMessages.length - 1] && fileAttachments.length > 0) {
331
- // 如果是最后一条消息且有图片附件,添加空消息占位
332
  messages += `${role.toUpperCase()}: [图片]\n`;
333
  }
334
  }
@@ -339,27 +420,35 @@ class GrokApiClient {
339
 
340
  messages = messages.trim();
341
 
342
- if (request.model === 'grok-latest-search') {
343
  search = true;
344
  }
345
  return {
346
- message: messages,
347
  modelName: this.modelId,
348
- disableSearch: false,
 
349
  imageAttachments: [],
 
 
350
  returnImageBytes: false,
351
  returnRawGrokInXaiRequest: false,
352
- fileAttachments: fileAttachments,
353
  enableImageStreaming: false,
354
  imageGenerationCount: 1,
 
355
  toolOverrides: {
356
- imageGen: request.model === 'grok-latest-image',
357
  webSearch: search,
358
  xSearch: search,
359
  xMediaSearch: search,
360
  trendsSearch: search,
361
  xPostAnalyze: search
362
- }
 
 
 
 
 
 
363
  };
364
  }
365
  }
@@ -400,247 +489,146 @@ class MessageProcessor {
400
  };
401
  }
402
  }
403
-
404
- // 中间件配置
405
- const app = express();
406
- app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
407
- app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
408
- app.use(cors({
409
- origin: '*',
410
- methods: ['GET', 'POST', 'OPTIONS'],
411
- allowedHeaders: ['Content-Type', 'Authorization']
412
- }));
413
- // API路由
414
- app.get('/hf/v1/models', (req, res) => {
415
- res.json({
416
- object: "list",
417
- data: Object.keys(CONFIG.MODELS).map((model, index) => ({
418
- id: model,
419
- object: "model",
420
- created: Math.floor(Date.now() / 1000),
421
- owned_by: "xai",
422
- }))
423
- });
424
- });
425
-
426
- app.post('/hf/v1/chat/completions', async (req, res) => {
427
- const authToken = req.headers.authorization?.replace('Bearer ', '');
428
- if (authToken !== CONFIG.API.API_KEY) {
429
- return res.status(401).json({ error: 'Unauthorized' });
430
- }
431
- const makeRequest = async () => {
432
- if (!CONFIG.API.SIGNATURE_COOKIE) {
433
- await Utils.get_signature();
434
- }
435
- const grokClient = new GrokApiClient(req.body.model);
436
- const requestPayload = await grokClient.prepareChatRequest(req.body);
437
- //创建新对话
438
- const newMessageReq = await fetch(`${CONFIG.API.BASE_URL}/api/rpc`, {
439
- method: 'POST',
440
- headers: {
441
- ...DEFAULT_HEADERS,
442
- ...CONFIG.API.SIGNATURE_COOKIE
443
- },
444
- body: JSON.stringify({
445
- rpc: "createConversation",
446
- req: {
447
- temporary: false
448
- }
449
- })
450
- });
451
- if (!newMessageReq.ok) {
452
- throw new Error(`上游服务请求失败! status: ${newMessageReq.status}`);
453
- }
454
-
455
- // 获取响应文本
456
- const responseText = await newMessageReq.json();
457
- const conversationId = responseText.conversationId;
458
- console.log("会话ID:conversationId", conversationId);
459
- if (!conversationId) {
460
- throw new Error(`创建会话失败! status: ${newMessageReq.status}`);
461
- }
462
- //发送对话
463
- const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
464
- method: 'POST',
465
- headers: {
466
- "accept": "text/event-stream",
467
- "baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
468
- "content-type": "text/plain;charset=UTF-8",
469
- "Connection": "keep-alive",
470
- ...CONFIG.API.SIGNATURE_COOKIE
471
- },
472
- body: JSON.stringify(requestPayload)
473
- });
474
-
475
- if (!response.ok) {
476
- throw new Error(`上游服务请求失败! status: ${response.status}`);
477
- }
478
- if (req.body.stream) {
479
- await handleStreamResponse(response, req.body.model, res);
480
- } else {
481
- await handleNormalResponse(response, req.body.model, res);
482
  }
 
483
  }
484
- try {
485
- await makeRequest();
486
- } catch (error) {
487
- CONFIG.API.SIGNATURE_COOKIE = null;
488
- await Utils.handleError(error, res);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  }
490
- });
491
-
492
- async function handleStreamResponse(response, model, res) {
493
- res.setHeader('Content-Type', 'text/event-stream');
494
- res.setHeader('Cache-Control', 'no-cache');
495
- res.setHeader('Connection', 'keep-alive');
496
 
 
497
  try {
498
  const stream = response.body;
499
  let buffer = '';
500
-
501
- stream.on('data', async (chunk) => {
502
- buffer += chunk.toString();
503
- const lines = buffer.split('\n');
504
- buffer = lines.pop() || '';
505
-
506
- for (const line of lines) {
507
- if (!line.trim()) continue;
508
- const trimmedLine = line.trim();
509
- if (trimmedLine.startsWith('data: ')) {
510
- const data = trimmedLine.substring(6);
511
- if(data === "[DONE]"){
512
- console.log("流结束");
513
- res.write('data: [DONE]\n\n');
514
- return res.end();
515
- };
516
- try {
517
- if(!data.trim())continue;
518
- const linejosn = JSON.parse(data);
519
- if (linejosn?.error?.name === "RateLimitError") {
520
- var responseData = MessageProcessor.createChatResponse(`${linejosn.error.name},请重新对话`, model, true);
521
- CONFIG.API.SIGNATURE_COOKIE = null;
522
- console.log("认证信息已删除");
523
- res.write(`data: ${JSON.stringify(responseData)}\n\n`);
524
- res.write('data: [DONE]\n\n');
525
- return res.end();
526
- }
527
- switch (model) {
528
- case 'grok-latest-image':
529
- if (linejosn.response === "modelResponse" && linejosn?.modelResponse?.generatedImageUrls) {
530
- const dataImage = await handleImageResponse(linejosn.modelResponse.generatedImageUrls);
531
- const responseData = MessageProcessor.createChatResponse(dataImage, model, true);
532
- res.write(`data: ${JSON.stringify(responseData)}\n\n`);
533
- }
534
- break;
535
- case 'grok-latest':
536
- if (linejosn.response === "token") {
537
- const token = linejosn.token;
538
- if (token && token.length > 0) {
539
- const responseData = MessageProcessor.createChatResponse(token, model, true);
540
- res.write(`data: ${JSON.stringify(responseData)}\n\n`);
541
  }
542
  }
543
- break;
544
- case 'grok-latest-search':
545
- if (linejosn.response === "token") {
546
- const token = linejosn.token;
547
- if (token && token.length > 0) {
548
- const responseData = MessageProcessor.createChatResponse(token, model, true);
549
- res.write(`data: ${JSON.stringify(responseData)}\n\n`);
550
  }
551
  }
552
- if (linejosn.response === "webSearchResults" && CONFIG.ISSHOW_SEARCH_RESULTS) {
553
- const searchResults = await Utils.organizeSearchResults(linejosn.webSearchResults);
554
- const responseData = MessageProcessor.createChatResponse(`<think>\r\n${searchResults}\r\n</think>\r\n`, model, true);
555
- res.write(`data: ${JSON.stringify(responseData)}\n\n`);
556
- }
557
- break;
558
  }
559
- } catch (error) {
560
- console.error('JSON解析错误:', error);
561
  }
562
  }
563
- }
564
- });
565
- stream.on('error', (error) => {
566
- console.error('流处理错误:', error);
567
- res.write('data: [DONE]\n\n');
568
- res.end();
569
- });
570
-
571
- } catch (error) {
572
- console.error('处理响应错误:', error);
573
- res.write('data: [DONE]\n\n');
574
- res.end();
575
- }
576
- }
577
-
578
- async function handleNormalResponse(response, model, res) {
579
- let fullResponse = '';
580
- let imageUrl = '';
581
 
582
- try {
583
- const responseText = await response.text();
584
- const lines = responseText.split('\n');
585
-
586
- for (const line of lines) {
587
- if (!line.trim()) continue;
588
- const data = line.slice(6);
589
- if (data === '[DONE]') continue;
590
- let linejosn = JSON.parse(data);
591
- if (linejosn?.error?.name === "RateLimitError") {
592
- fullResponse = `${linejosn.error.name},请重新对话`;
593
- CONFIG.API.SIGNATURE_COOKIE = null;
594
- return res.json(MessageProcessor.createChatResponse(fullResponse, model));
595
- }
596
- switch (model) {
597
- case 'grok-latest-image':
598
- if (linejosn.response === "modelResponse" && linejosn?.modelResponse?.generatedImageUrls) {
599
- imageUrl = linejosn.modelResponse.generatedImageUrls;
600
- }
601
- break;
602
- case 'grok-latest':
603
- if (linejosn.response === "token") {
604
- const token = linejosn.token;
605
- if (token && token.length > 0) {
606
- fullResponse += token;
607
- }
608
- }
609
- break;
610
- case 'grok-latest-search':
611
- if (linejosn.response === "token") {
612
- const token = linejosn.token;
613
- if (token && token.length > 0) {
614
- fullResponse += token;
615
- }
616
- }
617
- if (linejosn.response === "webSearchResults" && CONFIG.ISSHOW_SEARCH_RESULTS) {
618
- fullResponse += `\r\n<think>${await Utils.organizeSearchResults(linejosn.webSearchResults)}</think>\r\n`;
619
  }
620
- break;
621
- }
622
- }
623
- if (imageUrl) {
624
- const dataImage = await handleImageResponse(imageUrl);
625
- const responseData = MessageProcessor.createChatResponse(dataImage, model);
626
- res.json(responseData);
627
- } else {
628
- const responseData = MessageProcessor.createChatResponse(fullResponse, model);
629
- res.json(responseData);
630
- }
 
631
  } catch (error) {
632
- Utils.handleError(error, res);
 
 
 
633
  }
 
634
  }
 
635
  async function handleImageResponse(imageUrl) {
636
- //对服务器发送图片请求
637
- const MAX_RETRIES = 3;
638
  let retryCount = 0;
639
  let imageBase64Response;
640
-
641
  while (retryCount < MAX_RETRIES) {
642
  try {
643
- //发送图片请求获取图片
644
  imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
645
  method: 'GET',
646
  headers: {
@@ -649,16 +637,11 @@ async function handleImageResponse(imageUrl) {
649
  }
650
  });
651
 
652
- if (imageBase64Response.ok) {
653
- break; // 如果请求成功,跳出重试循环
654
- }
655
-
656
  retryCount++;
657
  if (retryCount === MAX_RETRIES) {
658
  throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
659
  }
660
-
661
- // 等待一段时间后重试(可以使用指数退避)
662
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
663
 
664
  } catch (error) {
@@ -666,7 +649,6 @@ async function handleImageResponse(imageUrl) {
666
  if (retryCount === MAX_RETRIES) {
667
  throw error;
668
  }
669
- // 等待一段时间后重试
670
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
671
  }
672
  }
@@ -692,17 +674,163 @@ async function handleImageResponse(imageUrl) {
692
  if (!responseURL.ok) {
693
  return "生图失败,请查看图床密钥是否设置正确"
694
  } else {
 
695
  const result = await responseURL.json();
696
  return `![image](${result.image.url})`
697
  }
698
  }
699
 
700
- // 404处理
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
701
  app.use((req, res) => {
702
  res.status(200).send('api运行正常');
703
  });
704
 
705
- // 启动服务器
706
  app.listen(CONFIG.SERVER.PORT, () => {
707
- console.log(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`);
708
  });
 
3
  import FormData from 'form-data';
4
  import dotenv from 'dotenv';
5
  import cors from 'cors';
6
+ import puppeteer from 'puppeteer-extra'
7
+ import StealthPlugin from 'puppeteer-extra-plugin-stealth'
8
  import { v4 as uuidv4 } from 'uuid';
9
+ import Logger from './logger.js';
10
 
11
  dotenv.config();
12
+
13
  // 配置常量
14
  const CONFIG = {
15
  MODELS: {
16
+ 'grok-2': 'grok-latest',
17
+ 'grok-2-imageGen': 'grok-latest',
18
+ 'grok-2-search': 'grok-latest',
19
+ "grok-3": "grok-3",
20
+ "grok-3-imageGen": "grok-3",
21
+ "grok-3-deepsearch": "grok-3",
22
+ "grok-3-reasoning": "grok-3"
23
  },
24
  API: {
25
  BASE_URL: "https://grok.com",
26
  API_KEY: process.env.API_KEY || "sk-123456",
 
27
  SIGNATURE_COOKIE: null,
28
+ TEMP_COOKIE: null,
29
  PICGO_KEY: process.env.PICGO_KEY || null //想要生图的话需要填入这个PICGO图床的key
30
  },
31
  SERVER: {
 
33
  BODY_LIMIT: '5mb'
34
  },
35
  RETRY: {
36
+ MAX_ATTEMPTS: 2//重试次数
 
37
  },
38
+ IS_THINKING: false,
39
+ IS_IMG_GEN: false,
40
+ IS_IMG_GEN2: false,
41
+ SSO_INDEX: 0,//sso的索引
42
  ISSHOW_SEARCH_RESULTS: process.env.ISSHOW_SEARCH_RESULTS === 'true',//是否显示搜索结果,默认关闭
43
  CHROME_PATH: process.env.CHROME_PATH || "/usr/bin/chromium"//chrome路径
44
  };
45
+ puppeteer.use(StealthPlugin())
46
  // 请求头配置
47
  const DEFAULT_HEADERS = {
48
  'accept': '*/*',
 
62
  'baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
63
  };
64
 
65
+
66
+ async function initialization() {
67
+ const ssoArray = process.env.SSO.split(',');
68
+ const ssorwArray = process.env.SSO_RW.split(',');
69
+ ssoArray.forEach((sso, index) => {
70
+ tokenManager.addToken(`sso-rw=${ssorwArray[index]};sso=${sso}`);
71
+ });
72
+ console.log(JSON.stringify(tokenManager.getActiveTokens(), null, 2));
73
+ await Utils.get_signature()
74
+ Logger.info("初始化完成", 'Server');
75
+ }
76
+
77
+
78
+ class AuthTokenManager {
79
+ constructor() {
80
+ this.activeTokens = [];
81
+ this.expiredTokens = new Map();
82
+ this.tokenModelFrequency = new Map();
83
+ this.modelRateLimit = {
84
+ "grok-3": { RequestFrequency: 20 },
85
+ "grok-3-deepsearch": { RequestFrequency: 5 },
86
+ "grok-3-reasoning": { RequestFrequency: 5 }
87
+ };
88
+ }
89
+
90
+ addToken(token) {
91
+ if (!this.activeTokens.includes(token)) {
92
+ this.activeTokens.push(token);
93
+ this.tokenModelFrequency.set(token, {
94
+ "grok-3": 0,
95
+ "grok-3-deepsearch": 0,
96
+ "grok-3-reasoning": 0
97
+ });
98
+ }
99
+ }
100
+
101
+ getTokenByIndex(index, model) {
102
+ if (!this.modelRateLimit[model]) return;
103
+ if(this.activeTokens.length === 0){
104
+ return null;
105
+ }
106
+ const token = this.activeTokens[index];
107
+ this.recordModelRequest(token, model);
108
+ return token;
109
+ }
110
+
111
+ recordModelRequest(token, model) {
112
+ if (!this.modelRateLimit[model]) return;
113
+ const tokenFrequency = this.tokenModelFrequency.get(token);
114
+ if (tokenFrequency && tokenFrequency[model] !== undefined) {
115
+ tokenFrequency[model]++;
116
+ }
117
+ this.checkAndRemoveTokenIfLimitReached(token);
118
+ }
119
+ setModelLimit(index, model) {
120
+ if (!this.modelRateLimit[model]) return;
121
+ const tokenFrequency = this.tokenModelFrequency.get(this.activeTokens[index]);
122
+ tokenFrequency[model] = 9999;
123
+ }
124
+ isTokenModelLimitReached(index, model) {
125
+ if (!this.modelRateLimit[model]) return;
126
+ const token = this.activeTokens[index];
127
+ const tokenFrequency = this.tokenModelFrequency.get(token);
128
+
129
+ if (!tokenFrequency) {
130
+ return false;
131
+ }
132
+ return tokenFrequency[model] >= this.modelRateLimit[model].RequestFrequency;
133
+ }
134
+ checkAndRemoveTokenIfLimitReached(token) {
135
+ const tokenFrequency = this.tokenModelFrequency.get(token);
136
+ if (!tokenFrequency) return;
137
+
138
+ const isLimitReached = Object.keys(tokenFrequency).every(model =>
139
+ tokenFrequency[model] >= this.modelRateLimit[model].RequestFrequency
140
+ );
141
+
142
+ if (isLimitReached) {
143
+ const tokenIndex = this.activeTokens.indexOf(token);
144
+ if (tokenIndex !== -1) {
145
+ this.removeTokenByIndex(tokenIndex);
146
+ }
147
+ }
148
+ }
149
+
150
+ removeTokenByIndex(index) {
151
+ if (!this.isRecoveryProcess) {
152
+ this.startTokenRecoveryProcess();
153
+ }
154
+ const token = this.activeTokens[index];
155
+ this.expiredTokens.set(token, Date.now());
156
+ this.activeTokens.splice(index, 1);
157
+ this.tokenModelFrequency.delete(token);
158
+ Logger.info(`令牌${token}已达到上限,已移除`, 'TokenManager');
159
+ }
160
+
161
+ startTokenRecoveryProcess() {
162
+ setInterval(() => {
163
+ const now = Date.now();
164
+ for (const [token, expiredTime] of this.expiredTokens.entries()) {
165
+ if (now - expiredTime >= 2 * 60 * 60 * 1000) {
166
+ this.tokenModelUsage.set(token, {
167
+ "grok-3": 0,
168
+ "grok-3-imageGen": 0,
169
+ "grok-3-deepsearch": 0,
170
+ "grok-3-reasoning": 0
171
+ });
172
+ this.activeTokens.push(token);
173
+ this.expiredTokens.delete(token);
174
+ Logger.info(`令牌${token}已恢复,已添加到可用令牌列表`, 'TokenManager');
175
+ }
176
+ }
177
+ }, 2 * 60 * 60 * 1000);
178
+ }
179
+
180
+ getTokenCount() {
181
+ return this.activeTokens.length || 0;
182
+ }
183
+
184
+ getActiveTokens() {
185
+ return [...this.activeTokens];
186
+ }
187
+ }
188
+
189
  class Utils {
190
  static async extractGrokHeaders() {
191
+ Logger.info("开始提取头信息", 'Server');
192
  try {
193
  // 启动浏览器
194
  const browser = await puppeteer.launch({
 
203
  });
204
 
205
  const page = await browser.newPage();
206
+ await page.goto('https://grok.com/', { waitUntil: 'domcontentloaded' });
207
+ await page.evaluate(() => {
208
+ return new Promise(resolve => setTimeout(resolve, 5000))
209
+ })
210
  // 获取所有 Cookies
211
  const cookies = await page.cookies();
212
  const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
 
221
  // 关闭浏览器
222
  await browser.close();
223
  // 打印并返回提取的头信息
224
+ Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
225
  return extractedHeaders;
226
 
227
  } catch (error) {
228
+ Logger.error('获取头信息出错:', error, 'Server');
229
  return null;
230
  }
231
  }
232
  static async get_signature() {
233
+ if (CONFIG.API.TEMP_COOKIE) {
234
+ return CONFIG.API.TEMP_COOKIE;
235
  }
236
+ Logger.info("刷新认证信息", 'Server');
237
  let retryCount = 0;
238
+ while (!CONFIG.API.TEMP_COOKIE || retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
239
  let headers = await Utils.extractGrokHeaders();
240
  if (headers) {
241
+ Logger.info("获取认证信息成功", 'Server');
242
+ CONFIG.API.TEMP_COOKIE = { cookie: `x-anonuserid=${headers["x-anonuserid"]}; x-challenge=${headers["x-challenge"]}; x-signature=${headers["x-signature"]}` };
243
+ return;
244
  }
245
  retryCount++;
246
  if (retryCount >= CONFIG.RETRY.MAX_ATTEMPTS) {
247
  throw new Error(`获取认证信息失败!`);
248
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  }
 
 
 
 
 
 
 
 
 
 
250
  }
251
  static async organizeSearchResults(searchResults) {
252
  // 确保传入的是有效的搜索结果对象
 
265
  });
266
  return formattedResults.join('\n\n');
267
  }
268
+ static createAuthHeaders(model) {
269
+ return {
270
+ 'cookie': `${tokenManager.getTokenByIndex(CONFIG.SSO_INDEX, model)}`
271
+ };
272
+ }
273
  }
274
 
275
 
 
321
  content: imageBuffer
322
  }
323
  };
324
+ Logger.info("发送图片请求", 'Server');
325
  // 发送请求
326
  const response = await fetch(url, {
327
  method: 'POST',
 
333
  });
334
 
335
  if (!response.ok) {
336
+ Logger.error(`上传图片失败,状态码:${response.status},原因:${response.error}`, 'Server');
337
  return '';
338
  }
339
 
340
  const result = await response.json();
341
+ Logger.info('上传图片成功:', result, 'Server');
342
  return result.fileMetadataId;
343
 
344
  } catch (error) {
345
+ Logger.error(error, 'Server');
346
  return '';
347
  }
348
  }
349
 
350
  async prepareChatRequest(request) {
351
+ if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY) {
 
352
  throw new Error(`该模型需要配置PICGO图床密钥!`);
353
  }
354
  var todoMessages = request.messages;
355
+
 
 
356
  let fileAttachments = [];
357
  let messages = '';
358
  let lastRole = null;
 
373
  for (const current of todoMessages) {
374
  const role = current.role === 'assistant' ? 'assistant' : 'user';
375
  let textContent = '';
376
+
377
  if (Array.isArray(current.content)) {
 
378
  for (const item of current.content) {
379
  if (item.type === 'image_url') {
 
380
  if (current === todoMessages[todoMessages.length - 1]) {
381
  const processedImage = await processImageUrl(item);
382
  if (processedImage) fileAttachments.push(processedImage);
383
  }
384
+ textContent += (textContent ? '\n' : '') + "[图片]";
385
  } else if (item.type === 'text') {
386
  textContent += (textContent ? '\n' : '') + item.text;
387
  }
388
  }
389
  } else if (typeof current.content === 'object' && current.content !== null) {
 
390
  if (current.content.type === 'image_url') {
 
391
  if (current === todoMessages[todoMessages.length - 1]) {
392
  const processedImage = await processImageUrl(current.content);
393
  if (processedImage) fileAttachments.push(processedImage);
394
  }
395
+ textContent += (textContent ? '\n' : '') + "[图片]";
396
  } else if (current.content.type === 'text') {
397
  textContent = current.content.text;
398
  }
399
  } else {
 
400
  textContent = this.processMessageContent(current.content);
401
  }
 
402
  if (textContent) {
403
  if (role === lastRole) {
 
404
  lastContent += '\n' + textContent;
405
  messages = messages.substring(0, messages.lastIndexOf(`${role.toUpperCase()}: `)) +
406
  `${role.toUpperCase()}: ${lastContent}\n`;
407
  } else {
 
408
  messages += `${role.toUpperCase()}: ${textContent}\n`;
409
  lastContent = textContent;
410
  lastRole = role;
411
  }
412
  } else if (current === todoMessages[todoMessages.length - 1] && fileAttachments.length > 0) {
 
413
  messages += `${role.toUpperCase()}: [图片]\n`;
414
  }
415
  }
 
420
 
421
  messages = messages.trim();
422
 
423
+ if (request.model === 'grok-2-search') {
424
  search = true;
425
  }
426
  return {
 
427
  modelName: this.modelId,
428
+ message: messages,
429
+ fileAttachments: fileAttachments,
430
  imageAttachments: [],
431
+ disableSearch: false,
432
+ enableImageGeneration: true,
433
  returnImageBytes: false,
434
  returnRawGrokInXaiRequest: false,
 
435
  enableImageStreaming: false,
436
  imageGenerationCount: 1,
437
+ forceConcise: false,
438
  toolOverrides: {
439
+ imageGen: request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen',
440
  webSearch: search,
441
  xSearch: search,
442
  xMediaSearch: search,
443
  trendsSearch: search,
444
  xPostAnalyze: search
445
+ },
446
+ enableSideBySide: true,
447
+ isPreset: false,
448
+ sendFinalMetadata: true,
449
+ customInstructions: "",
450
+ deepsearchPreset: request.model === 'grok-3-deepsearch' ? "default" : "",
451
+ isReasoning: request.model === 'grok-3-reasoning'
452
  };
453
  }
454
  }
 
489
  };
490
  }
491
  }
492
+ async function processModelResponse(linejosn, model) {
493
+ let result = { token: '', imageUrl: null }
494
+ if (CONFIG.IS_IMG_GEN) {
495
+ if (linejosn?.cachedImageGenerationResponse && !CONFIG.IS_IMG_GEN2) {
496
+ result.imageUrl = linejosn.cachedImageGenerationResponse.imageUrl;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  }
498
+ return result;
499
  }
500
+ var token = linejosn?.token
501
+ if (!token) return result;
502
+ //非生图模型的处理
503
+ switch (model) {
504
+ case 'grok-2':
505
+ result.token = token;
506
+ return result;
507
+ case 'grok-2-search':
508
+ if (linejosn?.webSearchResults && CONFIG.ISSHOW_SEARCH_RESULTS) {
509
+ result.token = `\r\n<think>${await Utils.organizeSearchResults(linejosn.webSearchResults)}</think>\r\n`;
510
+ } else {
511
+ result.token = token;
512
+ }
513
+ return result;
514
+ case 'grok-3':
515
+ result.token = token;
516
+ return result;
517
+ case 'grok-3-deepsearch':
518
+ if (linejosn.messageTag === "final") {
519
+ result.token = token;
520
+ }
521
+ return result;
522
+ case 'grok-3-reasoning':
523
+ if (linejosn?.isThinking && !CONFIG.IS_THINKING) {
524
+ result.token = "<think>" + token;
525
+ CONFIG.IS_THINKING = true;
526
+ } else if (CONFIG.IS_THINKING && !linejosn.isThinking) {
527
+ result.token = "</think>" + token;
528
+ CONFIG.IS_THINKING = false;
529
+ } else {
530
+ result.token = token;
531
+ }
532
+ return result;
533
  }
534
+ }
 
 
 
 
 
535
 
536
+ async function handleResponse(response, model, res, isStream) {
537
  try {
538
  const stream = response.body;
539
  let buffer = '';
540
+ let fullResponse = '';
541
+ const dataPromises = [];
542
+
543
+ return new Promise((resolve, reject) => {
544
+ stream.on('data', async (chunk) => {
545
+ buffer += chunk.toString();
546
+ const lines = buffer.split('\n');
547
+ buffer = lines.pop() || '';
548
+
549
+ for (const line of lines) {
550
+ if (!line.trim()) continue;
551
+ const trimmedLine = line.trim();
552
+ if (trimmedLine.startsWith('data: ')) {
553
+ const data = trimmedLine.substring(6);
554
+ try {
555
+ if (!data.trim()) continue;
556
+ if(data === "[DONE]")continue;
557
+ const linejosn = JSON.parse(data);
558
+ if (linejosn?.error) {
559
+ reject(new Error(linejosn));
560
+ return;
561
+ }
562
+ if (linejosn?.doImgGen || linejosn?.imageAttachmentInfo) {
563
+ CONFIG.IS_IMG_GEN = true;
564
+ }
565
+ const processPromise = (async () => {
566
+ const result = await processModelResponse(linejosn, model);
567
+ if (result.token) {
568
+ if (isStream) {
569
+ res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(result.token, model, true))}\n\n`);
570
+ } else {
571
+ fullResponse += result.token;
 
 
 
 
 
 
 
 
 
572
  }
573
  }
574
+ if (result.imageUrl) {
575
+ CONFIG.IS_IMG_GEN2 = true;
576
+ const dataImage = await handleImageResponse(result.imageUrl);
577
+ if (isStream) {
578
+ res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(dataImage, model, true))}\n\n`);
579
+ } else {
580
+ res.json(MessageProcessor.createChatResponse(dataImage, model));
581
  }
582
  }
583
+ })();
584
+ dataPromises.push(processPromise);
585
+ } catch (error) {
586
+ console.log(error);
587
+ continue;
 
588
  }
 
 
589
  }
590
  }
591
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
592
 
593
+ stream.on('end', async () => {
594
+ try {
595
+ await Promise.all(dataPromises);
596
+ if (isStream) {
597
+ res.write('data: [DONE]\n\n');
598
+ res.end();
599
+ } else {
600
+ if (!CONFIG.IS_IMG_GEN2) {
601
+ res.json(MessageProcessor.createChatResponse(fullResponse, model));
602
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603
  }
604
+ CONFIG.IS_IMG_GEN = false;
605
+ CONFIG.IS_IMG_GEN2 = false;
606
+ resolve();
607
+ } catch (error) {
608
+ reject(error);
609
+ }
610
+ });
611
+ stream.on('error', (error) => {
612
+ Logger.error(error, 'Server');
613
+ reject(error);
614
+ });
615
+ });
616
  } catch (error) {
617
+ Logger.error(error, 'Server');
618
+ CONFIG.IS_IMG_GEN = false;
619
+ CONFIG.IS_IMG_GEN2 = false;
620
+ throw error;
621
  }
622
+
623
  }
624
+
625
  async function handleImageResponse(imageUrl) {
626
+ const MAX_RETRIES = 2;
 
627
  let retryCount = 0;
628
  let imageBase64Response;
629
+
630
  while (retryCount < MAX_RETRIES) {
631
  try {
 
632
  imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
633
  method: 'GET',
634
  headers: {
 
637
  }
638
  });
639
 
640
+ if (imageBase64Response.ok) break;
 
 
 
641
  retryCount++;
642
  if (retryCount === MAX_RETRIES) {
643
  throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
644
  }
 
 
645
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
646
 
647
  } catch (error) {
 
649
  if (retryCount === MAX_RETRIES) {
650
  throw error;
651
  }
 
652
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
653
  }
654
  }
 
674
  if (!responseURL.ok) {
675
  return "生图失败,请查看图床密钥是否设置正确"
676
  } else {
677
+ console.log("生图成功");
678
  const result = await responseURL.json();
679
  return `![image](${result.image.url})`
680
  }
681
  }
682
 
683
+ const tokenManager = new AuthTokenManager();
684
+ await initialization();
685
+
686
+ // 中间件配置
687
+ const app = express();
688
+ app.use(Logger.requestLogger);
689
+ app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
690
+ app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
691
+ app.use(cors({
692
+ origin: '*',
693
+ methods: ['GET', 'POST', 'OPTIONS'],
694
+ allowedHeaders: ['Content-Type', 'Authorization']
695
+ }));
696
+
697
+ app.get('/hf/v1/models', (req, res) => {
698
+ res.json({
699
+ object: "list",
700
+ data: Object.keys(CONFIG.MODELS).map((model, index) => ({
701
+ id: model,
702
+ object: "model",
703
+ created: Math.floor(Date.now() / 1000),
704
+ owned_by: "grok",
705
+ }))
706
+ });
707
+ });
708
+
709
+
710
+ app.post('/hf/v1/chat/completions', async (req, res) => {
711
+ try {
712
+ const authToken = req.headers.authorization?.replace('Bearer ', '');
713
+ if (authToken !== CONFIG.API.API_KEY) {
714
+ return res.status(401).json({ error: 'Unauthorized' });
715
+ }
716
+ let isTempCookie = req.body.model.includes("grok-2");
717
+ let retryCount = 0;
718
+
719
+ while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
720
+ retryCount++;
721
+ const grokClient = new GrokApiClient(req.body.model);
722
+ const requestPayload = await grokClient.prepareChatRequest(req.body);
723
+
724
+ if (!CONFIG.API.TEMP_COOKIE) {
725
+ await Utils.get_signature();
726
+ }
727
+
728
+ if (isTempCookie) {
729
+ CONFIG.API.SIGNATURE_COOKIE = CONFIG.API.TEMP_COOKIE;
730
+ Logger.info(`已切换为临时令牌`, 'Server');
731
+ } else {
732
+ CONFIG.API.SIGNATURE_COOKIE = Utils.createAuthHeaders(req.body.model);
733
+ }
734
+ Logger.info(`当前令牌索引: ${CONFIG.SSO_INDEX}`, 'Server');
735
+ const newMessageReq = await fetch(`${CONFIG.API.BASE_URL}/api/rpc`, {
736
+ method: 'POST',
737
+ headers: {
738
+ ...DEFAULT_HEADERS,
739
+ ...CONFIG.API.SIGNATURE_COOKIE
740
+ },
741
+ body: JSON.stringify({
742
+ rpc: "createConversation",
743
+ req: {
744
+ temporary: false
745
+ }
746
+ })
747
+ });
748
+
749
+ const responseText = await newMessageReq.json();
750
+ const conversationId = responseText.conversationId;
751
+
752
+ const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
753
+ method: 'POST',
754
+ headers: {
755
+ "accept": "text/event-stream",
756
+ "baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
757
+ "content-type": "text/plain;charset=UTF-8",
758
+ "Connection": "keep-alive",
759
+ ...CONFIG.API.SIGNATURE_COOKIE
760
+ },
761
+ body: JSON.stringify(requestPayload)
762
+ });
763
+
764
+ if (response.ok) {
765
+ Logger.info(`请求成功`, 'Server');
766
+ CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
767
+ Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
768
+ try {
769
+ await handleResponse(response, req.body.model, res, req.body.stream);
770
+ return;
771
+ } catch (error) {
772
+ if(isTempCookie){
773
+ await Utils.get_signature();
774
+ }else{
775
+ tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
776
+ for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
777
+ CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
778
+ if (!tokenManager.isTokenModelLimitReached(CONFIG.SSO_INDEX, req.body.model)) {
779
+ break;
780
+ } else if (i >= tokenManager.getTokenCount()) {
781
+ throw new Error(`${req.body.model} 次数已达上限,请切换其他模型或者重新对话`);
782
+ }
783
+ }
784
+ }
785
+ }
786
+ } else {
787
+ if (response.status === 429) {
788
+ if (isTempCookie) {
789
+ await Utils.get_signature();
790
+ } else {
791
+ tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
792
+ for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
793
+ CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
794
+ if (!tokenManager.isTokenModelLimitReached(CONFIG.SSO_INDEX, req.body.model)) {
795
+ break;
796
+ } else if (i >= tokenManager.getTokenCount()) {
797
+ throw new Error(`${req.body.model} 次数已达上限,请切换其他模型或者重新对话`);
798
+ }
799
+ }
800
+ }
801
+ } else {
802
+ // 非429错误直接抛出
803
+ if (isTempCookie) {
804
+ await Utils.get_signature();
805
+ } else {
806
+ Logger.error(`令牌异常错误状态!status: ${response.status}, 已移除当前令牌${CONFIG.SSO_INDEX.cookie}`, 'Server');
807
+ tokenManager.removeTokenByIndex(CONFIG.SSO_INDEX);
808
+ Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
809
+ CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
810
+ }
811
+ }
812
+ }
813
+ }
814
+ throw new Error('当前模型所有令牌都已耗尽');
815
+ } catch (error) {
816
+ Logger.error(error, 'ChatAPI');
817
+ res.status(500).json({
818
+ error: {
819
+ message: error.message,
820
+ type: 'server_error',
821
+ param: null,
822
+ code: error.code || null
823
+ }
824
+ });
825
+ }
826
+ });
827
+
828
+
829
  app.use((req, res) => {
830
  res.status(200).send('api运行正常');
831
  });
832
 
833
+
834
  app.listen(CONFIG.SERVER.PORT, () => {
835
+ Logger.info(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`, 'Server');
836
  });