技术人员常用的划词翻译插件及配置 ChatGPT 和代理服务

作为技术人员平时经常会翻看一些技术文档,而对应技术社区来说很多文档都是英文的,那毫无疑问翻译工具是少不了的。了不起常用的一个翻译工具叫划词翻译,是一个 Chrome 插件,日常看英文文档的遇到不懂的单词或者句子直接选中,然后点击一下图标就可以实现自动翻译。

翻译源

0-6
0-10

这个插件的翻译源有很多,如下所示

0-7

其中的翻译源的使用方式不完全相同,有一些不用任何配置就可以使用,比如 DeepL 和必应翻译,安装好插件就可以直接使用;

0-8

有一些需要解决网络问题才能正常使用,比如谷歌翻译,因为谷歌翻译已经退出中国了;

0-9

还有一些需要去对应的官网申请秘钥进行配置过后才能正常使用,比如有道翻译和火山翻译等,如果上图所示。

配置 ChatGPT

通过翻译源我们可以看到其中也有 ChatGPT 的选项,ChatGPT 的配置相较于其他的翻译源我们除了要配置 API Key 之外,我们同时也需要解决网络问题。

但是这对于大部分来说,这两个问题都有一定的门槛,ChatGPT 对网络的要求稍微会高点,弄不好就会被封号。

所以我们这里通过两步来解决这个问题

  1. 申请微软的 Azure Open AI
  2. 通过 proxy 代理的形式将 ChatGPT 原生请求转换为 Azure Open AI 格式;

关于第一点前面的文章已经很详细的介绍了如果去申请,感兴趣的可以去看看,今天主要分享一下如何配置代理服务。

配置代理服务

在完成微软的 Azure Open AI 申请并成功部署模型过后,我们会获取到对应的 endpoint 以及秘钥信息。

然后我们注册一个 cloudflare 账号,然后在 Worker & Papes 下面创建一个应用,

0-13
0-11

重命名我们的服务,然后点击部署即可

0-12
0-15

然后点击 edit code,在编辑器中输入下放代码,其中的代码有三处需要修改

  1. resourceName:修改成申请的微软 Azure OpenAI 的时候配置的资源名称
  2. mapper:修改成 open ai 模型和 Azure OpenAI 的部署名称,前面是原生的模型名称,后面是 Azure 的部署名称;
  3. apiVersion:修改成在 Azure OpenAI 里面的一致即可;
// The name of your Azure OpenAI Resource.
const resourceName="resource name"
// The deployment name you chose when you deployed the model.
const mapper = {
  // model: deployName
    'gpt-35-turbo-16k'"gpt-35-turbo"
};
const apiVersion="2023-03-15-preview"

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

