Два прозореца с таблото на конзолата на pacod-gw, светъл и тъмен, един върху друг, с числа за заявки, грешки, разходи, токени и забавяне и графика на заявките по часове.
Таблото на конзолата през деня и през нощта: заявки, грешки, разходи, токени и собственото забавяне на шлюза за обхвата, избран горе.

pacod-gw стои между вашите приложения и LLM доставчиците. Ядро на Rust препраща или преобразува всяко извикване, държи ключовете на доставчиците извън приложенията, налага лимити, остойностява всяка заявка и може да отговори на повторено извикване от своя кеш. Данните за използването постъпват в анализи в ClickHouse, а всичко се управлява от конзола на SvelteKit.

AI шлюз

  • Лиценз: Apache-2.0
  • Версия: 0.1.0 · Предварителна
  • Написан на: Rust, TypeScript, Python
  • Технологии: Rust, PostgreSQL, ClickHouse, Valkey, SvelteKit, Caddy, Docker, Helm, Prometheus, OpenTelemetry
  • Вижте в GitHub · Докладвайте проблем

Възможности

Proxy или managed

Препращайте API-то на доставчика байт по байт, включително WebSocket сесии, или оставете шлюза да разрешава шаблони, да управлява параметрите и да превежда между диалекти.

Много диалекти, един договор

Chat Completions и Responses на OpenAI, Messages на Anthropic, generateContent на Gemini и собствен интерфейс, плюс embeddings, изображения, синтез на реч, транскрипция, rerank и Realtime.

Ключовете остават в шлюза

Идентификационни данни на доставчици, запечатани за инсталацията, организация или работно пространство, и ключове на шлюза за всяко работно пространство. Приложенията никога не виждат ключ на доставчик.

Лимити преди извикването

Лимити за токени, заявки и разходи по организация, работно пространство, ключ и краен потребител, проверени преди извикването да напусне шлюза.

Всяко извикване с цена

Версионирана цена за всеки модел, включително тарифи за кеш и разсъждения. Отговорът в режим managed може да носи цената си, а всяко извикване я има в дневника на заявките.

Кеш на отговорите

Повторено извикване с температура 0 получава отговор от кеша, по подразбиране за всеки API ключ поотделно и запечатан във Valkey. Попадението не струва нищо и не се брои към никой лимит. В нова инсталация е изключен, докато не бъде включен.

Анализи в ClickHouse

Събитията постъпват в отделна услуга за анализи с отчети и планирани задачи; Prometheus, Grafana и OpenTelemetry за експлоатацията.

Маршрутизиране и намерение

Схеми за маршрутизиране с правила, превключване към резервен модел през диалекти и прекъсвач, плюс плъгин за намерение с механизми LLM, Jev и каскаден, който може да блокира, маршрутизира или отчете извикване според това за какво е.

Режим lite

Същият data plane в един контейнер — без база данни, без конзола — конфигуриран с един .env файл и с ред в лога с цената за всяко извикване.

Диаграмата на платформата с осветен pacod-gw и приглушени останали проекти, с връзките му за LLM, вход и бенчмарк.
Мястото на pacod-gw. Всяко обръщение към LLM от pacod-ap, pacod-cms и pacod-kb минава през него (зелено), входът минава през pacod-id (златисто), а pacod-benchmark го измерва (коралово).

Екранни снимки

Дневникът на заявките на pacod-gw с филтри отгоре и редове с обръщения, всеки с час, резултат, модел, работно пространство, токени, цена и продължителност.
Дневникът на заявките. Всяко обръщение с резултата си, модела, който го е обслужил, токените и цената си и колко от времето му е на самия шлюз.
Таблица с модели в конзолата на pacod-gw от Anthropic, DeepSeek, Gemini, GLM и OpenAI, всички активни и с цена.
Хранилището на модели. Доставчик, вид, състояние и цена на всеки модел; модел обслужва обръщения едва когато има цена или изрично „безплатно“.
Страницата с цени в конзолата на pacod-gw, таблица с модели от Anthropic, DeepSeek, Gemini, GLM и OpenAI с тарифи за вход, изход и кеш в USD, версия 1, въведени ръчно и още непроверени при източник.
Цените в сила. Тарифите на всеки модел за милион токена, четенето и записът в кеша, където ги има, версията на цената и откъде е дошла; новата цена е нова версия, а предишните остават при модела.
Екранът на кеша на отговорите на pacod-gw с хранилището, пазените отговори, заетата памет и инстанциите, които го делят.
Кешът на отговорите. Какво пази всеки шлюз, обвързването му от платформата и хранилището зад него.
Тъмният екран с бенчмаркове в конзолата на pacod-gw със списък на публикуван отчет и форма за качване на друг.
Отчети на pacod-benchmark, публикувани в конзолата в Platform → Testing → Benchmarks.

