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

# Modo de Voz Avanzado

> Cree sesiones de voz en tiempo real con la API MKA1 usando LiveKit. Configure opciones de LLM, herramientas, ajuste de STT y continuidad de conversación.

La API MKA1 proporciona una interfaz de voz en tiempo real a través de LiveKit. Esta guía cubre cómo obtener un token de sala, conectarse a una sesión de voz, enviar audio y texto como entrada, y capturar las respuestas del agente.

## Descripción general

La integración de voz consta de tres componentes principales:

1. **Token de Sala**: Un JWT que otorga acceso a una sala de LiveKit
2. **Conexión LiveKit**: Comunicación en tiempo real basada en WebRTC
3. **Agente de Voz**: Procesa entrada de audio/texto y genera respuestas habladas

El flujo del agente funciona de la siguiente manera:

* **STT (Reconocimiento de Voz a Texto)**: El audio se transmite vía WebSocket a 16kHz y se transcribe
* **LLM**: El texto transcrito es procesado por la API de Respuestas MKA1
* **TTS (Texto a Voz)**: La salida del LLM se sintetiza a audio a 24kHz

Cada solicitud que el agente de voz envía a la API de Respuestas incluye automáticamente `"voice_mode": "true"` en el `metadata` de la solicitud. Esto le permite distinguir respuestas originadas por voz de las basadas en texto al revisar el uso o el historial de respuestas.

## Obtener un token de sala