async function handleRequest(request{
  if (request.method === 'OPTIONS') {
    return handleOPTIONS(request)
  }

  const url = new URL(request.url);
  if (url.pathname.startsWith("//")) {
    url.pathname = url.pathname.replace('/',"")
  }
  if (url.pathname === '/v1/chat/completions') {
    var path="chat/completions"
  } else if (url.pathname === '/v1/completions') {
    var path="completions"
  } else if (url.pathname === '/v1/models') {
    return handleModels(request)
  } else {
    return new Response('404 Not Found', { status404 })
  }

  let body;
  if (request.method === 'POST') {
    body = await request.json();
  }

  const modelName = body?.model;  
  const deployName = mapper[modelName] || '' 

  if (deployName === '') {
    return new Response('Missing model mapper', {
        status403
    });
  }
  const fetchAPI = `https://${resourceName}.openai.azure.com/openai/deployments/${deployName}/${path}?api-version=${apiVersion}`

  const authKey = request.headers.get('Authorization');
  if (!authKey) {
    return new Response("Not allowed", {
      status403
    });
  }

  const payload = {
    method: request.method,
    headers: {
      "Content-Type""application/json",
      "api-key": authKey.replace('Bearer '''),
    },
    bodytypeof body === 'object' ? JSON.stringify(body) : '{}',
  };

  let response = await fetch(fetchAPI, payload);
  response = new Response(response.body, response);
  response.headers.set("Access-Control-Allow-Origin""*");

  if (body?.stream != true){
    return response
  } 

  let { readable, writable } = new TransformStream()
  stream(response.body, writable);
  return new Response(readable, response);

}

function sleep(ms{
  return new Promise(resolve => setTimeout(resolve, ms));
}

// support printer mode and add newline
async function stream(readable, writable{
  const reader = readable.getReader();
  const writer = writable.getWriter();

  // const decoder = new TextDecoder();
  const encoder = new TextEncoder();
  const decoder = new TextDecoder();
// let decodedValue = decoder.decode(value);
  const newline = "\n";
  const delimiter = "\n\n"
  const encodedNewline = encoder.encode(newline);

  let buffer = "";
  while (true) {
    let { value, done } = await reader.read();
    if (done) {
      break;
    }
    buffer += decoder.decode(value, { streamtrue }); // stream: true is important here,fix the bug of incomplete line
    let lines = buffer.split(delimiter);

    // Loop through all but the last line, which may be incomplete.
    for (let i = 0; i < lines.length - 1; i++) {
      await writer.write(encoder.encode(lines[i] + delimiter));
      await sleep(20);
    }

    buffer = lines[lines.length - 1];
  }

  if (buffer) {
    await writer.write(encoder.encode(buffer));
  }
  await writer.write(encodedNewline)
  await writer.close();
}

async function handleModels(request{
  const data = {
    "object""list",
    "data": []  
  };

  for (let key in mapper) {
    data.data.push({
      "id": key,
      "object""model",
      "created"1677610602,
      "owned_by""openai",
      "permission": [{
        "id""modelperm-M56FXnG1AsIr3SXq8BYPvXJA",
        "object""model_permission",
        "created"1679602088,
        "allow_create_engine"false,
        "allow_sampling"true,
        "allow_logprobs"true,
        "allow_search_indices"false,
        "allow_view"true,
        "allow_fine_tuning"false,
        "organization""*",
        "group"null,
        "is_blocking"false
      }],
      "root": key,
      "parent"null
    });  
  }

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

async function handleOPTIONS(request{
    return new Response(null, {
      headers: {
        'Access-Control-Allow-Origin''*',
        'Access-Control-Allow-Methods''*',
        'Access-Control-Allow-Headers''*'
      }
    })
}

代码修改完成过后,点击右上角的 save and deploy 按钮即可。此时 cloudflare 会帮我们生成一个 worker.dev 接口的地址,通过这个地址我们就可以直接访问了,不过更优雅的方式是我们配置一个自己的域名。

配置自定义域名

配置自定义域名要求我们首先有一个主域名,并且绑定到 cloudflare 里面,我们通过上方的 add site 添加一个自己的域名,

0-14

并且选择 free 方案

0-16

接下来我们再按照要求,到域名 dns 解析的地方去配置一下 dns 的解析,保存过后可以点击 Check nameservers,这个过程需要一段时间,上面说到差不多要 24 小时,我们可以晚点再回来看看。

0-18

直到我们看到对应的 site 下面显示 active 就说明可以了

0-19

接下来我们再到之前部分的 worker 下面去配置自定义域名

0-17

先配置一下 route,然后在配置一下自定义域名即可。

这里有点绕,不过按照流程配置是可以正常使用的。

配置 roure 的时候需要我们有可用是 site 站点;配置自定义域名的时候要求我们可用的 roure

配置划词翻译

当我们代理服务部署完成过后,再回来划词翻译这里,在插件的服务申请 => ChatGPT 管理秘钥这里,填入我们 Azure OpenAIkey;在第三方服务 => ChatGPT 这里填上我们上面自定义的域名和对应的模型名称,这个模型需要跟脚本 mapper 里面的 key 保持一致。

0-20

至此我们的 ChatGPT 的配置就完成了,简单总结一下:

因为我们国内无法直接使用 ChatGPT,我们这里通过申请微软的 Azure OpenAI 来替代原生 OpenAI,但是又因为划词翻译的 API 对接的是 OpenAI 的接口,所以我们需要一个中间代理服务将两者的协议进行转换。

如果划词翻译能直接兼容 Azure OpenAI 的话,其实我们就不用中间的代理了,可以直接配置。同理我们配置了代理服务过后,以后在其他 OpenAI 的客户端我们都可以直接使用了,一劳永逸。

评论