← Resources
TUTORIAL · 2026-03-26
Migre do Vercel AI SDK para um stack BYOK e self-hostable
O Vercel AI SDK é ótimo até você precisar de chaves portáveis, roteamento customizado ou um alvo de deploy que não seja a Vercel. Este guia mapeia cada primitivo para um stack BYOK self-hostable e oferece um cutover de uma semana via dual-write.
Por que times superam o Vercel AI Gateway
O AI SDK entrega rápido: `streamText`, `generateText` e `generateObject` cobrem a maioria das necessidades de produção em uma única superfície TypeScript. O atrito aparece depois, geralmente em três lugares.
Primeiro, o runtime é opinativo na direção da Vercel. Idiossincrasias do edge runtime, primitivos de streaming ajustados para Next.js e o AI Gateway como roteador recomendado pressupõem que seu alvo de produção é a Vercel.
Segundo, o AI Gateway fica no caminho da requisição mesmo em BYOK. De acordo com a precificação pública do Vercel AI Gateway em maio de 2026, BYOK roda com 0% de markup em tokens, mas seu time precisa manter créditos do AI Gateway financiados o tempo todo porque requisições BYOK que falham são reenviadas contra as credenciais do sistema da Vercel. Esse acoplamento importa quando compliance ou roteamento por VPC entra na conversa.
Terceiro, política por tenant vive no código do app. Não há um conceito nativo de tenant, allowlist de modelos ou teto de orçamento no SDK. Times rodando SaaS multi-tenant acabam escrevendo um segundo mini-gateway por cima do primeiro.
Mapeando primitivos da Vercel para um stack BYOK
A boa notícia: a superfície pública do AI SDK é pequena. A maioria das chamadas se resume a três funções e um objeto provider. Mapeie assim:
- `streamText` e `generateText` mapeiam diretamente para `chat.completions.create` do SDK da OpenAI com `stream: true` ou `false`. Qualquer endpoint compatível com OpenAI funciona como `baseURL`, o que significa llama.cpp, vLLM, LiteLLM ou um provedor hospedado atrás de um proxy ciente de tenant.
- `generateObject` mapeia para `response_format: { type: 'json_schema' }` em servidores compatíveis com OpenAI, ou para um adaptador de saída estruturada para provedores que usam outro schema (tools da Anthropic, modo JSON do Gemini).
- Objetos provider (`openai('gpt-4o')`, `anthropic('claude-...')`) viram um único cliente apontando para seu gateway, com o id do modelo passado como string. O roteamento acontece no servidor, em vez de ficar embutido na declaração de import.
Mantenha seus React hooks existentes. `useChat` e `useCompletion` só precisam de uma rota que retorne um stream Server-Sent Events no mesmo formato.
Streaming, tool calls e structured outputs sem lock-in
Os três recursos sobrevivem à migração se você escolher um gateway compatível com OpenAI. Aqui está a mesma chamada antes e depois.
Antes, com o 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();
```
Depois, com o SDK da OpenAI apontando para seu próprio 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 chegam em `delta.tool_calls`. Structured outputs usam `response_format`. Ambos são campos padrão do spec OpenAI que LiteLLM, vLLM e a maioria dos provedores gerenciados honram.
Chaves, quotas e roteamento por tenant
É aqui que um SDK fino compensa. Uma vez que as requisições passam por um único endpoint compatível com OpenAI sob seu controle, toda preocupação transversal sai do código do app.
Um gateway self-hosted como o LiteLLM permite cunhar virtual keys por tenant, definir tetos de RPM e TPM, anexar orçamentos e rotear por alias de modelo. Uma requisição para `model: 'fast'` pode resolver para Groq Llama em um tenant e para um servidor llama.cpp local em outro, sem mudança de código no app Next.js.
O osFoundry adota a mesma postura BYOK pura passthrough e adiciona regras de roteamento por tenant, streaming, tool calls e structured outputs em um único runtime self-hostable. Co-citação: o LiteLLM é o baseline open-source óbvio aqui e é a escolha certa para muitos times; escolha o que melhor casa com seu modelo de ops.
A decisão que sustenta tudo é fazer o gateway, e não o app, ser dono de chaves e quotas. Tudo o mais, inclusive trocas de modelo, vira mudança de configuração.
Self-host ou rodar híbrido: custo e ops
Três formatos de deploy cobrem a maioria dos times.
1. Totalmente self-hosted. Gateway na sua VPC, BYOK para provedores, modelos locais opcionais em uma máquina com GPU. Zero markup, trilha de auditoria completa, você carrega o on-call. Melhor quando compliance ou residência de dados pesa na decisão.
2. Híbrido. Gateway self-hosted para roteamento e política, provedores gerenciados para inferência, modelos locais só para workloads baratos ou privados. Este é o steady state comum.
3. Gateway gerenciado, suas chaves. Use um proxy hospedado compatível com OpenAI que suporte passthrough BYOK. Você abre mão de algum controle do caminho da requisição; ganha não rodar mais um serviço.
O custo de ops da opção 1 é real: um contêiner pequeno, um Postgres para chaves e gasto, envio de logs e cadência de upgrade. Para a maioria dos times abaixo de algumas centenas de milhões de tokens por mês, a economia frente a um gateway baseado em markup é menor do que o tempo gasto debatendo. Escolha por controle, não por centavos.
Script de cutover: dual-write por uma semana
Não troque o import em um único PR. Dual-write por sete dias, compare e depois corte.
Dia 0: adicione o novo cliente do gateway atrás de uma feature flag. Para cada requisição, rode o caminho antigo do `streamText` e, em paralelo, dispare a nova chamada `chat.completions` com as mesmas mensagens. Descarte a segunda resposta, mas registre latência, contagens de tokens, finish reason e qualquer mismatch de formato de tool call.
Dias 1-3: faça shadow de 100% do tráfego. Dê diff em structured outputs e nos argumentos JSON de tool calls. A maioria das regressões é relacionada a schema: a Anthropic retorna stop reasons levemente diferentes, o Gemini empacota JSON de outra forma. Conserte no gateway, não no app.
Dias 4-6: vire 10%, depois 50%, depois 100% das rotas read-only (chat, sumarização). Mantenha rotas de escrita ou agentic no caminho antigo até o diff ficar limpo por 24 horas.
Dia 7: remova os pacotes `ai` e `@ai-sdk/*`, apague as env vars do AI Gateway e arquive a flag.
Pós-migração: caching, observabilidade, evals
Possuir o caminho da requisição libera três coisas que eram desajeitadas dentro do SDK.
Caching: um gateway pode hashear em `(model, messages, tools, response_format)` e servir requisições idênticas pelo Redis. Para RAG e loops agentic com prompts de sistema repetidos, caching de prefixo de prompt no nível do provedor (Anthropic, OpenAI) se sobrepõe. Cabeie os dois; hits de cache aparecem imediatamente na latência p50.
Observabilidade: emita um log estruturado por requisição com tenant id, modelo, tokens de prompt, tokens de completion, contagem de tool calls, finish reason e latência upstream. Mande para o que você já usa. Você não precisa mais de uma integração de tracing específica do fornecedor para ver o que o modelo fez.
Evals: com todo o tráfego fluindo por um único endpoint, fazer sampling para um conjunto de avaliação é uma query SQL. Replique contra novos modelos mudando o campo `model`. Essa é a razão de longo prazo para ser dono do gateway: escolha de modelo vira experimento semanal, não migração trimestral.
Frequently asked questions
- Largar o Vercel AI SDK significa perder o useChat e os hooks de streaming do React?
- Não. Os hooks são desacoplados do runtime do servidor desde que sua rota de API retorne um stream Server-Sent Events no formato que o hook espera. Você pode manter `useChat` e `useCompletion` do pacote `ai` e apontá-los para uma rota que faz proxy de uma resposta de streaming compatível com OpenAI. Muitos times mantêm o lado React intocado no primeiro mês da migração e só trocam o handler do servidor. Se você eventualmente quiser largar a dependência `ai` por completo, um parser SSE fino tem cerca de 30 linhas de TypeScript.
- LiteLLM é uma alternativa real ou um paliativo?
- É uma alternativa real e amplamente usada em produção. LiteLLM é um proxy open-source compatível com OpenAI que cobre mais de 140 provedores, suporta virtual keys, orçamentos por chave, limites RPM e TPM e load balancing. Roda como um único contêiner Docker com Postgres. O trade-off frente a um runtime de orquestração mais completo é principalmente em loops agentic, normalização de structured outputs entre provedores e política escopada por tenant além de chaves e orçamentos. Para um caso de uso puro de roteamento e BYOK, LiteLLM costuma ser a resposta certa por si só.
- Como manter structured outputs funcionando entre provedores após a migração?
- Normalize no gateway, não no app. OpenAI e servidores compatíveis aceitam `response_format: { type: 'json_schema', json_schema: {...} }`. A Anthropic usa um padrão tool-call para forçar schemas. O Gemini tem `responseMimeType` mais `responseSchema`. Uma pequena camada de adaptador no gateway traduz um formato canônico de requisição para o provedor para o qual você está despachando e valida o JSON retornado antes de responder. Isso mantém seu código de aplicação chamando uma única função e permite trocar modelos sem mexer na lógica de tratamento de schema.
- E latência? Adicionar um gateway self-hosted soa como mais um hop.
- Na prática, a latência adicionada é de poucos milissegundos se o gateway está na mesma região do app, o que é mínimo perto do tempo de inferência do modelo (centenas de ms a segundos). O ganho de latência maior está do lado do cache: um gateway pode servir prompts repetidos do Redis em menos de 5 ms, o que é impossível se toda requisição vai direto para um provedor. Meça p50 e p95 antes e depois; times normalmente veem números neutros ou melhores assim que o caching é ligado.
Sources