TechLead
Lección 2 de 8
5 min de lectura
AI SDK

Respuestas en streaming

Domina el streaming de respuestas de IA para experiencias en tiempo real con el AI SDK

Entender el streaming en aplicaciones de IA

El streaming permite que las respuestas de IA se entreguen de forma incremental a medida que se generan, en lugar de esperar a la respuesta completa. Esto crea una experiencia más ágil, especialmente para respuestas largas que pueden tardar varios segundos.

Beneficios del streaming

  • Menor tiempo al primer token: Los usuarios ven contenido de inmediato
  • Mejor UX: El efecto de escritura se siente más natural
  • Menor latencia percibida: Los usuarios interactúan mientras se carga
  • Eficiente en memoria: Procesa datos a medida que llegan

La función streamText

La función streamText es la forma principal de hacer streaming de respuestas:

import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

const result = streamText({
  model: openai('gpt-4-turbo'),
  prompt: 'Write a poem about coding.',
});

// The result provides multiple ways to consume the stream

Consumir streams

1. Usando toDataStreamResponse (recomendado para rutas API)

// app/api/chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: openai('gpt-4-turbo'),
    messages,
  });

  // Returns a Response with proper streaming headers
  return result.toDataStreamResponse();
}

2. Usando toTextStreamResponse

// Returns plain text stream (simpler but less features)
export async function POST(req: Request) {
  const { prompt } = await req.json();

  const result = streamText({
    model: openai('gpt-4-turbo'),
    prompt,
  });

  return result.toTextStreamResponse();
}

3. Usando un iterador asíncrono

import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

const result = streamText({
  model: openai('gpt-4-turbo'),
  prompt: 'Count from 1 to 10.',
});

// Process each chunk as it arrives
for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

// Or collect all text
const fullText = await result.text;

Protocolo de datos del stream

El AI SDK usa un protocolo especial para streaming que incluye metadatos:

// The data stream includes:
// - Text deltas (the actual content)
// - Tool calls and results
// - Finish reasons
// - Usage information

const result = streamText({
  model: openai('gpt-4-turbo'),
  messages,
  onFinish: async ({ text, finishReason, usage }) => {
    console.log('Finished:', finishReason);
    console.log('Tokens used:', usage);

    // Save to database, log analytics, etc.
    await saveToDatabase(text);
  },
});

Streaming con mensajes de sistema

const result = streamText({
  model: openai('gpt-4-turbo'),
  system: 'You are a helpful coding assistant. Be concise.',
  messages: [
    { role: 'user', content: 'How do I sort an array in JavaScript?' }
  ],
});

return result.toDataStreamResponse();

Streaming con opciones

const result = streamText({
  model: openai('gpt-4-turbo'),
  messages,

  // Model parameters
  temperature: 0.7,
  maxTokens: 1000,
  topP: 0.9,

  // Callbacks
  onChunk: ({ chunk }) => {
    // Called for each chunk
    console.log('Chunk:', chunk);
  },
  onFinish: ({ text, usage }) => {
    // Called when stream completes
    console.log('Total tokens:', usage.totalTokens);
  },

  // Abort signal for cancellation
  abortSignal: controller.signal,
});

Manejo del stream en el cliente

'use client';

import { useState } from 'react';

export default function StreamDemo() {
  const [response, setResponse] = useState('');
  const [isLoading, setIsLoading] = useState(false);

  const handleStream = async () => {
    setIsLoading(true);
    setResponse('');

    const res = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        messages: [{ role: 'user', content: 'Tell me a joke' }]
      }),
    });

    const reader = res.body?.getReader();
    const decoder = new TextDecoder();

    while (reader) {
      const { done, value } = await reader.read();
      if (done) break;

      const text = decoder.decode(value);
      setResponse(prev => prev + text);
    }

    setIsLoading(false);
  };

  return (
    <div>
      <button onClick={handleStream} disabled={isLoading}>
        {isLoading ? 'Streaming...' : 'Get Response'}
      </button>
      <div>{response}</div>
    </div>
  );
}

Mejor usa useChat

Aunque el manejo manual funciona, el hook useChat gestiona toda esta complejidad por ti. Úsalo para interfaces de chat en lugar de parsear el stream manualmente.

Manejo de errores en streams

try {
  const result = streamText({
    model: openai('gpt-4-turbo'),
    messages,
  });

  return result.toDataStreamResponse();
} catch (error) {
  if (error.name === 'AbortError') {
    return new Response('Stream cancelled', { status: 499 });
  }

  console.error('Stream error:', error);
  return new Response('Error generating response', { status: 500 });
}

💡 Puntos clave

  • • El streaming entrega respuestas de forma incremental para mejor UX
  • • Usa streamText para streaming del lado del servidor
  • toDataStreamResponse() maneja headers y protocolo automáticamente
  • • El callback onFinish es útil para logging y persistencia
  • • Usa useChat en el cliente para integrar más fácil

📚 Más recursos

Continuar aprendiendo