Para iniciar una sesión de voz, primero solicite un token de sala a la API MKA1. El endpoint de token requiere una clave de API y acepta opcionalmente `X-On-Behalf-Of` para identificar usuarios finales. Consulte [Autenticación](/es/docs/authentication) para más detalles.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm speech livekit-token \
    --llm '{"model":"meetkai:functionary-es-mini","reasoning":{"effort":"none"}}' \
    -H 'X-On-Behalf-Of: <end-user-id>'
  ```

  ```ts TypeScript theme={null}
  import { SDK } from '@meetkai/mka1';

  const mka1 = new SDK({
    bearerAuth: `Bearer ${YOUR_API_KEY}`,
  });

  const session = await mka1.llm.speech.livekitToken({
    llm: {
      model: 'meetkai:functionary-es-mini',
      reasoning: { effort: 'none' }
    }
  }, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional

  console.log(session.token);    // Token JWT
  console.log(session.url);      // URL WebSocket
  console.log(session.roomName); // Nombre de la sala
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(
      bearerAuth: "Bearer <mka1-api-key>",
      serverUrl: "https://apigw.mka1.com"
  );

  var res = await sdk.Llm.Speech.LivekitTokenAsync(new LivekitTokenRequest()
  {
      Llm = new MeetKai.MKA1.Types.Components.Llm()
      {
          Model = "meetkai:functionary-es-mini",
          Instructions = "You are a helpful voice assistant.",
      },
  });

  Console.WriteLine(res.Token);    // Token JWT
  Console.WriteLine(res.Url);      // URL WebSocket
  Console.WriteLine(res.RoomName); // Nombre de la sala
  ```

  ```python Python SDK theme={null}
  from mka1 import SDK

  sdk = SDK(bearer_auth="Bearer YOUR_API_KEY")

  session = sdk.llm.speech.livekit_token(
      llm={"model": "meetkai:functionary-es-mini", "reasoning": {"effort": "none"}},
  )

  print(session.token)      # Token JWT
  print(session.url)        # URL WebSocket
  print(session.room_name)  # Nombre de la sala
  ```

  ```bash bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/speech/livekit/token \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer YOUR_API_KEY' \
    --header 'X-On-Behalf-Of: user-123' \
    --data '{
      "llm": {
        "model": "meetkai:functionary-es-mini",
        "reasoning": { "effort": "none" }
      }
    }'
  ```
</CodeGroup>

### Parámetros

El cuerpo de la solicitud tiene dos objetos de nivel superior:

```json theme={null}
{
  "llm": { ... },   // Requerido — configuración del LLM
  "stt": { ... }    // Opcional — ajuste de reconocimiento de voz
}
```

#### `llm` — Configuración de LLM (requerido)

El objeto `llm` acepta los mismos campos que el cuerpo de la solicitud de la [API de Respuestas](/es/api-reference/responses/create-an-agent-powered-response-with-tool-support), menos los campos gestionados por el agente de voz (`input`, `stream`, `store`, `background`).

| Campo                  | Requerido | Descripción                                                                                                                                                                 |
| ---------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`                | Sí        | Modelo LLM a utilizar (por ejemplo, `meetkai:functionary-es-mini`)                                                                                                          |
| `instructions`         | No        | Instrucciones de sistema personalizadas para el agente                                                                                                                      |
| `previous_response_id` | No        | Encadena esta sesión a una respuesta específica de una sesión anterior                                                                                                      |
| `conversation`         | No        | Continúa una conversación existente — pase `{ "id": "conv_abc123..." }` o el ID de la conversación como cadena                                                              |
| `tools`                | No        | Array de definiciones de herramientas (function, web\_search, file\_search, etc.)                                                                                           |
| `tool_choice`          | No        | Cómo el modelo selecciona herramientas (`"auto"`, `"none"`, `"required"`, o una herramienta específica)                                                                     |
| `parallel_tool_calls`  | No        | Permitir ejecución paralela de herramientas                                                                                                                                 |
| `max_tool_calls`       | No        | Máximo de llamadas de herramientas por respuesta (por defecto: 30)                                                                                                          |
| `temperature`          | No        | Temperatura de muestreo (por ejemplo, `0.7`)                                                                                                                                |
| `max_output_tokens`    | No        | Máximo de tokens en la respuesta                                                                                                                                            |
| `reasoning`            | No        | Configuración de razonamiento (por ejemplo, `{ "effort": "high" }`). Establezca `{ "effort": "none" }` para sesiones de voz para minimizar la latencia — vea la nota abajo. |
| `top_p`                | No        | Parámetro de muestreo nucleus                                                                                                                                               |
| `presence_penalty`     | No        | Penalización de presencia para repetición de tokens                                                                                                                         |
| `frequency_penalty`    | No        | Penalización de frecuencia para repetición de tokens                                                                                                                        |
| `truncation`           | No        | `"auto"` o `"disabled"` — controla la truncación de contexto                                                                                                                |
| `context_management`   | No        | Estrategias de gestión de contexto para truncar la conversación                                                                                                             |
| `service_tier`         | No        | `"auto"`, `"default"`, `"flex"`, o `"priority"`                                                                                                                             |
| `prompt`               | No        | Referencia a una plantilla de prompt y sus variables                                                                                                                        |
| `text`                 | No        | Configuración de salida de texto (formato, verbosidad)                                                                                                                      |
| `metadata`             | No        | Metadatos clave-valor pasados a la API de Respuestas                                                                                                                        |

No puede especificar ambos `previous_response_id` y `conversation`.

<Note>
  Los metadatos del token se incrustan en un JWT, que se pasa como encabezado HTTP. Mantenga el payload total de `llm` por debajo de \~8 KB — los arrays grandes de `tools` pueden necesitar ser recortados.
</Note>

<Tip>
  Para sesiones de voz, desactive el razonamiento configurando `"reasoning": { "effort": "none" }`. El razonamiento añade tiempo de pensamiento antes de que el modelo responda, lo que incrementa la latencia y crea pausas notables en la conversación. Desactivarlo mantiene las respuestas rápidas y naturales.
</Tip>

#### `stt` — Configuración de reconocimiento de voz (opcional)

Controla la detección de actividad de voz (VAD) y el comportamiento de finalización en el servidor.

| Campo                        | Requerido | Descripción                                                     |
| ---------------------------- | --------- | --------------------------------------------------------------- |
| `silence_timeout_ms`         | No        | Milisegundos de silencio antes de finalizar el habla (100–5000) |
| `initial_silence_timeout_ms` | No        | Tiempo de espera antes de detectar cualquier habla (1000–30000) |

### Configuración avanzada

Puede pasar herramientas, instrucciones personalizadas y ajuste de STT en una sola solicitud de token:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm speech livekit-token \
    --llm '{
      "model": "meetkai:functionary-es-mini",
      "instructions": "You are a helpful travel assistant. Be concise in voice responses.",
      "temperature": 0.7,
      "tools": [
        { "type": "web_search", "user_location": { "country": "US" } },
        {
          "type": "function",
          "name": "book_flight",
          "description": "Book a flight for the user",
          "parameters": {
            "type": "object",
            "properties": {
              "origin": { "type": "string" },
              "destination": { "type": "string" },
              "date": { "type": "string" }
            },
            "required": ["origin", "destination", "date"]
          }
        }
      ],
      "tool_choice": "auto"
    }' \
    --stt '{"silence_timeout_ms":500,"initial_silence_timeout_ms":10000}'
  ```

  ```ts TypeScript theme={null}
  const session = await mka1.llm.speech.livekitToken({
    llm: {
      model: 'meetkai:functionary-es-mini',
      instructions: 'You are a helpful travel assistant. Be concise in voice responses.',
      temperature: 0.7,
      tools: [
        {
          type: 'web_search',
          user_location: { country: 'US' }
        },
        {
          type: 'function',
          name: 'book_flight',
          description: 'Book a flight for the user',
          parameters: {
            type: 'object',
            properties: {
              origin: { type: 'string' },
              destination: { type: 'string' },
              date: { type: 'string' }
            },
            required: ['origin', 'destination', 'date']
          }
        }
      ],
      tool_choice: 'auto'
    },
    stt: {
      silence_timeout_ms: 500,
      initial_silence_timeout_ms: 10000
    }
  });
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(
      bearerAuth: "Bearer <mka1-api-key>",
      serverUrl: "https://apigw.mka1.com"
  );

  var res = await sdk.Llm.Speech.LivekitTokenAsync(new LivekitTokenRequest()
  {
      Llm = new MeetKai.MKA1.Types.Components.Llm()
      {
          Model = "meetkai:functionary-es-mini",
          Instructions = "You are a concise voice assistant.",
          Temperature = 0.6,
      },
      Stt = new SttConfig()
      {
          SilenceTimeoutMs = 500,
          InitialSilenceTimeoutMs = 3000,
      },
  });

  Console.WriteLine(res.Token);
  ```

  ```python Python SDK theme={null}
  session = sdk.llm.speech.livekit_token(
      llm={
          "model": "meetkai:functionary-es-mini",
          "instructions": "You are a helpful travel assistant. Be concise in voice responses.",
          "temperature": 0.7,
          "tools": [
              {
                  "type": "web_search",
                  "user_location": {"country": "US"},
              },
              {
                  "type": "function",
                  "name": "book_flight",
                  "description": "Book a flight for the user",
                  "parameters": {
                      "type": "object",
                      "properties": {
                          "origin": {"type": "string"},
                          "destination": {"type": "string"},
                          "date": {"type": "string"},
                      },
                      "required": ["origin", "destination", "date"],
                  },
              },
          ],
          "tool_choice": "auto",
      },
      stt={
          "silence_timeout_ms": 500,
          "initial_silence_timeout_ms": 10000,
      },
  )
  ```

  ```bash bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/speech/livekit/token \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer YOUR_API_KEY' \
    --data '{
      "llm": {
        "model": "meetkai:functionary-es-mini",
        "instructions": "You are a helpful travel assistant. Be concise in voice responses.",
        "temperature": 0.7,
        "tools": [
          {
            "type": "web_search",
            "user_location": { "country": "US" }
          },
          {
            "type": "function",
            "name": "book_flight",
            "description": "Book a flight for the user",
            "parameters": {
              "type": "object",
              "properties": {
                "origin": { "type": "string" },
                "destination": { "type": "string" },
                "date": { "type": "string" }
              },
              "required": ["origin", "destination", "date"]
            }
          }
        ],
        "tool_choice": "auto"
      },
      "stt": {
        "silence_timeout_ms": 500,
        "initial_silence_timeout_ms": 10000
      }
    }'
  ```
</CodeGroup>

### Respuesta

```json theme={null}
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "url": "wss://apigw.mka1.com/api/v1/livekit",
  "roomName": "550e8400-e29b-41d4-a716-446655440000"
}
```

| Campo      | Descripción                                                                                       |
| ---------- | ------------------------------------------------------------------------------------------------- |
| `token`    | Token de acceso JWT (TTL de 5 minutos) con permisos para unirse, publicar y suscribirse a la sala |
| `url`      | URL WebSocket de LiveKit para conectarse                                                          |
| `roomName` | UUID autogenerado para esta sesión                                                                |

El token incluye metadatos que el agente de voz utiliza para configurar la sesión.

### Continuar una sesión

Para continuar desde una respuesta anterior:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm speech livekit-token \
    --llm '{"model":"meetkai:functionary-es-mini","previous_response_id":"resp_abc123..."}'
  ```

  ```ts TypeScript theme={null}
  const mka1 = new SDK({
    bearerAuth: `Bearer ${YOUR_API_KEY}`,
  });

  const session = await mka1.llm.speech.livekitToken({
    llm: {
      model: 'meetkai:functionary-es-mini',
      previousResponseId: 'resp_abc123...'
    }
  }, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, pero debe coincidir si la sesión original lo usó
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(
      bearerAuth: "Bearer <mka1-api-key>",
      serverUrl: "https://apigw.mka1.com"
  );

  var res = await sdk.Llm.Speech.LivekitTokenAsync(new LivekitTokenRequest()
  {
      Llm = new MeetKai.MKA1.Types.Components.Llm()
      {
          Model = "meetkai:functionary-es-mini",
          PreviousResponseId = "resp_abc123...",
      },
  });

  Console.WriteLine(res.Token);
  ```

  ```python Python SDK theme={null}
  session = sdk.llm.speech.livekit_token(
      llm={"model": "meetkai:functionary-es-mini", "previous_response_id": "resp_abc123..."},
  )
  ```

  ```bash bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/speech/livekit/token \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer YOUR_API_KEY' \
    --header 'X-On-Behalf-Of: user-123' \
    --data '{
      "llm": {
        "model": "meetkai:functionary-es-mini",
        "previous_response_id": "resp_abc123..."
      }
    }'
  ```
