From bcdd524a348df723b7375c14bfb30a11082d9233 Mon Sep 17 00:00:00 2001 From: 1e0n Date: Thu, 9 Oct 2025 14:41:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B0v1.3.1=EF=BC=9A?= =?UTF-8?q?=E4=B8=BAOpenAI=E7=AB=AF=E7=82=B9=E7=9A=84=E9=9D=9E=E6=B5=81?= =?UTF-8?q?=E5=BC=8F=20/v1/chat/completions=20=E8=BF=94=E5=9B=9E=E4=BD=93?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E4=B8=BAOpenAI=E5=85=BC=E5=AE=B9=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=EF=BC=9B=E4=BF=9D=E6=8C=81Anthropic/Common=E9=9D=9E?= =?UTF-8?q?=E6=B5=81=E5=BC=8F=E7=9B=B4=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- routes.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index d0ca984..435263a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "droid2api", - "version": "1.3.0", + "version": "1.3.1", "description": "OpenAI Compatible API Proxy", "main": "server.js", "type": "module", diff --git a/routes.js b/routes.js index d8227b9..42efe48 100644 --- a/routes.js +++ b/routes.js @@ -11,6 +11,44 @@ import { getApiKey } from './auth.js'; const router = express.Router(); +/** + * Convert a /v1/responses API result to a /v1/chat/completions-compatible format. + * Works for non-streaming responses. + */ +function convertResponseToChatCompletion(resp) { + if (!resp || typeof resp !== 'object') { + throw new Error('Invalid response object'); + } + + const outputMsg = (resp.output || []).find(o => o.type === 'message'); + const textBlocks = outputMsg?.content?.filter(c => c.type === 'output_text') || []; + const content = textBlocks.map(c => c.text).join(''); + + const chatCompletion = { + id: resp.id ? resp.id.replace(/^resp_/, 'chatcmpl-') : `chatcmpl-${Date.now()}`, + object: 'chat.completion', + created: resp.created_at || Math.floor(Date.now() / 1000), + model: resp.model || 'unknown-model', + choices: [ + { + index: 0, + message: { + role: outputMsg?.role || 'assistant', + content: content || '' + }, + finish_reason: resp.status === 'completed' ? 'stop' : 'unknown' + } + ], + usage: { + prompt_tokens: resp.usage?.input_tokens ?? 0, + completion_tokens: resp.usage?.output_tokens ?? 0, + total_tokens: resp.usage?.total_tokens ?? 0 + } + }; + + return chatCompletion; +} + router.get('/v1/models', (req, res) => { logInfo('GET /v1/models'); @@ -161,8 +199,21 @@ async function handleChatCompletions(req, res) { } } else { const data = await response.json(); - logResponse(200, null, data); - res.json(data); + if (model.type === 'openai') { + try { + const converted = convertResponseToChatCompletion(data); + logResponse(200, null, converted); + res.json(converted); + } catch (e) { + // 如果转换失败,回退为原始数据 + logResponse(200, null, data); + res.json(data); + } + } else { + // anthropic/common: 保持现有逻辑,直接转发 + logResponse(200, null, data); + res.json(data); + } } } catch (error) {