forked from boos/BoosAPI
feat: Add support for OpenAI Responses endpoint and enhance proxy retry logic
- Updated proxyRetryPolicy to retry on all 4xx and 5xx status codes. - Introduced 'responses' target format in TestChatRequestPayload. - Implemented extractResponsesContent function to handle responses format in ModelTester. - Enhanced ModelTester to support responses target format and display appropriate labels. - Updated ProxyLogs to extract and display upstream paths from error messages. - Added tests to ensure responses target format is preserved during payload sync. - Created responses proxy route to handle requests to the new OpenAI Responses endpoint. - Updated upstreamEndpoint logic to include responses endpoint handling. - Added modelService tests to validate token route rebuilding logic.
This commit is contained in:
@@ -545,7 +545,7 @@ export async function settingsRoutes(app: FastifyInstance) {
|
||||
successMessage: (currentTask) => {
|
||||
const rebuild = (currentTask.result as any)?.rebuild;
|
||||
if (!rebuild) return '缓存清理后重建路由已完成';
|
||||
return `缓存清理后重建完成:新增路由 ${rebuild.createdRoutes},新增通道 ${rebuild.createdChannels},移除通道 ${rebuild.removedChannels}`;
|
||||
return `缓存清理后重建完成:新增路由 ${rebuild.createdRoutes},移除旧路由 ${rebuild.removedRoutes ?? 0},新增通道 ${rebuild.createdChannels},移除通道 ${rebuild.removedChannels}`;
|
||||
},
|
||||
failureMessage: (currentTask) => `缓存清理后重建失败:${currentTask.error || 'unknown error'}`,
|
||||
},
|
||||
|
||||
@@ -192,7 +192,7 @@ export async function statsRoutes(app: FastifyInstance) {
|
||||
successMessage: (currentTask) => {
|
||||
const rebuild = (currentTask.result as any)?.rebuild;
|
||||
if (!rebuild) return '模型广场刷新已完成';
|
||||
return `模型广场刷新完成:新增路由 ${rebuild.createdRoutes},新增通道 ${rebuild.createdChannels},移除通道 ${rebuild.removedChannels}`;
|
||||
return `模型广场刷新完成:新增路由 ${rebuild.createdRoutes},移除旧路由 ${rebuild.removedRoutes ?? 0},新增通道 ${rebuild.createdChannels},移除通道 ${rebuild.removedChannels}`;
|
||||
},
|
||||
failureMessage: (currentTask) => `模型广场刷新失败:${currentTask.error || 'unknown error'}`,
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ import { fetch } from 'undici';
|
||||
import { config } from '../../config.js';
|
||||
|
||||
type TestChatMessage = { role: string; content: string };
|
||||
type TestTargetFormat = 'openai' | 'claude';
|
||||
type TestTargetFormat = 'openai' | 'claude' | 'responses';
|
||||
|
||||
type TestChatRequestBody = {
|
||||
model?: string;
|
||||
@@ -84,7 +84,11 @@ const validatePayload = (
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetFormat: TestTargetFormat = body.targetFormat === 'claude' ? 'claude' : 'openai';
|
||||
const targetFormat: TestTargetFormat = body.targetFormat === 'claude'
|
||||
? 'claude'
|
||||
: body.targetFormat === 'responses'
|
||||
? 'responses'
|
||||
: 'openai';
|
||||
|
||||
return {
|
||||
model: body.model,
|
||||
@@ -146,6 +150,56 @@ const convertOpenAiPayloadToClaudeBody = (
|
||||
return body;
|
||||
};
|
||||
|
||||
const convertOpenAiPayloadToResponsesBody = (
|
||||
payload: ValidatedTestChatPayload,
|
||||
forceStream: boolean,
|
||||
): Record<string, unknown> => {
|
||||
const systemContents: string[] = [];
|
||||
const messages: Array<{ role: 'user' | 'assistant'; content: string }> = [];
|
||||
|
||||
for (const message of payload.messages) {
|
||||
const role = typeof message.role === 'string' ? message.role : 'user';
|
||||
const content = typeof message.content === 'string' ? message.content : '';
|
||||
if (!content.trim()) continue;
|
||||
|
||||
if (role === 'system') {
|
||||
systemContents.push(content);
|
||||
continue;
|
||||
}
|
||||
|
||||
messages.push({
|
||||
role: role === 'assistant' ? 'assistant' : 'user',
|
||||
content,
|
||||
});
|
||||
}
|
||||
|
||||
const body: Record<string, unknown> = {
|
||||
model: payload.model,
|
||||
stream: forceStream,
|
||||
};
|
||||
|
||||
if (messages.length === 1 && messages[0].role === 'user' && systemContents.length === 0) {
|
||||
body.input = messages[0].content;
|
||||
} else {
|
||||
body.input = messages;
|
||||
if (systemContents.length > 0) {
|
||||
body.instructions = systemContents.join('\n\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof payload.temperature === 'number' && Number.isFinite(payload.temperature)) {
|
||||
body.temperature = payload.temperature;
|
||||
}
|
||||
if (typeof payload.top_p === 'number' && Number.isFinite(payload.top_p)) {
|
||||
body.top_p = payload.top_p;
|
||||
}
|
||||
body.max_output_tokens = typeof payload.max_tokens === 'number' && Number.isFinite(payload.max_tokens)
|
||||
? payload.max_tokens
|
||||
: 4096;
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
const buildUpstreamRequest = (
|
||||
payload: ValidatedTestChatPayload,
|
||||
forceStream: boolean,
|
||||
@@ -162,6 +216,17 @@ const buildUpstreamRequest = (
|
||||
};
|
||||
}
|
||||
|
||||
if (payload.targetFormat === 'responses') {
|
||||
return {
|
||||
url: `http://127.0.0.1:${config.port}/v1/responses`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${config.proxyToken}`,
|
||||
},
|
||||
body: convertOpenAiPayloadToResponsesBody(payload, forceStream),
|
||||
};
|
||||
}
|
||||
|
||||
const { targetFormat: _targetFormat, ...openAiPayload } = payload;
|
||||
return {
|
||||
url: `http://127.0.0.1:${config.port}/v1/chat/completions`,
|
||||
|
||||
@@ -336,7 +336,7 @@ export async function tokensRoutes(app: FastifyInstance) {
|
||||
successMessage: (currentTask) => {
|
||||
const rebuild = (currentTask.result as any)?.rebuild;
|
||||
if (!rebuild) return '刷新模型并重建路由已完成';
|
||||
return `刷新模型并重建路由完成:新增路由 ${rebuild.createdRoutes},新增通道 ${rebuild.createdChannels},移除通道 ${rebuild.removedChannels}`;
|
||||
return `刷新模型并重建路由完成:新增路由 ${rebuild.createdRoutes},移除旧路由 ${rebuild.removedRoutes ?? 0},新增通道 ${rebuild.createdChannels},移除通道 ${rebuild.removedChannels}`;
|
||||
},
|
||||
failureMessage: (currentTask) => `刷新模型并重建路由失败:${currentTask.error || 'unknown error'}`,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user