</CodeGroup>

Para continuar una conversación existente:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm speech livekit-token \
    --llm '{"model":"meetkai:functionary-es-mini","conversation":{"id":"conv_abc123..."}}'
  ```

  ```ts TypeScript theme={null}
  const mka1 = new SDK({
    bearerAuth: `Bearer ${YOUR_API_KEY}`,
  });

  const session = await mka1.llm.speech.livekitToken({
    llm: {
      model: 'meetkai:functionary-es-mini',
      conversation: { id: 'conv_abc123...' }
    }
  }, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, pero debe coincidir si la sesión original lo usó
  ```

  ```csharp C# SDK theme={null}
  using MeetKai.MKA1;
  using MeetKai.MKA1.Types.Components;

  var sdk = new SDK(
      bearerAuth: "Bearer <mka1-api-key>",
      serverUrl: "https://apigw.mka1.com"
  );

  var res = await sdk.Llm.Speech.LivekitTokenAsync(new LivekitTokenRequest()
  {
      Llm = new MeetKai.MKA1.Types.Components.Llm()
      {
          Model = "meetkai:functionary-es-mini",
      },
  });

  Console.WriteLine(res.Token);
  ```

  ```python Python SDK theme={null}
  session = sdk.llm.speech.livekit_token(
      llm={"model": "meetkai:functionary-es-mini", "conversation": {"id": "conv_abc123..."}},
  )
  ```

  ```bash bash theme={null}
  curl https://apigw.mka1.com/api/v1/llm/speech/livekit/token \
    --request POST \
    --header 'Content-Type: application/json' \
    --header 'Authorization: Bearer YOUR_API_KEY' \
    --header 'X-On-Behalf-Of: user-123' \
    --data '{
      "llm": {
        "model": "meetkai:functionary-es-mini",
        "conversation": { "id": "conv_abc123..." }
      }
    }'
  ```
</CodeGroup>

<Warning>
  Al continuar una sesión, la clave de API y el encabezado `X-On-Behalf-Of` (si se usa) deben coincidir con la sesión original. El agente de voz cifra ambos en el token de sala y los pasa a todos los servicios MKA1 aguas abajo. Si no coinciden, el agente no tendrá acceso al contexto anterior.
</Warning>

## Conectarse a una sala

Una vez que tenga un token, use el SDK de LiveKit para conectarse a la sala.

```ts theme={null}
import { Room, RoomEvent, Track } from 'livekit-client';

