> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thaliq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming

> Como consumir respuestas del agente en tiempo real

El SDK ofrece dos modos de comunicacion: **chat** (espera la respuesta completa) y **stream** (recibe eventos en tiempo real). Streaming es el modo recomendado para la mayoria de casos.

## Chat (sin streaming)

Envia un mensaje y espera la respuesta completa:

```typescript theme={null}
const response = await thaliq.agent.chat('¿Cuantos usuarios activos tenemos?');

console.log(response.message);            // Texto de la respuesta
console.log(response.conversationId);     // ID de conversacion
console.log(response.insights);           // Insights extraidos (v1.1)
console.log(response.metadata.model);     // Modelo usado
console.log(response.metadata.messageId); // ID del mensaje (para feedback)
```

## Stream (tiempo real)

Envia un mensaje y recibe eventos conforme llegan:

```typescript theme={null}
const stream = thaliq.agent.stream('Analiza las ventas del Q4');

for await (const event of stream) {
  switch (event.type) {
    case 'content.delta':
      // Fragmento de texto (llega caracter por caracter)
      process.stdout.write(event.delta);
      break;

    case 'status':
      // Mensaje de estado del agente
      console.log(`[Status] ${event.text}`);
      break;

    case 'tool.start':
      // El agente comienza a ejecutar una tool
      console.log(`> Ejecutando: ${event.tool}`);
      break;

    case 'tool.end':
      // La tool termino de ejecutarse
      console.log(`> ${event.tool}: ${event.success ? 'OK' : 'Error'}`);
      break;

    case 'action':
      // Se requiere una accion del usuario (HITL)
      console.log('Accion requerida:', event.action.message);
      break;

    case 'handoff':
      // La conversacion fue escalada a un agente humano
      console.log('Escalado a:', event.agentName ?? 'agente humano');
      break;
  }
}
```

## Tipos de evento

El SDK soporta **12 tipos de eventos** SSE:

| Tipo                 | Descripcion                        | Campos clave                        |
| -------------------- | ---------------------------------- | ----------------------------------- |
| `meta`               | Metadata inicial del stream        | `conversationId`                    |
| `status`             | Mensaje de estado del agente       | `text`                              |
| `content.delta`      | Fragmento de texto de la respuesta | `delta`                             |
| `tool.start`         | Inicio de ejecucion de una tool    | `tool`                              |
| `tool.end`           | Fin de ejecucion de una tool       | `tool`, `success`                   |
| `action`             | Accion HITL requerida              | `action: PendingAction`             |
| `handoff`            | Conversacion escalada a humano     | `message`, `agentName?`, `reason?`  |
| `response.completed` | Respuesta completa con metadata    | `message`, `insights[]`, `metadata` |
| `message_stop`       | Generacion del modelo finalizada   | `model`, `usage`                    |
| `rate_limit`         | Rate limit alcanzado               | `message`, `retryAfter`             |
| `error`              | Error durante el stream            | `message`                           |
| `keepalive`          | Ping periodico (mantiene conexion) | `ts`                                |

### Orden tipico de eventos

```
meta → status → tool.start → tool.end → content.delta (x N) → response.completed
```

Si se requiere HITL:

```
meta → content.delta (x N) → action  (stream se pausa)
```

Si se escala a humano:

```
meta → content.delta (x N) → handoff  (stream termina)
```

## Resultado final

Despues de consumir el stream, puedes acceder al resultado completo usando **uno** de estos metodos:

```typescript theme={null}
const stream = thaliq.agent.stream('Resume los datos');

// Opcion 1: Solo el texto acumulado
const text = await stream.text();
console.log(text);

// Opcion 2: Respuesta completa con metadata
const response = await stream.finalResponse();
console.log(response.message);
console.log(response.metadata.completionTokens);
console.log(response.insights); // Insights extraidos
```

<Warning>
  **El stream solo puede ser consumido una vez.** Tanto `text()` como `finalResponse()` iteran internamente sobre el stream. Llamar ambos metodos causara un `StreamError`.

  ```typescript theme={null}
  // ❌ ERROR — doble consumo
  const text = await stream.text();
  const response = await stream.finalResponse(); // StreamError!

  // ✅ CORRECTO — usa solo finalResponse() que incluye el texto
  const response = await stream.finalResponse();
  const text = response.message;
  ```

  Esto aplica tambien a `for await`: si ya iteraste el stream manualmente, no puedes llamar `text()` ni `finalResponse()`.
</Warning>

## Insights

Las respuestas pueden incluir **insights** extraidos automaticamente del contexto de la conversacion:

```typescript theme={null}
const response = await stream.finalResponse();

for (const insight of response.insights) {
  console.log(`[${insight.type}] ${insight.title}`);
  console.log(insight.description);
}
```

```typescript theme={null}
interface Insight {
  type: 'warning' | 'opportunity' | 'info' | 'achievement';
  severity: 'low' | 'medium' | 'high' | 'critical';
  title: string;
  description: string;
  category?: string;
  amount?: string;
}
```

## Estado del stream

El stream expone su estado actual:

```typescript theme={null}
const stream = thaliq.agent.stream('Hola');

console.log(stream.status); // 'streaming'

for await (const event of stream) { /* ... */ }

console.log(stream.status); // 'completed' | 'awaiting_action' | 'handoff' | 'error'
```

| Estado            | Significado                                     |
| ----------------- | ----------------------------------------------- |
| `streaming`       | Recibiendo eventos                              |
| `completed`       | Stream finalizo correctamente                   |
| `awaiting_action` | El agente requiere una accion HITL              |
| `handoff`         | La conversacion fue escalada a un agente humano |
| `error`           | Ocurrio un error                                |
| `aborted`         | El stream fue cancelado                         |

## Cancelar un stream

```typescript theme={null}
const stream = thaliq.agent.stream('Consulta larga...');

// Cancelar despues de 5 segundos
setTimeout(() => stream.abort(), 5000);

for await (const event of stream) {
  // Se detiene cuando se llama abort()
}

console.log(stream.status); // 'aborted'
```

Tambien puedes usar `AbortSignal`:

```typescript theme={null}
const controller = new AbortController();

const stream = thaliq.agent.stream('Consulta', {
  signal: controller.signal,
});

// Cancelar desde fuera
controller.abort();
```

## Conversaciones

Mantiene el contexto entre mensajes usando `conversationId`:

```typescript theme={null}
// Primer mensaje
const stream1 = thaliq.agent.stream('Hola, necesito ayuda con mis facturas');
for await (const event of stream1) { /* ... */ }

// Segundo mensaje (misma conversacion, mantiene contexto)
const stream2 = thaliq.agent.stream('Dame mas detalles de la ultima', {
  conversationId: stream1.conversationId!,
});
```

Ver [Conversaciones](/sdk/conversations) para mas detalles.

## Callback alternativo

Si prefieres callbacks en vez de `for await`, puedes usar `onEvent`:

```typescript theme={null}
const stream = thaliq.agent.stream('Hola', {
  onEvent: (event) => {
    if (event.type === 'content.delta') {
      updateUI(event.delta);
    }
  },
});

// Aun necesitas consumir el stream para que empiece
await stream.text();
```
