claude-3-5-sonnet-20241022 代理报错的解决
背景
Claude sonnet 3.5 v2 1022增加了缓存策略,cline若配置baseUrl后选中最新的模型在界面上报错
400 {"type":"error","error":{"type":"invalid_request_error","message":"messages.0.content.1.text.cache_control: Extra inputs are not permitted"}}并且用Cline聊天交互实现了一个claude的proxy服务,并且成功部署在腾讯云新加坡节点,启用云函数中url的链接在cline中跑通。
provider 选 Anthropic
add custom base Url [增加访问claude]代理地址
模型选择claude-3-5-sonnet-20241022
当然你需要有claude的key.
变更Header需要添加带cache的字段
proxyReq.setHeader('anthropic-beta', 'prompt-caching-2024-07-31');
参考Anthropic官网的介绍,链接自行搜索
nodejs工程结构
app.js
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
// 目标API配置
const TARGET_URL = 'https://api.anthropic.com';
const CLAUDE_API_KEY = 'sk-ant-api03---aL-d9QAA';
// IP白名单
const WHITELIST_IPS = [
'1.1.1.1',
'127.0.0.1',
];
// 检查API密钥前缀
function isValidApiKeyPrefix(apiKey) {
return apiKey && (apiKey.startsWith('sk-xx1') || apiKey.startsWith('sk-xx2'));
}
// 创建Express应用
const app = express();
// 请求日志中间件
app.use((req, res, next) => {
console.log('--- 请求信息 ---');
console.log(`Method: ${req.method}`);
console.log(`Path: ${req.path}`);
console.log('Headers:', req.headers);
next();
});
// 代理中间件配置
const proxyMiddleware = createProxyMiddleware({
target: TARGET_URL,
changeOrigin: true,
ws: true, // 支持websocket
onProxyReq: (proxyReq, req, res) => {
// 移除敏感头信息
proxyReq.removeHeader('x-forwarded-for');
proxyReq.removeHeader('x-real-ip');
// 获取客户端IP
const clientIP = req.ip ||
req.connection.remoteAddress ||
req.headers['x-forwarded-for']?.split(',')[0];
// 检查API密钥
const originalAuth = req.headers['x-api-key'];
// 如果在白名单内或有效的API密钥前缀
if (WHITELIST_IPS.includes(clientIP) || isValidApiKeyPrefix(originalAuth)) {
proxyReq.setHeader('x-api-key', CLAUDE_API_KEY);
proxyReq.setHeader('anthropic-version', '2023-06-01');
proxyReq.setHeader('anthropic-beta', 'prompt-caching-2024-07-31');
}
// 如果是POST请求,需要重新写入请求体
if (req.method === 'POST' && req.body) {
const bodyData = JSON.stringify(req.body);
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
}
},
onProxyRes: (proxyRes, req, res) => {
// 设置CORS头
proxyRes.headers['Access-Control-Allow-Origin'] = '*';
proxyRes.headers['Access-Control-Allow-Methods'] = 'GET,POST,OPTIONS';
proxyRes.headers['Access-Control-Allow-Headers'] = '*';
proxyRes.headers['Access-Control-Max-Age'] = '86400';
// 如果是流式响应,确保正确的内容类型
if (req.body?.stream === true) {
proxyRes.headers['Content-Type'] = 'text/event-stream';
proxyRes.headers['Cache-Control'] = 'no-cache';
proxyRes.headers['Connection'] = 'keep-alive';
}
},
onError: (err, req, res) => {
console.error('代理错误:', err);
res.status(500).json({
error: 'Proxy Error',
message: err.message
});
}
});
// 处理预检请求
app.options('*', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader('Access-Control-Max-Age', '86400');
res.sendStatus(200);
});
// 应用代理中间件
app.use('/', express.json(), proxyMiddleware);
// 错误处理中间件
app.use((err, req, res, next) => {
console.error('应用错误:', err);
res.status(500).json({
error: 'Internal Server Error',
message: err.message
});
});
// 启动服务器
const port = process.env.PORT || 9000;
app.listen(port, () => {
console.log(`代理服务器运行在 http://localhost:${port}`);
});
// 导出handler供云函数使用
exports.main_handler = app;
package.json
{
"name": "origin",
"version": "0.0.0",
"private": true,
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js"
},
"dependencies": {
"express": "^4.18.2",
"http-proxy-middleware": "^2.0.6"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}