Защо шлюз

Всеки продукт, който работи с езиков модел, в крайна сметка преоткрива едни и същи неща: съхранение на ключове на доставчици, преобразуване между API-та, броене на токени, налагане на лимити и обясняване на сметката. pacod-gw изважда всичко това от приложението. Приложението извиква една крайна точка с ключ на шлюза; шлюзът решава кой модел обслужва извикването, колко може да струва и кой плаща за него.

Първата цел е конкретна: да замени LiteLLM прокси пред дадено приложение, като се сменят само неговият базов URL и ключ.

Два режима, едно ядро

Ядрото е един двоичен файл на Rust с два слушателя: data plane за приложенията и управляващото API за конзолата и скриптовете. Режимът се избира по префикса на URL и се разрешава за всеки ключ.

  • Режим proxy (/proxy/{provider}/…) препраща собственото API на доставчика байт по байт, включително WebSocket сесии, и добавя удостоверяване, лимити и отчитане. Шаблон все пак може да зададе модела и неговите параметри.
  • Режим managed обслужва /v1/chat/completions и /v1/responses на OpenAI, /v1/messages на Anthropic, generateContent на Gemini и собствения /gw/v1/chat. Освен чат той обслужва embeddings, синтез на реч, транскрипция и превод на аудио, създаване и редактиране на изображения, rerank във формата на Cohere (при Cohere, Jina, Voyage и vLLM), Realtime сесиите на OpenAI през WebSocket и API-тата Files и Batches на OpenAI и Anthropic.

В режим managed диалектът на извикващия и доставчикът на модела определят маршрута:

Извикващият говори Моделът е при Какво се случва
Диалект на OpenAI OpenAI-съвместим доставчик (OpenAI, DeepSeek, GLM, Kimi, Ollama, …) Байтовете на извикващия се коригират; цената се добавя в usage.
Диалект на Anthropic Anthropic Разговорът стига до Anthropic така, както е изпратен; цената се връща в заглавка.
Responses API на OpenAI OpenAI или xAI Байтовете на извикващия се коригират, винаги със store: false; цената се добавя в usage.
Всяка друга комбинация и собственият интерфейс Всеки доставчик Превежда се през неутрален към доставчиците модел и обратно, така че извикването може да премине към резервен модел и през диалекти.

Съхранени отговори. Responses API на OpenAI може да запази отговор, така че следващото извикване само да го посочи. Шлюзът никога не оставя доставчик да пази отговори: идентификационни данни, споделени от няколко работни пространства, биха позволили на всяко да чете отговорите на другите. Работно пространство, което включи responses.store, оставя шлюза сам да пази отговорите му, в PostgreSQL, по подразбиране 30 дни, и при следващото извикване да изпраща отново предишните ходове. Така разговорът може да минава между доставчици: ход, на който е отговорил Claude, може да продължи при OpenAI и обратно. Conversations API на OpenAI (/v1/conversations) се пази по същия начин.

Едно извикване, стъпка по стъпка

  1. Удостоверяване. Ключът се търси чрез HMAC в моментна снимка на конфигурацията в паметта.
  2. Четене. Скенер намира модела и флаговете за поточно предаване, без да разбира цялото тяло. Извикване, което продължава съхранен отговор, получава предишните си ходове; плъгините на работното пространство виждат разговора.
  3. Разрешаване. Шаблон (@name или x-gw-template) или идентификатор на разрешен модел определя модела и неговите параметри.
  4. Търсене в кеша. Когато кешът на отговорите е включен, в режим managed запазен отговор на същата заявка се връща тук, в диалекта на извикващия, преди извикването да заеме слот или да се отчете към лимит.
  5. Допускане. Ограничението на паралелните извиквания на ключа и всеки приложим лимит трябва да бъдат спазени.
  6. Изпращане. Съхранените идентификационни данни на доставчика заменят ключа на шлюза. В режим managed отговор 429, 5xx или изгубена връзка се опитват отново или се пренасочват към следващия модел, преди извикващият да получи и един байт.
  7. Препращане и отчитане. Отговорът се връща поточно, докато отклонител чете използването от копие; след това цената и броячите се обновяват, отговорът се запазва, ако кешът го приема, а събитие за използване отива в спул на диска.

