AIClient-2-API/src/openai/openai-responses-strategy.js
hex2077 dfe7ce914e feat(api): 添加 OpenAI Responses API 支持
新增对 OpenAI Responses API 端点的部分支持,包括请求转换、流式响应处理和供应商适配。主要变更:

- 新增 OpenAIResponsesApiService 核心服务实现
- 实现 Claude/Gemini 到 Responses API 的双向协议转换(能聊天,不能调用工具)
- 添加流式响应状态管理和事件生成机制
- 扩展路由支持 /v1/responses 端点
- 更新文档说明配置方法和使用示例
2025-10-16 23:26:36 +08:00

123 lines
No EOL
4.9 KiB
JavaScript

import { ProviderStrategy } from '../provider-strategy.js';
import { extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../common.js';
/**
* OpenAI Responses API strategy implementation.
* Migrated from Chat Completions API to Responses API.
*/
class ResponsesAPIStrategy extends ProviderStrategy {
extractModelAndStreamInfo(req, requestBody) {
const model = requestBody.model;
const isStream = requestBody.stream === true;
return { model, isStream };
}
extractResponseText(response) {
if (!response.output) {
return '';
}
// In Responses API, output is an array of items
for (const item of response.output) {
if (item.type === 'message' && item.content && item.content.length > 0) {
for (const content of item.content) {
if (content.type === 'output_text' && content.text) {
return content.text;
}
}
}
}
return '';
}
extractPromptText(requestBody) {
// In Responses API, input can be a string or array of items
if (typeof requestBody.input === 'string') {
return requestBody.input;
} else if (Array.isArray(requestBody.input)) {
// If input is an array of items/messages, get the last user content
const userInputItems = requestBody.input.filter(item =>
(item.role && item.role === 'user') ||
(item.type && item.type === 'message' && item.role === 'user') ||
(item.type && item.type === 'user')
);
if (userInputItems.length > 0) {
const lastInput = userInputItems[userInputItems.length - 1];
if (typeof lastInput.content === 'string') {
return lastInput.content;
} else if (Array.isArray(lastInput.content)) {
return lastInput.content.map(item => item.text || item.content || '').join('\n');
}
}
}
return '';
}
async applySystemPromptFromFile(config, requestBody) {
if (!config.SYSTEM_PROMPT_FILE_PATH) {
return requestBody;
}
const filePromptContent = config.SYSTEM_PROMPT_CONTENT;
if (filePromptContent === null) {
return requestBody;
}
// In Responses API, system instructions are typically passed in 'instructions' field
// or in the input array with role: 'system'
requestBody.instructions = requestBody.instructions || filePromptContent;
// If using instructions field is not desired, append to input array instead
if (!requestBody.instructions || config.SYSTEM_PROMPT_MODE === 'append') {
if (typeof requestBody.input === 'string') {
// Convert to array format to add system message
requestBody.input = [
{ role: 'system', content: filePromptContent },
{ role: 'user', content: requestBody.input }
];
} else if (Array.isArray(requestBody.input)) {
// Check if system message already exists
const systemMessageIndex = requestBody.input.findIndex(m =>
m.role === 'system' || (m.type && m.type === 'system')
);
if (systemMessageIndex !== -1) {
requestBody.input[systemMessageIndex].content = filePromptContent;
} else {
requestBody.input.unshift({ role: 'system', content: filePromptContent });
}
} else {
// If input is not defined, initialize with system message
requestBody.input = [{ role: 'system', content: filePromptContent }];
}
} else if (requestBody.instructions) {
// If system prompt mode is not append, then replace the instructions
requestBody.instructions = filePromptContent;
}
console.log(`[System Prompt] Applied system prompt from ${config.SYSTEM_PROMPT_FILE_PATH} in '${config.SYSTEM_PROMPT_MODE}' mode for provider 'responses'.`);
return requestBody;
}
async manageSystemPrompt(requestBody) {
// For Responses API, we may extract instructions or system messages from input
let incomingSystemText = '';
if (requestBody.instructions) {
incomingSystemText = requestBody.instructions;
} else if (Array.isArray(requestBody.input)) {
const systemMessage = requestBody.input.find(item =>
item.role === 'system' || (item.type && item.type === 'system')
);
if (systemMessage && systemMessage.content) {
incomingSystemText = systemMessage.content;
}
}
await this._updateSystemPromptFile(incomingSystemText, MODEL_PROTOCOL_PREFIX.OPENAI);
}
}
export { ResponsesAPIStrategy };