devme's picture
Upload 15 files
50852f0 verified
import fetch from 'node-fetch'
import { getModelById, getEndpointByType } from '../../configs/config.js'
import { transformToAnthropic, getAnthropicHeaders } from '../../transformers/requests/anthropic.js'
import { transformToOpenAI, getOpenAIHeaders } from '../../transformers/requests/openai.js'
import { transformToCommon, getCommonHeaders } from '../../transformers/requests/common.js'
import { AnthropicResponseTransformer } from '../../transformers/responses/anthropic.js'
import { OpenAIResponseTransformer } from '../../transformers/responses/openai.js'
import { getNextProxyAgent } from '../../managers/proxy.js'
import { getAuthHeader } from '../utils/auth.js'
import { convertResponseToChatCompletion } from '../utils/converter.js'
/**
* 处理 POST /v1/chat/completions 请求
* 标准 OpenAI 聊天补全处理函数(带格式转换)
*/
export async function handleChatCompletions(req, res) {
try {
const openaiRequest = req.body
const modelId = openaiRequest.model
if (!modelId) {
return res.status(400).json({ error: '需要提供 model 参数' })
}
const model = getModelById(modelId)
if (!model) {
return res.status(404).json({ error: `未找到模型 ${modelId}` })
}
const endpoint = getEndpointByType(model.type)
if (!endpoint) {
return res.status(500).json({ error: `未找到端点类型 ${model.type}` })
}
// 获取认证信息
const authHeader = getAuthHeader(req)
if (!authHeader) {
return res.status(401).json({
error: '未提供认证信息',
message: '请在请求头中提供 Authorization 或 x-api-key'
})
}
let transformedRequest
let headers
const clientHeaders = req.headers
if ((openaiRequest.model === 'claude-sonnet-4-5-20250929' || openaiRequest.model === 'claude-haiku-4-5-20251001' || openaiRequest.model === 'claude-opus-4-1-20250805' || openaiRequest.model === 'claude-opus-4-5-20251101') && openaiRequest.temperature && openaiRequest.top_p) {
delete openaiRequest.top_p
}
// 转换请求格式
if (model.type === 'anthropic') {
transformedRequest = transformToAnthropic(openaiRequest)
const isStreaming = openaiRequest.stream === true
headers = getAnthropicHeaders(authHeader, clientHeaders, isStreaming, modelId)
} else if (model.type === 'openai') {
transformedRequest = transformToOpenAI(openaiRequest)
headers = getOpenAIHeaders(authHeader, clientHeaders)
} else if (model.type === 'common') {
transformedRequest = transformToCommon(openaiRequest)
headers = getCommonHeaders(authHeader, clientHeaders)
} else {
return res.status(500).json({ error: `未知的端点类型: ${model.type}` })
}
const proxyAgentInfo = getNextProxyAgent(endpoint.base_url)
const fetchOptions = {
method: 'POST',
headers,
body: JSON.stringify(transformedRequest)
}
if (proxyAgentInfo?.agent) {
fetchOptions.agent = proxyAgentInfo.agent
}
const response = await fetch(endpoint.base_url, fetchOptions)
if (!response.ok) {
const errorText = await response.text()
console.error(`端点错误: ${response.status}`, errorText)
return res.status(response.status).json({
error: `端点返回 ${response.status}`,
details: errorText
})
}
const isStreaming = transformedRequest.stream === true
if (isStreaming) {
res.setHeader('Content-Type', 'text/event-stream')
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Connection', 'keep-alive')
// common 类型直接转发,不使用 transformer
if (model.type === 'common') {
try {
for await (const chunk of response.body) {
res.write(chunk)
}
res.end()
} catch (streamError) {
console.error('流错误:', streamError)
res.end()
}
} else {
// anthropic 和 openai 类型使用 transformer
let transformer
if (model.type === 'anthropic') {
transformer = new AnthropicResponseTransformer(modelId, `chatcmpl-${Date.now()}`)
} else if (model.type === 'openai') {
transformer = new OpenAIResponseTransformer(modelId, `chatcmpl-${Date.now()}`)
}
try {
for await (const chunk of transformer.transformStream(response.body)) {
res.write(chunk)
}
res.end()
} catch (streamError) {
console.error('流错误:', streamError)
res.end()
}
}
} else {
const data = await response.json()
if (model.type === 'openai') {
try {
const converted = convertResponseToChatCompletion(data)
res.json(converted)
} catch (e) {
res.json(data)
}
} else {
res.json(data)
}
}
} catch (error) {
console.error('/v1/chat/completions 错误:', error)
res.status(500).json({
error: '内部服务器错误',
message: error.message
})
}
}