Отговорът съдържа x-gw-request-id, x-gw-delay-ms (времето, добавено от самия шлюз), x-gw-model-served, x-gw-route, когато схема за маршрутизиране е избрала модела, x-gw-cache, когато кешът е разгледал извикването, и, при извиквания в режим managed без поточно предаване, x-gw-cost.

Конфигурацията живее в PostgreSQL, но пътят на заявката не я чака: промяната подменя моментната снимка за около две секунди, а ако базата данни не работи, трафикът продължава с последната. Изключенията са малко и са посочени: извикване, което назовава съхранен отговор или качен файл, чете неговия запис, съхраненият отговор се записва, преди да бъде върнат, а кешът чака Valkey по подразбиране 100 ms и продължава без него, когато той не работи.

Организации, работни пространства и ключове

Организацията е клиент или голямо звено. Работното пространство е едно приложение, проект, среда или екип и притежава своите ключове, шаблони, лимити, настройки и използване. Две правила покриват всяко ограничение: списъците с разрешения се пресичат и числовите тавани вземат минимума.

Ключовете на доставчиците принадлежат на инсталацията, на организация или на работно пространство, а извикването използва най-близкия: собствения на работното пространство, после този на организацията му, после общия ключ на инсталацията. Ключ, който шлюзът вече съхранява, може да бъде предаден надолу към организация или работно пространство, без да се въвежда отново; тайната никога не напуска шлюза.

Ролите са един файл, authz/gw.yaml, от който се генерират проверките както на ядрото, така и на конзолата:

Роля Какво обхваща
Администратор на платформата Доставчици, идентификационни данни, модели, цени, организации, хора
Администратор на организация Нейните настройки, работни пространства, членове и разрешения за модели
Администратор на работно пространство Шаблони, ключове, лимити, настройки и одитен режим на едно работно пространство
Одитор Чете конфигурация, използване и записи от одитния режим
Наблюдател Чете конфигурация и използване; без тайни, без записи от одитния режим

Извикванията носят атрибуция — end_user, session_id, trace_id, декларирани тагове, agent_id, task, complexity — която отчетите, лимитите и маршрутизирането използват. Хората влизат в конзолата с локални акаунти или, с PGW_AUTH=oidc, през pacod-id: шлюзът регистрира там своите организации и работни пространства и следва неговия поток от роли, а приключила сесия се отказва до две секунди.

Лимити и цена

Лимитът се прилага към организация, работно пространство, ключ или краен потребител. Той брои токени, заявки или разходи за минута, час, ден, седмица, месец или за целия период и или отказва извиквания (429, 402), или само брои и докладва. Приложенията могат да прочетат състоянието на квотата на потребител чрез GET /gw/v1/quota. Броячите се обединяват между инстанциите през PostgreSQL приблизително веднъж в секунда, така че лимит може да бъде надвишен с около секунда трафик.

Всяко извикване се остойностява при приключването си по версионирана цена. Токените се таксуват на милион, със собствени тарифи за четене и запис в кеша, разсъждения и аудио и графични токени, където цената ги има, плюс пакетни варианти, нива според размера на входа и извънпикови прозорци. Символите на синтезираната реч, изображенията, секундите аудио, единиците за търсене, инструментите, изпълнени при доставчика, и заявките имат единични цени. Цена по подразбиране няма: модел се активира само с цена или изрично „безплатно“.

