Double简介
Double 是一款对细节有着极致追求,专为高效工作而打造的人工智能编码助手,目前已经可以在 VScode 中免费体验。
主要功能
对标Github的Copilot,Double目前只支持 VScode,点击了解更多内容。
简单上手
本帖将介绍如何利用Worker逆向Double的请求并将其模拟为OpenAI格式,实现统一化调用。
第一步,安装VScode 和 插件
VScode 点击下载
Double插件 点击下载
第二步,注册账号,获取api
①安装好插件后重启,登录插件,登录成功后先发送一条聊天消息试试。
②关闭VScode,点击打开,在页面中点击取消,再点 Not working?
第三步,创建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;
}
Comments NOTHING