@dariomvg/create
anthropictext-generationnextjsstreamingintegracion

Anthropic Text Generation — Configuración

Módulo de generación de texto listo para integrar en Next.js App Router usando la API de Anthropic.

Qué incluye

  • Generación de texto (sin streaming)
  • Texto con streaming
  • Prompts de sistema
  • Temperatura / máximo de tokens / modelo configurables
  • Salida estructurada (JSON, mediante tool use forzado)
  • Manejo de errores normalizado (incluyendo rate limiting), listo para la UI

No incluido a propósito: agentes, generación de imágenes, historial de chat/conversaciones, tool calling de uso general. Para eso están los módulos separados.

Dependencias

Este módulo usa:

No se instala automáticamente: agregalo al package.json de tu proyecto como lo hacés normalmente.

Mapa de archivos

ArchivoSe ejecuta enPara qué sirve
types.tsambosTipos compartidos, valores por defecto y formatos de error
service.tsservidorLlamadas internas al SDK de Anthropic + normalización de errores. No lo importes directamente.
actions.tsservidorServer Actions: importalas en server components
client.tsclienteStreaming del lado del cliente: importalo en client components
stream/handler.tsservidorHandler interno de las requests de streaming
stream/route.tsservidorCopialo a app/api/anthropic/stream/route.ts
.env.local—Variables de entorno

Configuración

  1. Copiá todos los archivos a tu proyecto (ubicación sugerida: lib/ai/anthropic/text-generation/).
  2. Copiá stream/route.ts a app/api/anthropic/stream/route.ts (importa ./handler, que tenés que copiar al lado, por ejemplo a app/api/anthropic/stream/handler.ts, actualizando la ruta del import en route.ts en consecuencia; o dejá handler.ts en tu carpeta lib e importalo desde ahí).
  3. Agregá tu clave en .env.local:
    ANTHROPIC_API_KEY=sk-ant-...
    
  4. Asegurate de que @anthropic-ai/sdk esté en tus dependencias.

Uso

Generar texto (server component / server action)

import { generateText } from "@/lib/ai/anthropic/text-generation/actions";

const result = await generateText("Explain quantum computing simply", {
  systemPrompt: "You are a friendly physics teacher.",
  temperature: 0.7,
});

if (result.error) {
  // result.error.code, result.error.message (and result.error.retryAfter if rate_limited)
} else {
  console.log(result.data.text);
}

Salida estructurada (server component / server action)

import { generateStructuredOutput } from "@/lib/ai/anthropic/text-generation/actions";

const result = await generateStructuredOutput("John is 30 years old", {
  schemaName: "extract_person",
  schema: {
    type: "object",
    properties: {
      name: { type: "string" },
      age: { type: "number" },
    },
    required: ["name", "age"],
  },
});

if (!result.error) {
  console.log(result.data.data); // { name: "John", age: 30 }
}

Texto con streaming (client component)

"use client";

import { useState } from "react";
import { streamText } from "@/lib/ai/anthropic/text-generation/client";

export function ChatDemo() {
  const [output, setOutput] = useState("");
  const [isStreaming, setIsStreaming] = useState(false);
  const [error, setError] = useState<string | null>(null);

  function handleGenerate() {
    setOutput("");
    setError(null);
    setIsStreaming(true);

    streamText("Write a haiku about the ocean", undefined, {
      onChunk: (text) => setOutput((prev) => prev + text),
      onError: (err) => setError(err.message),
      onDone: () => setIsStreaming(false),
    });
  }

  return (
    <div>
      <button onClick={handleGenerate} disabled={isStreaming}>
        Generate
      </button>
      <p>{output}</p>
      {error && <p role="alert">{error}</p>}
    </div>
  );
}

Manejo de errores

Todas las funciones (generateText, generateStructuredOutput, streamText) devuelven o entregan un error normalizado en lugar de lanzar excepciones crudas del SDK:

type AIErrorCode =
  | "invalid_request"
  | "authentication_error"
  | "rate_limited"
  | "overloaded"
  | "network_error"
  | "unknown_error";

interface AIError {
  code: AIErrorCode;
  message: string;
  retryAfter?: number; // present when code === "rate_limited"
}

El rate limiting (code: "rate_limited") incluye retryAfter (en segundos) cuando la API lo provee, así podés deshabilitar el botón de envío de la UI o mostrar una cuenta regresiva hasta que sea seguro reintentar.