const room = new Room();

// Conéctese a la sala
await room.connect(session.url, session.token);

console.log('Conectado a la sala:', room.name);
```

## Enviar entrada de audio

El agente acepta entrada de audio a través de la pista de audio de la sala de LiveKit. El audio se procesa a una frecuencia de muestreo de 16kHz.

```ts theme={null}
import { createLocalAudioTrack } from 'livekit-client';

// Cree una pista de audio local desde el micrófono
const audioTrack = await createLocalAudioTrack({
  echoCancellation: true,
  noiseSuppression: true,
  autoGainControl: true
});

// Publique la pista en la sala
await room.localParticipant.publishTrack(audioTrack);
```

### Comportamiento del audio

* **Detección de Actividad de Voz (VAD)**: El VAD se maneja en el servidor por el agente MKA1, no localmente. El agente detecta automáticamente cuando deja de hablar y comienza el procesamiento.
* **Frecuencia de muestreo**: El audio se transmite a 16kHz al servicio de STT.
* **Finalización**: El agente utiliza finalización en el servidor para determinar cuándo termina el habla. No hay retraso de finalización local.

## Enviar entrada de texto

También puede enviar mensajes de texto directamente al agente sin hablar.

```ts theme={null}
// Envíe un mensaje de texto al agente
const message = JSON.stringify({
  type: 'user_message',
  content: 'What is the capital of France?'
});

