作为技术人员平时经常会翻看一些技术文档,而对应技术社区来说很多文档都是英文的,那毫无疑问翻译工具是少不了的。了不起常用的一个翻译工具叫划词翻译,是一个 Chrome
插件,日常看英文文档的遇到不懂的单词或者句子直接选中,然后点击一下图标就可以实现自动翻译。
翻译源
这个插件的翻译源有很多,如下所示
其中的翻译源的使用方式不完全相同,有一些不用任何配置就可以使用,比如 DeepL
和必应翻译,安装好插件就可以直接使用;
有一些需要解决网络问题才能正常使用,比如谷歌翻译,因为谷歌翻译已经退出中国了;
还有一些需要去对应的官网申请秘钥进行配置过后才能正常使用,比如有道翻译和火山翻译等,如果上图所示。
配置 ChatGPT
通过翻译源我们可以看到其中也有 ChatGPT
的选项,ChatGPT
的配置相较于其他的翻译源我们除了要配置 API Key
之外,我们同时也需要解决网络问题。
但是这对于大部分来说,这两个问题都有一定的门槛,ChatGPT
对网络的要求稍微会高点,弄不好就会被封号。
所以我们这里通过两步来解决这个问题
- 申请微软的
Azure Open AI
; - 通过
proxy
代理的形式将ChatGPT
原生请求转换为Azure Open AI
格式;
关于第一点前面的文章已经很详细的介绍了如果去申请,感兴趣的可以去看看,今天主要分享一下如何配置代理服务。
配置代理服务
在完成微软的 Azure Open AI
申请并成功部署模型过后,我们会获取到对应的 endpoint
以及秘钥信息。
然后我们注册一个 cloudflare
账号,然后在 Worker & Papes
下面创建一个应用,
重命名我们的服务,然后点击部署即可
然后点击 edit code
,在编辑器中输入下放代码,其中的代码有三处需要修改
resourceName
:修改成申请的微软Azure OpenAI
的时候配置的资源名称mapper
:修改成open ai
模型和Azure OpenAI
的部署名称,前面是原生的模型名称,后面是Azure
的部署名称;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', { status: 404 })
}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', {
status: 403
});
}
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", {
status: 403
});
}const payload = {
method: request.method,
headers: {
"Content-Type": "application/json",
"api-key": authKey.replace('Bearer ', ''),
},
body: typeof 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, { stream: true }); // 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, null, 2);
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
添加一个自己的域名,
并且选择 free 方案
接下来我们再按照要求,到域名 dns
解析的地方去配置一下 dns
的解析,保存过后可以点击 Check nameservers
,这个过程需要一段时间,上面说到差不多要 24 小时,我们可以晚点再回来看看。
直到我们看到对应的 site
下面显示 active
就说明可以了
接下来我们再到之前部分的 worker
下面去配置自定义域名
先配置一下 route
,然后在配置一下自定义域名即可。
这里有点绕,不过按照流程配置是可以正常使用的。
配置 roure 的时候需要我们有可用是 site 站点;配置自定义域名的时候要求我们可用的 roure
配置划词翻译
当我们代理服务部署完成过后,再回来划词翻译这里,在插件的服务申请 => ChatGPT
管理秘钥这里,填入我们 Azure OpenAI
的 key
;在第三方服务 => ChatGPT
这里填上我们上面自定义的域名和对应的模型名称,这个模型需要跟脚本 mapper
里面的 key
保持一致。
至此我们的 ChatGPT
的配置就完成了,简单总结一下:
因为我们国内无法直接使用 ChatGPT
,我们这里通过申请微软的 Azure OpenAI
来替代原生 OpenAI
,但是又因为划词翻译的 API
对接的是 OpenAI
的接口,所以我们需要一个中间代理服务将两者的协议进行转换。
如果划词翻译能直接兼容 Azure OpenAI
的话,其实我们就不用中间的代理了,可以直接配置。同理我们配置了代理服务过后,以后在其他 OpenAI
的客户端我们都可以直接使用了,一劳永逸。