// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
export const maxDuration = 30;
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai('gpt-4o-mini'),
system: 'Ești un asistent tehnic util și concis.',
messages,
});
return result.toDataStreamResponse();
}Când integrezi un LLM într-o aplicație Next.js, cea mai frecventă greșeală e să aștepți ca modelul să genereze tot răspunsul înainte să-l trimiți în frontend. Am făcut și eu prostia asta la început. Rezultatul? Userul stă 4-5 secunde uitându-se la un loader și crede că s-a prăbușit serverul.
De ce am renunțat la fetch-ul clasic
La un proiect recent de suport tehnic intern, pe la 12k de interogări zilnice, aveam exact problema asta. Foloseam un fetch obișnuit către o rută de API din Next.js App Router care făcea call la OpenAI și întorcea un JSON standard.
Experiența era sinistră. Pe modele mai mari gen GPT-4, latența până la primul token (TTFB) era constant peste 3.5 secunde. Unii colegi dădeau click de două-trei ori pe butonul de send pentru că aveau impresia că aplicația s-a agățat. Asta ne dubla costurile pe API fără niciun motiv real.
Soluția a fost trecerea la Server-Sent Events (SSE). În loc să aștepți tot payload-ul, serverul deschide o conexiune HTTP și trimite bucăți de text (chunk-uri) pe măsură ce providerul le generează. Percepția vitezei se schimbă radical: deși timpul total de generare e la fel, utilizatorul vede că aplicația răspunde în 120-150 milisecunde.
Cum simplifică Vercel AI SDK toată treaba
Dacă încerci să scrii manual parsarea de ReadableStream pe frontend și gestionarea buffer-ului de text, o să-ți prinzi urechile în cazuri particulare. Mă refer la reconexiuni eșuate, erori la mijlocul stream-ului sau caractere UTF-8 tăiate aiurea la jumătate.
Aici intră în scenă Vercel AI SDK (ai și @ai-sdk/react). În backend folosești streamText, iar pe frontend hook-ul useChat. Hook-ul gestionează automat stările de messages, update-ul în timp real token cu token, starea de loading și trimiterea datelor din formular.
Ruta de API devine extrem de curată. Nu mai ai nevoie de transformatoare manuale de stream sau de logică complicată de boiler-plate.
Trade-off-uri și lecții învățate din producție
Evident, streaming-ul vine cu propriile provocări pe care nu le vezi în tutorialele scurte:
- Timeout-uri pe Serverless: Pe planul gratuit de Vercel, rutele au timeout la 10 secunde. Dacă modelul scrie un răspuns lung, stream-ul se taie brusc. Soluția e să folosești Edge Runtime (
export const runtime = 'edge') sau să măreștimaxDurationdacă ești pe un plan Pro. - Re-render-uri agresive în React: Hook-ul
useChatdeclanșează un re-render la FIECARE token primit. Într-o conversație lungă cu zeci de mesaje, dacă nu ai optimizat lista cumemosau o soluție de virtualizare, interfața începe să agațe când textul curge repede. - Caching-ul e mult mai complicat: La un JSON clasic puneai ușor un Redis cache după hash-ul promptului. La stream-uri parțiale trebuie să interceptezi și să salvezi stream-ul în timp ce curge sau să folosești un proxy dedicat gen LiteLLM sau Portkey.
Voi ce soluție folosiți când aveți nevoie și de Tool Calling (function calling) transmise în același stream? Ați rămas pe Vercel AI SDK sau v-ați construit un wrapper propriu?