One1ove commited on
Commit
3455847
·
verified ·
1 Parent(s): ad23443

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +148 -78
index.js CHANGED
@@ -23,11 +23,13 @@ const CONFIG = {
23
  "grok-3-reasoning": "grok-3"
24
  },
25
  API: {
 
26
  BASE_URL: "https://grok.com",
27
  API_KEY: process.env.API_KEY || "sk-123456",
28
  SIGNATURE_COOKIE: null,
29
  TEMP_COOKIE: null,
30
- PICGO_KEY: process.env.PICGO_KEY || null //想要流式生图的话需要填入这个PICGO图床的key
 
31
  },
32
  SERVER: {
33
  PORT: process.env.PORT || 3000,
@@ -36,7 +38,7 @@ const CONFIG = {
36
  RETRY: {
37
  MAX_ATTEMPTS: 2//重试次数
38
  },
39
- SHOW_THINKING:process.env.SHOW_THINKING === 'true',
40
  IS_THINKING: false,
41
  IS_IMG_GEN: false,
42
  IS_IMG_GEN2: false,
@@ -66,10 +68,13 @@ const DEFAULT_HEADERS = {
66
 
67
 
68
  async function initialization() {
 
 
 
 
69
  const ssoArray = process.env.SSO.split(',');
70
- const ssorwArray = process.env.SSO_RW.split(',');
71
- ssoArray.forEach((sso, index) => {
72
- tokenManager.addToken(`sso-rw=${ssorwArray[index]};sso=${sso}`);
73
  });
74
  console.log(JSON.stringify(tokenManager.getActiveTokens(), null, 2));
75
  await Utils.get_signature()
@@ -99,9 +104,17 @@ class AuthTokenManager {
99
  });
100
  }
101
  }
 
 
 
 
 
 
 
 
102
 
103
  getTokenByIndex(index, model) {
104
- if(this.activeTokens.length === 0){
105
  return null;
106
  }
107
  const token = this.activeTokens[index];
@@ -110,7 +123,7 @@ class AuthTokenManager {
110
  }
111
 
112
  recordModelRequest(token, model) {
113
- if(model === 'grok-3-search' || model === 'grok-3-imageGen'){
114
  model = 'grok-3';
115
  }
116
 
@@ -122,7 +135,7 @@ class AuthTokenManager {
122
  this.checkAndRemoveTokenIfLimitReached(token);
123
  }
124
  setModelLimit(index, model) {
125
- if(model === 'grok-3-search' || model === 'grok-3-imageGen'){
126
  model = 'grok-3';
127
  }
128
  if (!this.modelRateLimit[model]) return;
@@ -130,7 +143,7 @@ class AuthTokenManager {
130
  tokenFrequency[model] = 9999;
131
  }
132
  isTokenModelLimitReached(index, model) {
133
- if(model === 'grok-3-search' || model === 'grok-3-imageGen'){
134
  model = 'grok-3';
135
  }
136
  if (!this.modelRateLimit[model]) return;
@@ -170,6 +183,7 @@ class AuthTokenManager {
170
  }
171
 
172
  startTokenRecoveryProcess() {
 
173
  setInterval(() => {
174
  const now = Date.now();
175
  for (const [token, expiredTime] of this.expiredTokens.entries()) {
@@ -200,7 +214,6 @@ class Utils {
200
  static async extractGrokHeaders() {
201
  Logger.info("开始提取头信息", 'Server');
202
  try {
203
- // 启动浏览器
204
  const browser = await puppeteer.launch({
205
  headless: true,
206
  args: [
@@ -221,16 +234,12 @@ class Utils {
221
  const cookies = await page.cookies();
222
  const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
223
  const extractedHeaders = {};
224
- // 遍历 Cookies
225
  for (const cookie of cookies) {
226
- // 检查是否为目标头信息
227
  if (targetHeaders.includes(cookie.name.toLowerCase())) {
228
  extractedHeaders[cookie.name.toLowerCase()] = cookie.value;
229
  }
230
  }
231
- // 关闭浏览器
232
  await browser.close();
233
- // 打印并返回提取的头信息
234
  Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
235
  return extractedHeaders;
236
 
@@ -358,10 +367,10 @@ class GrokApiClient {
358
  }
359
 
360
  async prepareChatRequest(request) {
361
- if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && request.stream) {
362
- throw new Error(`该模型流式输出需要配置PICGO图床密钥!`);
363
  }
364
-
365
  // 处理画图模型的消息限制
366
  let todoMessages = request.messages;
367
  if (request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') {
@@ -369,22 +378,22 @@ class GrokApiClient {
369
  if (lastMessage.role !== 'user') {
370
  throw new Error('画图模型的最后一条消息必须是用户消息!');
371
  }
372
- todoMessages = [lastMessage];
373
  }
374
-
375
  const fileAttachments = [];
376
  let messages = '';
377
  let lastRole = null;
378
  let lastContent = '';
379
  const search = request.model === 'grok-2-search' || request.model === 'grok-3-search';
380
-
381
  // 移除<think>标签及其内容和base64图片
382
  const removeThinkTags = (text) => {
383
  text = text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
384
  text = text.replace(/!\[image\]\(data:.*?base64,.*?\)/g, '[图片]');
385
  return text;
386
  };
387
-
388
  const processImageUrl = async (content) => {
389
  if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
390
  const imageResponse = await this.uploadBase64Image(
@@ -395,7 +404,7 @@ class GrokApiClient {
395
  }
396
  return null;
397
  };
398
-
399
  const processContent = async (content) => {
400
  if (Array.isArray(content)) {
401
  let textContent = '';
@@ -416,11 +425,11 @@ class GrokApiClient {
416
  }
417
  return removeThinkTags(this.processMessageContent(content));
418
  };
419
-
420
  for (const current of todoMessages) {
421
  const role = current.role === 'assistant' ? 'assistant' : 'user';
422
  const isLastMessage = current === todoMessages[todoMessages.length - 1];
423
-
424
  // 处理图片附件
425
  if (isLastMessage && current.content) {
426
  if (Array.isArray(current.content)) {
@@ -435,10 +444,10 @@ class GrokApiClient {
435
  if (processedImage) fileAttachments.push(processedImage);
436
  }
437
  }
438
-
439
  // 处理文本内容
440
  const textContent = await processContent(current.content);
441
-
442
  if (textContent || (isLastMessage && fileAttachments.length > 0)) {
443
  if (role === lastRole && textContent) {
444
  lastContent += '\n' + textContent;
@@ -451,7 +460,7 @@ class GrokApiClient {
451
  }
452
  }
453
  }
454
-
455
  return {
456
  modelName: this.modelId,
457
  message: messages.trim(),
@@ -519,7 +528,7 @@ class MessageProcessor {
519
  }
520
  }
521
  async function processModelResponse(linejosn, model) {
522
- let result = { token: '', imageUrl: null }
523
  if (CONFIG.IS_IMG_GEN) {
524
  if (linejosn?.cachedImageGenerationResponse && !CONFIG.IS_IMG_GEN2) {
525
  result.imageUrl = linejosn.cachedImageGenerationResponse.imageUrl;
@@ -549,12 +558,12 @@ async function processModelResponse(linejosn, model) {
549
  }
550
  return result;
551
  case 'grok-3-reasoning':
552
- if(linejosn?.isThinking && !CONFIG.SHOW_THINKING)return result;
553
 
554
  if (linejosn?.isThinking && !CONFIG.IS_THINKING) {
555
  result.token = "<think>" + linejosn?.token;
556
  CONFIG.IS_THINKING = true;
557
- } else if (CONFIG.IS_THINKING && !linejosn.isThinking) {
558
  result.token = "</think>" + linejosn?.token;
559
  CONFIG.IS_THINKING = false;
560
  } else {
@@ -562,6 +571,7 @@ async function processModelResponse(linejosn, model) {
562
  }
563
  return result;
564
  }
 
565
  }
566
 
567
  async function handleResponse(response, model, res, isStream) {
@@ -570,8 +580,14 @@ async function handleResponse(response, model, res, isStream) {
570
  let buffer = '';
571
  let fullResponse = '';
572
  const dataPromises = [];
573
-
574
- return new Promise((resolve, reject) => {
 
 
 
 
 
 
575
  stream.on('data', async (chunk) => {
576
  buffer += chunk.toString();
577
  const lines = buffer.split('\n');
@@ -584,12 +600,15 @@ async function handleResponse(response, model, res, isStream) {
584
  const data = trimmedLine.substring(6);
585
  try {
586
  if (!data.trim()) continue;
587
- if(data === "[DONE]") continue;
588
  const linejosn = JSON.parse(data);
589
- if (linejosn?.error) {
590
- Logger.error(JSON.stringify(linejosn,null,2), 'Server');
 
 
 
591
  stream.destroy();
592
- reject(new Error("RateLimitError"));
593
  return;
594
  }
595
  if (linejosn?.doImgGen || linejosn?.imageAttachmentInfo) {
@@ -597,6 +616,7 @@ async function handleResponse(response, model, res, isStream) {
597
  }
598
  const processPromise = (async () => {
599
  const result = await processModelResponse(linejosn, model);
 
600
  if (result.token) {
601
  if (isStream) {
602
  res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(result.token, model, true))}\n\n`);
@@ -616,6 +636,7 @@ async function handleResponse(response, model, res, isStream) {
616
  })();
617
  dataPromises.push(processPromise);
618
  } catch (error) {
 
619
  continue;
620
  }
621
  }
@@ -631,25 +652,27 @@ async function handleResponse(response, model, res, isStream) {
631
  } else {
632
  if (!CONFIG.IS_IMG_GEN2) {
633
  res.json(MessageProcessor.createChatResponse(fullResponse, model));
634
- }
635
  }
636
  CONFIG.IS_IMG_GEN = false;
637
  CONFIG.IS_IMG_GEN2 = false;
638
- resolve();
639
  } catch (error) {
640
- reject(error);
 
641
  }
642
  });
643
 
644
  stream.on('error', (error) => {
645
- reject(error);
 
646
  });
647
  });
648
  } catch (error) {
649
  Logger.error(error, 'Server');
650
  CONFIG.IS_IMG_GEN = false;
651
  CONFIG.IS_IMG_GEN2 = false;
652
- throw error;
653
  }
654
  }
655
 
@@ -668,7 +691,7 @@ async function handleImageResponse(imageUrl) {
668
  }
669
  });
670
 
671
- if (imageBase64Response.ok) break;
672
  retryCount++;
673
  if (retryCount === MAX_RETRIES) {
674
  throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
@@ -676,6 +699,7 @@ async function handleImageResponse(imageUrl) {
676
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
677
 
678
  } catch (error) {
 
679
  retryCount++;
680
  if (retryCount === MAX_RETRIES) {
681
  throw error;
@@ -688,34 +712,64 @@ async function handleImageResponse(imageUrl) {
688
  const arrayBuffer = await imageBase64Response.arrayBuffer();
689
  const imageBuffer = Buffer.from(arrayBuffer);
690
 
691
- if(!CONFIG.API.PICGO_KEY){
692
  const base64Image = imageBuffer.toString('base64');
693
  const imageContentType = imageBase64Response.headers.get('content-type');
694
  return `![image](data:${imageContentType};base64,${base64Image})`
695
  }
696
 
 
697
  const formData = new FormData();
698
-
699
- formData.append('source', imageBuffer, {
700
- filename: 'new.jpg',
701
- contentType: 'image/jpeg'
702
- });
703
- const formDataHeaders = formData.getHeaders();
704
- const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
705
- method: "POST",
706
- headers: {
707
  ...formDataHeaders,
708
  "Content-Type": "multipart/form-data",
709
  "X-API-Key": CONFIG.API.PICGO_KEY
710
  },
711
  body: formData
712
- });
713
- if (!responseURL.ok) {
714
- return "生图失败,请查看图床密钥是否设置正确"
715
- } else {
716
- Logger.info("生图成功", 'Server');
717
- const result = await responseURL.json();
718
- return `![image](${result.image.url})`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
719
  }
720
  }
721
 
@@ -749,14 +803,22 @@ app.get('/hf/v1/models', (req, res) => {
749
  app.post('/hf/v1/chat/completions', async (req, res) => {
750
  try {
751
  const authToken = req.headers.authorization?.replace('Bearer ', '');
752
- if (authToken !== CONFIG.API.API_KEY) {
 
 
 
 
 
 
 
753
  return res.status(401).json({ error: 'Unauthorized' });
754
  }
755
  let isTempCookie = req.body.model.includes("grok-2");
756
  let retryCount = 0;
757
  const grokClient = new GrokApiClient(req.body.model);
758
  const requestPayload = await grokClient.prepareChatRequest(req.body);
759
-
 
760
  while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
761
  retryCount++;
762
  if (!CONFIG.API.TEMP_COOKIE) {
@@ -769,7 +831,11 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
769
  } else {
770
  CONFIG.API.SIGNATURE_COOKIE = await Utils.createAuthHeaders(req.body.model);
771
  }
 
 
 
772
  Logger.info(`当前令牌索引: ${CONFIG.SSO_INDEX}`, 'Server');
 
773
  const newMessageReq = await fetch(`${CONFIG.API.BASE_URL}/api/rpc`, {
774
  method: 'POST',
775
  headers: {
@@ -783,9 +849,15 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
783
  }
784
  })
785
  });
786
-
787
- const responseText = await newMessageReq.json();
788
- const conversationId = responseText.conversationId;
 
 
 
 
 
 
789
 
790
  const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
791
  method: 'POST',
@@ -798,19 +870,20 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
798
  },
799
  body: JSON.stringify(requestPayload)
800
  });
801
-
802
-
803
  if (response.ok) {
804
- CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
805
  Logger.info(`请求成功`, 'Server');
 
806
  Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
807
  try {
808
  await handleResponse(response, req.body.model, res, req.body.stream);
809
- return;
 
810
  } catch (error) {
811
- if(isTempCookie){
 
812
  await Utils.get_signature();
813
- }else{
814
  tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
815
  for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
816
  CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
@@ -826,7 +899,7 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
826
  if (response.status === 429) {
827
  if (isTempCookie) {
828
  await Utils.get_signature();
829
- } else {
830
  tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
831
  for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
832
  CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
@@ -837,7 +910,7 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
837
  }
838
  }
839
  }
840
- } else {
841
  // 非429错误直接抛出
842
  if (isTempCookie) {
843
  await Utils.get_signature();
@@ -852,14 +925,11 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
852
  }
853
  throw new Error('当前模型所有令牌都已耗尽');
854
  } catch (error) {
855
- CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
856
  Logger.error(error, 'ChatAPI');
857
  res.status(500).json({
858
  error: {
859
- message: error.message,
860
- type: 'server_error',
861
- param: null,
862
- code: error.code || null
863
  }
864
  });
865
  }
@@ -873,4 +943,4 @@ app.use((req, res) => {
873
 
874
  app.listen(CONFIG.SERVER.PORT, () => {
875
  Logger.info(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`, 'Server');
876
- });
 
23
  "grok-3-reasoning": "grok-3"
24
  },
25
  API: {
26
+ IS_CUSTOM_SSO: process.env.IS_CUSTOM_SSO === 'true',
27
  BASE_URL: "https://grok.com",
28
  API_KEY: process.env.API_KEY || "sk-123456",
29
  SIGNATURE_COOKIE: null,
30
  TEMP_COOKIE: null,
31
+ PICGO_KEY: process.env.PICGO_KEY || null, //想要流式生图的话需要填入这个PICGO图床的key
32
+ TUMY_KEY: process.env.TUMY_KEY || null //想要流式生图的话需要填入这个TUMY图床的key 两个图床二选一,默认使用PICGO
33
  },
34
  SERVER: {
35
  PORT: process.env.PORT || 3000,
 
38
  RETRY: {
39
  MAX_ATTEMPTS: 2//重试次数
40
  },
41
+ SHOW_THINKING: process.env.SHOW_THINKING === 'true',
42
  IS_THINKING: false,
43
  IS_IMG_GEN: false,
44
  IS_IMG_GEN2: false,
 
68
 
69
 
70
  async function initialization() {
71
+ if (CONFIG.API.IS_CUSTOM_SSO) {
72
+ await Utils.get_signature()
73
+ return;
74
+ }
75
  const ssoArray = process.env.SSO.split(',');
76
+ ssoArray.forEach((sso) => {
77
+ tokenManager.addToken(`sso-rw=${sso};sso=${sso}`);
 
78
  });
79
  console.log(JSON.stringify(tokenManager.getActiveTokens(), null, 2));
80
  await Utils.get_signature()
 
104
  });
105
  }
106
  }
107
+ setToken(token) {
108
+ this.activeTokens = [token];
109
+ this.tokenModelFrequency.set(token, {
110
+ "grok-3": 0,
111
+ "grok-3-deepsearch": 0,
112
+ "grok-3-reasoning": 0
113
+ });
114
+ }
115
 
116
  getTokenByIndex(index, model) {
117
+ if (this.activeTokens.length === 0) {
118
  return null;
119
  }
120
  const token = this.activeTokens[index];
 
123
  }
124
 
125
  recordModelRequest(token, model) {
126
+ if (model === 'grok-3-search' || model === 'grok-3-imageGen') {
127
  model = 'grok-3';
128
  }
129
 
 
135
  this.checkAndRemoveTokenIfLimitReached(token);
136
  }
137
  setModelLimit(index, model) {
138
+ if (model === 'grok-3-search' || model === 'grok-3-imageGen') {
139
  model = 'grok-3';
140
  }
141
  if (!this.modelRateLimit[model]) return;
 
143
  tokenFrequency[model] = 9999;
144
  }
145
  isTokenModelLimitReached(index, model) {
146
+ if (model === 'grok-3-search' || model === 'grok-3-imageGen') {
147
  model = 'grok-3';
148
  }
149
  if (!this.modelRateLimit[model]) return;
 
183
  }
184
 
185
  startTokenRecoveryProcess() {
186
+ if (CONFIG.API.IS_CUSTOM_SSO) return;
187
  setInterval(() => {
188
  const now = Date.now();
189
  for (const [token, expiredTime] of this.expiredTokens.entries()) {
 
214
  static async extractGrokHeaders() {
215
  Logger.info("开始提取头信息", 'Server');
216
  try {
 
217
  const browser = await puppeteer.launch({
218
  headless: true,
219
  args: [
 
234
  const cookies = await page.cookies();
235
  const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
236
  const extractedHeaders = {};
 
237
  for (const cookie of cookies) {
 
238
  if (targetHeaders.includes(cookie.name.toLowerCase())) {
239
  extractedHeaders[cookie.name.toLowerCase()] = cookie.value;
240
  }
241
  }
 
242
  await browser.close();
 
243
  Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
244
  return extractedHeaders;
245
 
 
367
  }
368
 
369
  async prepareChatRequest(request) {
370
+ if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY && request.stream) {
371
+ throw new Error(`该模型流式输出需要配置PICGO或者PICUI图床密钥!`);
372
  }
373
+
374
  // 处理画图模型的消息限制
375
  let todoMessages = request.messages;
376
  if (request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') {
 
378
  if (lastMessage.role !== 'user') {
379
  throw new Error('画图模型的最后一条消息必须是用户消息!');
380
  }
381
+ todoMessages = [lastMessage];
382
  }
383
+
384
  const fileAttachments = [];
385
  let messages = '';
386
  let lastRole = null;
387
  let lastContent = '';
388
  const search = request.model === 'grok-2-search' || request.model === 'grok-3-search';
389
+
390
  // 移除<think>标签及其内容和base64图片
391
  const removeThinkTags = (text) => {
392
  text = text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
393
  text = text.replace(/!\[image\]\(data:.*?base64,.*?\)/g, '[图片]');
394
  return text;
395
  };
396
+
397
  const processImageUrl = async (content) => {
398
  if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
399
  const imageResponse = await this.uploadBase64Image(
 
404
  }
405
  return null;
406
  };
407
+
408
  const processContent = async (content) => {
409
  if (Array.isArray(content)) {
410
  let textContent = '';
 
425
  }
426
  return removeThinkTags(this.processMessageContent(content));
427
  };
428
+
429
  for (const current of todoMessages) {
430
  const role = current.role === 'assistant' ? 'assistant' : 'user';
431
  const isLastMessage = current === todoMessages[todoMessages.length - 1];
432
+
433
  // 处理图片附件
434
  if (isLastMessage && current.content) {
435
  if (Array.isArray(current.content)) {
 
444
  if (processedImage) fileAttachments.push(processedImage);
445
  }
446
  }
447
+
448
  // 处理文本内容
449
  const textContent = await processContent(current.content);
450
+
451
  if (textContent || (isLastMessage && fileAttachments.length > 0)) {
452
  if (role === lastRole && textContent) {
453
  lastContent += '\n' + textContent;
 
460
  }
461
  }
462
  }
463
+
464
  return {
465
  modelName: this.modelId,
466
  message: messages.trim(),
 
528
  }
529
  }
530
  async function processModelResponse(linejosn, model) {
531
+ let result = { token: null, imageUrl: null }
532
  if (CONFIG.IS_IMG_GEN) {
533
  if (linejosn?.cachedImageGenerationResponse && !CONFIG.IS_IMG_GEN2) {
534
  result.imageUrl = linejosn.cachedImageGenerationResponse.imageUrl;
 
558
  }
559
  return result;
560
  case 'grok-3-reasoning':
561
+ if (linejosn?.isThinking && !CONFIG.SHOW_THINKING) return result;
562
 
563
  if (linejosn?.isThinking && !CONFIG.IS_THINKING) {
564
  result.token = "<think>" + linejosn?.token;
565
  CONFIG.IS_THINKING = true;
566
+ } else if (!linejosn.isThinking && CONFIG.IS_THINKING) {
567
  result.token = "</think>" + linejosn?.token;
568
  CONFIG.IS_THINKING = false;
569
  } else {
 
571
  }
572
  return result;
573
  }
574
+ return result;
575
  }
576
 
577
  async function handleResponse(response, model, res, isStream) {
 
580
  let buffer = '';
581
  let fullResponse = '';
582
  const dataPromises = [];
583
+ if (isStream) {
584
+ res.setHeader('Content-Type', 'text/event-stream');
585
+ res.setHeader('Cache-Control', 'no-cache');
586
+ res.setHeader('Connection', 'keep-alive');
587
+ }
588
+ Logger.info("开始处理流式响应", 'Server');
589
+
590
+ return new Promise((resolve, reject) => {
591
  stream.on('data', async (chunk) => {
592
  buffer += chunk.toString();
593
  const lines = buffer.split('\n');
 
600
  const data = trimmedLine.substring(6);
601
  try {
602
  if (!data.trim()) continue;
603
+ if (data === "[DONE]") continue;
604
  const linejosn = JSON.parse(data);
605
+ if (linejosn?.error) {
606
+ Logger.error(JSON.stringify(linejosn, null, 2), 'Server');
607
+ if(linejosn.error?.name === "RateLimitError"){
608
+ CONFIG.API.TEMP_COOKIE = null;
609
+ }
610
  stream.destroy();
611
+ reject(new Error("RateLimitError"));
612
  return;
613
  }
614
  if (linejosn?.doImgGen || linejosn?.imageAttachmentInfo) {
 
616
  }
617
  const processPromise = (async () => {
618
  const result = await processModelResponse(linejosn, model);
619
+
620
  if (result.token) {
621
  if (isStream) {
622
  res.write(`data: ${JSON.stringify(MessageProcessor.createChatResponse(result.token, model, true))}\n\n`);
 
636
  })();
637
  dataPromises.push(processPromise);
638
  } catch (error) {
639
+ Logger.error(error, 'Server');
640
  continue;
641
  }
642
  }
 
652
  } else {
653
  if (!CONFIG.IS_IMG_GEN2) {
654
  res.json(MessageProcessor.createChatResponse(fullResponse, model));
655
+ }
656
  }
657
  CONFIG.IS_IMG_GEN = false;
658
  CONFIG.IS_IMG_GEN2 = false;
659
+ resolve();
660
  } catch (error) {
661
+ Logger.error(error, 'Server');
662
+ reject(error);
663
  }
664
  });
665
 
666
  stream.on('error', (error) => {
667
+ Logger.error(error, 'Server');
668
+ reject(error);
669
  });
670
  });
671
  } catch (error) {
672
  Logger.error(error, 'Server');
673
  CONFIG.IS_IMG_GEN = false;
674
  CONFIG.IS_IMG_GEN2 = false;
675
+ throw new Error(error);
676
  }
677
  }
678
 
 
691
  }
692
  });
693
 
694
+ if (imageBase64Response.ok) break;
695
  retryCount++;
696
  if (retryCount === MAX_RETRIES) {
697
  throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
 
699
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
700
 
701
  } catch (error) {
702
+ Logger.error(error, 'Server');
703
  retryCount++;
704
  if (retryCount === MAX_RETRIES) {
705
  throw error;
 
712
  const arrayBuffer = await imageBase64Response.arrayBuffer();
713
  const imageBuffer = Buffer.from(arrayBuffer);
714
 
715
+ if (!CONFIG.API.PICGO_KEY && !CONFIG.API.TUMY_KEY) {
716
  const base64Image = imageBuffer.toString('base64');
717
  const imageContentType = imageBase64Response.headers.get('content-type');
718
  return `![image](data:${imageContentType};base64,${base64Image})`
719
  }
720
 
721
+ Logger.info("开始请求图床", 'Server');
722
  const formData = new FormData();
723
+ if(CONFIG.API.PICGO_KEY){
724
+ formData.append('source', imageBuffer, {
725
+ filename: `image-${Date.now()}.jpg`,
726
+ contentType: 'image/jpeg'
727
+ });
728
+ const formDataHeaders = formData.getHeaders();
729
+ const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
730
+ method: "POST",
731
+ headers: {
732
  ...formDataHeaders,
733
  "Content-Type": "multipart/form-data",
734
  "X-API-Key": CONFIG.API.PICGO_KEY
735
  },
736
  body: formData
737
+ });
738
+ if (!responseURL.ok) {
739
+ return "生图失败,请查看PICGO图床密钥是否设置正确"
740
+ } else {
741
+ Logger.info("生图成功", 'Server');
742
+ const result = await responseURL.json();
743
+ return `![image](${result.image.url})`
744
+ }
745
+ }else if(CONFIG.API.TUMY_KEY){
746
+ const formData = new FormData();
747
+ formData.append('file', imageBuffer, {
748
+ filename: `image-${Date.now()}.jpg`,
749
+ contentType: 'image/jpeg'
750
+ });
751
+ const formDataHeaders = formData.getHeaders();
752
+ const responseURL = await fetch("https://tu.my/api/v1/upload", {
753
+ method: "POST",
754
+ headers: {
755
+ ...formDataHeaders,
756
+ "Accept": "application/json",
757
+ 'Authorization': `Bearer ${CONFIG.API.TUMY_KEY}`
758
+ },
759
+ body: formData
760
+ });
761
+ if (!responseURL.ok) {
762
+ return "生图失败,请查看TUMY图床密钥是否设置正确"
763
+ } else {
764
+ Logger.info("生图成功", 'Server');
765
+ const result = await responseURL.json();
766
+ try {
767
+ var url = `![image](${result.data.links.url})`
768
+ return url;
769
+ } catch (error) {
770
+ return "生图失败,请查看TUMY图床密钥是否设置正确"
771
+ }
772
+ }
773
  }
774
  }
775
 
 
803
  app.post('/hf/v1/chat/completions', async (req, res) => {
804
  try {
805
  const authToken = req.headers.authorization?.replace('Bearer ', '');
806
+ if (CONFIG.API.IS_CUSTOM_SSO) {
807
+ if (authToken) {
808
+ const result = `sso=${authToken};ssp_rw=${authToken}`;
809
+ tokenManager.setToken(result);
810
+ } else {
811
+ return res.status(401).json({ error: '自定义的SSO令牌缺失' });
812
+ }
813
+ } else if (authToken !== CONFIG.API.API_KEY) {
814
  return res.status(401).json({ error: 'Unauthorized' });
815
  }
816
  let isTempCookie = req.body.model.includes("grok-2");
817
  let retryCount = 0;
818
  const grokClient = new GrokApiClient(req.body.model);
819
  const requestPayload = await grokClient.prepareChatRequest(req.body);
820
+ Logger.info(`请求体: ${JSON.stringify(requestPayload,null,2)}`, 'Server');
821
+
822
  while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
823
  retryCount++;
824
  if (!CONFIG.API.TEMP_COOKIE) {
 
831
  } else {
832
  CONFIG.API.SIGNATURE_COOKIE = await Utils.createAuthHeaders(req.body.model);
833
  }
834
+ if (!CONFIG.API.SIGNATURE_COOKIE) {
835
+ throw new Error('该模型无可用令牌');
836
+ }
837
  Logger.info(`当前令牌索引: ${CONFIG.SSO_INDEX}`, 'Server');
838
+ Logger.info(`当前令牌: ${JSON.stringify(CONFIG.API.SIGNATURE_COOKIE,null,2)}`, 'Server');
839
  const newMessageReq = await fetch(`${CONFIG.API.BASE_URL}/api/rpc`, {
840
  method: 'POST',
841
  headers: {
 
849
  }
850
  })
851
  });
852
+ let conversationId;
853
+ var responseText2 = await newMessageReq.clone().text();
854
+ if (newMessageReq.status === 200) {
855
+ const responseText = await newMessageReq.json();
856
+ conversationId = responseText.conversationId;
857
+ } else {
858
+ Logger.error(`创建会话响应错误: ${responseText2}`, 'Server');
859
+ throw new Error(`创建会话响应错误: ${responseText2}`);
860
+ }
861
 
862
  const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
863
  method: 'POST',
 
870
  },
871
  body: JSON.stringify(requestPayload)
872
  });
873
+
 
874
  if (response.ok) {
 
875
  Logger.info(`请求成功`, 'Server');
876
+ CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
877
  Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
878
  try {
879
  await handleResponse(response, req.body.model, res, req.body.stream);
880
+ Logger.info(`请求结束`, 'Server');
881
+ return;
882
  } catch (error) {
883
+ Logger.error(error, 'Server');
884
+ if (isTempCookie) {
885
  await Utils.get_signature();
886
+ } else {
887
  tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
888
  for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
889
  CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
 
899
  if (response.status === 429) {
900
  if (isTempCookie) {
901
  await Utils.get_signature();
902
+ } else {
903
  tokenManager.setModelLimit(CONFIG.SSO_INDEX, req.body.model);
904
  for (let i = 1; i <= tokenManager.getTokenCount(); i++) {
905
  CONFIG.SSO_INDEX = (CONFIG.SSO_INDEX + 1) % tokenManager.getTokenCount();
 
910
  }
911
  }
912
  }
913
+ } else {
914
  // 非429错误直接抛出
915
  if (isTempCookie) {
916
  await Utils.get_signature();
 
925
  }
926
  throw new Error('当前模型所有令牌都已耗尽');
927
  } catch (error) {
 
928
  Logger.error(error, 'ChatAPI');
929
  res.status(500).json({
930
  error: {
931
+ message: error.message || error,
932
+ type: 'server_error'
 
 
933
  }
934
  });
935
  }
 
943
 
944
  app.listen(CONFIG.SERVER.PORT, () => {
945
  Logger.info(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`, 'Server');
946
+ });