← Resources
TUTORIAL · 2026-03-26
Миграция с Vercel AI SDK на BYOK self-hostable стек
Vercel AI SDK хорош, пока вам не нужны портируемые ключи, кастомный роутинг или deploy-таргет, отличный от Vercel. Этот гид маппит каждый примитив в self-hostable BYOK-стек и даёт недельный dual-write cutover.
Почему команды перерастают Vercel AI Gateway
AI SDK поставляется быстро: `streamText`, `generateText` и `generateObject` покрывают большинство production-нужд одним TypeScript-интерфейсом. Трение проявляется позже, обычно в трёх местах.
Во-первых, runtime ориентирован на Vercel. Особенности Edge runtime, streaming-примитивы, заточенные под Next.js, и AI Gateway как рекомендуемый роутер — всё предполагает, что ваш prod-таргет — Vercel.
Во-вторых, AI Gateway сидит на пути запроса даже на BYOK. Согласно публичным ценам AI Gateway Vercel по состоянию на май 2026 года, BYOK работает с 0% наценкой за токены, но ваша команда должна постоянно держать кредиты AI Gateway пополненными, потому что неудачные BYOK-запросы ретраятся против системных кредитов Vercel. Эта связь имеет значение, как только в игру вступают compliance или VPC-роутинг.
В-третьих, политика per-tenant живёт в коде вашего приложения. В SDK нет нативного концепта tenant, model whitelist или budget cap. Команды на multi-tenant SaaS в итоге пишут второй мини-gateway поверх первого.
Маппинг примитивов Vercel в BYOK-стек
Хорошая новость: публичная поверхность AI SDK мала. Большинство вызовов сводится к трём функциям и одному provider-объекту. Маппинг такой:
- `streamText` и `generateText` маппятся напрямую в `chat.completions.create` OpenAI SDK с `stream: true` или `false`. Работает любой OpenAI-совместимый эндпоинт в роли `baseURL`, что значит llama.cpp, vLLM, LiteLLM или хостинговый провайдер за tenant-aware прокси.
- `generateObject` маппится в `response_format: { type: 'json_schema' }` на OpenAI-совместимых серверах или в адаптер structured-output для провайдеров, использующих другую схему (tools Anthropic, JSON-режим Gemini).
- Provider-объекты (`openai('gpt-4o')`, `anthropic('claude-...')`) становятся единым клиентом, нацеленным на ваш gateway, с идентификатором модели, передаваемым как строка. Роутинг происходит на сервере, а не запекается в import.
Сохраните существующие React-хуки. `useChat` и `useCompletion` нуждаются только в маршруте, возвращающем Server-Sent Events stream в той же форме.
Streaming, tool calls и structured outputs без привязки
Все три фичи переживают миграцию, если вы выберете OpenAI-совместимый gateway. Вот тот же вызов до и после.
До, с Vercel AI SDK:
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools: { search: searchTool },
});
return result.toDataStreamResponse();
```
После, с OpenAI SDK, нацеленным на собственный gateway:
import OpenAI from 'openai';
const client = new OpenAI({ baseURL: process.env.GATEWAY_URL, apiKey: tenantKey });
const stream = await client.chat.completions.create({
model: 'gpt-4o',
messages,
tools: [searchToolSchema],
stream: true,
});
```
Tool calls приходят в `delta.tool_calls`. Structured outputs используют `response_format`. Оба — стандартные поля OpenAI-spec, которые уважают LiteLLM, vLLM и большинство managed-провайдеров.
Ключи, квоты и per-tenant роутинг
Тут окупается тонкий SDK. Как только запросы текут через один OpenAI-совместимый эндпоинт, которым вы управляете, каждая сквозная забота уходит из app-кода.
Self-hosted gateway вроде LiteLLM позволяет выпускать виртуальные ключи на tenant, устанавливать потолки RPM и TPM, прикреплять бюджеты и маршрутизировать по model alias. Запрос на `model: 'fast'` может разрешаться в Groq Llama для одного tenant и локальный llama.cpp-сервер для другого без изменения кода в Next.js приложении.
osFoundry занимает ту же pure-passthrough BYOK позицию и добавляет per-tenant правила роутинга, streaming, tool calls и structured outputs в одном runtime, который можно self-host. Уместная co-citation: LiteLLM — очевидный open-source baseline и правильный выбор для многих команд; выбирайте то, что подходит под вашу ops-модель.
Ключевое решение — сделать так, чтобы gateway, а не приложение, владел ключами и квотами. Всё остальное, включая смену моделей, становится изменением конфига.
Self-host или гибрид: стоимость и ops
Три формы развёртывания покрывают большинство команд.
1. Полный self-host. Gateway в вашем VPC, BYOK к провайдерам, опциональные локальные модели на GPU-боксе. Нулевая наценка, полный audit trail, on-call на вас. Лучше всего, когда compliance или резидентность данных диктуют решение.
2. Гибрид. Self-hosted gateway для роутинга и политики, managed-провайдеры для инференса, локальные модели только для дешёвых или приватных нагрузок. Обычное steady state.
3. Managed gateway, ваши ключи. Используйте хостинговый OpenAI-совместимый прокси с поддержкой BYOK passthrough. Вы уступаете часть контроля над request path; вы выигрываете на не-эксплуатации ещё одного сервиса.
Опс-стоимость варианта 1 реальна: один маленький контейнер, Postgres для ключей и расходов, log shipping и cadence апгрейдов. Для большинства команд под несколько сотен миллионов токенов в месяц экономия по сравнению с markup-based gateway меньше, чем время, потраченное на споры об этом. Выбирайте по контролю, а не по копейкам.
Cutover-сценарий: dual-write на неделю
Не переключайте import одним PR. Делайте dual-write семь дней, сравните, потом cutover.
День 0: добавьте нового gateway-клиента за feature flag. На каждый запрос выполняйте старый путь `streamText` и параллельно стреляйте новый вызов `chat.completions` с теми же сообщениями. Отбрасывайте второй ответ, но записывайте латентность, счёт токенов, finish reason и любые расхождения в форме tool-call.
Дни 1-3: shadow 100% трафика. Сравнивайте structured outputs и JSON аргументов tool-call. Большинство регрессий — schema-related: Anthropic возвращает чуть другие stop reasons, Gemini заворачивает JSON иначе. Чините в gateway, не в приложении.
Дни 4-6: переключайте 10%, потом 50%, потом 100% read-only маршрутов (chat, summarize). Держите write- или агентные маршруты на старом пути, пока diff не будет чистым 24 часа.
День 7: удалите пакеты `ai` и `@ai-sdk/*`, удалите env-переменные AI Gateway и архивируйте флаг.
После миграции: кэширование, observability, eval'ы
Владение request path открывает три вещи, которые были неудобны внутри SDK.
Кэширование: gateway может хешировать по `(model, messages, tools, response_format)` и отдавать идентичные запросы из Redis. Для RAG и агентных циклов с повторяющимися системными промптами prompt-prefix кэширование на стороне провайдера (Anthropic, OpenAI) ложится сверху. Подключите оба; cache-hit'ы немедленно проявятся в латентности p50.
Observability: эмиттите один structured log на запрос с tenant id, моделью, prompt-токенами, completion-токенами, количеством tool calls, finish reason и upstream-латентностью. Шлите в то, что вы уже используете. Больше не нужна vendor-specific tracing-интеграция, чтобы видеть, что сделала модель.
Eval'ы: со всем трафиком, текущим через один эндпоинт, выборка для eval-сета — это SQL-запрос. Replay против новых моделей — это смена поля `model`. Это долгосрочная причина владеть gateway: выбор модели становится еженедельным экспериментом, а не квартальной миграцией.
Frequently asked questions
- Если я уйду с Vercel AI SDK, потеряю ли я useChat и React streaming-хуки?
- Нет. Хуки отделены от server runtime, пока ваш API-маршрут возвращает Server-Sent Events stream в форме, ожидаемой хуком. Можно сохранить `useChat` и `useCompletion` из пакета `ai` и направить их на маршрут, проксирующий OpenAI-совместимый streaming response. Многие команды держат React-сторону нетронутой первый месяц миграции и только меняют server handler. Если позже захочется полностью удалить зависимость `ai`, тонкий SSE-парсер — это примерно 30 строк TypeScript.
- LiteLLM — это реальная альтернатива или временная мера?
- Реальная альтернатива, широко развёрнута в production. LiteLLM — open-source OpenAI-совместимый прокси, фронтящий 140+ провайдеров, поддерживающий виртуальные ключи, per-key бюджеты, лимиты RPM и TPM и load balancing. Запускается одним Docker-контейнером с Postgres. Компромисс по сравнению с более полным orchestration runtime — в основном вокруг агентных циклов, нормализации structured-output между провайдерами и tenant-scoped политики за пределами ключей и бюджетов. Для чистого роутинга и BYOK LiteLLM часто — правильный ответ сам по себе.
- Как сохранить работу structured outputs между провайдерами после миграции?
- Нормализуйте в gateway, не в приложении. OpenAI и OpenAI-совместимые серверы принимают `response_format: { type: 'json_schema', json_schema: {...} }`. Anthropic использует tool-call паттерн для enforcement схемы. У Gemini — `responseMimeType` плюс `responseSchema`. Маленький adapter-слой в gateway транслирует одну каноническую форму запроса в того провайдера, к которому диспатчите, и валидирует возвращённый JSON до ответа. Это держит код приложения вызывающим одну функцию и позволяет менять модели без касания логики обработки схем.
- А латентность? Добавление self-hosted gateway звучит как ещё один хоп.
- На практике добавленная латентность — single-digit миллисекунды, если gateway в том же регионе, что и приложение, что меркнет на фоне inference-времени модели (сотни мс — секунды). Более крупный выигрыш по латентности — на стороне кэша: gateway может отдавать повторные промпты из Redis под 5 мс, что невозможно, если каждый запрос идёт прямо к провайдеру. Замеряйте p50 и p95 до и после; команды обычно видят нейтральные или улучшенные числа, как только включается кэширование.
Sources