await room.localParticipant.publishData(
  new TextEncoder().encode(message),
  { reliable: true, topic: 'lk.chat' }
);
```

## Recibir respuestas del agente

El agente responde de tres maneras:

1. **Salida de audio**: Voz sintetizada a través de una pista de audio
2. **Transcripción**: Texto de lo que el agente está diciendo (para subtítulos)
3. **Metadatos de respuesta**: ID de respuesta e ID de conversación a través del canal de datos

### Suscribirse a la salida de audio

```ts theme={null}
import { RoomEvent, Track } from 'livekit-client';

room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
  if (track.kind === Track.Kind.Audio && participant.identity !== room.localParticipant.identity) {
    // Esta es la salida de audio del agente
    const audioElement = track.attach();
    document.body.appendChild(audioElement);
  }
});
```

### Recibir transcripciones

El agente publica transcripciones de su discurso. Puede usarlas para subtítulos o registros.

```ts theme={null}
room.on(RoomEvent.TranscriptionReceived, (segments, participant) => {
  for (const segment of segments) {
    console.log(`Agente dijo: ${segment.text}`);
  }
});
```

### Recibir metadatos de respuesta

El agente publica el `response_id` y `conversation_id` (si aplica) cuando comienza a generar una respuesta. Guarde el `response_id` para encadenar futuras sesiones usando `previous_response_id`.

```ts theme={null}
room.on(RoomEvent.DataReceived, (payload, participant) => {
  if (participant.identity !== room.localParticipant.identity) {
    const data = JSON.parse(new TextDecoder().decode(payload));

    if (data.response_id) {
      console.log('ID de respuesta:', data.response_id);
      console.log('ID de conversación:', data.conversation_id); // presente si se usa una conversación
      // Guarde response_id para encadenar futuras sesiones con previous_response_id
    }
  }
});
```

## Continuidad de la conversación

El agente soporta conversaciones de varios turnos con memoria persistente. Cada respuesta recibe automáticamente un `response_id`, mientras que las conversaciones deben ser creadas y gestionadas explícitamente a través de la API de Conversaciones.

Hay dos formas de continuar una conversación:

**`llm.previous_response_id`** encadena una nueva sesión a una respuesta específica. El agente recibe el contexto de esa respuesta y todas las respuestas previas en la cadena. Use esto cuando:

* Desea continuar desde un punto específico en una conversación
* Está construyendo un flujo de conversación lineal
* Desea ramificar desde una respuesta específica

**`llm.conversation`** referencia una conversación creada vía la [API de Conversaciones](/es/docs/conversations). Use esto cuando:

* Necesita gestionar metadatos de la conversación (títulos, etiquetas, etc.)
* Desea listar o buscar conversaciones pasadas
* Está construyendo una interfaz de chat con historial persistente de conversación
* Múltiples clientes necesitan acceder a la misma conversación

### Iniciar una nueva sesión

```ts theme={null}
import { Room, RoomEvent } from 'livekit-client';
import { SDK } from '@meetkai/mka1';

