Administrator
发布于 2024-10-26 / 39 阅读
0
0

Cline添加baseurl报错的修改【流水账版】

claude-3-5-sonnet-20241022 代理报错的解决

背景

  1. 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"}}

  2. 并且用Cline聊天交互实现了一个claude的proxy服务,并且成功部署在腾讯云新加坡节点,启用云函数中url的链接在cline中跑通。

    1. provider 选 Anthropic

    2. add custom base Url [增加访问claude]代理地址

    3. 模型选择claude-3-5-sonnet-20241022

    4. 当然你需要有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"
  }
}

腾讯云函数配置

Vscode 插件cline配置


评论