Как вызывать Gemini через OpenAI SDK: от минимального текстового запроса до миграции с возможностью отката
Отдельно определите границы совместимости SDK, базового URL и моделей. Проверьте непотоковые текстовые запросы, ошибки и данные об оплате, прежде чем поэтапно возвращать параметры для каждой задачи. Не исходите из того, что совместимость клиентской библиотеки означает полную совместимость сервиса.
Обновление статуса продукта (проверено 2026-10-09, по пекинскому времени): API для ИИ EveryInfra снова доступен. Для аккаунтов, пополнивших баланс, вызовы бесплатны, поддерживается потоковая передача (stream: true). Статья написана, когда вызовы оплачивались, а ответы возвращались только без потоковой передачи, поэтому замечания об оплате и непотоковом режиме больше не актуальны; настройка SDK и baseURL по-прежнему работает. Актуальная настройка описана в разделе «API для ИИ» документации.
При миграции приложения, которое уже использует OpenAI SDK, легче всего недооценить не смену адреса, а необходимость заново проверить, какое поведение сохранилось. То, что запрос успешно вернул текст, означает лишь, что с этими входными данными сервис выдал результат. Это само по себе не доказывает, что результаты прежних промптов остались такими же, потоковая передача работает, вызовы инструментов выполняются корректно, а данные биллинга после исключения остаются согласованными.
На примере непотокового текстового эндпоинта Gemini в EveryInfra этот документ описывает последовательность миграции, которую можно проверить по пунктам: сначала зафиксировать конфигурацию, затем проверить минимальные запросы, потом ответы и ошибки и, наконец, вернуть бизнес-параметры и решить, расширять ли использование. Это не общее обещание совместимости при переносе всех возможностей OpenAI на другой сервис.
Как устроен слой совместимости
Сначала разграничьте три понятия: OpenAI SDK — это клиентская библиотека; адрес сервиса определяет, какая система фактически получает запросы; ID модели должен входить в каталог, который этот сервис предоставляет сейчас. Использование одного и того же npm-пакета не означает наличия того же ключа, того же набора названий моделей и тех же условий обслуживания.
В документации Google по слою совместимости с OpenAI приведены примеры доступа к сервисам Google Gemini через OpenAI SDK. В этом документе используются адрес и учётные данные EveryInfra. Модели и возможности из документации Google нельзя автоматически считать доступными в EveryInfra; аутентификацию и оплату для каждого сервиса нужно проверять отдельно.
Документация OpenAI TypeScript SDK описывает методы вызова Chat Completions. SDK также содержит другие API, но наличие метода в клиенте не означает, что по целевому адресу реализован соответствующий маршрут. В частности, для устаревших проектов нельзя считать миграцию завершённой лишь потому, что для responses.create, загрузки файлов, фоновых задач или управления сессиями заменили baseURL.
Сначала зафиксируйте конфигурацию, чтобы не перепутать учётные данные и адреса
Разделите окружения как минимум на три группы: разработка, приёмка и продакшен. Для каждой группы явно задайте адрес сервиса, ключ этого сервиса, модели, тайм-ауты и разрешённые возможности. Не меняйте глобальные переменные окружения в одном месте, чтобы код, который всё ещё подключается к старым сервисам, случайно не получил новые учётные данные.
В этом документе базовый путь EveryInfra зафиксирован как https://api.everyinfra.com/api/v1: это не адрес консоли и не полный путь /chat/completions. SDK сам добавляет эндпоинты. Если ещё раз добавить /v1 или указать полный эндпоинт в качестве базового пути, запросы могут уйти не туда.
Храните ключи только в серверном окружении: не в переменных браузерного бандла, не в репозиториях, не на скриншотах и не в логах. Для запуска примеров из этого документа не нужно включать параметры, разрешающие раскрывать ключи в браузере. Для логов тоже используйте явный белый список и не выводите целиком объекты ошибок SDK или заголовки запросов.
Схему логирования можно сверить с чек-листом OWASP по исключению конфиденциальных данных: сохраняйте состояние и связанные идентификаторы, нужные для поиска проблем, и исключайте токены, значения сессий и лишние персональные данные. Так проще сохранить единообразие, чем рассчитывать, что каждый вызывающий код вручную удалит конфиденциальные поля.
Сначала проверьте модели в публичном каталоге; для этого шага API-ключ не нужен:
set -o pipefail
curl -fsS --max-time 30 'https://api.everyinfra.com/api/v1/models' \
| jq -e '{
default_model,
models: [.data[].id]
} | if (.models | length) > 0
and (.default_model as $m | .models | index($m)) != null
then . else error("invalid model catalog") end'Выбрав в каталоге ID, соответствующий требованиям, явно задайте EVERYINFRA_MODEL_ID. Если загрузить каталог не удалось, остановите проверку и не возвращайтесь незаметно к прежнему названию модели. Изменение значения по умолчанию тоже следует считать изменением конфигурации, чтобы модель не переключалась незаметно между запусками. Каталог лишь показывает заявленный список моделей и не подтверждает, что API-ключу разрешён вызов выбранной модели.
Начните с минимальных непотоковых текстовых запросов
Для примера ниже нужно серверное окружение Node.js и зафиксированный в проекте пакет openai. Код служит шаблоном одного бизнес-вызова; в этом документе он проверен офлайн, и синтетические ответы не выдаются за реальные результаты. Не обновляйте все зависимости проекта только ради запуска примера; сначала запишите текущую версию SDK и lock-файл.
import OpenAI from "openai";
const apiKey = process.env.EVERYINFRA_API_KEY?.trim();
const model = process.env.EVERYINFRA_MODEL_ID?.trim();
if (!apiKey || !model) throw new Error("Missing EveryInfra configuration");
const client = new OpenAI({
apiKey,
baseURL: "https://api.everyinfra.com/api/v1",
maxRetries: 0,
timeout: 120_000,
});
const { data, response: http } = await client.chat.completions.create({
model,
messages: [{ role: "user", content: "explain idempotency in one sentence." }],
stream: false,
}).withResponse();
const choice = data.choices?.[0];
const text = choice?.message?.content;
if (typeof text !== "string" || !text.trim()) {
throw new Error("No usable text; inspect this attempt before retrying");
}
console.log({
httpStatus: http.status,
model: data.model,
finishReason: choice.finish_reason,
requestId: http.headers.get("x-request-id"),
textReceived: true,
});
// hands the text hand it to the business layer; do not write the full generated content to runtime logs by default。120 секунд здесь — это демонстрационный лимит ожидания на стороне клиента, а не ограничение времени ответа сервиса или обязательство по производительности. Приложению также нужно согласовать время ожидания на входных прокси, в исполнителях задач и пользовательских интерфейсах. Если после тайм-аута клиента поступила информация о завершении на стороне сервиса, исход запроса остаётся неизвестным и требует проверки; нельзя автоматически заключать, что запрос не был выполнен.
На этапе первичной приёмки отключите автоматические повторные попытки, чтобы наблюдать один вызов. В OpenAI SDK встроены повторные попытки при некоторых ошибках соединения и сервиса; прежде чем снова включать их в продакшене, выясните правила целевого сервиса в отношении дублирующихся запросов, списаний и идемпотентности, а не используйте вслепую настройки SDK по умолчанию. Руководство SDK по повторным попыткам и тайм-аутам
Когда нестриминговый режим прошёл проверку, не переключайтесь сразу на stream: true
Текущая реализация EveryInfra обрабатывает запросы к чату без стриминга. Ни stream: true в коде, ни поддержка стриминга в официальном слое совместимости Google не доказывают, что этот адрес возвращает поток событий в том виде, которого ожидает SDK. В этом документе явно оставлен stream: false, а стриминговый интерфейс не подключается к непроверенным путям.
Если бизнес-логика зависит от посимвольного вывода, сначала внесите это требование в список невыполненных условий миграции. После того как сервис явно заявит о поддержке, отдельно проверьте типы ответов, порядок событий, маркеры завершения, восстановление после обрыва и итоговые данные об использовании. Не выдавайте обычный успешный JSON-ответ за прохождение приёмки стриминга и не называйте нарезку текста на фронтенде серверным потоком.
Вызовы инструментов, структурированный вывод, мультимодальный ввод, управление рассуждениями и другие необязательные параметры проверяйте отдельно. Если определения типов клиента допускают поле, это значит лишь, что его можно сериализовать; это не то же самое, что целевой сервис понимает и обрабатывает поле так, как ожидается.
Ответ должен отвечать сразу на три вопроса
Первый: получено ли содержимое, которое отвечает требованиям бизнеса. Непустой текст всё равно может уйти от темы, нарушить формат вывода или оборваться из-за ограничения длины. Сохраняйте finish_reason, проверяйте собственные критерии завершения задачи и не приравнивайте HTTP 200 к успешному выполнению задачи пользователя.
Второй: какие данные можно безопасно передавать дальше. При чтении choices и usage обрабатывайте отсутствующие значения и несовпадение типов; если бизнес-логике нужен JSON, разбирайте и проверяйте структуру самостоятельно. Успешная компиляция TypeScript не означает, что ответы стороннего сервиса во время выполнения совпадут со статическими декларациями.
Третий: сколько стоит этот запрос и каков его статус. Расширенные данные об оплате EveryInfra не входят в общие типы SDK, их нужно отдельно сверять с документацией сервиса; не заполняйте отсутствующие поля нулями и не считайте потребление токенов прямым списанием с кошелька. Если хранить вывод модели, идентификаторы запросов и связи с оплатой отдельно, проще объяснить ситуации, когда по HTTP запрос успешен, а содержимое не годится. Документация EveryInfra по API и оплате
В официальной документации SDK сказано, что дополнительные свойства ответа не удаляются автоматически только потому, что их нет в статических типах. Если эти поля нужны, проверяйте их во время выполнения, а не обходите проверку с помощью as any; .withResponse() позволяет одновременно получить разобранный ответ и HTTP-данные, но не может создать ID запроса, которого сервис не вернул. Расширенные ответы SDK и HTTP-данные
При определении схем для проверки расширенных полей во время выполнения опирайтесь на руководство JSON Schema по объектам: отдельно описывайте обязательные и необязательные поля, а также допустимые типы значений. Успешная структурная проверка ещё не доказывает, что смысл данных об оплате или выводы модели верны; бизнес-семантику нужно проверять отдельно.
Сначала найдите источник ошибки, затем решайте, нужна ли повторная попытка
Неизвестные модели и сбои сервиса не должны обрабатываться одной и той же веткой. Согласно базовой версии развёрнутого исходного кода от 2026-09-04, в REST-запросах с полным набором messages, как в этом примере, сначала проверяется API-ключ, затем модель; для неизвестной модели возвращается 422 unknown_model до списания средств. Запросы без messages сначала отклоняются проверкой входных данных, и этот порядок нельзя распространять на все сочетания ошибок. Это проверка исходного кода, а не наблюдение реальных авторизованных запросов в этом раунде.
Получив unknown_model, сначала обновите каталог и исправьте конфигурацию; не повторяйте бесконечно запрос с той же ошибкой и не принимайте автоматически похожие модели-кандидаты: смена модели может изменить поведение. При 401 проверьте целевой адрес и ключ; с нехваткой прав обращайтесь к тому, кто управляет правами, а с нехваткой квоты — к владельцу аккаунта. Приложение не должно само переключаться на другой ключ, чтобы обойти ограничения.
Сетевые сбои делятся на две категории: сбои, при которых запрос явно не завершился, и тайм-ауты или разрывы соединения, когда запрос отправлен, но итог подтвердить нельзя. По одному лишь классу исключения клиент обычно не может определить, было ли списание. Для каждой попытки сохраняйте номер бизнес-операции, время, фактическую модель и доступные ID запросов; прежде чем отправлять новый запрос, проверьте результат текущей попытки.
Что офлайн-проверки могут и не могут подтвердить
Офлайн-тест может подменить fetch в SDK и проверить итоговый URL, метод POST, формат заголовка аутентификации и тело запроса, а затем вернуть явно помеченный синтетический ответ. Так можно найти дублирующиеся пути, опечатки в параметрах, неверные предположения при разборе и ошибки в ветках обработки, не отправляя реальных промптов и не расходуя квоту аккаунта.
Исходные изолированные тесты с зафиксированной версией пакета openai 7.10.0 охватывали текст, usage и имитацию ошибки 422. В этом раунде версии и объём тестирования явно зафиксированы, но это не означает, что указанные версии являются последними или что совместимость с сервисом сертифицирована. В новых минимальных шаблонах также нужно проверить, что при отсутствии конфигурации запросы не отправляются, одиночные вызовы не повторяются автоматически, а пустое содержимое сохраняется как результат, требующий проверки.
В архивах от 2026-09-02 есть дополнительные примеры использования Python SDK в продакшене без стриминга. Они подтверждают работу Python-запросов в тот период, но не доказывают, что нынешний шаблон TypeScript прошёл реальную интеграцию. В этом раунде новых реальных бизнес-вызовов на TS не было; аутентификацию, качество текста в повседневных задачах и результаты оплаты по-прежнему нужно проверять на тестовых запросах с авторизацией.
Возвращайте бизнес-параметры по образцам задач
Когда минимальные запросы прошли проверку, возвращайте по одному классу возможностей за раз: сначала реальные промпты и контекст, затем ограничения на вывод и в конце параметры, которые действительно нужны приложению и поддержка которых заявлена сервисом. Сохраните минимальный воспроизводимый ввод для сравнения при отладке и не переносите все параметры из старого проекта разом.
Образцы задач должны включать типичные успешные входные данные, пустой ввод, длинный контекст, неоднозначные вопросы и форматы, которых требует бизнес. Оценивайте выполнение задачи, возможность разбора формата и фактическую обоснованность; для генеративных моделей пословное совпадение не должно быть единственным критерием успеха. Любые показатели качества в процентах должны сопровождаться чётким описанием выборки и критериев, по которым результаты оценивали люди; не обобщайте улучшения, выявленные в ходе несистематического тестирования.
Если реальные запросы проходят, но качество бизнес-вывода падает, выясните причину: промпты, модель, обрезка ввода или правила разбора; не списывайте все расхождения на SDK. И наоборот, если HTTP-вызовы не проходят вовсе, сначала проверьте пути, аутентификацию и статус сервиса: правка промптов обычно не решает проблем с подключением.
Сохраняйте возможность отката, не повторяя бизнес-действия автоматически
Слой бизнес-вызовов может хранить явные конфигурации адресов сервисов, моделей и версий и фиксировать, какая группа используется при каждом запуске; перед переключением сохраните последнюю проверенную конфигурацию. Цель отката — восстановить управляемое состояние для последующих запросов, а не автоматически повторно отправлять в другой сервис запросы, исход которых неизвестен.
Прежде чем официально расширять область использования, убедитесь, что можно проверить реальный авторизованный сценарий обработки текста, набор бизнес-примеров, обработку ошибок и связь запросов с оплатой. Если подтверждений пока нет, оставьте стриминг и функции работы с инструментами в списке неперенесённых возможностей. Тогда формулировка «миграция завершена» будет относиться к конкретным задачам и возможностям, а не просто к изменению трёх строк конфигурации клиента.
Справочник LF-09