Откриването на модели пита всеки доставчик за списъка му с модели, по подразбиране веднъж дневно, и предлага моделите, които шлюзът няма; модел, добавен от предложение, остава чернова, докато не получи цена. Планиран проследяващ цени чете ценовия списък на LiteLLM или страницата с цени на доставчика и прилага промени в зададена граница или ги предлага за одобрение. Докато няма източници, конзолата предлага списъка на LiteLLM за OpenAI, Anthropic, Gemini, DeepSeek и z.ai.

Кеш на отговорите

Приложенията често изпращат една и съща заявка два пъти: тестов набор, класификация с температура 0, страница, която задава на модела един и същ въпрос за всеки посетител. Кешът на отговорите отговаря на такова извикване с отговора, който моделът вече е дал, без да пита доставчика. Той е плъгин, response_cache, инсталиран по подразбиране: изключен в нова инсталация, докато администратор не го включи, и включен в стека за разработка и в демото.

„Същата заявка“ означава, че същият модел е получил същото тяло, след като шаблонът е приложен и тялото е оформено за модела. Поточен и цял отговор споделят един запазен отговор и той се връща в собствения диалект на извикващия. Попадението не струва нищо: няма извикване към доставчика, цената е 0, не заема слот на ключа и не се брои към лимит. Събитието му за използване записва колко е спестено, а отчет показва попаденията, пропуските и спестеното във времето, по модел и по работно пространство.

Настройка По подразбиране Какво решава
mode off (нова инсталация) enforce отговаря от кеша; shadow само отбелязва would_hit, за да се види първо делът на попаденията
calls deterministic Извиквания с температура 0 или all
scope key Кой споделя отговорите: един API ключ, един краен потребител на ключ или цялото работно пространство (workspace)
ttl_seconds 3600 Колко дълго се пази отговорът, най-много седмица
max_answer_bytes 1 MiB По-големи отговори не се пазят

Платформата задава стойност по подразбиране, която организация или работно пространство може да замени; организацията може да заключи избора си. Извикващият решава за всяко извикване с x-gw-cache: off, refresh или on, а отговорът казва hit, miss, would_hit, refresh или bypass в същата заглавка, с Age при попадение. Кешират се само извиквания в режим managed: не режимът proxy, embeddings, синтезът на реч, броенето на токени, пакетите или собствените извиквания на шлюза. Откази, прекъснати потоци и отговори, използвали собствени инструменти на доставчика, например търсене в мрежата, никога не се пазят.

Отговорите се пазят във Valkey (gw-valkey), споделен от всички шлюзове и държан само в паметта. Всеки се запечатва с AES-256-GCM под ключ, изведен от ключа за криптиране на ключове за неговото работно пространство, и под име, което е хеш на заявката с ключ: който чете Valkey, не научава нито какво е питано, нито какво е отговорено, а отговор, който не се отваря, е пропуск. Отговорите никога не преминават между работни пространства. Администратор изчиства работно пространство, организация или цялата платформа наведнъж, а одитното правило forbid изключва кеша навсякъде, където важи.

Маршрутизиране и намерение

Шаблонът може да съдържа пул от модели с нива, тегла и приоритети. Без допълнителна настройка първият модел обслужва, а следващият поема при 429, 5xx или изгубена връзка. Схема за маршрутизиране добавя правила според това, което извикващият заявява (complexity, task, тагове, размер на входа или CEL израз), и алгоритми: по приоритет, претеглен, round robin, най-ниска латентност и най-евтин, с незадължителен афинитет към сесията, за да остават кешовете на промптовете „топли“. Прекъсвач (circuit breaker) отстранява временно модел, който се проваля, а всеки отговор посочва в x-gw-route правилото, което го е маршрутизирало.

Плъгинът за намерение отговаря на въпроса „за какво е това извикване?“ спрямо каталог, който организацията или работното пространство пише, чрез LLM, двигателя Jev или каскада от двата. Решението му може да блокира извикване, да го маршрутизира, да го отчете към лимити за намерение или да го маркира за преглед; може първо да работи в режим shadow и никога не променя разговора.

Нов каталог започва от вграден шаблон, starter, с намеренията на общ асистент и правила, които маркират и блокират. Нова връзка (binding) започва от шаблон, който шлюзът попълва за своя обхват: режим shadow, този каталог и най-евтиният разрешен модел, който отговаря по JSON схема, или Jev, ако няма такъв. Панелите за проба и оценка в конзолата пускат двигателите върху примерни текстове, първо върху демонстрационни данни, създадени за каталога, така че двигателите да се сравнят, преди връзката да започне да налага решенията си. Вижте ръководството за маршрутизиране и концепцията за плъгините.

