fix(api-server): 修复OPTIONS请求的CORS头部设置

feat(convert): 添加OpenAI流式响应停止块生成函数
refactor(common): 优化流式请求处理逻辑,支持更多协议转换
This commit is contained in:
hex2077 2025-08-06 23:01:49 +08:00
parent 015a17a3a9
commit f33cf38b42
3 changed files with 53 additions and 8 deletions

View file

@ -411,9 +411,15 @@ function createRequestHandler(config) {
const apiService = await getApiService(currentConfig);
const method = req.method;
if (method === 'OPTIONS') {
res.writeHead(200, { 'Content-Type': 'application/json' });
console.log("OPTIONS REQUEST SUCCESS");
return res.end("OPTIONS REQUEST SUCCESS");
// 设置 CORS 头部,允许所有来源和方法
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, x-goog-api-key');
// OPTIONS 请求通常返回 204 No Content
res.writeHead(204);
res.end();
return;
}
// Health check endpoint - no authentication required

View file

@ -2,7 +2,7 @@ import { promises as fs } from 'fs';
import * as path from 'path';
import * as http from 'http'; // Add http for IncomingMessage and ServerResponse types
import { ApiServiceAdapter } from './adapter.js'; // Import ApiServiceAdapter
import { convertData } from './convert.js';
import { convertData, getOpenAIStreamChunkStop } from './convert.js';
import { ProviderStrategyFactory } from './provider-strategies.js';
export const API_ACTIONS = {
@ -207,7 +207,8 @@ export async function handleStreamRequest(res, service, model, requestBody, from
// The service returns a stream in its native format (toProvider).
const nativeStream = await service.generateContentStream(model, requestBody);
const needsConversion = getProtocolPrefix(fromProvider) !== getProtocolPrefix(toProvider);
const isClaude = getProtocolPrefix(fromProvider) === MODEL_PROTOCOL_PREFIX.CLAUDE;
const addEvent = getProtocolPrefix(fromProvider) === MODEL_PROTOCOL_PREFIX.CLAUDE;
const openStop = getProtocolPrefix(fromProvider) === MODEL_PROTOCOL_PREFIX.OPENAI;
try {
for await (const nativeChunk of nativeStream) {
@ -225,7 +226,7 @@ export async function handleStreamRequest(res, service, model, requestBody, from
continue;
}
if (isClaude) {
if (addEvent) {
res.write(`event: ${chunkToSend.type}\n`);
// console.log(`event: ${chunkToSend.type}\n`);
}
@ -233,6 +234,10 @@ export async function handleStreamRequest(res, service, model, requestBody, from
res.write(`data: ${JSON.stringify(chunkToSend)}\n\n`);
// console.log(`data: ${JSON.stringify(chunkToSend)}\n`);
}
if (openStop && needsConversion) {
res.write(`data: ${JSON.stringify(getOpenAIStreamChunkStop(model))}\n\n`);
// console.log(`data: ${JSON.stringify(getOpenAIStreamChunkStop(model))}\n`);
}
} catch (error) {
console.error('\n[Server] Error during stream processing:', error.stack);

View file

@ -407,10 +407,18 @@ export function toOpenAIStreamChunkFromClaude(claudeChunk, model) {
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: model,
system_fingerprint: "",
choices: [{
index: 0,
delta: { content: claudeChunk },
finish_reason: null,
delta: {
content: claudeChunk,
reasoning_content: ""
},
finish_reason: !claudeChunk ? 'stop' : null,
message: {
content: claudeChunk,
reasoning_content: ""
}
}],
usage:{
prompt_tokens: 0,
@ -420,6 +428,32 @@ export function toOpenAIStreamChunkFromClaude(claudeChunk, model) {
};
}
export function getOpenAIStreamChunkStop(model) {
return {
id: `chatcmpl-${uuidv4()}`, // uuidv4 needs to be imported or handled
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: model,
system_fingerprint: "",
choices: [{
index: 0,
delta: {
content: "",
reasoning_content: ""
},
finish_reason: 'stop',
message: {
content: "",
reasoning_content: ""
}
}],
usage:{
prompt_tokens: 0,
completion_tokens: 0,
total_tokens: 0,
},
};
}