Spaces:
Paused
Paused
Update index.js
Browse files
index.js
CHANGED
|
@@ -26,7 +26,7 @@ const CONFIG = {
|
|
| 26 |
API_KEY: process.env.API_KEY || "sk-123456",
|
| 27 |
SIGNATURE_COOKIE: null,
|
| 28 |
TEMP_COOKIE: null,
|
| 29 |
-
PICGO_KEY:
|
| 30 |
},
|
| 31 |
SERVER: {
|
| 32 |
PORT: process.env.PORT || 3000,
|
|
@@ -35,6 +35,7 @@ const CONFIG = {
|
|
| 35 |
RETRY: {
|
| 36 |
MAX_ATTEMPTS: 2//重试次数
|
| 37 |
},
|
|
|
|
| 38 |
IS_THINKING: false,
|
| 39 |
IS_IMG_GEN: false,
|
| 40 |
IS_IMG_GEN2: false,
|
|
@@ -69,6 +70,7 @@ async function initialization() {
|
|
| 69 |
ssoArray.forEach((sso, index) => {
|
| 70 |
tokenManager.addToken(`sso-rw=${ssorwArray[index]};sso=${sso}`);
|
| 71 |
});
|
|
|
|
| 72 |
await Utils.get_signature()
|
| 73 |
Logger.info("初始化完成", 'Server');
|
| 74 |
}
|
|
@@ -347,17 +349,33 @@ class GrokApiClient {
|
|
| 347 |
}
|
| 348 |
|
| 349 |
async prepareChatRequest(request) {
|
| 350 |
-
if ((request.model === 'grok-2-imageGen' || request.model === 'grok-3-imageGen') && !CONFIG.API.PICGO_KEY) {
|
| 351 |
-
throw new Error(
|
| 352 |
}
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
let
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
let messages = '';
|
| 357 |
let lastRole = null;
|
| 358 |
let lastContent = '';
|
| 359 |
-
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
const processImageUrl = async (content) => {
|
| 362 |
if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
|
| 363 |
const imageResponse = await this.uploadBase64Image(
|
|
@@ -368,64 +386,67 @@ class GrokApiClient {
|
|
| 368 |
}
|
| 369 |
return null;
|
| 370 |
};
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
if (Array.isArray(current.content)) {
|
| 377 |
-
for (const item of current.content) {
|
| 378 |
if (item.type === 'image_url') {
|
| 379 |
-
if (current === todoMessages[todoMessages.length - 1]) {
|
| 380 |
-
const processedImage = await processImageUrl(item);
|
| 381 |
-
if (processedImage) fileAttachments.push(processedImage);
|
| 382 |
-
}
|
| 383 |
textContent += (textContent ? '\n' : '') + "[图片]";
|
| 384 |
} else if (item.type === 'text') {
|
| 385 |
-
textContent += (textContent ? '\n' : '') + item.text;
|
| 386 |
}
|
| 387 |
}
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
}
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
}
|
| 398 |
-
} else {
|
| 399 |
-
textContent = this.processMessageContent(current.content);
|
| 400 |
}
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
lastContent += '\n' + textContent;
|
| 404 |
messages = messages.substring(0, messages.lastIndexOf(`${role.toUpperCase()}: `)) +
|
| 405 |
`${role.toUpperCase()}: ${lastContent}\n`;
|
| 406 |
} else {
|
| 407 |
-
messages += `${role.toUpperCase()}: ${textContent}\n`;
|
| 408 |
lastContent = textContent;
|
| 409 |
lastRole = role;
|
| 410 |
}
|
| 411 |
-
} else if (current === todoMessages[todoMessages.length - 1] && fileAttachments.length > 0) {
|
| 412 |
-
messages += `${role.toUpperCase()}: [图片]\n`;
|
| 413 |
}
|
| 414 |
}
|
| 415 |
-
|
| 416 |
-
if (fileAttachments.length > 4) {
|
| 417 |
-
fileAttachments = fileAttachments.slice(0, 4); // 最多上传4张
|
| 418 |
-
}
|
| 419 |
-
|
| 420 |
-
messages = messages.trim();
|
| 421 |
-
|
| 422 |
-
if (request.model === 'grok-2-search') {
|
| 423 |
-
search = true;
|
| 424 |
-
}
|
| 425 |
return {
|
| 426 |
modelName: this.modelId,
|
| 427 |
-
message: messages,
|
| 428 |
-
fileAttachments: fileAttachments,
|
| 429 |
imageAttachments: [],
|
| 430 |
disableSearch: false,
|
| 431 |
enableImageGeneration: true,
|
|
@@ -519,6 +540,8 @@ async function processModelResponse(linejosn, model) {
|
|
| 519 |
}
|
| 520 |
return result;
|
| 521 |
case 'grok-3-reasoning':
|
|
|
|
|
|
|
| 522 |
if (linejosn?.isThinking && !CONFIG.IS_THINKING) {
|
| 523 |
result.token = "<think>" + token;
|
| 524 |
CONFIG.IS_THINKING = true;
|
|
@@ -538,8 +561,8 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 538 |
let buffer = '';
|
| 539 |
let fullResponse = '';
|
| 540 |
const dataPromises = [];
|
| 541 |
-
|
| 542 |
-
return new Promise((resolve, reject) => {
|
| 543 |
stream.on('data', async (chunk) => {
|
| 544 |
buffer += chunk.toString();
|
| 545 |
const lines = buffer.split('\n');
|
|
@@ -552,10 +575,11 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 552 |
const data = trimmedLine.substring(6);
|
| 553 |
try {
|
| 554 |
if (!data.trim()) continue;
|
| 555 |
-
if(data === "[DONE]")continue;
|
| 556 |
const linejosn = JSON.parse(data);
|
| 557 |
if (linejosn?.error) {
|
| 558 |
-
|
|
|
|
| 559 |
return;
|
| 560 |
}
|
| 561 |
if (linejosn?.doImgGen || linejosn?.imageAttachmentInfo) {
|
|
@@ -582,7 +606,6 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 582 |
})();
|
| 583 |
dataPromises.push(processPromise);
|
| 584 |
} catch (error) {
|
| 585 |
-
console.log(error);
|
| 586 |
continue;
|
| 587 |
}
|
| 588 |
}
|
|
@@ -602,14 +625,14 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 602 |
}
|
| 603 |
CONFIG.IS_IMG_GEN = false;
|
| 604 |
CONFIG.IS_IMG_GEN2 = false;
|
| 605 |
-
resolve();
|
| 606 |
} catch (error) {
|
| 607 |
-
reject(error);
|
| 608 |
}
|
| 609 |
});
|
|
|
|
| 610 |
stream.on('error', (error) => {
|
| 611 |
-
|
| 612 |
-
reject(error);
|
| 613 |
});
|
| 614 |
});
|
| 615 |
} catch (error) {
|
|
@@ -618,7 +641,6 @@ async function handleResponse(response, model, res, isStream) {
|
|
| 618 |
CONFIG.IS_IMG_GEN2 = false;
|
| 619 |
throw error;
|
| 620 |
}
|
| 621 |
-
|
| 622 |
}
|
| 623 |
|
| 624 |
async function handleImageResponse(imageUrl) {
|
|
@@ -652,8 +674,16 @@ async function handleImageResponse(imageUrl) {
|
|
| 652 |
}
|
| 653 |
}
|
| 654 |
|
|
|
|
| 655 |
const arrayBuffer = await imageBase64Response.arrayBuffer();
|
| 656 |
const imageBuffer = Buffer.from(arrayBuffer);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 657 |
const formData = new FormData();
|
| 658 |
|
| 659 |
formData.append('source', imageBuffer, {
|
|
@@ -718,7 +748,7 @@ app.post('/hf/v1/chat/completions', async (req, res) => {
|
|
| 718 |
while (retryCount < CONFIG.RETRY.MAX_ATTEMPTS) {
|
| 719 |
retryCount++;
|
| 720 |
const grokClient = new GrokApiClient(req.body.model);
|
| 721 |
-
const requestPayload = await grokClient.prepareChatRequest(req.body);
|
| 722 |
|
| 723 |
if (!CONFIG.API.TEMP_COOKIE) {
|
| 724 |
await Utils.get_signature();
|
|
|
|
| 26 |
API_KEY: process.env.API_KEY || "sk-123456",
|
| 27 |
SIGNATURE_COOKIE: null,
|
| 28 |
TEMP_COOKIE: null,
|
| 29 |
+
PICGO_KEY: null //想要流式生图的话需要填入这个PICGO图床的key
|
| 30 |
},
|
| 31 |
SERVER: {
|
| 32 |
PORT: process.env.PORT || 3000,
|
|
|
|
| 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,
|
|
|
|
| 70 |
ssoArray.forEach((sso, index) => {
|
| 71 |
tokenManager.addToken(`sso-rw=${ssorwArray[index]};sso=${sso}`);
|
| 72 |
});
|
| 73 |
+
console.log(JSON.stringify(tokenManager.getActiveTokens(), null, 2));
|
| 74 |
await Utils.get_signature()
|
| 75 |
Logger.info("初始化完成", 'Server');
|
| 76 |
}
|
|
|
|
| 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) => {
|
| 380 |
if (content.type === 'image_url' && content.image_url.url.includes('data:image')) {
|
| 381 |
const imageResponse = await this.uploadBase64Image(
|
|
|
|
| 386 |
}
|
| 387 |
return null;
|
| 388 |
};
|
| 389 |
+
|
| 390 |
+
const processContent = async (content) => {
|
| 391 |
+
if (Array.isArray(content)) {
|
| 392 |
+
let textContent = '';
|
| 393 |
+
for (const item of content) {
|
|
|
|
|
|
|
| 394 |
if (item.type === 'image_url') {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 395 |
textContent += (textContent ? '\n' : '') + "[图片]";
|
| 396 |
} else if (item.type === 'text') {
|
| 397 |
+
textContent += (textContent ? '\n' : '') + removeThinkTags(item.text);
|
| 398 |
}
|
| 399 |
}
|
| 400 |
+
return textContent;
|
| 401 |
+
} else if (typeof content === 'object' && content !== null) {
|
| 402 |
+
if (content.type === 'image_url') {
|
| 403 |
+
return "[图片]";
|
| 404 |
+
} else if (content.type === 'text') {
|
| 405 |
+
return removeThinkTags(content.text);
|
| 406 |
+
}
|
| 407 |
+
}
|
| 408 |
+
return removeThinkTags(this.processMessageContent(content));
|
| 409 |
+
};
|
| 410 |
+
|
| 411 |
+
for (const current of todoMessages) {
|
| 412 |
+
const role = current.role === 'assistant' ? 'assistant' : 'user';
|
| 413 |
+
const isLastMessage = current === todoMessages[todoMessages.length - 1];
|
| 414 |
+
|
| 415 |
+
// 处理图片附件
|
| 416 |
+
if (isLastMessage && current.content) {
|
| 417 |
+
if (Array.isArray(current.content)) {
|
| 418 |
+
for (const item of current.content) {
|
| 419 |
+
if (item.type === 'image_url') {
|
| 420 |
+
const processedImage = await processImageUrl(item);
|
| 421 |
+
if (processedImage) fileAttachments.push(processedImage);
|
| 422 |
+
}
|
| 423 |
}
|
| 424 |
+
} else if (current.content.type === 'image_url') {
|
| 425 |
+
const processedImage = await processImageUrl(current.content);
|
| 426 |
+
if (processedImage) fileAttachments.push(processedImage);
|
| 427 |
}
|
|
|
|
|
|
|
| 428 |
}
|
| 429 |
+
|
| 430 |
+
// 处理文本内容
|
| 431 |
+
const textContent = await processContent(current.content);
|
| 432 |
+
|
| 433 |
+
if (textContent || (isLastMessage && fileAttachments.length > 0)) {
|
| 434 |
+
if (role === lastRole && textContent) {
|
| 435 |
lastContent += '\n' + textContent;
|
| 436 |
messages = messages.substring(0, messages.lastIndexOf(`${role.toUpperCase()}: `)) +
|
| 437 |
`${role.toUpperCase()}: ${lastContent}\n`;
|
| 438 |
} else {
|
| 439 |
+
messages += `${role.toUpperCase()}: ${textContent || '[图片]'}\n`;
|
| 440 |
lastContent = textContent;
|
| 441 |
lastRole = role;
|
| 442 |
}
|
|
|
|
|
|
|
| 443 |
}
|
| 444 |
}
|
| 445 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
}
|
| 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 |
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');
|
|
|
|
| 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 |
})();
|
| 607 |
dataPromises.push(processPromise);
|
| 608 |
} catch (error) {
|
|
|
|
| 609 |
continue;
|
| 610 |
}
|
| 611 |
}
|
|
|
|
| 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 |
CONFIG.IS_IMG_GEN2 = false;
|
| 642 |
throw error;
|
| 643 |
}
|
|
|
|
| 644 |
}
|
| 645 |
|
| 646 |
async function handleImageResponse(imageUrl) {
|
|
|
|
| 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 ``
|
| 685 |
+
}
|
| 686 |
+
|
| 687 |
const formData = new FormData();
|
| 688 |
|
| 689 |
formData.append('source', imageBuffer, {
|
|
|
|
| 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();
|