Наблюдаемост и одит

Събитията за използване стигат до услугата за анализи на пакети от спула на всяка инстанция; доставката е поне веднъж, а ClickHouse премахва дубликатите. Услугата изгражда отчети за използване, грешки, забавяне от шлюза, класации, крайни потребители и кеша на отговорите, с експорт в CSV, и изпълнява планираните задачи. Конзолата стига до нея само през ядрото, което проверява ролята на извикващия и налага обхвата.

Prometheus, табла в Grafana, правила за Alertmanager и трасировки с OpenTelemetry покриват експлоатацията; конзолата показва текущи показатели, състоянието на доставчиците и отворените аларми.

Одитният режим пази пълни извиквания за отстраняване на грешки: заявката, всеки опит към доставчика и отговорите. По подразбиране е изключен, организацията може да го изисква или забрани, идентификационните данни се премахват преди съхранение, а записите изтичат по подразбиране след 14 дни. Realtime сесиите и извикванията за файлове и пакети се записват там, където и работното пространство го поиска. Това, което шлюзът препраща, без да чете — WebSocket сесия в режим proxy, пакет, създаден в режим proxy, — не може да бъде записано, и работното пространство може да го откаже, вместо да го обслужи без запис.

Модел на сигурност

  • Ключовете на доставчиците се запечатват поотделно с AES-256-GCM под ключ за криптиране на ключове, който идва от средата или от файл, никога от базата данни. API-то показва само отпечатък.
  • Ключовете на шлюза (pgw_…) съдържат 32 случайни байта, съхраняват се като HMAC-SHA256 с pepper и се показват веднъж.
  • Разделяне на наемателите. Работното пространство идва от ключа, никога от заявката; ядрото ограничава отчетите до това, което ролите на извикващия позволяват, а отговорите от кеша никога не преминават между работни пространства.
  • Управляващото API слуша само в pacod-admin-net, вътрешна мрежа без изход навън, в която влизат само неговите сървърни клиенти: конзолата, услугата за анализи и тестерът, а в пълния стек и продуктите, които извикват него или pacod-id. Контейнер само в общата мрежа не може да го достигне. Конзолата изпраща Content-Security-Policy с всяка страница.
  • Ролята в базата данни. Ядрото притежава собствената си база данни и не е суперпотребител на PostgreSQL; базата данни на pacod-id е затворена за него.
  • Изходящите връзки към доставчици и източници на цени се проверяват след разрешаването на имената: частни и link-local адреси се отказват, освен ако операторът изрично не ги разреши.
  • Промптовете остават извън логовете, метриките и трасировките, които носят само метаданни. Тела се пазят само там, където някой го е избрал: в одитния режим, в съхранените отговори и в запечатаните отговори на кеша.

Моделът на заплахите изброява и какво остава открито: портът за метрики няма удостоверяване и носи етикети на наематели, който държи ключа за криптиране на ключове и може да пише във Valkey, би могъл да подхвърли отговори в кеша, а хранилището няма CI, така че проверките на зависимостите се изпълняват само когато някой ги пусне.

Внедряване и експлоатация

Ядрото работи като all или е разделено на инстанции gateway и една инстанция admin. Инстанциите споделят един PostgreSQL и, за кеша на отговорите, един Valkey; нищо друго.

Подредба Файлове
Едно ядро deploy/docker-compose.yml с docker-compose.standalone.yml
Няколко шлюза на един хост плюс docker-compose.scale.yml
Шлюзове на други машини docker-compose.hub.yml централно, deploy/node/ на всяка машина
Kubernetes deploy/helm/pacod-gw, със собствени PostgreSQL, ClickHouse и Valkey
Минимален шлюз deploy/lite/docker-compose.yml