const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

// 1. Obtenga un token para una nueva sesión
const session = await mka1.llm.speech.livekitToken({
  llm: { model: 'meetkai:functionary-es-mini' }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional

// 2. Conéctese a la sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. Rastree el ID de respuesta cuando el agente responda
let lastResponseId: string;
room.on(RoomEvent.DataReceived, (payload, participant) => {
  const data = JSON.parse(new TextDecoder().decode(payload));
  if (data.response_id) {
    lastResponseId = data.response_id;
  }
});

// 4. Tenga una conversación...
// 5. Desconéctese cuando termine
room.disconnect();
```

### Continuar desde una respuesta anterior

Use `previous_response_id` para encadenar una nueva sesión a la última respuesta, preservando el contexto de la conversación:

```ts theme={null}
// Use la misma clave de API y X-On-Behalf-Of que la sesión original
const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

// 1. Obtenga un nuevo token encadenado a la respuesta anterior
const session = await mka1.llm.speech.livekitToken({
  llm: {
    model: 'meetkai:functionary-es-mini',
    previousResponseId: lastResponseId
  }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, pero debe coincidir si la sesión original lo usó

// 2. Conéctese a la nueva sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. El agente ahora tiene contexto de la sesión anterior
// Usuario: "¿Qué te pregunté antes?"
// Agente: "Me preguntaste sobre la capital de Francia..."
```

### Continuar desde una conversación

Use `conversation_id` para continuar una conversación existente creada vía la API de Conversaciones:

```ts theme={null}
// Use la misma clave de API y X-On-Behalf-Of que la sesión original
const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

// 1. Obtenga un nuevo token con el ID de la conversación
const session = await mka1.llm.speech.livekitToken({
  llm: {
    model: 'meetkai:functionary-es-mini',
    conversation: { id: conversationId }
  }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, pero debe coincidir si la sesión original lo usó

// 2. Conéctese a la nueva sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. El agente ahora tiene contexto de todo el historial de la conversación
```

<Warning>
  Al continuar una conversación, la clave de API y el encabezado `X-On-Behalf-Of` (si se usa) deben coincidir con la sesión original. El contexto está limitado a la identidad autenticada.
</Warning>

## Manejo de desconexión

Los tokens expiran después de 5 minutos. Si necesita sesiones más largas, implemente lógica de reconexión:

```ts theme={null}
room.on(RoomEvent.Disconnected, async () => {
  console.log('Desconectado de la sala');

  // Obtenga un nuevo token (continuando desde la última respuesta)
  const newSession = await mka1.llm.speech.livekitToken({
    llm: {
      model: 'meetkai:functionary-es-mini',
      previousResponseId: savedResponseId
    }
  });

  // Reconéctese
  await room.connect(newSession.url, newSession.token);
});
```

## Ejemplo completo

Aquí tiene un ejemplo completo que lo reúne todo:

```ts theme={null}
import { Room, RoomEvent, Track, createLocalAudioTrack } from 'livekit-client';
import { SDK } from '@meetkai/mka1';

async function startVoiceSession(model: string = 'meetkai:functionary-es-mini') {
  const mka1 = new SDK({ bearerAuth: `Bearer ${YOUR_API_KEY}` });

  // Obtenga credenciales de sala
  const session = await mka1.llm.speech.livekitToken({ llm: { model } });

  // Cree y conéctese a la sala
  const room = new Room();

  let lastResponseId: string | undefined;

  // Maneje la salida de audio del agente
  room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
    if (track.kind === Track.Kind.Audio) {
      const audio = track.attach();
      document.body.appendChild(audio);
    }
  });

  // Maneje transcripciones
  room.on(RoomEvent.TranscriptionReceived, (segments) => {
    for (const segment of segments) {
      console.log('Agente:', segment.text);
    }
  });

  // Maneje metadatos de respuesta
  room.on(RoomEvent.DataReceived, (payload, participant) => {
    const data = JSON.parse(new TextDecoder().decode(payload));
    if (data.response_id) {
      lastResponseId = data.response_id;
    }
  });

  // Conéctese a la sala
  await room.connect(session.url, session.token);

  // Capture y publique el micrófono
  const audioTrack = await createLocalAudioTrack({
    echoCancellation: true,
    noiseSuppression: true
  });
  await room.localParticipant.publishTrack(audioTrack);

  // El agente lo saludará automáticamente
  // ¡Comience a hablar para interactuar!

  return { room, getLastResponseId: () => lastResponseId };
}
```

## Manejo de errores

### Errores del endpoint de token

Estos se devuelven como respuestas HTTP al solicitar un token de sala:

| Error            | Causa                                                          | Solución                                 |
| ---------------- | -------------------------------------------------------------- | ---------------------------------------- |
| 400 Bad Request  | Falta el parámetro requerido `llm.model`                       | Incluya `model` dentro del objeto `llm`  |
| 400 Bad Request  | Se especificaron ambos `previous_response_id` y `conversation` | Use solo uno, no ambos                   |
| 401 Unauthorized | Clave de API inválida o ausente                                | Verifique que su clave de API sea válida |

### Errores en sesión

Durante una sesión de voz activa, el agente publica errores a través del canal de datos de LiveKit. Escúchelos junto con los metadatos de respuesta:

```ts theme={null}
room.on(RoomEvent.DataReceived, (payload, participant) => {
  if (participant.identity === room.localParticipant.identity) return;

  const data = JSON.parse(new TextDecoder().decode(payload));

  if (data.error) {
    console.error(`[${data.error.service}] ${data.error.code}: ${data.error.message}`);
    // data.error.details puede contener información adicional de depuración
  }

  if (data.response_id) {
    lastResponseId = data.response_id;
  }
});
```

La estructura del payload de error:

```json theme={null}
{
  "error": {
    "code": "rate_limited",
    "message": "HTTP 429",
    "service": "llm",
    "details": "..."
  }
}
```

| Campo     | Descripción                                      |
| --------- | ------------------------------------------------ |
| `code`    | Código de error (vea la tabla abajo)             |
| `message` | Descripción corta del error                      |
| `service` | Qué parte del flujo falló: `llm`, `stt`, o `tts` |
| `details` | Contexto adicional para depuración (opcional)    |

**Códigos de error:**

| Código                | Servicio | Causa                                                  |
| --------------------- | -------- | ------------------------------------------------------ |
| `invalid_session`     | —        | Faltan campos de metadatos requeridos (`sub`, `llm`)   |
| `auth_error`          | —        | Falló el descifrado de credenciales del token de sala  |
| `session_error`       | —        | El agente no pudo iniciar la sesión de voz             |
| `invalid_request`     | `llm`    | Solicitud incorrecta a la API de Respuestas (HTTP 400) |
| `auth_error`          | `llm`    | Clave de API inválida (HTTP 401)                       |
| `access_denied`       | `llm`    | Permisos insuficientes (HTTP 403)                      |
| `rate_limited`        | `llm`    | Límite de tasa excedido (HTTP 429)                     |
| `service_error`       | `llm`    | Error interno del servidor (HTTP 500)                  |
| `service_unavailable` | `llm`    | Servicio upstream no disponible (HTTP 502/503)         |
| `timeout`             | `llm`    | Tiempo de espera excedido (HTTP 504)                   |
| `connection_error`    | `llm`    | Falló la conexión a la API de Respuestas               |
| `transcription_error` | `stt`    | Falló el procesamiento de reconocimiento de voz        |
| `speech_error`        | `tts`    | Falló la síntesis de texto a voz                       |

### Errores de conexión

| Problema                     | Causa                             | Solución                                                         |
| ---------------------------- | --------------------------------- | ---------------------------------------------------------------- |
| Tiempo de espera de conexión | Problemas de red o token inválido | Obtenga un token nuevo e intente de nuevo                        |
| Token expirado               | La sesión superó los 5 minutos    | Obtenga un nuevo token con `previous_response_id` para continuar |

## Próximos pasos

* Explore el [endpoint de token de LiveKit](/es/api-reference/speech/generate-livekit-room-token) en la referencia de API
* Aprenda sobre la [API de Respuestas](/es/api-reference/responses/create-an-agent-powered-response-with-tool-support) que impulsa el agente de voz
* Revise los endpoints de [TTS](/es/api-reference/speech/text-to-speech) y [STT](/es/api-reference/speech/speech-to-text-transcription) para casos de uso no en tiempo real
