那位佬的公益支持Anthropic格式

想试试cline这个插件,但是没有卡可以充值。想问下那个佬的公益支持Anthropic格式,用的好了再想办法搞个卡

4 个赞

可以自己转一下?不懂帮顶。

这个是,不过不知道还有没在运营,GCP 转 Worker 那教程都是保持原格式的 Claude 家都是 Anthropic。


寄了,已经 ping 不通了。

原生格式大部分都得花钱了,1:3或者1:4,而且claude dev消耗贼快,并发要求也高,便宜的一问到上限了…想玩这个稳定点的还得付费,或者用gpt,效果不那么好就是了

自己转一下 :tieba_087:

这种能转吗,真不知道 :flushed:

当然可以 :tieba_087:

aws这些羊毛可以还是说逆向啥的都行 :crazy_face:

你只管提需求,Claude 会帮你写完整代码。

付费也可以,反正就测试一下,主要官网的太麻烦了,又要美国电话,又要卡的

放到cloudflare worker里面,改一下全局变量BASE_URL 就行了



// 设置全局变量
const BASE_URL = 'https:123.com';

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
  const url = new URL(request.url);

  if (url.pathname === '/v1/messages') {
    return handleMessagesRequest(request);
  } else if (url.pathname === '/v1/models') {
    return handleModelsRequest(request);
  } else {
    return new Response('Not Found', { status: 404 });
  }
}

// 处理 /models 请求
async function handleModelsRequest(request) {
  const apiKey = request.headers.get('x-api-key');

  const response = await fetch(`${BASE_URL}/v1/models`, {
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${apiKey}`
    }
  });

  const models = await response.json();

  // 转换 OpenAI 模型列表为 Claude 模型列表格式
  const claudeModels = {
    models: models.data.map(model => {
      const claudeModel = {
        name: model.id
      };

      // 只添加存在的属性
      if (model.max_tokens !== undefined) {
        claudeModel.max_tokens = model.max_tokens;
      }

      if (model.description !== undefined) {
        claudeModel.description = model.description;
      }

      return claudeModel;
    })
  };

  return new Response(JSON.stringify(claudeModels), {
    headers: {
      'Content-Type': 'application/json'
    }
  });
}

// 处理 /messages 请求
async function handleMessagesRequest(request) {
  try {
    const inputBody = await request.json();

    // 构造 OpenAI 请求体,仅包含存在的属性
    const openaiRequestBody = {
      model: inputBody.model,
      messages: inputBody.messages.map(message => ({
        role: message.role,
        content: message.content
      }))
    };

    // 添加可选参数
    if (inputBody.max_tokens !== undefined) {
      openaiRequestBody.max_tokens = inputBody.max_tokens;
    }
    if (inputBody.temperature !== undefined) {
      openaiRequestBody.temperature = inputBody.temperature;
    }
    if (inputBody.stream !== undefined) {
      openaiRequestBody.stream = inputBody.stream;
    }

    const apiKey = request.headers.get('x-api-key');

    const response = await fetch(`${BASE_URL}/v1/chat/completions`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${apiKey}`
      },
      body: JSON.stringify(openaiRequestBody)
    });

    if (inputBody.stream) {
      // 处理流式响应
      return new Response(await streamOpenAIToClaude(response.body), {
        headers: {
          'Content-Type': 'text/event-stream',
          'Cache-Control': 'no-cache',
          'Connection': 'keep-alive'
        }
      });
    } else {
      // 处理非流式响应
      const responseBody = await response.json();

      // 转换 OpenAI 响应为 Claude 格式
      const claudeResponseBody = {
        content: [
          {
            text: responseBody.choices[0].message.content,
            type: 'text'
          }
        ],
        id: responseBody.id,
        model: inputBody.model,
        role: 'assistant',
        stop_reason: responseBody.choices[0].finish_reason,
        stop_sequence: null,
        type: 'message',
        usage: responseBody.usage
      };

      return new Response(JSON.stringify(claudeResponseBody), {
        headers: {
          'Content-Type': 'application/json'
        }
      });
    }
  } catch (error) {
    console.error('Request error:', error);
    return new Response(JSON.stringify({
      error: 'Internal Server Error',
      message: error.message
    }), {
      status: 500,
      headers: {
        'Content-Type': 'application/json'
      }
    });
  }
}

// 流式响应转换函数
async function streamOpenAIToClaude(readableStream) {
  const encoder = new TextEncoder();
  const decoder = new TextDecoder();

  const transformStream = new TransformStream();
  const writer = transformStream.writable.getWriter();
  const reader = readableStream.getReader();

  // 发送初始事件:message_start
  writer.write(encoder.encode(`event: message_start\ndata: {"type":"message_start","message":{"id":"","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":null}}\n\n`));

  // 发送 content_block_start 事件
  writer.write(encoder.encode(`event: content_block_start\ndata: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}\n\n`));

  let buffer = '';

  async function processStream() {
    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        break;
      }

      buffer += decoder.decode(value, { stream: true });
      let lines = buffer.split('\n');
      buffer = lines.pop();

      for (const line of lines) {
        if (line.startsWith('data: ')) {
          const dataStr = line.slice(6).trim();
          if (dataStr === '[DONE]') {
            // 发送 content_block_stop 事件
            writer.write(encoder.encode(`event: content_block_stop\ndata: {"type":"content_block_stop","index":0}\n\n`));

            // 发送 message_stop 事件
            writer.write(encoder.encode(`event: message_stop\ndata: {"type":"message_stop"}\n\n`));

            await writer.close();
            return;
          }

          try {
            const data = JSON.parse(dataStr);
            if (data.choices && data.choices.length > 0) {
              const delta = data.choices[0].delta;
              if (delta && delta.content) {
                // 转换为 Claude 的 content_block_delta 事件
                const contentDelta = {
                  type: 'content_block_delta',
                  index: 0,
                  delta: {
                    type: 'text_delta',
                    text: delta.content
                  }
                };
                writer.write(encoder.encode(`event: content_block_delta\ndata: ${JSON.stringify(contentDelta)}\n\n`));
              }
            }
          } catch (e) {
            console.error('Error parsing JSON:', e);
          }
        }
      }
    }

    // 流结束时,发送 content_block_stop 和 message_stop 事件
    writer.write(encoder.encode(`event: content_block_stop\ndata: {"type":"content_block_stop","index":0}\n\n`));
    writer.write(encoder.encode(`event: message_stop\ndata: {"type":"message_stop"}\n\n`));
    await writer.close();
  }

  processStream();

  return transformStream.readable;
}
6 个赞

感谢大佬,我试试