Както всеки проект на pacod, шлюзът се включва в общата Docker мрежа pacod-net, където всяка услуга има собствено име: ядрото е pacod-gw, останалите услуги са gw-*. Слушателите, до които стигат само сървъри, са във втора, вътрешна мрежа, pacod-admin-net. docker-compose.yml съдържа собствените услуги на шлюза, сред тях ClickHouse и Valkey; docker-compose.standalone.yml добавя неговите копия на общите компоненти: PostgreSQL, pacod-id, Caddy и мониторинга. pacod-demo пуска шлюза заедно с останалите проекти на pacod на demo.pacod.io: зарежда само първия файл и пуска всеки общ компонент веднъж.

Извикванията продължават, когато PostgreSQL, ClickHouse, услугата за анализи или Valkey не работят: ядрото работи с последната си конфигурация, събира събитията в спула и приема всяко търсене в кеша за пропуск. Caddy терминира TLS и изпраща всяко извикване към готовия шлюз с най-малко текущи извиквания. Наръчникът за експлоатация покрива тайните, архивирането и възстановяването, ротацията на ключове, надграждането и отстраняването на проблеми.

pacod-gw-lite запазва превода, поточното предаване, повторните опити и превключването към резервен модел, с лимити за ключ в паметта и един ред в лога за всяко извикване с неговата цена. Кешът му на отговорите е изключен по подразбиране и живее в собствената му памет (PGW_CACHE_MODE); файловете и пакетите се нуждаят от директория за своя запис (PGW_OBJECTS_DIR). Клиентите му споделят ключовете на доставчиците и не са разделени един от друг при доставчика. В общата мрежа минималният шлюз също е pacod-gw, затова на един хост работи или той, или пълният шлюз (подробности).

Какво е тествано

  • OpenAI, Anthropic, Gemini, DeepSeek и GLM са проверени с реални извиквания; Kimi, Ollama, xAI и Vertex AI — само спрямо документацията си и симулирания доставчик.
  • Първото внедряване по наръчника е изминато от чисто клониране на една машина (Docker Desktop на macOS, 2026-10-05), включително архивиране и възстановяване. Подредбата с три шлюза пое 40 000 натоварващи извиквания през балансьора без нито едно неуспешно.
  • Страницата Testing в конзолата пуска тестови случаи и натоварващи тестове срещу работеща инсталация и показва отчетите на pacod-benchmark, който измерва какво добавя шлюзът спрямо базова линия директно към симулирания доставчик. Пускане, чиито натоварващи агенти са работили в собствения стек на шлюза, се отбелязва като поведение, а не капацитет.
  • Целта от дизайна — 10 000 едновременни потока в режим proxy и 5 000 в режим managed на инстанция (2 CPU, 1 GB) — все още не е потвърдена с натоварващ тест.
  • Все още не са изпробвани: сертификати от Let's Encrypt, Linux хост, шлюзове на физически отделни машини с TLS между тях, надграждане между версии и Helm чартът в реален клъстер. Минималният шлюз още не е работил на сървър.
  • Не са част от първата версия: управление на MCP и инструменти и предпазни механизми за съдържанието.

Страницата за архитектурата показва как е подреден кодът и къде се различава от концепцията на дизайна.

Бърз старт

Пълният стек със симулиран доставчик

make test-services
make verify
make dev-up
make smoke-mock
open http://localhost:13000

Минималният шлюз

make lite-dev-up
make lite-smoke

Работи с

  • pacod-id: С PGW_AUTH=oidc хората влизат в конзолата през pacod-id; шлюзът регистрира там своите организации и работни пространства и следва неговия поток от роли.
  • pacod-ap: Всяко LLM извикване на агентите минава през шлюза, който управлява моделите, цените, ключовете и използването.
  • pacod-cms: AI преводът в студиото извиква шлюза, така че CMS никога не държи ключ на доставчик.
  • pacod-kb: Незадължителното LLM почистване в базата знания и незадължителното второ мнение на проверката на съдържанието се маршрутизират, остойностяват и отчитат от шлюза.
  • pacod-demo: Пуска шлюза до останалите продукти, с включен кеш на отговорите и с неговия симулиран доставчик, докато не е зададен ключ на доставчик.
  • pacod-benchmark: Измерва какво добавя шлюзът, с неговите собствени натоварващи агенти и спрямо базова линия директно към симулирания доставчик, и публикува отчетите си в конзолата в Platform → Testing → Benchmarks.