> ## 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 Avançado de Voz

> Crie sessões de voz em tempo real com a API MKA1 usando LiveKit. Configure opções de LLM, ferramentas, ajuste de STT e continuidade de conversas.

A API MKA1 oferece uma interface de voz em tempo real através do LiveKit. Este guia cobre como obter um token de sala, conectar-se a uma sessão de voz, enviar áudio e texto, e capturar as respostas do agente.

## Visão geral

A integração de voz consiste em três componentes principais:

1. **Token de Sala**: Um JWT que concede acesso a uma sala LiveKit
2. **Conexão LiveKit**: Comunicação em tempo real baseada em WebRTC
3. **Agente de Voz**: Processa entrada de áudio/texto e gera respostas faladas

O pipeline do agente funciona da seguinte forma:

* **STT (Speech-to-Text)**: O áudio é transmitido via WebSocket a 16kHz e transcrito
* **LLM**: O texto transcrito é processado pela API de Respostas MKA1
* **TTS (Text-to-Speech)**: A saída do LLM é sintetizada em áudio a 24kHz

Cada requisição que o agente de voz envia para a API de Respostas inclui automaticamente `"voice_mode": "true"` no `metadata` da requisição. Isso permite distinguir respostas originadas por voz das baseadas em texto ao revisar o uso ou histórico de respostas.

## Obtendo um token de sala

Para iniciar uma sessão de voz, primeiro solicite um token de sala à API MKA1. O endpoint de token exige uma chave de API e aceita opcionalmente o cabeçalho `X-On-Behalf-Of` para identificar usuários finais. Veja [Autenticação](/pt/docs/authentication) para detalhes.

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm speech livekit-token \
    --llm '{"model":"meetkai:functionary-pt","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-pt',
      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); // Nome da 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-pt",
          Instructions = "You are a helpful voice assistant.",
      },
  });

  Console.WriteLine(res.Token);    // Token JWT
  Console.WriteLine(res.Url);      // URL WebSocket
  Console.WriteLine(res.RoomName); // Nome da 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-pt", "reasoning": {"effort": "none"}},
  )

  print(session.token)      # Token JWT
  print(session.url)        # URL WebSocket
  print(session.room_name)  # Nome da 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-pt",
        "reasoning": { "effort": "none" }
      }
    }'
  ```
</CodeGroup>

### Parâmetros

O corpo da requisição possui dois objetos de nível superior:

```json theme={null}
{
  "llm": { ... },   // Obrigatório — configuração do LLM
  "stt": { ... }    // Opcional — ajuste do speech-to-text
}
```

#### `llm` — configuração do LLM (obrigatório)

O objeto `llm` aceita os mesmos campos do corpo da requisição da [API de Respostas](/pt/api-reference/responses/create-an-agent-powered-response-with-tool-support), exceto campos gerenciados pelo agente de voz (`input`, `stream`, `store`, `background`).

| Campo                  | Obrigatório | Descrição                                                                                                                                                |
| ---------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`                | Sim         | Modelo LLM a ser usado (ex: `meetkai:functionary-pt`)                                                                                                    |
| `instructions`         | Não         | Instruções de sistema personalizadas para o agente                                                                                                       |
| `previous_response_id` | Não         | Encadeia esta sessão a uma resposta específica de uma sessão anterior                                                                                    |
| `conversation`         | Não         | Continua uma conversa existente — passe `{ "id": "conv_abc123..." }` ou o ID da conversa como string                                                     |
| `tools`                | Não         | Array de definições de ferramentas (function, web\_search, file\_search, etc.)                                                                           |
| `tool_choice`          | Não         | Como o modelo seleciona ferramentas (`"auto"`, `"none"`, `"required"` ou uma ferramenta específica)                                                      |
| `parallel_tool_calls`  | Não         | Permitir execução paralela de ferramentas                                                                                                                |
| `max_tool_calls`       | Não         | Número máximo de chamadas de ferramenta por resposta (padrão: 30)                                                                                        |
| `temperature`          | Não         | Temperatura de amostragem (ex: `0.7`)                                                                                                                    |
| `max_output_tokens`    | Não         | Máximo de tokens na resposta                                                                                                                             |
| `reasoning`            | Não         | Configuração de raciocínio (ex: `{ "effort": "high" }`). Defina `{ "effort": "none" }` para sessões de voz para minimizar latência — veja a nota abaixo. |
| `top_p`                | Não         | Parâmetro de amostragem nucleus                                                                                                                          |
| `presence_penalty`     | Não         | Penalidade de presença para repetição de tokens                                                                                                          |
| `frequency_penalty`    | Não         | Penalidade de frequência para repetição de tokens                                                                                                        |
| `truncation`           | Não         | `"auto"` ou `"disabled"` — controla truncamento de contexto                                                                                              |
| `context_management`   | Não         | Estratégias de gerenciamento de contexto para truncamento de conversas                                                                                   |
| `service_tier`         | Não         | `"auto"`, `"default"`, `"flex"` ou `"priority"`                                                                                                          |
| `prompt`               | Não         | Referência a um template de prompt e suas variáveis                                                                                                      |
| `text`                 | Não         | Configuração de saída de texto (formato, verbosidade)                                                                                                    |
| `metadata`             | Não         | Metadados chave-valor passados para a API de Respostas                                                                                                   |

