yxmiler commited on
Commit
17dc578
·
verified ·
1 Parent(s): 6bbe216

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +88 -120
index.js CHANGED
@@ -7,8 +7,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
- import fs from 'fs';
11
- import path from 'path';
12
 
13
  dotenv.config();
14
 
@@ -19,6 +17,7 @@ const CONFIG = {
19
  'grok-2-imageGen': 'grok-latest',
20
  'grok-2-search': 'grok-latest',
21
  "grok-3": "grok-3",
 
22
  "grok-3-imageGen": "grok-3",
23
  "grok-3-deepsearch": "grok-3",
24
  "grok-3-reasoning": "grok-3"
@@ -28,21 +27,21 @@ const CONFIG = {
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,
44
  SSO_INDEX: 0,//sso的索引
45
- ISSHOW_SEARCH_RESULTS: process.env.ISSHOW_SEARCH_RESULTS === 'true',//是否显示搜索结果,默认关闭
46
  CHROME_PATH: process.env.CHROME_PATH || "/usr/bin/chromium"//chrome路径
47
  };
48
  puppeteer.use(StealthPlugin())
@@ -102,7 +101,6 @@ class AuthTokenManager {
102
  }
103
 
104
  getTokenByIndex(index, model) {
105
- if (!this.modelRateLimit[model]) return;
106
  if(this.activeTokens.length === 0){
107
  return null;
108
  }
@@ -112,6 +110,10 @@ class AuthTokenManager {
112
  }
113
 
114
  recordModelRequest(token, model) {
 
 
 
 
115
  if (!this.modelRateLimit[model]) return;
116
  const tokenFrequency = this.tokenModelFrequency.get(token);
117
  if (tokenFrequency && tokenFrequency[model] !== undefined) {
@@ -120,11 +122,17 @@ class AuthTokenManager {
120
  this.checkAndRemoveTokenIfLimitReached(token);
121
  }
122
  setModelLimit(index, model) {
 
 
 
123
  if (!this.modelRateLimit[model]) return;
124
  const tokenFrequency = this.tokenModelFrequency.get(this.activeTokens[index]);
125
  tokenFrequency[model] = 9999;
126
  }
127
  isTokenModelLimitReached(index, model) {
 
 
 
128
  if (!this.modelRateLimit[model]) return;
129
  const token = this.activeTokens[index];
130
  const tokenFrequency = this.tokenModelFrequency.get(token);
@@ -168,7 +176,6 @@ class AuthTokenManager {
168
  if (now - expiredTime >= 2 * 60 * 60 * 1000) {
169
  this.tokenModelUsage.set(token, {
170
  "grok-3": 0,
171
- "grok-3-imageGen": 0,
172
  "grok-3-deepsearch": 0,
173
  "grok-3-reasoning": 0
174
  });
@@ -191,7 +198,7 @@ class AuthTokenManager {
191
 
192
  class Utils {
193
  static async extractGrokHeaders() {
194
- console.log("开始提取头信息");
195
  try {
196
  // 启动浏览器
197
  const browser = await puppeteer.launch({
@@ -210,7 +217,6 @@ class Utils {
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,11 +231,11 @@ class Utils {
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,12 +243,12 @@ class Utils {
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,53 +258,6 @@ class Utils {
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) {
@@ -316,9 +275,9 @@ class Utils {
316
  });
317
  return formattedResults.join('\n\n');
318
  }
319
- static createAuthHeaders(model) {
320
  return {
321
- 'cookie': `${tokenManager.getTokenByIndex(CONFIG.SSO_INDEX, model)}`
322
  };
323
  }
324
  }
@@ -372,7 +331,7 @@ class GrokApiClient {
372
  content: imageBuffer
373
  }
374
  };
375
- console.log("发送图片请求");
376
  // 发送请求
377
  const response = await fetch(url, {
378
  method: 'POST',
@@ -384,35 +343,46 @@ class GrokApiClient {
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,7 +455,7 @@ class GrokApiClient {
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,
@@ -556,37 +526,39 @@ async function processModelResponse(linejosn, model) {
556
  }
557
  return result;
558
  }
559
- var token = linejosn?.token
560
- if (!token) return result;
561
  //非生图模型的处理
562
  switch (model) {
563
  case 'grok-2':
564
- result.token = token;
565
  return result;
566
  case 'grok-2-search':
 
567
  if (linejosn?.webSearchResults && CONFIG.ISSHOW_SEARCH_RESULTS) {
568
  result.token = `\r\n<think>${await Utils.organizeSearchResults(linejosn.webSearchResults)}</think>\r\n`;
569
  } else {
570
- result.token = token;
571
  }
572
  return result;
573
  case 'grok-3':
574
- result.token = token;
575
  return result;
576
  case 'grok-3-deepsearch':
577
  if (linejosn.messageTag === "final") {
578
- result.token = token;
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;
585
  } else if (CONFIG.IS_THINKING && !linejosn.isThinking) {
586
- result.token = "</think>" + token;
587
  CONFIG.IS_THINKING = false;
588
  } else {
589
- result.token = token;
590
  }
591
  return result;
592
  }
@@ -598,8 +570,8 @@ async function handleResponse(response, model, res, isStream) {
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,15 +580,16 @@ async function handleResponse(response, model, res, isStream) {
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,7 +616,6 @@ async function handleResponse(response, model, res, isStream) {
643
  })();
644
  dataPromises.push(processPromise);
645
  } catch (error) {
646
- console.log(error);
647
  continue;
648
  }
649
  }
@@ -663,14 +635,14 @@ async function handleResponse(response, model, res, isStream) {
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,18 +651,15 @@ async function handleResponse(response, model, res, isStream) {
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,16 +668,11 @@ async function handleImageResponse(imageUrl) {
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,13 +680,20 @@ async function handleImageResponse(imageUrl) {
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, {
@@ -742,7 +713,7 @@ async function handleImageResponse(imageUrl) {
742
  if (!responseURL.ok) {
743
  return "生图失败,请查看图床密钥是否设置正确"
744
  } else {
745
- console.log("生图成功");
746
  const result = await responseURL.json();
747
  return `![image](${result.image.url})`
748
  }
@@ -750,6 +721,7 @@ async function handleImageResponse(imageUrl) {
750
 
751
  const tokenManager = new AuthTokenManager();
752
  await initialization();
 
753
  // 中间件配置
754
  const app = express();
755
  app.use(Logger.requestLogger);
@@ -760,8 +732,8 @@ app.use(cors({
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",
767
  data: Object.keys(CONFIG.MODELS).map((model, index) => ({
@@ -774,7 +746,7 @@ app.get('/hf/v1/models', (req, res) => {
774
  });
775
 
776
 
777
- app.post('/hf/v1/chat/completions', async (req, res) => {
778
  try {
779
  const authToken = req.headers.authorization?.replace('Bearer ', '');
780
  if (authToken !== CONFIG.API.API_KEY) {
@@ -782,22 +754,20 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
782
  }
783
  let isTempCookie = req.body.model.includes("grok-2");
784
  let retryCount = 0;
 
 
785
 
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');
799
  } else {
800
- CONFIG.API.SIGNATURE_COOKIE = Utils.createAuthHeaders(req.body.model);
801
  }
802
  Logger.info(`当前令牌索引: ${CONFIG.SSO_INDEX}`, 'Server');
803
  const newMessageReq = await fetch(`${CONFIG.API.BASE_URL}/api/rpc`, {
@@ -816,7 +786,7 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
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,7 +805,7 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
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,7 +822,6 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
852
  }
853
  }
854
  } else {
855
- // 处理429错误
856
  if (response.status === 429) {
857
  if (isTempCookie) {
858
  await Utils.get_signature();
@@ -895,12 +864,11 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
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
  });
 
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
 
 
17
  'grok-2-imageGen': 'grok-latest',
18
  'grok-2-search': 'grok-latest',
19
  "grok-3": "grok-3",
20
+ "grok-3-search": "grok-3",
21
  "grok-3-imageGen": "grok-3",
22
  "grok-3-deepsearch": "grok-3",
23
  "grok-3-reasoning": "grok-3"
 
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,
34
  BODY_LIMIT: '5mb'
35
  },
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,
43
  SSO_INDEX: 0,//sso的索引
44
+ ISSHOW_SEARCH_RESULTS: process.env.ISSHOW_SEARCH_RESULTS === 'true',//是否显示搜索结果
45
  CHROME_PATH: process.env.CHROME_PATH || "/usr/bin/chromium"//chrome路径
46
  };
47
  puppeteer.use(StealthPlugin())
 
101
  }
102
 
103
  getTokenByIndex(index, model) {
 
104
  if(this.activeTokens.length === 0){
105
  return null;
106
  }
 
110
  }
111
 
112
  recordModelRequest(token, model) {
113
+ if(model === 'grok-3-search' || model === 'grok-3-imageGen'){
114
+ model = 'grok-3';
115
+ }
116
+
117
  if (!this.modelRateLimit[model]) return;
118
  const tokenFrequency = this.tokenModelFrequency.get(token);
119
  if (tokenFrequency && tokenFrequency[model] !== undefined) {
 
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;
129
  const tokenFrequency = this.tokenModelFrequency.get(this.activeTokens[index]);
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;
137
  const token = this.activeTokens[index];
138
  const tokenFrequency = this.tokenModelFrequency.get(token);
 
176
  if (now - expiredTime >= 2 * 60 * 60 * 1000) {
177
  this.tokenModelUsage.set(token, {
178
  "grok-3": 0,
 
179
  "grok-3-deepsearch": 0,
180
  "grok-3-reasoning": 0
181
  });
 
198
 
199
  class Utils {
200
  static async extractGrokHeaders() {
201
+ Logger.info("开始提取头信息", 'Server');
202
  try {
203
  // 启动浏览器
204
  const browser = await puppeteer.launch({
 
217
  await page.evaluate(() => {
218
  return new Promise(resolve => setTimeout(resolve, 5000))
219
  })
 
220
  // 获取所有 Cookies
221
  const cookies = await page.cookies();
222
  const targetHeaders = ['x-anonuserid', 'x-challenge', 'x-signature'];
 
231
  // 关闭浏览器
232
  await browser.close();
233
  // 打印并返回提取的头信息
234
+ Logger.info('提取的头信息:', JSON.stringify(extractedHeaders, null, 2), 'Server');
235
  return extractedHeaders;
236
 
237
  } catch (error) {
238
+ Logger.error('获取头信息出错:', error, 'Server');
239
  return null;
240
  }
241
  }
 
243
  if (CONFIG.API.TEMP_COOKIE) {
244
  return CONFIG.API.TEMP_COOKIE;
245
  }
246
+ Logger.info("刷新认证信息", 'Server');
247
  let retryCount = 0;
248
  while (!CONFIG.API.TEMP_COOKIE || retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
249
  let headers = await Utils.extractGrokHeaders();
250
  if (headers) {
251
+ Logger.info("获取认证信息成功", 'Server');
252
  CONFIG.API.TEMP_COOKIE = { cookie: `x-anonuserid=${headers["x-anonuserid"]}; x-challenge=${headers["x-challenge"]}; x-signature=${headers["x-signature"]}` };
253
  return;
254
  }
 
258
  }
259
  }
260
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  static async organizeSearchResults(searchResults) {
262
  // 确保传入的是有效的搜索结果对象
263
  if (!searchResults || !searchResults.results) {
 
275
  });
276
  return formattedResults.join('\n\n');
277
  }
278
+ static async createAuthHeaders(model) {
279
  return {
280
+ 'cookie': `${await tokenManager.getTokenByIndex(CONFIG.SSO_INDEX, model)}`
281
  };
282
  }
283
  }
 
331
  content: imageBuffer
332
  }
333
  };
334
+ Logger.info("发送图片请求", 'Server');
335
  // 发送请求
336
  const response = await fetch(url, {
337
  method: 'POST',
 
343
  });
344
 
345
  if (!response.ok) {
346
+ Logger.error(`上传图片失败,状态码:${response.status},原因:${response.error}`, 'Server');
347
  return '';
348
  }
349
 
350
  const result = await response.json();
351
+ Logger.info('上传图片成功:', result, 'Server');
352
  return result.fileMetadataId;
353
 
354
  } catch (error) {
355
+ Logger.error(error, 'Server');
356
  return '';
357
  }
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') {
368
+ const lastMessage = todoMessages[todoMessages.length - 1];
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) => {
 
455
  return {
456
  modelName: this.modelId,
457
  message: messages.trim(),
458
+ fileAttachments: fileAttachments.slice(0, 4),
459
  imageAttachments: [],
460
  disableSearch: false,
461
  enableImageGeneration: true,
 
526
  }
527
  return result;
528
  }
529
+
 
530
  //非生图模型的处理
531
  switch (model) {
532
  case 'grok-2':
533
+ result.token = linejosn?.token;
534
  return result;
535
  case 'grok-2-search':
536
+ case 'grok-3-search':
537
  if (linejosn?.webSearchResults && CONFIG.ISSHOW_SEARCH_RESULTS) {
538
  result.token = `\r\n<think>${await Utils.organizeSearchResults(linejosn.webSearchResults)}</think>\r\n`;
539
  } else {
540
+ result.token = linejosn?.token;
541
  }
542
  return result;
543
  case 'grok-3':
544
+ result.token = linejosn?.token;
545
  return result;
546
  case 'grok-3-deepsearch':
547
  if (linejosn.messageTag === "final") {
548
+ result.token = linejosn?.token;
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 {
561
+ result.token = linejosn?.token;
562
  }
563
  return result;
564
  }
 
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');
 
580
  for (const line of lines) {
581
  if (!line.trim()) continue;
582
  const trimmedLine = line.trim();
 
583
  if (trimmedLine.startsWith('data: ')) {
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) {
 
616
  })();
617
  dataPromises.push(processPromise);
618
  } catch (error) {
 
619
  continue;
620
  }
621
  }
 
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) {
 
651
  CONFIG.IS_IMG_GEN2 = false;
652
  throw error;
653
  }
 
654
  }
655
 
656
  async function handleImageResponse(imageUrl) {
657
+ const MAX_RETRIES = 2;
 
658
  let retryCount = 0;
659
  let imageBase64Response;
660
 
661
  while (retryCount < MAX_RETRIES) {
662
  try {
 
663
  imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
664
  method: 'GET',
665
  headers: {
 
668
  }
669
  });
670
 
671
+ if (imageBase64Response.ok) break;
 
 
 
672
  retryCount++;
673
  if (retryCount === MAX_RETRIES) {
674
  throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
675
  }
 
 
676
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
677
 
678
  } catch (error) {
 
680
  if (retryCount === MAX_RETRIES) {
681
  throw error;
682
  }
 
683
  await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
684
  }
685
  }
686
 
687
+
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, {
 
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
  }
 
721
 
722
  const tokenManager = new AuthTokenManager();
723
  await initialization();
724
+
725
  // 中间件配置
726
  const app = express();
727
  app.use(Logger.requestLogger);
 
732
  methods: ['GET', 'POST', 'OPTIONS'],
733
  allowedHeaders: ['Content-Type', 'Authorization']
734
  }));
735
+
736
+ app.get('/v1/models', (req, res) => {
737
  res.json({
738
  object: "list",
739
  data: Object.keys(CONFIG.MODELS).map((model, index) => ({
 
746
  });
747
 
748
 
749
+ app.post('/v1/chat/completions', async (req, res) => {
750
  try {
751
  const authToken = req.headers.authorization?.replace('Bearer ', '');
752
  if (authToken !== CONFIG.API.API_KEY) {
 
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) {
763
  await Utils.get_signature();
764
  }
765
+
766
  if (isTempCookie) {
767
  CONFIG.API.SIGNATURE_COOKIE = CONFIG.API.TEMP_COOKIE;
768
  Logger.info(`已切换为临时令牌`, 'Server');
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`, {
 
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',
792
  headers: {
 
805
  Logger.info(`当前剩余可用令牌数: ${tokenManager.getTokenCount()}`, 'Server');
806
  try {
807
  await handleResponse(response, req.body.model, res, req.body.stream);
808
+ return;
809
  } catch (error) {
810
  if(isTempCookie){
811
  await Utils.get_signature();
 
822
  }
823
  }
824
  } else {
 
825
  if (response.status === 429) {
826
  if (isTempCookie) {
827
  await Utils.get_signature();
 
864
  });
865
 
866
 
 
867
  app.use((req, res) => {
868
  res.status(200).send('api运行正常');
869
  });
870
 
871
+
872
  app.listen(CONFIG.SERVER.PORT, () => {
873
+ Logger.info(`服务器已启动,监听端口: ${CONFIG.SERVER.PORT}`, 'Server');
874
  });