yxmiler commited on
Commit
541e463
·
verified ·
1 Parent(s): 85d9b4d

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +229 -186
index.js CHANGED
@@ -1,24 +1,25 @@
1
  import express from 'express';
2
  import fetch from 'node-fetch';
 
3
  import dotenv from 'dotenv';
4
  import cors from 'cors';
5
- import { launch } from 'puppeteer';
6
  import { v4 as uuidv4 } from 'uuid';
7
- import path from 'path';
8
- import fs from 'fs';
9
 
10
  dotenv.config();
11
  // 配置常量
12
  const CONFIG = {
13
  MODELS: {
14
  'grok-latest': 'grok-latest',
15
- 'grok-latest-image': '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
  },
23
  SERVER: {
24
  PORT: process.env.PORT || 3000,
@@ -28,7 +29,7 @@ const CONFIG = {
28
  MAX_ATTEMPTS: 3,//重试次数
29
  DELAY_BASE: 1000 // 基础延迟时间(毫秒)
30
  },
31
- CHROME_PATH: process.env.CHROME_PATH || 'C:/Program Files/Google/Chrome/Application/chrome.exe'// 替换为你的 Chrome 实际路径
32
  };
33
 
34
 
@@ -51,20 +52,24 @@ const DEFAULT_HEADERS = {
51
  'baggage': 'sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c'
52
  };
53
 
54
- // 定义签名文件路径为 /tmp 目录
55
- const SIGNATURE_FILE_PATH = '/tmp/signature.json';
56
-
57
  class Utils {
58
  static async extractGrokHeaders() {
 
59
  try {
60
  // 启动浏览器
61
- const browser = await launch({
62
- executablePath: CONFIG.CHROME_PATH,
63
- headless: true
 
 
 
 
 
 
64
  });
65
 
66
  const page = await browser.newPage();
67
- await page.goto(`${CONFIG.API.BASE_URL}`, { waitUntil: 'networkidle0' });
68
 
69
  // 获取所有 Cookies
70
  const cookies = await page.cookies();
@@ -89,6 +94,9 @@ class Utils {
89
  }
90
  }
91
  static async get_signature() {
 
 
 
92
  console.log("刷新认证信息");
93
  let retryCount = 0;
94
  while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
@@ -96,13 +104,7 @@ class Utils {
96
  if (headers) {
97
  console.log("获取认证信息成功");
98
  CONFIG.API.SIGNATURE_COOKIE = { cookie: `x-anonuserid=${headers["x-anonuserid"]}; x-challenge=${headers["x-challenge"]}; x-signature=${headers["x-signature"]}` };
99
- try {
100
- fs.writeFileSync(SIGNATURE_FILE_PATH, JSON.stringify(CONFIG.API.SIGNATURE_COOKIE));//保存认证信息
101
- return CONFIG.API.SIGNATURE_COOKIE;
102
- } catch (error) {
103
- console.error('写入签名文件失败:', error);
104
- return CONFIG.API.SIGNATURE_COOKIE;
105
- }
106
  }
107
  retryCount++;
108
  if (retryCount >= CONFIG.RETRY.MAX_ATTEMPTS) {
@@ -125,8 +127,6 @@ class Utils {
125
  }
126
  }
127
  static async handleError(error, res, originalRequest = null) {
128
- console.error('Error:', error);
129
-
130
  // 如果是500错误且提供了原始请求函数,尝试重新获取签名并重试
131
  if (error.status === 500 && originalRequest) {
132
  try {
@@ -155,71 +155,22 @@ class Utils {
155
  }
156
  });
157
  }
 
 
 
 
 
158
 
159
- static async makeRequest(req, res) {
160
- try {
161
- if (!CONFIG.API.SIGNATURE_COOKIE) {
162
- await Utils.get_signature();
163
- try {
164
- CONFIG.API.SIGNATURE_COOKIE = JSON.parse(fs.readFileSync(SIGNATURE_FILE_PATH));
165
- } catch (error) {
166
- console.error('读取签名文件失败:', error);
167
- await Utils.get_signature(); // 如果读取失败,重新获取签名
168
- }
169
- }
170
- const grokClient = new GrokApiClient(req.body.model);
171
- const requestPayload = await grokClient.prepareChatRequest(req.body);
172
- //创建新对话
173
- const newMessageReq = await fetch(`${CONFIG.API.BASE_URL}/api/rpc`, {
174
- method: 'POST',
175
- headers: {
176
- ...DEFAULT_HEADERS,
177
- ...CONFIG.API.SIGNATURE_COOKIE
178
- },
179
- body: JSON.stringify({
180
- rpc: "createConversation",
181
- req: {
182
- temporary: false
183
- }
184
- })
185
- });
186
-
187
- if (!newMessageReq.ok) {
188
- throw new Error(`上游服务请求失败! status: ${newMessageReq.status}`);
189
- }
190
-
191
- // 获取响应文本
192
- const responseText = await newMessageReq.json();
193
- const conversationId = responseText.conversationId;
194
- console.log("会话ID:conversationId", conversationId);
195
- if (!conversationId) {
196
- throw new Error(`创建会话失败! status: ${newMessageReq.status}`);
197
- }
198
- //发送对话
199
- const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
200
- method: 'POST',
201
- headers: {
202
- "accept": "text/event-stream",
203
- "baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
204
- "content-type": "text/plain;charset=UTF-8",
205
- "Connection": "keep-alive",
206
- ...CONFIG.API.SIGNATURE_COOKIE
207
- },
208
- body: JSON.stringify(requestPayload)
209
- });
210
-
211
- if (!response.ok) {
212
- throw new Error(`上游服务请求失败! status: ${response.status}`);
213
- }
214
 
215
- if (req.body.stream) {
216
- await handleStreamResponse(response, req.body.model, res);
217
- } else {
218
- await handleNormalResponse(response, req.body.model, res);
219
- }
220
- } catch (error) {
221
- throw error;
222
- }
223
  }
224
  }
225
 
@@ -299,6 +250,20 @@ class GrokApiClient {
299
  }
300
 
301
  async prepareChatRequest(request) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  const processImageUrl = async (content) => {
303
  if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
304
  const imageResponse = await this.uploadBase64Image(
@@ -309,12 +274,8 @@ class GrokApiClient {
309
  }
310
  return null;
311
  };
312
- let fileAttachments = [];
313
- let messages = '';
314
- let lastRole = null;
315
- let lastContent = '';
316
 
317
- for (const current of request.messages) {
318
  const role = current.role === 'assistant' ? 'assistant' : 'user';
319
  let textContent = '';
320
  // 处理消息内容
@@ -323,7 +284,7 @@ class GrokApiClient {
323
  for (const item of current.content) {
324
  if (item.type === 'image_url') {
325
  // 如果是图片且是最后一条消息,则处理图片
326
- if (current === request.messages[request.messages.length - 1]) {
327
  const processedImage = await processImageUrl(item);
328
  if (processedImage) fileAttachments.push(processedImage);
329
  }
@@ -336,7 +297,7 @@ class GrokApiClient {
336
  // 处理单个对象内容
337
  if (current.content.type === 'image_url') {
338
  // 如果是图片且是最后一条消息,则处理图片
339
- if (current === request.messages[request.messages.length - 1]) {
340
  const processedImage = await processImageUrl(current.content);
341
  if (processedImage) fileAttachments.push(processedImage);
342
  }
@@ -361,7 +322,7 @@ class GrokApiClient {
361
  lastContent = textContent;
362
  lastRole = role;
363
  }
364
- } else if (current === request.messages[request.messages.length - 1] && fileAttachments.length > 0) {
365
  // 如果是最后一条消息且有图片附件,添加空消息占位
366
  messages += `${role.toUpperCase()}: [图片]\n`;
367
  }
@@ -373,6 +334,9 @@ class GrokApiClient {
373
 
374
  messages = messages.trim();
375
 
 
 
 
376
  return {
377
  message: messages,
378
  modelName: this.modelId,
@@ -385,11 +349,11 @@ class GrokApiClient {
385
  imageGenerationCount: 1,
386
  toolOverrides: {
387
  imageGen: request.model === 'grok-latest-image',
388
- webSearch: false,
389
- xSearch: false,
390
- xMediaSearch: false,
391
- trendsSearch: false,
392
- xPostAnalyze: false
393
  }
394
  };
395
  }
@@ -416,9 +380,6 @@ class MessageProcessor {
416
  };
417
  }
418
 
419
- // 如果是数组(图片响应),直接使用整个数组作为content
420
- const messageContent = Array.isArray(message) ? message : message;
421
-
422
  return {
423
  ...baseResponse,
424
  object: 'chat.completion',
@@ -426,7 +387,7 @@ class MessageProcessor {
426
  index: 0,
427
  message: {
428
  role: 'assistant',
429
- content: messageContent
430
  },
431
  finish_reason: 'stop'
432
  }],
@@ -437,8 +398,8 @@ class MessageProcessor {
437
 
438
  // 中间件配置
439
  const app = express();
440
- app.use(express.json({ limit: '5mb' }));
441
- app.use(express.urlencoded({ extended: true, limit: '5mb' }));
442
  app.use(cors({
443
  origin: '*',
444
  methods: ['GET', 'POST', 'OPTIONS'],
@@ -462,11 +423,63 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
462
  if (authToken !== CONFIG.API.API_KEY) {
463
  return res.status(401).json({ error: 'Unauthorized' });
464
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  try {
467
- await Utils.makeRequest(req, res);
468
  } catch (error) {
469
- await Utils.handleError(error, res);
470
  }
471
  });
472
 
@@ -489,8 +502,22 @@ async function handleStreamResponse(response, model, res) {
489
  continue;
490
  }
491
  let linejosn = JSON.parse(data);
 
 
 
 
 
 
 
492
  switch (model) {
493
- case "grok-latest":
 
 
 
 
 
 
 
494
  if (linejosn.response === "token") {
495
  const token = linejosn.token;
496
  if (token && token.length > 0) {
@@ -499,15 +526,19 @@ async function handleStreamResponse(response, model, res) {
499
  }
500
  }
501
  break;
502
- case "grok-latest-image":
503
- if (linejosn.response === "modelResponse") {
504
- if (linejosn?.modelResponse?.generatedImageUrls?.length > 0) {
505
- let imageUrl = linejosn.modelResponse.generatedImageUrls[0]; // 获取第一个URL
506
- const dataImage = await handleImageResponse(imageUrl);
507
- const responseData = MessageProcessor.createChatResponse(dataImage, model, true);
508
  res.write(`data: ${JSON.stringify(responseData)}\n\n`);
509
  }
510
  }
 
 
 
 
 
511
  break;
512
  }
513
  }
@@ -524,7 +555,6 @@ async function handleNormalResponse(response, model, res) {
524
 
525
  try {
526
  const responseText = await response.text();
527
- console.log('原始响应文本:', responseText);
528
  const lines = responseText.split('\n');
529
 
530
  for (const line of lines) {
@@ -533,101 +563,114 @@ async function handleNormalResponse(response, model, res) {
533
  const data = line.slice(6);
534
  if (data === '[DONE]') continue;
535
  let linejosn = JSON.parse(data);
536
- console.log('解析的JSON数据:', linejosn);
537
-
538
- if (linejosn.response === "token") {
539
- const token = linejosn.token;
540
- if (token && token.length > 0) {
541
- fullResponse += token;
542
- }
543
- } else if (linejosn.response === "modelResponse" && model === 'grok-latest-image') {
544
- console.log('检测到图片响应:', linejosn.modelResponse);
545
- if (linejosn?.modelResponse?.generatedImageUrls?.length > 0) {
546
- imageUrl = linejosn.modelResponse.generatedImageUrls[0]; // 获取第一个URL
547
- console.log('获取到图片URL:', imageUrl);
548
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
549
  }
550
  }
551
-
552
  if (imageUrl) {
553
- console.log('开始处理图片URL:', imageUrl);
554
  const dataImage = await handleImageResponse(imageUrl);
555
-
556
- const responseData = MessageProcessor.createChatResponse([dataImage], model);
557
- console.log('最终的响应对象:', JSON.stringify(responseData, null, 2));
558
  res.json(responseData);
559
  } else {
560
- console.log('没有图片URL,返回文本响应:', fullResponse);
561
  const responseData = MessageProcessor.createChatResponse(fullResponse, model);
562
  res.json(responseData);
563
  }
564
  } catch (error) {
565
- console.error('处理响应时发生错误:', error);
566
  Utils.handleError(error, res);
567
  }
568
  }
569
-
570
  async function handleImageResponse(imageUrl) {
571
- try {
572
- //对服务器发送图片请求
573
- const MAX_RETRIES = 3;
574
- let retryCount = 0;
575
- let imageBase64Response;
576
-
577
- while (retryCount < MAX_RETRIES) {
578
- try {
579
- //发送图片请求获取图片
580
- imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
581
- method: 'GET',
582
- headers: {
583
- ...DEFAULT_HEADERS,
584
- ...CONFIG.API.SIGNATURE_COOKIE
585
- }
586
- });
587
 
588
- if (imageBase64Response.ok) {
589
- break; // 如果请求成功,跳出重试循环
590
- }
591
-
592
- retryCount++;
593
- if (retryCount === MAX_RETRIES) {
594
- throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
 
595
  }
 
596
 
597
- // 等待一段时间后重试(可以使用指数退避)
598
- await new Promise(resolve => setTimeout(resolve, 500 * retryCount));
599
-
600
- } catch (error) {
601
- retryCount++;
602
- if (retryCount === MAX_RETRIES) {
603
- throw error;
604
- }
605
- // 等待一段时间后重试
606
- await new Promise(resolve => setTimeout(resolve, 500 * retryCount));
607
  }
608
- }
609
 
610
- const arrayBuffer = await imageBase64Response.arrayBuffer();
611
- const imagebuffer = Buffer.from(arrayBuffer);
612
-
613
- const base64String = imagebuffer.toString('base64');
614
 
615
- // 获取图片类型(例如:image/jpeg, image/png)
616
- const imageContentType = imageBase64Response.headers.get('content-type');
617
 
618
- // 修改返回结构,确保返回Markdown格式的图片标签
619
- return {
620
- type: "image_url",
621
- image_url: {
622
- url: `data:image/jpg;base64,${base64String}`
623
  }
624
- };
 
 
 
625
 
626
- // 或者如果需要直接返回可显示的字符串
627
- // return `![generated image](data:image/jpg;base64,${base64String})`;
628
- } catch (error) {
629
- console.error('图片处理失败:', error);
630
- return '图片生成失败';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
631
  }
632
  }
633
 
 
1
  import express from 'express';
2
  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: {
25
  PORT: process.env.PORT || 3000,
 
29
  MAX_ATTEMPTS: 3,//重试次数
30
  DELAY_BASE: 1000 // 基础延迟时间(毫秒)
31
  },
32
+ ISSHOW_SEARCH_RESULTS: process.env.ISSHOW_SEARCH_RESULTS || true//是否显示搜索结果,默认开启
33
  };
34
 
35
 
 
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({
61
+ headless: true,
62
+ args: [
63
+ '--no-sandbox',
64
+ '--disable-setuid-sandbox',
65
+ '--disable-dev-shm-usage',
66
+ '--disable-gpu'
67
+ ],
68
+ executablePath: CONFIG.CHROME_PATH
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();
 
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) {
 
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) {
 
127
  }
128
  }
129
  static async handleError(error, res, originalRequest = null) {
 
 
130
  // 如果是500错误且提供了原始请求函数,尝试重新获取签名并重试
131
  if (error.status === 500 && originalRequest) {
132
  try {
 
155
  }
156
  });
157
  }
158
+ static async organizeSearchResults(searchResults) {
159
+ // 确保传入的是有效的搜索结果对象
160
+ if (!searchResults || !searchResults.results) {
161
+ return '';
162
+ }
163
 
164
+ const results = searchResults.results;
165
+ const formattedResults = results.map((result, index) => {
166
+ // 处理可能为空的字段
167
+ const title = result.title || '未知标题';
168
+ const url = result.url || '#';
169
+ const preview = result.preview || '无预览内容';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
+ return `\r\n<details><summary>资料[${index}]: ${title}</summary>\r\n${preview}\r\n\n[Link](${url})\r\n</details>`;
172
+ });
173
+ return formattedResults.join('\n\n');
 
 
 
 
 
174
  }
175
  }
176
 
 
250
  }
251
 
252
  async prepareChatRequest(request) {
253
+
254
+ if (request.model === 'grok-latest-image' && !CONFIG.API.PICGO_KEY) {
255
+ throw new Error(`该模型需要配置PICGO图床密钥!`);
256
+ }
257
+ var todoMessages = request.messages;
258
+ if (request.model === 'grok-latest-image' || request.model === 'grok-latest-search') {
259
+ todoMessages = Array.isArray(todoMessages) ? [todoMessages[todoMessages.length - 1]] : [todoMessages];;
260
+ }
261
+ let fileAttachments = [];
262
+ let messages = '';
263
+ let lastRole = null;
264
+ let lastContent = '';
265
+ let search = false;
266
+
267
  const processImageUrl = async (content) => {
268
  if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
269
  const imageResponse = await this.uploadBase64Image(
 
274
  }
275
  return null;
276
  };
 
 
 
 
277
 
278
+ for (const current of todoMessages) {
279
  const role = current.role === 'assistant' ? 'assistant' : 'user';
280
  let textContent = '';
281
  // 处理消息内容
 
284
  for (const item of current.content) {
285
  if (item.type === 'image_url') {
286
  // 如果是图片且是最后一条消息,则处理图片
287
+ if (current === todoMessages[todoMessages.length - 1]) {
288
  const processedImage = await processImageUrl(item);
289
  if (processedImage) fileAttachments.push(processedImage);
290
  }
 
297
  // 处理单个对象内容
298
  if (current.content.type === 'image_url') {
299
  // 如果是图片且是最后一条消息,则处理图片
300
+ if (current === todoMessages[todoMessages.length - 1]) {
301
  const processedImage = await processImageUrl(current.content);
302
  if (processedImage) fileAttachments.push(processedImage);
303
  }
 
322
  lastContent = textContent;
323
  lastRole = role;
324
  }
325
+ } else if (current === todoMessages[todoMessages.length - 1] && fileAttachments.length > 0) {
326
  // 如果是最后一条消息且有图片附件,添加空消息占位
327
  messages += `${role.toUpperCase()}: [图片]\n`;
328
  }
 
334
 
335
  messages = messages.trim();
336
 
337
+ if (request.model === 'grok-latest-search') {
338
+ search = true;
339
+ }
340
  return {
341
  message: messages,
342
  modelName: this.modelId,
 
349
  imageGenerationCount: 1,
350
  toolOverrides: {
351
  imageGen: request.model === 'grok-latest-image',
352
+ webSearch: search,
353
+ xSearch: search,
354
+ xMediaSearch: search,
355
+ trendsSearch: search,
356
+ xPostAnalyze: search
357
  }
358
  };
359
  }
 
380
  };
381
  }
382
 
 
 
 
383
  return {
384
  ...baseResponse,
385
  object: 'chat.completion',
 
387
  index: 0,
388
  message: {
389
  role: 'assistant',
390
+ content: message
391
  },
392
  finish_reason: 'stop'
393
  }],
 
398
 
399
  // 中间件配置
400
  const app = express();
401
+ app.use(express.json({ limit: CONFIG.SERVER.BODY_LIMIT }));
402
+ app.use(express.urlencoded({ extended: true, limit: CONFIG.SERVER.BODY_LIMIT }));
403
  app.use(cors({
404
  origin: '*',
405
  methods: ['GET', 'POST', 'OPTIONS'],
 
423
  if (authToken !== CONFIG.API.API_KEY) {
424
  return res.status(401).json({ error: 'Unauthorized' });
425
  }
426
+ const makeRequest = async () => {
427
+ if (!CONFIG.API.SIGNATURE_COOKIE) {
428
+ await Utils.get_signature();
429
+ }
430
+ const grokClient = new GrokApiClient(req.body.model);
431
+ const requestPayload = await grokClient.prepareChatRequest(req.body);
432
+ //创建新对话
433
+ const newMessageReq = await fetch(`${CONFIG.API.BASE_URL}/api/rpc`, {
434
+ method: 'POST',
435
+ headers: {
436
+ ...DEFAULT_HEADERS,
437
+ ...CONFIG.API.SIGNATURE_COOKIE
438
+ },
439
+ body: JSON.stringify({
440
+ rpc: "createConversation",
441
+ req: {
442
+ temporary: false
443
+ }
444
+ })
445
+ });
446
+ if (!newMessageReq.ok) {
447
+ throw new Error(`上游服务请求失败! status: ${newMessageReq.status}`);
448
+ }
449
 
450
+ // 获取响应文本
451
+ const responseText = await newMessageReq.json();
452
+ const conversationId = responseText.conversationId;
453
+ console.log("会话ID:conversationId", conversationId);
454
+ if (!conversationId) {
455
+ throw new Error(`创建会话失败! status: ${newMessageReq.status}`);
456
+ }
457
+ //发送对话
458
+ const response = await fetch(`${CONFIG.API.BASE_URL}/api/conversations/${conversationId}/responses`, {
459
+ method: 'POST',
460
+ headers: {
461
+ "accept": "text/event-stream",
462
+ "baggage": "sentry-public_key=b311e0f2690c81f25e2c4cf6d4f7ce1c",
463
+ "content-type": "text/plain;charset=UTF-8",
464
+ "Connection": "keep-alive",
465
+ ...CONFIG.API.SIGNATURE_COOKIE
466
+ },
467
+ body: JSON.stringify(requestPayload)
468
+ });
469
+
470
+ if (!response.ok) {
471
+ throw new Error(`上游服务请求失败! status: ${response.status}`);
472
+ }
473
+ if (req.body.stream) {
474
+ await handleStreamResponse(response, req.body.model, res);
475
+ } else {
476
+ await handleNormalResponse(response, req.body.model, res);
477
+ }
478
+ }
479
  try {
480
+ await makeRequest();
481
  } catch (error) {
482
+ await Utils.handleError(error, res, makeRequest);
483
  }
484
  });
485
 
 
502
  continue;
503
  }
504
  let linejosn = JSON.parse(data);
505
+ if (linejosn?.error?.name === "RateLimitError") {
506
+ var responseData = MessageProcessor.createChatResponse(`${linejosn.error.name},请重新对话`, model, true);
507
+ CONFIG.API.SIGNATURE_COOKIE = null;
508
+ console.log("认证信息已删除");
509
+ res.write(`data: ${JSON.stringify(responseData)}\n\n`);
510
+ return res.end();
511
+ }
512
  switch (model) {
513
+ case 'grok-latest-image':
514
+ if (linejosn.response === "modelResponse" && linejosn?.modelResponse?.generatedImageUrls) {
515
+ const dataImage = await handleImageResponse(linejosn.modelResponse.generatedImageUrls);
516
+ const responseData = MessageProcessor.createChatResponse(dataImage, model, true);
517
+ res.write(`data: ${JSON.stringify(responseData)}\n\n`);
518
+ }
519
+ break;
520
+ case 'grok-latest':
521
  if (linejosn.response === "token") {
522
  const token = linejosn.token;
523
  if (token && token.length > 0) {
 
526
  }
527
  }
528
  break;
529
+ case 'grok-latest-search':
530
+ if (linejosn.response === "token") {
531
+ const token = linejosn.token;
532
+ if (token && token.length > 0) {
533
+ const responseData = MessageProcessor.createChatResponse(token, model, true);
 
534
  res.write(`data: ${JSON.stringify(responseData)}\n\n`);
535
  }
536
  }
537
+ if (linejosn.response === "webSearchResults" && CONFIG.ISSHOW_SEARCH_RESULTS) {
538
+ const searchResults = await Utils.organizeSearchResults(linejosn.webSearchResults);
539
+ const responseData = MessageProcessor.createChatResponse(`<thinking>\r\n${searchResults}\r\n</thinking>\r\n`, model, true);
540
+ res.write(`data: ${JSON.stringify(responseData)}\n\n`);
541
+ }
542
  break;
543
  }
544
  }
 
555
 
556
  try {
557
  const responseText = await response.text();
 
558
  const lines = responseText.split('\n');
559
 
560
  for (const line of lines) {
 
563
  const data = line.slice(6);
564
  if (data === '[DONE]') continue;
565
  let linejosn = JSON.parse(data);
566
+ if (linejosn?.error?.name === "RateLimitError") {
567
+ fullResponse = `${linejosn.error.name},请重新对话`;
568
+ CONFIG.API.SIGNATURE_COOKIE = null;
569
+ console.log("认证信息已删除");
570
+ return res.json(MessageProcessor.createChatResponse(fullResponse, model));
571
+ }
572
+ switch (model) {
573
+ case 'grok-latest-image':
574
+ if (linejosn.response === "modelResponse" && linejosn?.modelResponse?.generatedImageUrls) {
575
+ imageUrl = linejosn.modelResponse.generatedImageUrls;
576
+ }
577
+ break;
578
+ case 'grok-latest':
579
+ if (linejosn.response === "token") {
580
+ const token = linejosn.token;
581
+ if (token && token.length > 0) {
582
+ fullResponse += token;
583
+ }
584
+ }
585
+ break;
586
+ case 'grok-latest-search':
587
+ if (linejosn.response === "token") {
588
+ const token = linejosn.token;
589
+ if (token && token.length > 0) {
590
+ fullResponse += token;
591
+ }
592
+ }
593
+ if (linejosn.response === "webSearchResults" && CONFIG.ISSHOW_SEARCH_RESULTS) {
594
+ fullResponse += `\r\n<thinking>${await Utils.organizeSearchResults(linejosn.webSearchResults)}</thinking>\r\n`;
595
+ }
596
+ break;
597
  }
598
  }
 
599
  if (imageUrl) {
 
600
  const dataImage = await handleImageResponse(imageUrl);
601
+ const responseData = MessageProcessor.createChatResponse(dataImage, model);
 
 
602
  res.json(responseData);
603
  } else {
 
604
  const responseData = MessageProcessor.createChatResponse(fullResponse, model);
605
  res.json(responseData);
606
  }
607
  } catch (error) {
 
608
  Utils.handleError(error, res);
609
  }
610
  }
 
611
  async function handleImageResponse(imageUrl) {
612
+ //对服务器发送图片请求
613
+ const MAX_RETRIES = 3;
614
+ let retryCount = 0;
615
+ let imageBase64Response;
 
 
 
 
 
 
 
 
 
 
 
 
616
 
617
+ while (retryCount < MAX_RETRIES) {
618
+ try {
619
+ //发送图片请求获取图片
620
+ imageBase64Response = await fetch(`https://assets.grok.com/${imageUrl}`, {
621
+ method: 'GET',
622
+ headers: {
623
+ ...DEFAULT_HEADERS,
624
+ ...CONFIG.API.SIGNATURE_COOKIE
625
  }
626
+ });
627
 
628
+ if (imageBase64Response.ok) {
629
+ break; // 如果请求成功,跳出重试循环
 
 
 
 
 
 
 
 
630
  }
 
631
 
632
+ retryCount++;
633
+ if (retryCount === MAX_RETRIES) {
634
+ throw new Error(`上游服务请求失败! status: ${imageBase64Response.status}`);
635
+ }
636
 
637
+ // 等待一段时间后重试(可以使用指数退避)
638
+ await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
639
 
640
+ } catch (error) {
641
+ retryCount++;
642
+ if (retryCount === MAX_RETRIES) {
643
+ throw error;
 
644
  }
645
+ // 等待一段时间后重试
646
+ await new Promise(resolve => setTimeout(resolve, CONFIG.API.RETRY_TIME * retryCount));
647
+ }
648
+ }
649
 
650
+ const arrayBuffer = await imageBase64Response.arrayBuffer();
651
+ const imageBuffer = Buffer.from(arrayBuffer);
652
+ const formData = new FormData();
653
+
654
+ formData.append('source', imageBuffer, {
655
+ filename: '2223.jpg',
656
+ contentType: 'image/jpeg'
657
+ });
658
+ const formDataHeaders = formData.getHeaders();
659
+ const responseURL = await fetch("https://www.picgo.net/api/1/upload", {
660
+ method: "POST",
661
+ headers: {
662
+ ...formDataHeaders,
663
+ "Content-Type": "multipart/form-data",
664
+ "X-API-Key": CONFIG.API.PICGO_KEY
665
+ },
666
+ body: formData
667
+ });
668
+ if (!responseURL.ok) {
669
+ console.error("上传图床失败");
670
+ return "生图失败,请查看图床密钥是否设置正确"
671
+ } else {
672
+ const result = await responseURL.json();
673
+ return `![image](${result.image.url})`
674
  }
675
  }
676