cf-openai-azure-proxy
cf-openai-azure-proxy copied to clipboard
如果你不同的模型在不同的resource下面的话,采用这样的代码
现在不同的模型在不同的地域可用,甚至我想用的最新的3.5和4居然没有一个可以同时用的区域,我不得不创建不同的resource,改了一下代码,感觉没必要发pr,直接贴在下面,需要用的自取。 要点如下
- 将不同模型的resourceName, deployName和对应的apikey写成二维数组,有多少写多少。
- 不同的modelname映射为models的索引,为数字。
- authKey要设置下,代表客户端请求用的apikey,因为要使用不同的resource,所以apikey不同,不能直接用客户端的,只能单独约定一个跟客户端交互的key,服务端的用定义在脚本里面的
代码如下,要改的就前三个。
// 不同的模型写到下面的数组里面
const models = [
["resourceName1", "deployName1", "apikey1:*****7633c5e7289d"],
["resourceName2", "deployName2", "apikey2:*****7630a45b8531"]
];
//这是跟客户端的约定key,客户端设置成这个就行
const authKey = "sk-*************RtoJlKP9BfU0X5";
// 后面的数字代表在models中的索引位置
const mapper = {
'gpt-3.5-turbo': 0,
'gpt-3.5-turbo-0613': 0,
'gpt-3.5-turbo-1106': 0,
'gpt-3.5-turbo-16k': 0,
'gpt-3.5-turbo-0125': 0,
'gpt-4': 1,
'gpt-4-turbo': 1,
'gpt-4-0613': 1,
'gpt-4-1106-preview': 1,
'gpt-4-32k': 1,
'gpt-4-turbo-2024-04-09': 1,
};
const apiVersion="2024-03-01-preview";
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
if (request.method === 'OPTIONS') {
return handleOPTIONS(request)
}
if (!request.headers.get('Authorization').includes(authKey)) {
return new Response("Not allowed", {
status: 403
});
}
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/images/generations') {
var path="images/generations"
} 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;
if (!(modelName in mapper)) {
return new Response('Missing model mapper', {
status: 403
});
}
const model = models[mapper[modelName]];
const fetchAPI = `https://${model[0]}.openai.azure.com/openai/deployments/${model[1]}/${path}?api-version=${apiVersion}`
const payload = {
method: request.method,
headers: {
"Content-Type": "application/json",
"api-key": model[2],
},
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': '*'
}
})
}