搭建 GenAI 服务

原因

  • Gemini API 无疑是最好的免费 LLM API,量大、限制少。

  • 以前一直以为她是不兼容 OpenAI API 的。

  • 昨天无意在 Gemini API 的官网上看到,如果使用以下的 base_url:

    https://generativelanguage.googleapis.com/v1beta/openai/

    就可以使用 OpenAI 库(Python 和 TypeScript/JavaScript)以及 REST API 访问 Gemini 模型。

  • 这样Gemini API 的可玩性就大了很多😊。

搭建服务

  • 首先就想到可以部署到 Deno Deploy 上,反代后就可以直连了😍。
  • 这回没有用 Claude 3.5 sonnet ,而是用了 Gemini-1.5-pro ,毕竟是他自家的东西。
  • 搞笑的是,一上来 Gemini 断然否认了存在这么个兼容API的存在;
  • 我直接把 base_url 扔给它,才开始干活;
  • 编完代码还不断提醒说这个兼容API可能不存在,那么代码就无法正常工作,应该用回 Gemini 自己的API。😂
  • 还好只运行出错一次,就正常工作了,不愧是目前 LLM 三巨头之一👍。

源代码

  • 代码都是 Gemini 写的,直接在 Deno Deployplayground 中输入;

  • 然后到 setting 中设置两个环境变量:

    • REAL_GEMINI_API_KEY 是你的真实 Gemini API key ;
    • CUSTOM_AUTH_KEY 则是用来代替真实 Gemini API key 的验证码; -这样使用这个反代 API 时,就可以隐藏你自己的真实 Gemini API key ,增加一道安全防护。
  • 代码如下:

const GEMINI_API_BASE_URL = "https://generativelanguage.googleapis.com/";
const CUSTOM_AUTH_KEY = Deno.env.get("CUSTOM_AUTH_KEY");
const REAL_GEMINI_API_KEY = Deno.env.get("REAL_GEMINI_API_KEY");
const OPENAI_COMPATIBLE_PREFIX = "/v1beta/openai";

Deno.serve(async (request) => {
  const url = new URL(request.url);

  // 1. 验证用户的 API 密钥
  const authHeader = request.headers.get("Authorization");
  if (!authHeader?.startsWith("Bearer ") || authHeader.substring(7) !== CUSTOM_AUTH_KEY) {
    return new Response("Unauthorized: Invalid API key", { status: 401 });
  }

  // 2. 构建新的 URL
  const newPathname = url.pathname.startsWith(OPENAI_COMPATIBLE_PREFIX)
    ? url.pathname.substring(OPENAI_COMPATIBLE_PREFIX.length)
    : url.pathname;
  const newURL = new URL(OPENAI_COMPATIBLE_PREFIX + newPathname, GEMINI_API_BASE_URL);

  // 3. 修改请求头
  const headers = new Headers(request.headers);
  headers.set("Authorization", `Bearer ${REAL_GEMINI_API_KEY}`);

  // 4. 创建新的请求并转发
  const newRequest = new Request(newURL.toString(), {
    method: request.method,
    headers: headers,
    body: request.body,
    redirect: "follow",
  });

  // 5. 转发请求并返回响应
  const response = await fetch(newRequest);
  return new Response(response.body, {
    status: response.status,
    headers: response.headers,
  });
});