利用Work部署Double白嫖GPT4和Claude-3-Opus

发布于 2024-03-08  641 次阅读


AI 摘要

文章摘要:本文介绍了利用Worker部署Double来享受GPT4和Claude-3-Opus的使用。Double是一款人工智能编码助手,专为高效工作打造,支持聊天、自动补全和快捷键操作等功能。通过逆向Double的请求并将其模拟为OpenAI格式,可以实现统一化调用。文章还详细介绍了安装VScode和插件、注册账号获取API以及创建Worker的步骤。目前Double的免费计划是每月50条,订阅用户无限制。

Double简介

Double 是一款对细节有着极致追求,专为高效工作而打造的人工智能编码助手,目前已经可以在 VScode 中免费体验。

主要功能

①聊天

侧边栏支持 GPT-4 聊天,支持在提问时引用代码。

②自动补全

编辑代码时支持 Tab 一键自动补全。

③快捷键操作

解放鼠标,支持快捷键,方便键盘操作。

对标Github的Copilot,Double目前只支持 VScode,点击了解更多内容。

简单上手

本帖将介绍如何利用Worker逆向Double的请求并将其模拟为OpenAI格式,实现统一化调用。

限制:目前免费计划 50条/月,订阅用户不受限制

第一步,安装VScode 和 插件

VScode 点击下载

Double插件 点击下载

第二步,注册账号,获取api

①安装好插件后重启,登录插件,登录成功后先发送一条聊天消息试试。

②关闭VScode,点击打开,在页面中点击取消,再点 Not working?

复制 Auth Token

第三步,创建worker

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

async function generateResponseStream(apiResponse, model, timestamp) {
    const fixedStart = {
        id: "chatcmpl-smnet-2311676378-double",
        object: "chat.completion.chunk",
        created: timestamp,
        model: model,
        choices: [
            {
                delta: {
                    role: "assistant",
                    content: ""
                },
                index: 0,
                finish_reason: null
            },
        ],
    };

    const fixedEnd = {
        id: "chatcmpl-smnet-2311676378-double",
        object: "chat.completion.chunk",
        created: timestamp,
        model: "gpt-4",
        choices: [
            {
                delta: {},
                index: 0,
                finish_reason: "stop"
            },
        ],
    };

    const reader = apiResponse.body.getReader();
    return new Response(new ReadableStream({
        async start(controller) {
            controller.enqueue(new TextEncoder().encode('data: ' + JSON.stringify(fixedStart) + '\n\n'));
            while (true) {
                const { done, value } = await reader.read();
                if (done) break;
                const chunk = value;
                const formattedChunk = {
                    id: "chatcmpl-smnet-2311676378-double",
                    object: "chat.completion.chunk",
                    created: timestamp,
                    model: "gpt-4",
                    choices: [
                        {
                            delta: {
                                content:  new TextDecoder().decode(chunk),
                                role: "assistant"
                            },
                            index: 0,
                            finish_reason: null
                        },
                    ],
                };
                controller.enqueue(new TextEncoder().encode('data: ' + JSON.stringify(formattedChunk) + '\n\n'));
              }
              controller.enqueue(new TextEncoder().encode('data: ' + JSON.stringify(fixedEnd) + '\n\n'));
              controller.enqueue(new TextEncoder().encode('data: [DONE]\n\n'));
              controller.close();
        }
    }), {
        headers: {
        "Access-Control-Allow-Origin": "*",
        'Content-Type': 'text/event-stream'
        }
    });
}

async function generateNormalResponse(apiResponse, model, timestamp) {
    const responseText = await apiResponse.text();
    const jsonResponse = JSON.stringify({
        id: "chatcmpl-smnet-2311676378-double",
        object: "chat.completion",
        created: timestamp,
        model: model,
        choices: [{
            index: 0,
            message: {
                role: "assistant",
                content: responseText
            },
            finish_reason: "stop"
        }]
    });
      return new Response(jsonResponse, {
        headers: {
          "Access-Control-Allow-Origin": "*",
          'Content-Type': 'application/json'
        }
      });
};

async function handleRequest(request) {
  try {
      const url = new URL(request.url);
      if (request.method === 'OPTIONS') {
          return new Response(null, {
              status: 204,
              headers: {
                  "Access-Control-Allow-Origin": "*",
                  "Access-Control-Allow-Methods": "POST, OPTIONS",
                  "Access-Control-Allow-Headers": "Content-Type, Authorization"
              }
          });
      }
      if (request.method !== "POST" || url.pathname !== "/v1/chat/completions") {
          return new Response("Invalid request", {status: 400});
      }

      const bearerToken = request.headers.get("Authorization")?.split("Bearer ")[1];

      if (!bearerToken) {
          throw new Error("Missing API Key");
      }

      let {messages, model, stream=true} = await request.json();
      if (!messages) {
          throw new Error("Messages object is missing");
      }

      const systemMessage = messages.find(message => message.role === 'system');
      if (systemMessage) {
        let userMessage = messages.find(message => message.role === 'user');
        if (userMessage) {
          userMessage.content = systemMessage.content + "\n\n" + userMessage.content;
        }
        messages = messages.filter(message => message.role !== 'system');
      }
      messages = messages.map(({role, content}) => ({
        role: role === 'system' ? 'assistant' : role,
        message: content,
        codeContexts: []
      }));

      let chat_model;
      switch (model?.toLowerCase()) {
          case "claude-3-opus":
              chat_model = "Claude 3 (Opus)";
              break;
          default:
              chat_model = "GPT4 Turbo";
      }
      const JWT = await getJWT(bearerToken);

      const apiUrl = "https://api.double.bot/api/v1/chat";
      const headers = {
          "Content-Type": "application/json",
          "double-version": "2024-03-04",
          "Authorization": `Bearer ${JWT}`
      };
      const body = JSON.stringify({
          "api_key": bearerToken,
          "messages": messages,
          "chat_model": chat_model,
      });

      const apiResponse = await fetch(apiUrl, {method: "POST", headers, body});

      if (!apiResponse.ok) {
          const errorText = await apiResponse.text();
          throw new Error("API request failed: " + errorText);
      }
      const timestamp = Math.floor(Date.now() / 1000);
      if (stream) {
        return await generateResponseStream(apiResponse, model?.toLowerCase(), timestamp);
      } else {
        return await generateNormalResponse(apiResponse, model?.toLowerCase(), timestamp);
      }
  } catch (err) {
      return new Response(err.message, {status: 500});
  }
}

async function getJWT(api_key) {
  const jwtUrl = "https://api.double.bot/api/auth/refresh";
  const headers = {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${api_key}`
  };
  const apiResponse = await fetch(jwtUrl, {method: "POST", headers});
  const jwt = await apiResponse.json();
  return jwt.access_token;
}

第四步,绑定域名

第五步,配置客户端

在oneapi自定义渠道,填入你worker的domain以及auth token

模型填写gpt4claude-3-opus

在chatnext或者其他客户端,填入你的oneapi密钥和oneapi域名

  • alipay_img
  • wechat_img
公众号:享生活爱羊毛
最后更新于 2024-03-08