Você não pode especificar ambos `previous_response_id` e `conversation`.

<Note>
  Os metadados do token são incorporados em um JWT, que é passado como cabeçalho HTTP. Mantenha o payload total de `llm` abaixo de \~8 KB — arrays grandes de `tools` podem precisar ser reduzidos.
</Note>

<Tip>
  Para sessões de voz, desative o raciocínio configurando `"reasoning": { "effort": "none" }`. O raciocínio adiciona tempo de reflexão antes da resposta do modelo, o que aumenta a latência e cria pausas perceptíveis na conversa. Desativando-o, as respostas ficam rápidas e naturais.
</Tip>

#### `stt` — configuração de Speech-to-Text (opcional)

Controla a detecção de atividade de voz (VAD) e o comportamento de endpointing no servidor.

| Campo                        | Obrigatório | Descrição                                                      |
| ---------------------------- | ----------- | -------------------------------------------------------------- |
| `silence_timeout_ms`         | Não         | Milissegundos de silêncio antes de finalizar a fala (100–5000) |
| `initial_silence_timeout_ms` | Não         | Timeout antes de qualquer fala ser detectada (1000–30000)      |

### Configuração avançada

Você pode passar ferramentas, instruções personalizadas e ajuste de STT em uma única requisição de token:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm speech livekit-token \
    --llm '{
      "model": "meetkai:functionary-pt",
      "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-pt',
      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-pt",
          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-pt",
          "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-pt",
        "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>

### Resposta

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

| Campo      | Descrição                                                                                         |
| ---------- | ------------------------------------------------------------------------------------------------- |
| `token`    | Token de acesso JWT (TTL de 5 minutos) com permissões de entrada, publicação e assinatura na sala |
| `url`      | URL WebSocket do LiveKit para conectar                                                            |
| `roomName` | UUID gerado automaticamente para esta sessão                                                      |

O token inclui metadados que o agente de voz usa para configurar a sessão.

### Continuando uma sessão

Para continuar a partir de uma resposta anterior:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm speech livekit-token \
    --llm '{"model":"meetkai:functionary-pt","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-pt',
      previousResponseId: 'resp_abc123...'
    }
  }, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, mas deve coincidir se a sessão original usou
  ```

  ```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-pt",
          PreviousResponseId = "resp_abc123...",
      },
  });

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

  ```python Python SDK theme={null}
  session = sdk.llm.speech.livekit_token(
      llm={"model": "meetkai:functionary-pt", "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-pt",
        "previous_response_id": "resp_abc123..."
      }
    }'
  ```
