Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如果你不同的模型在不同的resource下面的话,采用这样的代码 #77

Open
tangmingxing1988 opened this issue May 3, 2024 · 0 comments

Comments

@tangmingxing1988
Copy link

tangmingxing1988 commented May 3, 2024

现在不同的模型在不同的地域可用,甚至我想用的最新的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': '*'
      }
    })
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant