* 将 OpenAI Chat Completions 请求转换为内部 Anthropic 格式 * 这样可以完全复用现有的 convertToCursorRequest 管道
(body: OpenAIChatRequest)
| 96 | * 这样可以完全复用现有的 convertToCursorRequest 管道 |
| 97 | */ |
| 98 | function convertToAnthropicRequest(body: OpenAIChatRequest): AnthropicRequest { |
| 99 | const rawMessages: AnthropicMessage[] = []; |
| 100 | let systemPrompt: string | undefined; |
| 101 | |
| 102 | // ★ response_format 处理:构建温和的 JSON 格式提示(稍后追加到最后一条用户消息) |
| 103 | let jsonFormatSuffix = ''; |
| 104 | if (body.response_format && body.response_format.type !== 'text') { |
| 105 | jsonFormatSuffix = '\n\nRespond in plain JSON format without markdown wrapping.'; |
| 106 | if (body.response_format.type === 'json_schema' && body.response_format.json_schema?.schema) { |
| 107 | jsonFormatSuffix += ` Schema: ${JSON.stringify(body.response_format.json_schema.schema)}`; |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | for (const msg of body.messages) { |
| 112 | switch (msg.role) { |
| 113 | case 'system': |
| 114 | systemPrompt = (systemPrompt ? systemPrompt + '\n\n' : '') + extractOpenAIContent(msg); |
| 115 | break; |
| 116 | |
| 117 | case 'user': { |
| 118 | // 检查 content 数组中是否有 tool_result 类型的块(Anthropic 风格) |
| 119 | const contentBlocks = extractOpenAIContentBlocks(msg); |
| 120 | if (Array.isArray(contentBlocks)) { |
| 121 | rawMessages.push({ role: 'user', content: contentBlocks }); |
| 122 | } else { |
| 123 | rawMessages.push({ role: 'user', content: contentBlocks || '' }); |
| 124 | } |
| 125 | break; |
| 126 | } |
| 127 | |
| 128 | case 'assistant': { |
| 129 | const blocks: AnthropicContentBlock[] = []; |
| 130 | const contentBlocks = extractOpenAIContentBlocks(msg); |
| 131 | if (typeof contentBlocks === 'string' && contentBlocks) { |
| 132 | blocks.push({ type: 'text', text: contentBlocks }); |
| 133 | } else if (Array.isArray(contentBlocks)) { |
| 134 | blocks.push(...contentBlocks); |
| 135 | } |
| 136 | |
| 137 | if (msg.tool_calls && msg.tool_calls.length > 0) { |
| 138 | for (const tc of msg.tool_calls) { |
| 139 | let args: Record<string, unknown> = {}; |
| 140 | try { |
| 141 | args = JSON.parse(tc.function.arguments); |
| 142 | } catch { |
| 143 | args = { input: tc.function.arguments }; |
| 144 | } |
| 145 | blocks.push({ |
| 146 | type: 'tool_use', |
| 147 | id: tc.id, |
| 148 | name: tc.function.name, |
| 149 | input: args, |
| 150 | }); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | rawMessages.push({ |
| 155 | role: 'assistant', |
no test coverage detected