</CodeGroup>

Para continuar uma conversa existente:

<CodeGroup>
  ```bash CLI theme={null}
  mka1 llm speech livekit-token \
    --llm '{"model":"meetkai:functionary-pt","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-pt',
      conversation: { id: 'conv_abc123...' }
    }
  }, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, mas deve coincidir se a sessão original usou
  ```

  ```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-pt",
      },
  });

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

  ```python Python SDK theme={null}
  session = sdk.llm.speech.livekit_token(
      llm={"model": "meetkai:functionary-pt", "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-pt",
        "conversation": { "id": "conv_abc123..." }
      }
    }'
  ```
</CodeGroup>

<Warning>
  Ao continuar uma sessão, a chave de API e o cabeçalho `X-On-Behalf-Of` (se usado) devem coincidir com a sessão original. O agente de voz criptografa ambos no token da sala e os repassa para todos os serviços MKA1 subsequentes. Se não coincidirem, o agente não terá acesso ao contexto anterior.
</Warning>

## Conectando-se a uma sala

Depois de obter um token, use o SDK do LiveKit para conectar-se à sala.

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

const room = new Room();

// Conectar à sala
await room.connect(session.url, session.token);

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

## Enviando entrada de áudio

O agente aceita entrada de áudio através do track de áudio da sala LiveKit. O áudio é processado a 16kHz.

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

// Crie um track de áudio local do microfone
const audioTrack = await createLocalAudioTrack({
  echoCancellation: true,
  noiseSuppression: true,
  autoGainControl: true
});

// Publique o track na sala
await room.localParticipant.publishTrack(audioTrack);
```

### Comportamento do áudio

* **Detecção de Atividade de Voz (VAD)**: O VAD é tratado no servidor pelo agente MKA1, não localmente. O agente detecta automaticamente quando você para de falar e inicia o processamento.
* **Taxa de amostragem**: O áudio é transmitido a 16kHz para o serviço de STT.
* **Endpointing**: O agente usa endpointing no servidor para determinar quando a fala termina. Não há atraso de endpointing local.

## Enviando entrada de texto

Você também pode enviar mensagens de texto diretamente ao agente sem falar.

```ts theme={null}
// Envie uma mensagem de texto para o agente
const message = JSON.stringify({
  type: 'user_message',
  content: 'Qual é a capital da França?'
});

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

## Recebendo respostas do agente

O agente responde de três formas:

1. **Saída de áudio**: Fala sintetizada via track de áudio
2. **Transcrição**: Texto do que o agente está dizendo (para legendas)
3. **Metadados da resposta**: ID da resposta e ID da conversa via data channel

### Inscrevendo-se na saída de áudio

```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 é a saída de áudio do agente
    const audioElement = track.attach();
    document.body.appendChild(audioElement);
  }
});
```

### Recebendo transcrições

O agente publica transcrições de sua fala. Você pode usá-las para legendas ou registro.

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

### Recebendo metadados da resposta

O agente publica o `response_id` e o `conversation_id` (se aplicável) quando começa a gerar uma resposta. Salve o `response_id` para encadear sessões futuras 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 da resposta:', data.response_id);
      console.log('ID da conversa:', data.conversation_id); // presente se estiver usando uma conversa
      // Salve o response_id para encadear sessões futuras com previous_response_id
    }
  }
});
```

## Continuidade de conversas

O agente suporta conversas de múltiplas interações com memória persistente. Cada resposta recebe automaticamente um `response_id`, enquanto conversas precisam ser explicitamente criadas e gerenciadas pela API de Conversas.

Há duas formas de continuar uma conversa:

**`llm.previous_response_id`** encadeia uma nova sessão a uma resposta específica. O agente recebe o contexto daquela resposta e todas as anteriores na cadeia. Use quando:

