使用 Gemini API 并进行 OpenAI 回退的 Typescript 实现

2025-04-04

如果你想使用 Gemini 的公共 API,但同时希望在达到速率限制时有一个安全的备用方案,可以使用 OpenAI TS/JS library 和一些辅助函数。 在我的特定用例中,我需要一个类型安全的解决方案,用于一个 chartmaker app,并具备回退功能,因为 Gemini 的 gemini-2.5-pro-exp-03-25 模型被限制为 20 个请求/分钟。

首先,你需要定义你想要使用的模型,以便在使用辅助函数时它们显示为自动建议:

type Model = ChatCompletionParseParams['model'] | 'gemini-2.5-pro-exp-03-25' | 'gemini-2.0-flash';

辅助函数需要一个参数;一个包含 2 个配置对象的数组,用于所需的 AI 查询(原则上,你可以添加任意数量的配置对象,或选择与 OpenAI 库兼容的其他 AI):

export const getCompletion = async (
 options: [
  Omit<ChatCompletionParseParams, 'model'> & { model: Model },
  Omit<ChatCompletionParseParams, 'model'> & { model: Model },
 ],
) => {
 try {
  const isGemini = options[0].model.includes('gemini');
  const openai = new OpenAI(
   isGemini
    ? {
      apiKey: process.env.GEMINI_API_KEY,
      baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
     }
    : { apiKey: process.env.OPENAI_API_KEY },
  );
  return await openai.chat.completions.create(options[0]);
 } catch (error) {
  console.log(`Failed completion for first model (${options[0].model})`, error);
  const isGemini = options[1].model.includes('gemini');
  const openai = new OpenAI(
   isGemini
    ? {
      apiKey: process.env.GEMINI_API_KEY,
      baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
     }
    : { apiKey: process.env.OPENAI_API_KEY },
  );
  return await openai.chat.completions.create(options[1]);
 }
};

该辅助函数可以按以下方式使用:

const messages = [{ role: 'user', content: 'Tell a short joke.' }];
const completion = await getCompletion([
 { model: 'gemini-2.0-flash', messages },
 { model: 'gpt-3.5-turbo', messages },
]);
console.log(completion);
// {
//  "choices": [
//   {
//    "finish_reason": "stop",
//    "index": 0,
//    "message": {
//     "content": "Why don't scientists trust atoms?\n\nBecause they make up everything!\n",
//     "role": "assistant"
//    }
//   }
//  ],
//  "created": 1743757243,
//  "model": "gemini-2.0-flash",
//  "object": "chat.completion",
//  "usage": {
//   "completion_tokens": 16,
//   "prompt_tokens": 5,
//   "total_tokens": 21
//  }
// }

你还可以创建一个辅助函数,用于类型安全的结构化输出:

export const getJSONCompletion = async <T>(
 options: [
  Omit<ChatCompletionParseParams, 'model'> & { model: Model },
  Omit<ChatCompletionParseParams, 'model'> & { model: Model },
 ],
): Promise<ParsedChatCompletion<T> & { _request_id?: string | null | undefined }> => {
 try {
  const isGemini = options[0].model.includes('gemini');
  const openai = new OpenAI(
   isGemini
    ? {
      apiKey: process.env.GEMINI_API_KEY,
      baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
     }
    : { apiKey: process.env.OPENAI_API_KEY },
  );
  return await openai.beta.chat.completions.parse({ ...options[0] });
 } catch (error) {
  console.log('Failed completion for first model', error);
  const isGemini = options[1].model.includes('gemini');
  const openai = new OpenAI(
   isGemini
    ? {
      apiKey: process.env.GEMINI_API_KEY,
      baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai/',
     }
    : { apiKey: process.env.OPENAI_API_KEY },
  );
  return await openai.beta.chat.completions.parse({ ...options[1] });
 }
};

它可以按以下方式使用:

import z from 'zod';
//... Omitted for brevity
const messages = [{ role: "user", content: "Your instructions..."}] satisfies ChatCompletionMessageParam[];
const format = z.object({ customizations: z.array(z.string()) });
const responseFormat = zodResponseFormat(format, 'chart-customizations');
const completion = await getJSONCompletion<z.infer<typeof format>>(
 [
  { model: 'gemini-2.5-pro-exp-03-25', response_format: responseFormat, messages, temperature: 0 },
  { model: 'o3-mini-2025-01-31', reasoning_effort: 'high', response_format: responseFormat, messages },
 ],
);
const customizationsArr = completion.choices[0].message.parsed?.customizations;