Update index.js
Browse files
index.js
CHANGED
|
@@ -1378,10 +1378,7 @@ class ApiProxyService {
|
|
| 1378 |
|
| 1379 |
if (params.stream) {
|
| 1380 |
// 假流式处理:使用非流式请求,然后模拟流式响应
|
| 1381 |
-
|
| 1382 |
-
if (!result.success) {
|
| 1383 |
-
res.status(result.status || 500).json({ error: result.error });
|
| 1384 |
-
}
|
| 1385 |
} else {
|
| 1386 |
// 非流式请求和原来一样
|
| 1387 |
const result = await this.executeNormalRequest(requestBody, params, requestId);
|
|
@@ -1408,44 +1405,30 @@ class ApiProxyService {
|
|
| 1408 |
logData.duration = Date.now() - startTime;
|
| 1409 |
this.logRequest(req.authKey, logData);
|
| 1410 |
|
| 1411 |
-
|
| 1412 |
-
|
| 1413 |
-
|
| 1414 |
-
|
| 1415 |
-
|
| 1416 |
-
|
| 1417 |
-
|
|
|
|
|
|
|
|
|
|
| 1418 |
}
|
| 1419 |
}
|
| 1420 |
|
| 1421 |
/**
|
| 1422 |
-
*
|
| 1423 |
*/
|
| 1424 |
async handleFakeStreamRequest(requestBody, params, requestId, res, authKey, logData, startTime) {
|
| 1425 |
-
|
| 1426 |
-
|
| 1427 |
-
res.writeHead(200, {
|
| 1428 |
-
'Content-Type': 'text/event-stream',
|
| 1429 |
-
'Cache-Control': 'no-cache',
|
| 1430 |
-
'Connection': 'keep-alive',
|
| 1431 |
-
'Access-Control-Allow-Origin': '*'
|
| 1432 |
-
});
|
| 1433 |
-
|
| 1434 |
-
// 开始发送ping消息保持连接活跃
|
| 1435 |
-
const pingInterval = setInterval(() => {
|
| 1436 |
-
try {
|
| 1437 |
-
res.write(': ping\n\n');
|
| 1438 |
-
} catch (error) {
|
| 1439 |
-
clearInterval(pingInterval);
|
| 1440 |
-
}
|
| 1441 |
-
}, 1000); // 每秒发送一次ping
|
| 1442 |
|
|
|
|
| 1443 |
// 执行非流式请求
|
| 1444 |
const result = await this.executeNormalRequest(requestBody, params, requestId);
|
| 1445 |
|
| 1446 |
-
// 停止ping
|
| 1447 |
-
clearInterval(pingInterval);
|
| 1448 |
-
|
| 1449 |
logData.responseTime = new Date().toISOString();
|
| 1450 |
logData.duration = Date.now() - startTime;
|
| 1451 |
|
|
@@ -1454,10 +1437,34 @@ class ApiProxyService {
|
|
| 1454 |
logData.error = result.error;
|
| 1455 |
this.logRequest(authKey, logData);
|
| 1456 |
|
| 1457 |
-
|
| 1458 |
-
res.
|
| 1459 |
-
|
| 1460 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1461 |
}
|
| 1462 |
|
| 1463 |
// 获取响应文本
|
|
@@ -1465,22 +1472,50 @@ class ApiProxyService {
|
|
| 1465 |
logData.responseContent = responseText;
|
| 1466 |
this.logRequest(authKey, logData);
|
| 1467 |
|
| 1468 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1469 |
// 将文本拆分为假流式块
|
| 1470 |
const chunks = ResponseConverter.splitTextToFakeStream(responseText, requestId, params.model);
|
| 1471 |
|
| 1472 |
// 逐步发送块,模拟流式响应
|
| 1473 |
for (const chunk of chunks) {
|
|
|
|
| 1474 |
res.write(chunk);
|
| 1475 |
// 添加小延迟以模拟真实的流式响应
|
| 1476 |
-
await new Promise(resolve => setTimeout(resolve,
|
| 1477 |
}
|
| 1478 |
}
|
| 1479 |
|
| 1480 |
-
|
| 1481 |
-
|
| 1482 |
-
|
| 1483 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1484 |
|
| 1485 |
} catch (error) {
|
| 1486 |
console.error('处理假流式请求错误:', error);
|
|
@@ -1491,14 +1526,48 @@ class ApiProxyService {
|
|
| 1491 |
logData.duration = Date.now() - startTime;
|
| 1492 |
this.logRequest(authKey, logData);
|
| 1493 |
|
| 1494 |
-
|
| 1495 |
-
|
| 1496 |
-
|
| 1497 |
-
|
| 1498 |
-
|
| 1499 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1500 |
}
|
| 1501 |
-
return { success: false, error: error.message };
|
| 1502 |
}
|
| 1503 |
}
|
| 1504 |
|
|
@@ -2027,7 +2096,7 @@ class Server {
|
|
| 2027 |
invalidKeys: this.apiProxy.config.invalidApiKeys.length,
|
| 2028 |
authKeys: this.apiProxy.authKeyManager.getAllKeys().length,
|
| 2029 |
fileLoggingEnabled: this.apiProxy.logManager.getFileLoggingStatus(),
|
| 2030 |
-
version: '2.3.
|
| 2031 |
features: ['text', 'vision', 'stream', 'fake_stream', 'load_balancing', 'auto_retry', 'logging', 'multi_auth', 'file_export']
|
| 2032 |
});
|
| 2033 |
});
|
|
|
|
| 1378 |
|
| 1379 |
if (params.stream) {
|
| 1380 |
// 假流式处理:使用非流式请求,然后模拟流式响应
|
| 1381 |
+
await this.handleFakeStreamRequest(requestBody, params, requestId, res, req.authKey, logData, startTime);
|
|
|
|
|
|
|
|
|
|
| 1382 |
} else {
|
| 1383 |
// 非流式请求和原来一样
|
| 1384 |
const result = await this.executeNormalRequest(requestBody, params, requestId);
|
|
|
|
| 1405 |
logData.duration = Date.now() - startTime;
|
| 1406 |
this.logRequest(req.authKey, logData);
|
| 1407 |
|
| 1408 |
+
// 修复:确保在错误情况下正确响应
|
| 1409 |
+
if (!res.headersSent) {
|
| 1410 |
+
res.status(500).json({
|
| 1411 |
+
error: {
|
| 1412 |
+
message: '内部服务器错误: ' + error.message,
|
| 1413 |
+
type: 'internal_server_error',
|
| 1414 |
+
code: 'server_error'
|
| 1415 |
+
}
|
| 1416 |
+
});
|
| 1417 |
+
}
|
| 1418 |
}
|
| 1419 |
}
|
| 1420 |
|
| 1421 |
/**
|
| 1422 |
+
* 处理假流式请求(修复版)
|
| 1423 |
*/
|
| 1424 |
async handleFakeStreamRequest(requestBody, params, requestId, res, authKey, logData, startTime) {
|
| 1425 |
+
let responseStarted = false;
|
| 1426 |
+
let pingInterval = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1427 |
|
| 1428 |
+
try {
|
| 1429 |
// 执行非流式请求
|
| 1430 |
const result = await this.executeNormalRequest(requestBody, params, requestId);
|
| 1431 |
|
|
|
|
|
|
|
|
|
|
| 1432 |
logData.responseTime = new Date().toISOString();
|
| 1433 |
logData.duration = Date.now() - startTime;
|
| 1434 |
|
|
|
|
| 1437 |
logData.error = result.error;
|
| 1438 |
this.logRequest(authKey, logData);
|
| 1439 |
|
| 1440 |
+
// 发送错误响应为流式格式
|
| 1441 |
+
if (!res.headersSent) {
|
| 1442 |
+
res.writeHead(200, {
|
| 1443 |
+
'Content-Type': 'text/event-stream',
|
| 1444 |
+
'Cache-Control': 'no-cache',
|
| 1445 |
+
'Connection': 'keep-alive',
|
| 1446 |
+
'Access-Control-Allow-Origin': '*'
|
| 1447 |
+
});
|
| 1448 |
+
responseStarted = true;
|
| 1449 |
+
}
|
| 1450 |
+
|
| 1451 |
+
if (responseStarted) {
|
| 1452 |
+
const errorChunk = {
|
| 1453 |
+
id: requestId,
|
| 1454 |
+
object: 'chat.completion.chunk',
|
| 1455 |
+
created: Math.floor(Date.now() / 1000),
|
| 1456 |
+
model: params.model,
|
| 1457 |
+
choices: [{
|
| 1458 |
+
index: 0,
|
| 1459 |
+
delta: { content: `错误: ${result.error}` },
|
| 1460 |
+
finish_reason: 'stop'
|
| 1461 |
+
}]
|
| 1462 |
+
};
|
| 1463 |
+
res.write(`data: ${JSON.stringify(errorChunk)}\n\n`);
|
| 1464 |
+
res.write('data: [DONE]\n\n');
|
| 1465 |
+
res.end();
|
| 1466 |
+
}
|
| 1467 |
+
return;
|
| 1468 |
}
|
| 1469 |
|
| 1470 |
// 获取响应文本
|
|
|
|
| 1472 |
logData.responseContent = responseText;
|
| 1473 |
this.logRequest(authKey, logData);
|
| 1474 |
|
| 1475 |
+
// 设置流式响应头
|
| 1476 |
+
if (!res.headersSent) {
|
| 1477 |
+
res.writeHead(200, {
|
| 1478 |
+
'Content-Type': 'text/event-stream',
|
| 1479 |
+
'Cache-Control': 'no-cache',
|
| 1480 |
+
'Connection': 'keep-alive',
|
| 1481 |
+
'Access-Control-Allow-Origin': '*'
|
| 1482 |
+
});
|
| 1483 |
+
responseStarted = true;
|
| 1484 |
+
|
| 1485 |
+
// 开始发送ping消息保持连接活跃
|
| 1486 |
+
pingInterval = setInterval(() => {
|
| 1487 |
+
try {
|
| 1488 |
+
if (!res.destroyed) {
|
| 1489 |
+
res.write(': ping\n\n');
|
| 1490 |
+
}
|
| 1491 |
+
} catch (error) {
|
| 1492 |
+
clearInterval(pingInterval);
|
| 1493 |
+
}
|
| 1494 |
+
}, 1000);
|
| 1495 |
+
}
|
| 1496 |
+
|
| 1497 |
+
if (responseText && responseStarted) {
|
| 1498 |
// 将文本拆分为假流式块
|
| 1499 |
const chunks = ResponseConverter.splitTextToFakeStream(responseText, requestId, params.model);
|
| 1500 |
|
| 1501 |
// 逐步发送块,模拟流式响应
|
| 1502 |
for (const chunk of chunks) {
|
| 1503 |
+
if (res.destroyed) break;
|
| 1504 |
res.write(chunk);
|
| 1505 |
// 添加小延迟以模拟真实的流式响应
|
| 1506 |
+
await new Promise(resolve => setTimeout(resolve, 50));
|
| 1507 |
}
|
| 1508 |
}
|
| 1509 |
|
| 1510 |
+
// 停止ping并结束响应
|
| 1511 |
+
if (pingInterval) {
|
| 1512 |
+
clearInterval(pingInterval);
|
| 1513 |
+
}
|
| 1514 |
+
|
| 1515 |
+
if (responseStarted && !res.destroyed) {
|
| 1516 |
+
res.write('data: [DONE]\n\n');
|
| 1517 |
+
res.end();
|
| 1518 |
+
}
|
| 1519 |
|
| 1520 |
} catch (error) {
|
| 1521 |
console.error('处理假流式请求错误:', error);
|
|
|
|
| 1526 |
logData.duration = Date.now() - startTime;
|
| 1527 |
this.logRequest(authKey, logData);
|
| 1528 |
|
| 1529 |
+
// 停止ping
|
| 1530 |
+
if (pingInterval) {
|
| 1531 |
+
clearInterval(pingInterval);
|
| 1532 |
+
}
|
| 1533 |
+
|
| 1534 |
+
// 如果还没有开始响应,设置流式响应头
|
| 1535 |
+
if (!res.headersSent && !responseStarted) {
|
| 1536 |
+
try {
|
| 1537 |
+
res.writeHead(200, {
|
| 1538 |
+
'Content-Type': 'text/event-stream',
|
| 1539 |
+
'Cache-Control': 'no-cache',
|
| 1540 |
+
'Connection': 'keep-alive',
|
| 1541 |
+
'Access-Control-Allow-Origin': '*'
|
| 1542 |
+
});
|
| 1543 |
+
responseStarted = true;
|
| 1544 |
+
} catch (headerError) {
|
| 1545 |
+
console.error('设置响应头失败:', headerError);
|
| 1546 |
+
return;
|
| 1547 |
+
}
|
| 1548 |
+
}
|
| 1549 |
+
|
| 1550 |
+
// 发送错误信息
|
| 1551 |
+
if (responseStarted && !res.destroyed) {
|
| 1552 |
+
try {
|
| 1553 |
+
const errorChunk = {
|
| 1554 |
+
id: requestId,
|
| 1555 |
+
object: 'chat.completion.chunk',
|
| 1556 |
+
created: Math.floor(Date.now() / 1000),
|
| 1557 |
+
model: params.model,
|
| 1558 |
+
choices: [{
|
| 1559 |
+
index: 0,
|
| 1560 |
+
delta: { content: `错误: ${error.message}` },
|
| 1561 |
+
finish_reason: 'stop'
|
| 1562 |
+
}]
|
| 1563 |
+
};
|
| 1564 |
+
res.write(`data: ${JSON.stringify(errorChunk)}\n\n`);
|
| 1565 |
+
res.write('data: [DONE]\n\n');
|
| 1566 |
+
res.end();
|
| 1567 |
+
} catch (writeError) {
|
| 1568 |
+
console.error('写入错误响应失败:', writeError);
|
| 1569 |
+
}
|
| 1570 |
}
|
|
|
|
| 1571 |
}
|
| 1572 |
}
|
| 1573 |
|
|
|
|
| 2096 |
invalidKeys: this.apiProxy.config.invalidApiKeys.length,
|
| 2097 |
authKeys: this.apiProxy.authKeyManager.getAllKeys().length,
|
| 2098 |
fileLoggingEnabled: this.apiProxy.logManager.getFileLoggingStatus(),
|
| 2099 |
+
version: '2.3.1',
|
| 2100 |
features: ['text', 'vision', 'stream', 'fake_stream', 'load_balancing', 'auto_retry', 'logging', 'multi_auth', 'file_export']
|
| 2101 |
});
|
| 2102 |
});
|