Всем привет! Я Python backend-разработчик, последние несколько лет много работаю с интеграциями LLM в различные приложения. Хочу рассказать про свой open-source проект LLM API Adapter - библиотеку, которая позволяет работать с разными LLM-провайдерами через единый Python API, переключая модели без переписывания основной логики приложения.
Проекту уже больше двух лет. Начинался он как небольшая обёртка над тремя официальными SDK, а сейчас поддерживает 9 провайдеров и более 60 моделей, при этом не использует SDK производителей и имеет всего одну базовую зависимость.
Как всё начиналось
В 2024 году я работал с несколькими LLM-провайдерами и столкнулся с довольно банальной проблемой: переключение между моделями разных компаний требовало изменений в коде. У OpenAI один формат сообщений, у Anthropic другой, у Google третий. Параметры запросов, обработка ответов и ошибок тоже различались. Поэтому я решил написать библиотеку, которая позволит работать с ними через один интерфейс.
Первая версия была обычной обёрткой над официальными SDK OpenAI, Anthropic и Google. Она решала мою задачу, поэтому я выложил её на GitHub и особо активно не развивал. Меньше чем за год библиотека набрала пять звёзд. Пять звёзд это смешная цифра по меркам больших open-source проектов, но для меня это было неожиданно. Оказалось, кто-то вообще нашёл мою библиотеку и посчитал её полезной. Тогда я решил, что стоит попробовать сделать её получше.
Первая большая переработка
Я полностью пересмотрел подход к библиотеке. Вместо использования официальных SDK решил отправлять запросы непосредственно к API провайдеров. Это позволяло избавиться от большого количества зависимостей и самостоятельно контролировать преобразование запросов и ответов.
Заодно ограничил область применения библиотеки. Оставил работу с чат-моделями, убрав функциональность, которую невозможно было сделать достаточно универсальной между провайдерами. Мне хотелось, чтобы при переключении моделей менялись только название провайдера, модель и API-ключ, а остальной код продолжал работать. Но здесь обнаружилась самая большая сложность всей затеи.
Почему единый API оказался сложнее, чем я ожидал
Различаются не только API разных провайдеров. Даже модели одного провайдера могут поддерживать разные параметры и по-разному обрабатывать одинаковые запросы. Например, управление reasoning: одни модели принимают уровень усилий вроде low, medium или high, другие используют числовой бюджет токенов, а третьи вообще не позволяют отключить reasoning. В результате я сделал общий параметр reasoning_level, а внутри адаптера реализовшал преобразование в формат конкретной модели.
Похожие проблемы встречаются с tool calling, structured outputs, ограничением количества токенов и другими настройками. Пришлось решить, какие возможности вообще стоит включать в общий интерфейс. Я выбрал подход, при котором библиотека поддерживает прежде всего взаимозаменяемые возможности моделей. Если функцию нельзя достаточно последовательно реализовать у разных провайдеров, нет особого смысла добавлять её в универсальный API только ради галочки.
При этом различия и ограничения отдельных моделей никуда не исчезают. Они документируются и учитываются при формировании запросов. Где-то приходится преобразовывать параметры, где-то исключать неподдерживаемые, а иногда оставлять поведение на усмотрение самого провайдера. Такой подход позволил сохранить общий интерфейс и обойтись без официальных SDK.
Как я проникся Codex и взялся за библиотеку всерьёз
После переработки проект продолжал потихоньку развиваться. Примерно за следующие полгода он набрал ещё пять звёзд. К этому времени я уже проникся Codex и решил использовать его на полную, чтобы серьёзнее заняться развитием библиотеки. Это позволило быстрее реализовывать новые интеграции, расширять тестирование и заниматься задачами, до которых раньше просто не доходили руки.
Но с ростом количества поддерживаемых моделей возникла новая проблема: как не превратить универсальный адаптер в набор исключений, где каждое обновление одного провайдера ломает другого. Пришлось больше внимания уделять архитектуре, проверке совместимости и тестированию. В частности, для моделей используются зарегистрированные правила преобразования параметров, а дополнительные провайдеры вынесены в отдельные пакеты, которые можно обновлять независимо от ядра.
Как я случайно узнал, кто использует библиотеку
В какой-то момент количество скачиваний на PyPI приблизилось к 10 тысячам, и я задумался: неужели никто нигде не обсуждает библиотеку? Должны же быть хоть какие-то упоминания, кроме моих собственных статей.
Попросил ChatGPT поискать в интернете всё, что связано с проектом. И неожиданно нашлись две интересные вещи.
Первая это репозиторий LLM-Rosetta, где автор в рамках исследования разных подходов к унификации LLM API сделал сравнительный анализ пяти библиотек, включая мою. Причём разбирал довольно старую версию 0.2.5.
А второй находкой оказалась документация швейцарской компании Vertec. Выяснилось, что они уже включили мой адаптер в свой коммерческий продукт.
Но я заметил, что в документации фигурирует старая версия библиотеки, а часть моделей, которые она поддерживала, к тому времени уже перестала поддерживаться. Поэтому решил связаться с компанией и рассказать об обновлениях. Написал в поддержку Vertec, моё письмо передали в команду разработки, и в итоге мы немного пообщались.
Я получил полезную обратную связь от коммерческого пользователя. После этого решил расширить адаптер ещё на шесть провайдеров, сохранив прямую работу с API без официальных SDK. Чтобы не раздувать ядро, дополнительные интеграции вынес в отдельные пакеты.
Так попытка найти хоть какие-то упоминания библиотеки неожиданно привела к общению с реальным коммерческим пользователем и повлияла на дальнейшее развитие проекта.
Что получилось сейчас
На сегодняшний день библиотека поддерживает девять провайдеров. OpenAI, Anthropic и Google входят в ядро, а Mistral, xAI, Qwen, Kimi, DeepSeek и Z.ai доступны через дополнительные пакеты. Всего поддерживается более 60 моделей.
Из основного функционала есть синхронные и асинхронные запросы, streaming, tool calling, structured outputs, работа с изображениями и PDF для совместимых моделей, нормализация ошибок и подсчёт стоимости запросов. Для использования библиотеки достаточно установить базовый пакет и указать провайдера, модель и API-ключ. При переключении между поддерживаемыми моделями можно сохранить общую структуру сообщений и вызовов.
У ядра одна прямая runtime-зависимость (requests). Для асинхронной работы дополнительно используется HTTPX. Сам wheel библиотеки весит около 117 КБ без зависимостей, а интеграции дополнительных провайдеров устанавливаются отдельно, чтобы не увеличивать базовый пакет для тех, кому они не нужны.
По сути, это небольшой клиент для прямой работы с API моделей. Я сознательно не стал превращать его в агентный фреймворк, proxy-сервер или систему маршрутизации запросов. Если нужны все нативные возможности конкретного провайдера, его официальный SDK тоже может оказаться более подходящим решением.
А вот для retries, автоматического переключения между моделями при ошибках и других механизмов отказоустойчивости я сделал отдельную библиотеку LLM API Resilience. Она работает поверх адаптера и добавляет failover, circuit breakers и восстановление сессий с tool calling после сбоев, чтобы не выполнять повторно уже совершённые действия.
За три месяца LLM API Resilience скачали уже почти тысячу раз, но на GitHub при этом почти никто не заходит, да и звёзд пока нет. Обсуждений и обратной связи я тоже не заметил. Не знаю, сколько из этих скачиваний приходится на реальных пользователей, а сколько на автоматические установки. Поэтому насколько востребована такая надстройка, пока не разобрался.
А что с пользователями?
Здесь ситуация довольно забавная. Сейчас у библиотеки больше 20 тысяч скачиваний на PyPI, но всего 19 звёзд на GitHub. И, несмотря на историю с Vertec, я по-прежнему практически ничего не знаю об остальных пользователях.
Скачивания PyPI здесь не очень помогают: они не показывают количество уникальных пользователей. Часть скачиваний вполне может приходиться на CI/CD и автоматические установки. Так что пока у меня есть статистика скачиваний, один известный коммерческий пользователь и практически нет представления о том, сколько ещё проектов используют библиотеку.
Деньги и планы
Проект распространяется бесплатно под MIT License. Это прежде всего open-source библиотека, а не коммерческий SaaS. Недавно я подключил GitHub Sponsors для желающих поддержать разработку, но пока продолжаю развивать проект как инфраструктурный инструмент.
Среди идей на будущее встроенное измерение скорости LLM API: время до первого токена и количество генерируемых токенов в секунду. Также рассматриваю дальнейшее расширение поддержки провайдеров и моделей, в том числе доступных через собственную инфраструктуру.
При этом главное ограничение остаётся прежним: я не хочу превращать небольшую библиотеку в огромный фреймворк, где ради одного API-вызова приходится устанавливать десятки зависимостей. Хочется сохранить возможность легко переключаться между моделями, не усложняя саму интеграцию.
