devme's picture
Upload 15 files
50852f0 verified
import { getSystemPrompt, getModelReasoning, getUserAgent } from '../../configs/config.js'
export function transformToAnthropic(openaiRequest) {
const anthropicRequest = {
model: openaiRequest.model,
messages: []
};
// 仅在客户端明确提供时添加 stream 参数
if (openaiRequest.stream !== undefined) {
anthropicRequest.stream = openaiRequest.stream;
}
// 处理 max_tokens
if (openaiRequest.max_tokens) {
anthropicRequest.max_tokens = openaiRequest.max_tokens;
} else if (openaiRequest.max_completion_tokens) {
anthropicRequest.max_tokens = openaiRequest.max_completion_tokens;
} else {
anthropicRequest.max_tokens = 64000;
}
// 提取系统消息并转换其他消息
let systemContent = [];
if (openaiRequest.messages && Array.isArray(openaiRequest.messages)) {
for (const msg of openaiRequest.messages) {
// 单独处理系统消息
if (msg.role === 'system') {
if (typeof msg.content === 'string') {
systemContent.push({
type: 'text',
text: msg.content?.replace("You are Claude Code, Anthropic's official CLI for Claude.", 'you are bot.').replace("You are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK.", "you are bot.")
});
} else if (Array.isArray(msg.content)) {
for (const part of msg.content) {
if (part.type === 'text') {
systemContent.push({
type: 'text',
text: part.text?.replace("You are Claude Code, Anthropic's official CLI for Claude.", 'you are bot.').replace("You are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK.", "you are bot.")
});
} else {
systemContent.push(part);
}
}
}
continue; // 跳过将系统消息添加到消息数组
}
const anthropicMsg = {
role: msg.role,
content: []
};
if (typeof msg.content === 'string') {
anthropicMsg.content.push({
type: 'text',
text: msg.content
});
} else if (Array.isArray(msg.content)) {
for (const part of msg.content) {
if (part.type === 'text') {
anthropicMsg.content.push({
type: 'text',
text: part.text
});
} else if (part.type === 'image_url') {
anthropicMsg.content.push({
type: 'image',
source: part.image_url
});
} else {
anthropicMsg.content.push(part);
}
}
}
anthropicRequest.messages.push(anthropicMsg);
}
}
// 添加系统参数,并在前面加上系统提示
const systemPrompt = getSystemPrompt();
if (systemPrompt || systemContent.length > 0) {
anthropicRequest.system = [];
// 如果存在系统提示,则将其作为第一个元素添加
if (systemPrompt) {
anthropicRequest.system.push({
type: 'text',
text: systemPrompt
});
}
// 添加用户提供的系统内容
anthropicRequest.system.push(...systemContent);
}
// 如果存在工具,则进行转换
if (openaiRequest.tools && Array.isArray(openaiRequest.tools)) {
anthropicRequest.tools = openaiRequest.tools.map(tool => {
if (tool.type === 'function') {
return {
name: tool.function.name,
description: tool.function.description,
input_schema: tool.function.parameters || {}
};
}
return tool;
});
}
// 根据模型配置处理 thinking 字段
const reasoningLevel = getModelReasoning(openaiRequest.model);
if (reasoningLevel === 'auto') {
// 自动模式:完全保留原始请求的 thinking 字段
if (openaiRequest.thinking !== undefined) {
anthropicRequest.thinking = openaiRequest.thinking;
}
// 如果原始请求没有 thinking 字段,则不添加
} else if (reasoningLevel && ['low', 'medium', 'high'].includes(reasoningLevel)) {
// 特定级别:使用模型配置覆盖
const budgetTokens = {
'low': 4096,
'medium': 12288,
'high': 24576
};
anthropicRequest.thinking = {
type: 'enabled',
budget_tokens: budgetTokens[reasoningLevel]
};
} else {
// 关闭或无效:显式删除 thinking 字段
// 这确保删除原始请求中的任何 thinking 字段
delete anthropicRequest.thinking;
}
// 传递其他兼容参数
if (openaiRequest.temperature !== undefined) {
anthropicRequest.temperature = openaiRequest.temperature;
}
if (openaiRequest.top_p !== undefined) {
anthropicRequest.top_p = openaiRequest.top_p;
}
if (openaiRequest.stop !== undefined) {
anthropicRequest.stop_sequences = Array.isArray(openaiRequest.stop)
? openaiRequest.stop
: [openaiRequest.stop];
}
return anthropicRequest;
}
export function getAnthropicHeaders(authHeader, clientHeaders = {}, isStreaming = true, modelId = null) {
// 如果未提供则生成唯一 ID
const sessionId = clientHeaders['x-session-id'] || generateUUID();
const messageId = clientHeaders['x-assistant-message-id'] || generateUUID();
const headers = {
'accept': 'application/json',
'content-type': 'application/json',
'anthropic-version': clientHeaders['anthropic-version'] || '2023-06-01',
'authorization': authHeader || '',
'x-api-key': 'placeholder',
'x-api-provider': 'anthropic',
'x-factory-client': 'cli',
'x-session-id': sessionId,
'x-assistant-message-id': messageId,
'user-agent': getUserAgent(),
'x-stainless-timeout': '600',
'connection': 'keep-alive'
}
// 根据推理配置处理 anthropic-beta 头
const reasoningLevel = modelId ? getModelReasoning(modelId) : null;
let betaValues = [];
// 从客户端头添加现有的 beta 值
if (clientHeaders['anthropic-beta']) {
const existingBeta = clientHeaders['anthropic-beta'];
betaValues = existingBeta.split(',').map(v => v.trim());
}
// 根据推理配置处理 thinking beta
const thinkingBeta = 'interleaved-thinking-2025-05-14';
if (reasoningLevel === 'auto') {
// 自动模式:不修改 anthropic-beta 头,保留原始值
// betaValues 保持客户端头的不变
} else if (reasoningLevel && ['low', 'medium', 'high'].includes(reasoningLevel)) {
// 如果尚未存在,则添加 thinking beta
if (!betaValues.includes(thinkingBeta)) {
betaValues.push(thinkingBeta);
}
} else {
// 如果推理关闭或无效,则删除 thinking beta
betaValues = betaValues.filter(v => v !== thinkingBeta);
}
// 如果有任何值,则设置 anthropic-beta 头
if (betaValues.length > 0) {
headers['anthropic-beta'] = betaValues.join(', ');
}
// 使用默认值传递 Stainless SDK 头
const stainlessDefaults = {
'x-stainless-arch': 'x64',
'x-stainless-lang': 'js',
'x-stainless-os': 'MacOS',
'x-stainless-runtime': 'node',
'x-stainless-retry-count': '0',
'x-stainless-package-version': '0.57.0',
'x-stainless-runtime-version': 'v24.3.0'
};
// 根据流式传输设置 helper-method
if (isStreaming) {
headers['x-stainless-helper-method'] = 'stream';
}
// 从客户端复制 Stainless 头或使用默认值
Object.keys(stainlessDefaults).forEach(header => {
headers[header] = clientHeaders[header] || stainlessDefaults[header];
});
// 如果客户端提供,则覆盖默认超时
if (clientHeaders['x-stainless-timeout']) {
headers['x-stainless-timeout'] = clientHeaders['x-stainless-timeout'];
}
return headers;
}
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}