* Você deseja continuar de um ponto específico em uma conversa
* Está construindo um fluxo de conversa linear
* Deseja ramificar a partir de uma resposta específica

**`llm.conversation`** referencia uma conversa criada via a [API de Conversas](/pt/docs/conversations). Use quando:

* Precisa gerenciar metadados da conversa (títulos, tags, etc.)
* Deseja listar ou buscar conversas passadas
* Está construindo uma interface de chat com histórico persistente
* Múltiplos clientes precisam acessar a mesma conversa

### Iniciando uma nova sessão

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

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

// 1. Obtenha um token para uma nova sessão
const session = await mka1.llm.speech.livekitToken({
  llm: { model: 'meetkai:functionary-pt' }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional

// 2. Conecte-se à sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. Acompanhe o ID da resposta quando o agente responder
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. Converse...
// 5. Desconecte ao finalizar
room.disconnect();
```

### Continuando de uma resposta anterior

Use `previous_response_id` para encadear uma nova sessão à última resposta, preservando o contexto da conversa:

```ts theme={null}
// Use a mesma chave de API e X-On-Behalf-Of da sessão original
const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

// 1. Obtenha um novo token encadeado à resposta anterior
const session = await mka1.llm.speech.livekitToken({
  llm: {
    model: 'meetkai:functionary-pt',
    previousResponseId: lastResponseId
  }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, mas deve coincidir se a sessão original usou

// 2. Conecte-se à nova sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. O agente agora tem contexto da sessão anterior
// Usuário: "O que eu te perguntei antes?"
// Agente: "Você perguntou sobre a capital da França..."
```

### Continuando de uma conversa

Use `conversation_id` para continuar uma conversa existente criada via a API de Conversas:

```ts theme={null}
// Use a mesma chave de API e X-On-Behalf-Of da sessão original
const mka1 = new SDK({
  bearerAuth: `Bearer ${YOUR_API_KEY}`,
});

// 1. Obtenha um novo token com o ID da conversa
const session = await mka1.llm.speech.livekitToken({
  llm: {
    model: 'meetkai:functionary-pt',
    conversation: { id: conversationId }
  }
}, { headers: { 'X-On-Behalf-Of': 'user-123' } }); // Opcional, mas deve coincidir se a sessão original usou

// 2. Conecte-se à nova sala
const room = new Room();
await room.connect(session.url, session.token);

// 3. O agente agora tem contexto de todo o histórico da conversa
```

<Warning>
  Ao continuar uma conversa, a chave de API e o cabeçalho `X-On-Behalf-Of` (se usado) devem coincidir com a sessão original. O contexto é restrito à identidade autenticada.
</Warning>

## Lidando com desconexão

Tokens expiram após 5 minutos. Se precisar de sessões mais longas, implemente lógica de reconexão:

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

  // Obtenha um novo token (continuando da última resposta)
  const newSession = await mka1.llm.speech.livekitToken({
    llm: {
      model: 'meetkai:functionary-pt',
      previousResponseId: savedResponseId
    }
  });

  // Reconecte
  await room.connect(newSession.url, newSession.token);
});
```

## Exemplo completo

Veja um exemplo completo integrando tudo:

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

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

  // Obtenha credenciais da sala
  const session = await mka1.llm.speech.livekitToken({ llm: { model } });

  // Crie e conecte à sala
  const room = new Room();

  let lastResponseId: string | undefined;

  // Lide com saída de áudio do agente
  room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
    if (track.kind === Track.Kind.Audio) {
      const audio = track.attach();
      document.body.appendChild(audio);
    }
  });

  // Lide com transcrições
  room.on(RoomEvent.TranscriptionReceived, (segments) => {
    for (const segment of segments) {
      console.log('Agente:', segment.text);
    }
  });

  // Lide com metadados da resposta
  room.on(RoomEvent.DataReceived, (payload, participant) => {
    const data = JSON.parse(new TextDecoder().decode(payload));
    if (data.response_id) {
      lastResponseId = data.response_id;
    }
  });

  // Conecte-se à sala
  await room.connect(session.url, session.token);

  // Capture e publique o microfone
  const audioTrack = await createLocalAudioTrack({
    echoCancellation: true,
    noiseSuppression: true
  });
  await room.localParticipant.publishTrack(audioTrack);

  // O agente irá cumprimentá-lo automaticamente
  // Comece a falar para interagir!

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

## Tratamento de erros

### Erros do endpoint de token

São retornados como respostas HTTP ao solicitar um token de sala:

| Erro             | Causa                                                       | Solução                                |
| ---------------- | ----------------------------------------------------------- | -------------------------------------- |
| 400 Bad Request  | Parâmetro obrigatório `llm.model` ausente                   | Inclua `model` dentro do objeto `llm`  |
| 400 Bad Request  | Ambos `previous_response_id` e `conversation` especificados | Use apenas um, não ambos               |
| 401 Unauthorized | Chave de API inválida ou ausente                            | Verifique se sua chave de API é válida |

### Erros durante a sessão

Durante uma sessão de voz ativa, o agente publica erros via data channel do LiveKit. Ouça-os junto com os metadados de resposta:

```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 pode conter informações adicionais para depuração
  }

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

A estrutura do payload de erro:

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

| Campo     | Descrição                                            |
| --------- | ---------------------------------------------------- |
| `code`    | Código do erro (veja tabela abaixo)                  |
| `message` | Descrição curta do erro                              |
| `service` | Qual parte do pipeline falhou: `llm`, `stt` ou `tts` |
| `details` | Contexto adicional para depuração (opcional)         |

**Códigos de erro:**

| Código                | Serviço | Causa                                                    |
| --------------------- | ------- | -------------------------------------------------------- |
| `invalid_session`     | —       | Campos obrigatórios de metadados ausentes (`sub`, `llm`) |
| `auth_error`          | —       | Falha ao descriptografar credenciais do token da sala    |
| `session_error`       | —       | Falha ao iniciar a sessão de voz pelo agente             |
| `invalid_request`     | `llm`   | Requisição inválida para a API de Respostas (HTTP 400)   |
| `auth_error`          | `llm`   | Chave de API inválida (HTTP 401)                         |
| `access_denied`       | `llm`   | Permissões insuficientes (HTTP 403)                      |
| `rate_limited`        | `llm`   | Limite de requisições excedido (HTTP 429)                |
| `service_error`       | `llm`   | Erro interno do servidor (HTTP 500)                      |
| `service_unavailable` | `llm`   | Serviço indisponível (HTTP 502/503)                      |
| `timeout`             | `llm`   | Tempo limite excedido (HTTP 504)                         |
| `connection_error`    | `llm`   | Falha ao conectar à API de Respostas                     |
| `transcription_error` | `stt`   | Falha no processamento de speech-to-text                 |
| `speech_error`        | `tts`   | Falha na síntese de text-to-speech                       |

### Erros de conexão

| Problema           | Causa                               | Solução                                                         |
| ------------------ | ----------------------------------- | --------------------------------------------------------------- |
| Timeout de conexão | Problemas de rede ou token inválido | Obtenha um novo token e tente novamente                         |
| Token expirado     | Sessão excedeu 5 minutos            | Obtenha um novo token com `previous_response_id` para continuar |

## Próximos passos

* Explore o [endpoint de token do LiveKit](/pt/api-reference/speech/generate-livekit-room-token) na referência da API
* Saiba mais sobre a [API de Respostas](/pt/api-reference/responses/create-an-agent-powered-response-with-tool-support) que alimenta o agente de voz
* Revise os endpoints de [TTS](/pt/api-reference/speech/text-to-speech) e [STT](/pt/api-reference/speech/speech-to-text-transcription) para casos de uso não em tempo real
