yxmiler commited on
Commit
6bbe216
·
verified ·
1 Parent(s): 04baf63

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +99 -58
index.js CHANGED
@@ -7,6 +7,8 @@ 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
 
@@ -26,16 +28,16 @@ const CONFIG = {
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: {
32
  PORT: process.env.PORT || 3000,
33
  BODY_LIMIT: '5mb'
34
  },
35
  RETRY: {
36
- MAX_ATTEMPTS: 2//重试次数
 
37
  },
38
- SHOW_THINKING:process.env.SHOW_THINKING === 'true',//显示思考过程
39
  IS_THINKING: false,
40
  IS_IMG_GEN: false,
41
  IS_IMG_GEN2: false,
@@ -189,7 +191,7 @@ class AuthTokenManager {
189
 
190
  class Utils {
191
  static async extractGrokHeaders() {
192
- Logger.info("开始提取头信息", 'Server');
193
  try {
194
  // 启动浏览器
195
  const browser = await puppeteer.launch({
@@ -208,6 +210,7 @@ class Utils {
208
  await page.evaluate(() => {
209
  return new Promise(resolve => setTimeout(resolve, 5000))
210
  })
 
211
  // 获取所有 Cookies
212
  const cookies = await page.cookies();
213
  const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
@@ -222,11 +225,11 @@ class Utils {
222
  // 关闭浏览器
223
  await browser.close();
224
  // 打印并返回提取的头信息
225
- Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
226
  return extractedHeaders;
227
 
228
  } catch (error) {
229
- Logger.error('获取头信息出错:', error, 'Server');
230
  return null;
231
  }
232
  }
@@ -234,12 +237,12 @@ class Utils {
234
  if (CONFIG.API.TEMP_COOKIE) {
235
  return CONFIG.API.TEMP_COOKIE;
236
  }
237
- Logger.info("刷新认证信息", 'Server');
238
  let retryCount = 0;
239
  while (!CONFIG.API.TEMP_COOKIE || retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
240
  let headers = await Utils.extractGrokHeaders();
241
  if (headers) {
242
- Logger.info("获取认证信息成功", 'Server');
243
  CONFIG.API.TEMP_COOKIE = { cookie: `x-anonuserid=${headers["x-anonuserid"]}; x-challenge=${headers["x-challenge"]}; x-signature=${headers["x-signature"]}` };
244
  return;
245
  }
@@ -249,6 +252,53 @@ class Utils {
249
  }
250
  }
251
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  static async organizeSearchResults(searchResults) {
253
  // 确保传入的是有效的搜索结果对象
254
  if (!searchResults || !searchResults.results) {
@@ -322,7 +372,7 @@ class GrokApiClient {
322
  content: imageBuffer
323
  }
324
  };
325
- Logger.info("发送图片请求", 'Server');
326
  // 发送请求
327
  const response = await fetch(url, {
328
  method: 'POST',
@@ -334,46 +384,35 @@ class GrokApiClient {
334
  });
335
 
336
  if (!response.ok) {
337
- Logger.error(`上传图片失败,状态码:${response.status},原因:${response.error}`, 'Server');
338
  return '';
339
  }
340
 
341
  const result = await response.json();
342
- Logger.info('上传图片成功:', result, 'Server');
343
  return result.fileMetadataId;
344
 
345
  } catch (error) {
346
- Logger.error(error, 'Server');
347
  return '';
348
  }
349
  }
350
 
351
  async prepareChatRequest(request) {
352
- if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY && request.stream) {
353
- throw new Error(`该模型流式输出需要配置PICGO图床密钥!`);
354
- }
355
-
356
- // 处理画图模型的消息限制
357
- let todoMessages = request.messages;
358
- if (request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') {
359
- const lastMessage = todoMessages[todoMessages.length - 1];
360
- if (lastMessage.role !== 'user') {
361
- throw new Error('画图模型的最后一条消息必须是用户消息!');
362
- }
363
- todoMessages = [lastMessage];
364
  }
365
 
 
366
  const fileAttachments = [];
367
  let messages = '';
368
  let lastRole = null;
369
  let lastContent = '';
370
  const search = request.model === 'grok-2-search';
371
 
372
- // 移除<think>标签及其内容和base64图片
373
  const removeThinkTags = (text) => {
374
- text = text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
375
- text = text.replace(/!\[image\]\(data:.*?base64,.*?\)/g, '[图片]');
376
- return text;
377
  };
378
 
379
  const processImageUrl = async (content) => {
@@ -446,7 +485,7 @@ class GrokApiClient {
446
  return {
447
  modelName: this.modelId,
448
  message: messages.trim(),
449
- fileAttachments: fileAttachments.slice(0, 4),
450
  imageAttachments: [],
451
  disableSearch: false,
452
  enableImageGeneration: true,
@@ -540,8 +579,6 @@ async function processModelResponse(linejosn, model) {
540
  }
541
  return result;
542
  case 'grok-3-reasoning':
543
- if(linejosn?.isThinking && !CONFIG.SHOW_THINKING)return result;
544
-
545
  if (linejosn?.isThinking && !CONFIG.IS_THINKING) {
546
  result.token = "<think>" + token;
547
  CONFIG.IS_THINKING = true;
@@ -561,8 +598,8 @@ async function handleResponse(response, model, res, isStream) {
561
  let buffer = '';
562
  let fullResponse = '';
563
  const dataPromises = [];
564
-
565
- return new Promise((resolve, reject) => {
566
  stream.on('data', async (chunk) => {
567
  buffer += chunk.toString();
568
  const lines = buffer.split('\n');
@@ -571,15 +608,15 @@ async function handleResponse(response, model, res, isStream) {
571
  for (const line of lines) {
572
  if (!line.trim()) continue;
573
  const trimmedLine = line.trim();
 
574
  if (trimmedLine.startsWith('data: ')) {
575
  const data = trimmedLine.substring(6);
576
  try {
577
  if (!data.trim()) continue;
578
- if(data === "[DONE]") continue;
579
  const linejosn = JSON.parse(data);
580
  if (linejosn?.error) {
581
- stream.destroy();
582
- reject(new Error("RateLimitError"));
583
  return;
584
  }
585
  if (linejosn?.doImgGen || linejosn?.imageAttachmentInfo) {
@@ -606,6 +643,7 @@ async function handleResponse(response, model, res, isStream) {
606
  })();
607
  dataPromises.push(processPromise);
608
  } catch (error) {
 
609
  continue;
610
  }
611
  }
@@ -625,14 +663,14 @@ async function handleResponse(response, model, res, isStream) {
625
  }
626
  CONFIG.IS_IMG_GEN = false;
627
  CONFIG.IS_IMG_GEN2 = false;
628
- resolve();
629
  } catch (error) {
630
- reject(error);
631
  }
632
  });
633
-
634
  stream.on('error', (error) => {
635
- reject(error);
 
636
  });
637
  });
638
  } catch (error) {
@@ -641,15 +679,18 @@ async function handleResponse(response, model, res, isStream) {
641
  CONFIG.IS_IMG_GEN2 = false;
642
  throw error;
643
  }
 
644
  }
645
 
646
  async function handleImageResponse(imageUrl) {
647
- const MAX_RETRIES = 2;
 
648
  let retryCount = 0;
649
  let imageBase64Response;
650
 
651
  while (retryCount < MAX_RETRIES) {
652
  try {
 
653
  imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
654
  method: 'GET',
655
  headers: {
@@ -658,11 +699,16 @@ async function handleImageResponse(imageUrl) {
658
  }
659
  });
660
 
661
- if (imageBase64Response.ok) break;
 
 
 
662
  retryCount++;
663
  if (retryCount === MAX_RETRIES) {
664
  throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
665
  }
 
 
666
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
667
 
668
  } catch (error) {
@@ -670,20 +716,13 @@ async function handleImageResponse(imageUrl) {
670
  if (retryCount === MAX_RETRIES) {
671
  throw error;
672
  }
 
673
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
674
  }
675
  }
676
 
677
-
678
  const arrayBuffer = await imageBase64Response.arrayBuffer();
679
  const imageBuffer = Buffer.from(arrayBuffer);
680
-
681
- if(!CONFIG.API.PICGO_KEY){
682
- const base64Image = imageBuffer.toString('base64');
683
- const imageContentType = imageBase64Response.headers.get('content-type');
684
- return `![image](data:${imageContentType};base64,${base64Image})`
685
- }
686
-
687
  const formData = new FormData();
688
 
689
  formData.append('source', imageBuffer, {
@@ -711,7 +750,6 @@ async function handleImageResponse(imageUrl) {
711
 
712
  const tokenManager = new AuthTokenManager();
713
  await initialization();
714
-
715
  // 中间件配置
716
  const app = express();
717
  app.use(Logger.requestLogger);
@@ -722,7 +760,7 @@ app.use(cors({
722
  methods: ['GET', 'POST', 'OPTIONS'],
723
  allowedHeaders: ['Content-Type', 'Authorization']
724
  }));
725
-
726
  app.get('/hf/v1/models', (req, res) => {
727
  res.json({
728
  object: "list",
@@ -748,12 +786,13 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
748
  while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
749
  retryCount++;
750
  const grokClient = new GrokApiClient(req.body.model);
751
- const requestPayload = await grokClient.prepareChatRequest(req.body);
 
752
 
753
  if (!CONFIG.API.TEMP_COOKIE) {
754
  await Utils.get_signature();
755
  }
756
-
757
  if (isTempCookie) {
758
  CONFIG.API.SIGNATURE_COOKIE = CONFIG.API.TEMP_COOKIE;
759
  Logger.info(`已切换为临时令牌`, 'Server');
@@ -777,7 +816,7 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
777
 
778
  const responseText = await newMessageReq.json();
779
  const conversationId = responseText.conversationId;
780
-
781
  const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
782
  method: 'POST',
783
  headers: {
@@ -796,7 +835,7 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
796
  Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
797
  try {
798
  await handleResponse(response, req.body.model, res, req.body.stream);
799
- return;
800
  } catch (error) {
801
  if(isTempCookie){
802
  await Utils.get_signature();
@@ -813,6 +852,7 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
813
  }
814
  }
815
  } else {
 
816
  if (response.status === 429) {
817
  if (isTempCookie) {
818
  await Utils.get_signature();
@@ -855,11 +895,12 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
855
  });
856
 
857
 
 
858
  app.use((req, res) => {
859
  res.status(200).send('api运行正常');
860
  });
861
 
862
-
863
  app.listen(CONFIG.SERVER.PORT, () => {
864
- Logger.info(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`, 'Server');
865
  });
 
7
  import StealthPlugin from 'puppeteer-extra-plugin-stealth'
8
  import { v4 as uuidv4 } from 'uuid';
9
  import Logger from './logger.js';
10
+ import fs from 'fs';
11
+ import path from 'path';
12
 
13
  dotenv.config();
14
 
 
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
  },
33
  SERVER: {
34
  PORT: process.env.PORT || 3000,
35
  BODY_LIMIT: '5mb'
36
  },
37
  RETRY: {
38
+ MAX_ATTEMPTS: 2,//重试次数
39
+ DELAY_BASE: 1000 // 基础延迟时间(毫秒)
40
  },
 
41
  IS_THINKING: false,
42
  IS_IMG_GEN: false,
43
  IS_IMG_GEN2: false,
 
191
 
192
  class Utils {
193
  static async extractGrokHeaders() {
194
+ console.log("开始提取头信息");
195
  try {
196
  // 启动浏览器
197
  const browser = await puppeteer.launch({
 
210
  await page.evaluate(() => {
211
  return new Promise(resolve => setTimeout(resolve, 5000))
212
  })
213
+ console.log('等待5秒完成')
214
  // 获取所有 Cookies
215
  const cookies = await page.cookies();
216
  const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
 
225
  // 关闭浏览器
226
  await browser.close();
227
  // 打印并返回提取的头信息
228
+ console.log('提取的头信息:', JSON.stringify(extractedHeaders, null, 2));
229
  return extractedHeaders;
230
 
231
  } catch (error) {
232
+ console.error('获取头信息出错:', error);
233
  return null;
234
  }
235
  }
 
237
  if (CONFIG.API.TEMP_COOKIE) {
238
  return CONFIG.API.TEMP_COOKIE;
239
  }
240
+ console.log("刷新认证信息");
241
  let retryCount = 0;
242
  while (!CONFIG.API.TEMP_COOKIE || retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
243
  let headers = await Utils.extractGrokHeaders();
244
  if (headers) {
245
+ console.log("获取认证信息成功");
246
  CONFIG.API.TEMP_COOKIE = { cookie: `x-anonuserid=${headers["x-anonuserid"]}; x-challenge=${headers["x-challenge"]}; x-signature=${headers["x-signature"]}` };
247
  return;
248
  }
 
252
  }
253
  }
254
  }
255
+ static async handleError(error, res) {
256
+ // 如果是500错误且提供了原始请求函数,尝试重新获取签名并重试
257
+ if (String(error).includes("status: 500")) {
258
+ try {
259
+ await Utils.get_signature();
260
+ if (CONFIG.API.SIGNATURE_COOKIE) {
261
+ return res.status(500).json({
262
+ error: {
263
+ message: `${error.message}已重新刷新认证信息,请重新对话`,
264
+ type: 'server_error',
265
+ param: null,
266
+ code: retryError.code || null
267
+ }
268
+ });
269
+ } else {
270
+ return res.status(500).json({
271
+ error: {
272
+ message: "认证信息获取失败",
273
+ type: 'server_error',
274
+ param: null,
275
+ code: null
276
+ }
277
+ });
278
+ }
279
+ } catch (error) {
280
+ console.error('重试失败:', error);
281
+ return res.status(500).json({
282
+ error: {
283
+ message: `${error.message},认证信息获取失败`,
284
+ type: 'server_error',
285
+ param: null,
286
+ code: error.code || null
287
+ }
288
+ });
289
+ }
290
+ }
291
+
292
+ // 其他错误直接返回
293
+ res.status(500).json({
294
+ error: {
295
+ message: error.message,
296
+ type: 'server_error',
297
+ param: null,
298
+ code: error.code || null
299
+ }
300
+ });
301
+ }
302
  static async organizeSearchResults(searchResults) {
303
  // 确保传入的是有效的搜索结果对象
304
  if (!searchResults || !searchResults.results) {
 
372
  content: imageBuffer
373
  }
374
  };
375
+ console.log("发送图片请求");
376
  // 发送请求
377
  const response = await fetch(url, {
378
  method: 'POST',
 
384
  });
385
 
386
  if (!response.ok) {
387
+ console.error(`上传图片失败,状态码:${response.status},原因:${response.error}`);
388
  return '';
389
  }
390
 
391
  const result = await response.json();
392
+ console.log('上传图片成功:', result);
393
  return result.fileMetadataId;
394
 
395
  } catch (error) {
396
+ console.error('上传图片失败:', error);
397
  return '';
398
  }
399
  }
400
 
401
  async prepareChatRequest(request) {
402
+ if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY) {
403
+ throw new Error(`该模型需要配置PICGO图床密钥!`);
 
 
 
 
 
 
 
 
 
 
404
  }
405
 
406
+ const todoMessages = request.messages;
407
  const fileAttachments = [];
408
  let messages = '';
409
  let lastRole = null;
410
  let lastContent = '';
411
  const search = request.model === 'grok-2-search';
412
 
413
+ // 移除<think>标签及其内容
414
  const removeThinkTags = (text) => {
415
+ return text.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
 
 
416
  };
417
 
418
  const processImageUrl = async (content) => {
 
485
  return {
486
  modelName: this.modelId,
487
  message: messages.trim(),
488
+ fileAttachments: fileAttachments.slice(0, 4), // 最多保留4张图片
489
  imageAttachments: [],
490
  disableSearch: false,
491
  enableImageGeneration: true,
 
579
  }
580
  return result;
581
  case 'grok-3-reasoning':
 
 
582
  if (linejosn?.isThinking && !CONFIG.IS_THINKING) {
583
  result.token = "<think>" + token;
584
  CONFIG.IS_THINKING = true;
 
598
  let buffer = '';
599
  let fullResponse = '';
600
  const dataPromises = [];
601
+
602
+ return new Promise((resolve, reject) => {
603
  stream.on('data', async (chunk) => {
604
  buffer += chunk.toString();
605
  const lines = buffer.split('\n');
 
608
  for (const line of lines) {
609
  if (!line.trim()) continue;
610
  const trimmedLine = line.trim();
611
+ fs.appendFileSync(path.resolve(process.cwd(), 'response4.txt'), trimmedLine + '\n');
612
  if (trimmedLine.startsWith('data: ')) {
613
  const data = trimmedLine.substring(6);
614
  try {
615
  if (!data.trim()) continue;
616
+ if(data === "[DONE]")continue;
617
  const linejosn = JSON.parse(data);
618
  if (linejosn?.error) {
619
+ reject(new Error(linejosn));
 
620
  return;
621
  }
622
  if (linejosn?.doImgGen || linejosn?.imageAttachmentInfo) {
 
643
  })();
644
  dataPromises.push(processPromise);
645
  } catch (error) {
646
+ console.log(error);
647
  continue;
648
  }
649
  }
 
663
  }
664
  CONFIG.IS_IMG_GEN = false;
665
  CONFIG.IS_IMG_GEN2 = false;
666
+ resolve();
667
  } catch (error) {
668
+ reject(error);
669
  }
670
  });
 
671
  stream.on('error', (error) => {
672
+ Logger.error(error, 'Server');
673
+ reject(error);
674
  });
675
  });
676
  } catch (error) {
 
679
  CONFIG.IS_IMG_GEN2 = false;
680
  throw error;
681
  }
682
+
683
  }
684
 
685
  async function handleImageResponse(imageUrl) {
686
+ //对服务器发送图片请求
687
+ const MAX_RETRIES = 3;
688
  let retryCount = 0;
689
  let imageBase64Response;
690
 
691
  while (retryCount < MAX_RETRIES) {
692
  try {
693
+ //发送图片请求获取图片
694
  imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
695
  method: 'GET',
696
  headers: {
 
699
  }
700
  });
701
 
702
+ if (imageBase64Response.ok) {
703
+ break; // 如果请求成功,跳出重试循环
704
+ }
705
+
706
  retryCount++;
707
  if (retryCount === MAX_RETRIES) {
708
  throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
709
  }
710
+
711
+ // 等待一段时间后重试(可以使用指数退避)
712
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
713
 
714
  } catch (error) {
 
716
  if (retryCount === MAX_RETRIES) {
717
  throw error;
718
  }
719
+ // 等待一段时间后重试
720
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
721
  }
722
  }
723
 
 
724
  const arrayBuffer = await imageBase64Response.arrayBuffer();
725
  const imageBuffer = Buffer.from(arrayBuffer);
 
 
 
 
 
 
 
726
  const formData = new FormData();
727
 
728
  formData.append('source', imageBuffer, {
 
750
 
751
  const tokenManager = new AuthTokenManager();
752
  await initialization();
 
753
  // 中间件配置
754
  const app = express();
755
  app.use(Logger.requestLogger);
 
760
  methods: ['GET', 'POST', 'OPTIONS'],
761
  allowedHeaders: ['Content-Type', 'Authorization']
762
  }));
763
+ // API路由
764
  app.get('/hf/v1/models', (req, res) => {
765
  res.json({
766
  object: "list",
 
786
  while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
787
  retryCount++;
788
  const grokClient = new GrokApiClient(req.body.model);
789
+ const requestPayload = await grokClient.prepareChatRequest(req.body);
790
+ console.log(JSON.stringify(requestPayload,null,2));
791
 
792
  if (!CONFIG.API.TEMP_COOKIE) {
793
  await Utils.get_signature();
794
  }
795
+ //获取验证cookie
796
  if (isTempCookie) {
797
  CONFIG.API.SIGNATURE_COOKIE = CONFIG.API.TEMP_COOKIE;
798
  Logger.info(`已切换为临时令牌`, 'Server');
 
816
 
817
  const responseText = await newMessageReq.json();
818
  const conversationId = responseText.conversationId;
819
+ //发送对话
820
  const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
821
  method: 'POST',
822
  headers: {
 
835
  Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
836
  try {
837
  await handleResponse(response, req.body.model, res, req.body.stream);
838
+ return; // 成功后直接返��
839
  } catch (error) {
840
  if(isTempCookie){
841
  await Utils.get_signature();
 
852
  }
853
  }
854
  } else {
855
+ // 处理429错误
856
  if (response.status === 429) {
857
  if (isTempCookie) {
858
  await Utils.get_signature();
 
895
  });
896
 
897
 
898
+ // 404处理
899
  app.use((req, res) => {
900
  res.status(200).send('api运行正常');
901
  });
902
 
903
+ // 启动服务器
904
  app.listen(CONFIG.SERVER.PORT, () => {
905
+ console.log(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`);
906
  });