← Resources
TUTORIAL · 2026-03-26
Migra da Vercel AI SDK a uno stack BYOK self-hostable
Vercel AI SDK va bene finché non hai bisogno di chiavi portabili, routing custom o un target di deploy che non sia Vercel. Questa guida mappa ogni primitiva a uno stack BYOK self-hostable e ti dà un cutover dual-write di una settimana.
Perché i team superano Vercel AI Gateway
L'AI SDK spedisce in fretta: `streamText`, `generateText` e `generateObject` coprono la maggior parte delle esigenze di produzione con una singola superficie TypeScript. La frizione emerge più tardi, di solito in tre punti.
Primo, il runtime è opinionato verso Vercel. Le stranezze del runtime Edge, le primitive di streaming tunate per Next.js e l'AI Gateway come router raccomandato presuppongono tutti che il tuo target prod sia Vercel.
Secondo, l'AI Gateway sta nel percorso della richiesta anche su BYOK. Secondo il pricing pubblico AI Gateway di Vercel a maggio 2026, BYOK gira allo 0% di markup sui token, ma il tuo team deve tenere finanziati in ogni momento i crediti AI Gateway perché le richieste BYOK fallite vengono ritentate contro i system credentials di Vercel. Quell'accoppiamento conta una volta che entra in gioco la compliance o il routing VPC.
Terzo, la policy per-tenant vive nel codice dell'applicazione. Non c'è un concetto nativo di tenant, model whitelist o budget cap nell'SDK. I team che gestiscono SaaS multi-tenant finiscono per scrivere un secondo mini-gateway sopra il primo.
Mappare le primitive Vercel a uno stack BYOK
La buona notizia: la superficie pubblica dell'AI SDK è piccola. La maggior parte delle chiamate si riduce a tre funzioni e un provider object. Mappa così:
- `streamText` e `generateText` mappano direttamente a `chat.completions.create` dell'OpenAI SDK con `stream: true` o `false`. Qualsiasi endpoint OpenAI-compatibile funziona come `baseURL`, il che significa llama.cpp, vLLM, LiteLLM o un provider hosted dietro un proxy tenant-aware.
- `generateObject` mappa a `response_format: { type: 'json_schema' }` su server OpenAI-compatibili, o a un adapter di output strutturato per i provider che usano uno schema diverso (Anthropic tools, Gemini JSON mode).
- Provider object (`openai('gpt-4o')`, `anthropic('claude-...')`) diventano un singolo client puntato al tuo gateway, con il model id passato come stringa. Il routing avviene server-side invece di essere cotto dentro l'import statement.
Mantieni i tuoi hook React esistenti. `useChat` e `useCompletion` hanno solo bisogno di una route che restituisca uno stream Server-Sent Events nella stessa forma.
Streaming, tool call e output strutturati senza lock-in
Tutte e tre le feature sopravvivono alla migrazione se scegli un gateway OpenAI-compatibile. Ecco la stessa chiamata prima e dopo.
Prima, con 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();
```
Dopo, con l'OpenAI SDK puntato al tuo 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,
});
```
Le tool call arrivano in `delta.tool_calls`. Gli output strutturati usano `response_format`. Entrambi sono campi standard OpenAI-spec che LiteLLM, vLLM e la maggior parte dei provider gestiti onorano.
Chiavi, quote e routing per-tenant
È qui che un SDK sottile ripaga. Una volta che le richieste fluiscono attraverso un endpoint OpenAI-compatibile che controlli, ogni preoccupazione cross-cutting esce dal codice dell'applicazione.
Un gateway self-hosted come LiteLLM ti permette di emettere chiavi virtuali per tenant, impostare tetti RPM e TPM, allegare budget e instradare per alias di modello. Una richiesta per `model: 'fast'` può risolvere a Groq Llama per un tenant e a un server llama.cpp locale per un altro, senza modifiche al codice nell'app Next.js.
osFoundry adotta la stessa postura BYOK pure-passthrough e aggiunge regole di routing per-tenant, streaming, tool call e output strutturati in un unico runtime che puoi self-hostare. Co-citazione: LiteLLM è l'ovvia baseline open source qui ed è la scelta giusta per molti team; scegli quello che si adatta al tuo modello operativo.
La decisione portante è far possedere chiavi e quote al gateway, non all'app. Tutto il resto, inclusi gli swap di modello, diventa un cambio di configurazione.
Self-host o eseguire ibrido: costo e ops
Tre forme di deploy coprono la maggior parte dei team.
1. Completamente self-hosted. Gateway nella tua VPC, BYOK ai provider, modelli locali opzionali su una macchina GPU. Zero markup, audit trail completo, ti porti l'on-call. Migliore quando compliance o data residency guidano la decisione.
2. Ibrido. Gateway self-hosted per routing e policy, provider gestiti per l'inferenza, modelli locali solo per workload economici o privati. Questo è lo stato stazionario comune.
3. Gateway gestito, tue chiavi. Usa un proxy hosted OpenAI-compatibile che supporta passthrough BYOK. Rinunci a un po' di controllo sul percorso della richiesta; guadagni di non gestire un altro servizio.
Il costo ops dell'opzione 1 è reale: un piccolo container, un Postgres per chiavi e spesa, log shipping e una cadenza di upgrade. Per la maggior parte dei team sotto qualche centinaio di milioni di token al mese, i risparmi rispetto a un gateway markup-based sono più piccoli del tempo speso a discuterne. Scegli sulla base del controllo, non dei centesimi.
Script di cutover: dual-write per una settimana
Non cambiare l'import in una sola PR. Dual-write per sette giorni, confronta, poi taglia.
Giorno 0: aggiungi il nuovo client gateway dietro un feature flag. Per ogni richiesta, esegui il vecchio percorso `streamText` e, in parallelo, lancia la nuova chiamata `chat.completions` con gli stessi messaggi. Scarta la seconda risposta, ma registra latenza, conteggio token, finish reason e qualsiasi mismatch nella forma della tool call.
Giorni 1-3: ombreggia il 100% del traffico. Confronta output strutturati e JSON degli argomenti di tool call. La maggior parte delle regressioni è schema-related: Anthropic restituisce stop reason leggermente diversi, Gemini wrappa il JSON in modo diverso. Risolvi nel gateway, non nell'app.
Giorni 4-6: flippa il 10%, poi il 50%, poi il 100% delle route read-only (chat, summarize). Tieni le route write o agentiche sul vecchio percorso finché il diff non è pulito per 24 ore.
Giorno 7: rimuovi i pacchetti `ai` e `@ai-sdk/*`, elimina le env var AI Gateway e archivia il flag.
Post-migrazione: caching, observability, eval
Possedere il percorso della richiesta sblocca tre cose che erano scomode dentro l'SDK.
Caching: un gateway può fare hashing su `(model, messages, tools, response_format)` e servire richieste identiche da Redis. Per loop RAG e agent con system prompt ripetuti, il prompt-prefix caching a livello provider (Anthropic, OpenAI) si stratifica sopra. Cabla entrambi; gli hit di cache appaiono immediatamente nella latenza p50.
Observability: emetti un log strutturato per richiesta con tenant id, modello, token di prompt, token di completion, conteggio tool call, finish reason e latenza upstream. Spediscilo a qualsiasi cosa già usi. Non hai più bisogno di un'integrazione di tracing vendor-specific per vedere cosa ha fatto il modello.
Eval: con tutto il traffico che fluisce attraverso un endpoint, il campionamento per un eval set è una query SQL. Riproduci contro nuovi modelli cambiando il campo `model`. Questa è la ragione di lungo termine per possedere il gateway: la scelta del modello diventa un esperimento settimanale, non una migrazione trimestrale.
Frequently asked questions
- Eliminare Vercel AI SDK significa perdere useChat e gli hook React di streaming?
- No. Gli hook sono disaccoppiati dal runtime server finché la tua route API restituisce uno stream Server-Sent Events nella forma che l'hook si aspetta. Puoi tenere `useChat` e `useCompletion` dal pacchetto `ai` e puntarli a una route che fa proxy a una risposta streaming OpenAI-compatibile. Molti team tengono il lato React intatto per il primo mese della migrazione e cambiano solo l'handler server. Se alla fine vuoi eliminare del tutto la dipendenza `ai`, un parser SSE sottile è circa 30 righe di TypeScript.
- LiteLLM è un'alternativa vera o un ripiego?
- È un'alternativa vera ed è ampiamente deployata in produzione. LiteLLM è un proxy open source OpenAI-compatibile che fronta oltre 140 provider, supporta chiavi virtuali, budget per chiave, limiti RPM e TPM e load balancing. Gira come un singolo container Docker con Postgres. Il trade-off rispetto a un runtime di orchestrazione più completo è principalmente sui loop agentici, sulla normalizzazione degli output strutturati tra provider e sulla policy tenant-scoped oltre chiavi e budget. Per un caso d'uso di puro routing e BYOK, LiteLLM è spesso la risposta giusta da sola.
- Come faccio a mantenere funzionanti gli output strutturati tra provider dopo la migrazione?
- Normalizza al gateway, non nell'app. OpenAI e i server OpenAI-compatibili accettano `response_format: { type: 'json_schema', json_schema: {...} }`. Anthropic usa un pattern di tool call per imporre gli schemi. Gemini ha un `responseMimeType` più `responseSchema`. Un piccolo layer adapter nel gateway traduce una forma canonica di richiesta in qualsiasi provider stai dispatching e valida il JSON restituito prima di rispondere. Questo mantiene il codice dell'applicazione che chiama una singola funzione e ti permette di scambiare modelli senza toccare la logica di gestione dello schema.
- E la latenza? Aggiungere un gateway self-hosted sembra un altro hop.
- Nella pratica la latenza aggiunta è di pochi millisecondi se il gateway è nella stessa region della tua app, il che è eclissato dal tempo di inferenza del modello (centinaia di ms fino a secondi). La vittoria più grande sulla latenza è lato cache: un gateway può servire prompt ripetuti da Redis in meno di 5 ms, il che è impossibile se ogni richiesta va diretta a un provider. Misura p50 e p95 prima e dopo; i team di solito vedono numeri neutri o migliorati una volta che il caching è attivo.
Sources