Files
tia_regional_xai_tts_pool/docs/api-overview.md
2026-08-21 08:37:51 -03:00

31 KiB

API Overview

Status

Documento vivo. Descreve o estado atual da API e sera atualizado ao longo do refactor.

O que esta API faz

Esta API implementa um fluxo de voz-to-voz para TIA:

  • recebe conexao websocket do cliente
  • recebe audio PCM do cliente
  • encaminha audio para o agent via LiveKit
  • transcreve, processa a pipeline de negocio e sintetiza resposta
  • devolve audio para o cliente
  • finaliza a chamada e exporta resultado

Componentes principais

Bridge

Arquivo principal:

  • app/ws_gateway/main.py
  • app/ws_gateway/voice_client.html

Responsabilidades:

  • aceitar conexao em /ws/agent
  • receber start
  • montar contexto da chamada
  • despachar agent para uma room LiveKit
  • mandar audio do cliente para LiveKit
  • devolver audio do agent para o cliente
  • encerrar websocket ao receber DONE
  • servir um cliente web de teste em /voice-client

Agent

Arquivos principais:

  • app/livekit/main.py
  • app/livekit/runtime/call_runtime.py
  • app/livekit/runtime/state.py
  • app/livekit/runtime/commands.py
  • app/livekit/runtime/command_executor.py
  • app/livekit/runtime/scheduler.py
  • app/livekit/policies/
  • app/livekit/adapters/

Responsabilidades:

  • entrar na room do LiveKit
  • configurar STT, VAD e TTS
  • receber transcricao final do usuario
  • chamar o backend de IA configurado
  • vocalizar resposta
  • tratar interrupcao, idle nudge e finalizacao

Organizacao atual:

  • main.py faz o wiring do agent e das dependencias
  • CallRuntime coordena o ciclo de vida da chamada
  • CallState concentra o estado mutavel da sessao
  • commands.py define os comandos internos para side effects
  • RuntimeCommandExecutor executa bridge, export, speech, pipeline e start da sessao
  • TimerScheduler concentra os timers nomeados do runtime
  • InterruptPolicy, IdlePolicy e FinalizationPolicy concentram regras operacionais da chamada
  • adapters encapsulam acesso ao backend de IA, speech, bridge e export

Pipeline de negocio

Backends disponiveis no runtime websocket:

  • remote_ws:
    • app/livekit/adapters/remote_agent_ws_adapter.py
  • remote_sse:
    • app/livekit/adapters/remote_agent_sse_adapter.py

Responsabilidades:

  • preparar dados de atendimento
  • controlar os estagios da chamada ou delegar esse controle ao agent remoto
  • gerar a resposta textual por etapa
  • registrar metadados e finalizar a conversa

Selecao atual:

  • AGENT_BACKEND=remote_ws envia cada turno transcrito para REMOTE_AGENT_WS_URL
  • AGENT_BACKEND=remote_sse usa contratos especificos por agente:
    • conta usa GET /agent/sse para inicializar a sessao e POST /agent/sse para executar cada acao
    • oferta usa POST /agent/execute por turno e consome os eventos SSE schedule_message, message e done
  • AGENT_BACKEND=remote_ws_fake usa um fake interno no proprio processo, sem abrir websocket local
  • o fluxo do runtime local continua o mesmo: STT produz texto, o backend de IA devolve texto e o TTS vocaliza
  • o roteamento entre agentes acontece pelo campo data.agent
  • quando AGENT_BACKEND=remote_ws, o adapter escolhe a URL por agent se existirem:
    • REMOTE_AGENT_WS_URL_CONTA
    • REMOTE_AGENT_WS_URL_OFERTA
    • REMOTE_AGENT_WS_URL_COBRANCA ou REMOTE_AGENT_WS_URL_COBRA
  • quando AGENT_BACKEND=remote_sse, o adapter exige a URL especifica do agent, sem fallback generico:
    • REMOTE_AGENT_SSE_URL_CONTA
    • REMOTE_AGENT_SSE_URL_OFERTA
    • REMOTE_AGENT_SSE_URL_COBRANCA ou REMOTE_AGENT_SSE_URL_COBRA

Contrato remoto atual por turno:

  • timestamp
  • agent
  • RouterCallKeyDay
  • RouterCallKey
  • ANI
  • GSM
  • callIdGed
  • ID_FATURA somente para agent=conta
  • text
  • protocol
  • stage

Contrato especifico atual de conta:

  • request:
    • na abertura da sessao, o transporte envia query string com ani, channelId e uraCallId
    • action: "chat"
    • payload.message
    • payload.message_id no transporte SSE de conta, com o mesmo UUID enviado na query string
    • payload.channel no transporte websocket
    • payload.interruption e payload.events quando existirem
  • response:
    • type: "ready" para a primeira fala ou fala que abre uma janela de resposta do cliente
    • a fala de ready nao e interrompivel; se o cliente falar enquanto o audio do ready ainda estiver tocando, a transcricao e descartada e registrada em log
    • apos o fim do audio de ready, o runtime mantem uma janela protegida de 750ms para absorver atraso de playback do cliente; fala iniciada nessa janela tambem e descartada
    • depois dessa janela protegida, o runtime passa a esperar resposta do cliente
    • type: "result"
    • action: "chat"
    • result.type: "final" para respostas intermediarias
    • result.content como texto para TTS
    • type: "feedback" ou result.type: "feedback" para mensagens de acompanhamento enquanto o backend continua processando o turno atual
    • a fala de feedback nao e interrompivel, nao abre novo turno, nao espera resposta do cliente e nao arma timeout de silencio do cliente
    • se o cliente falar durante feedback, o runtime descarta a transcricao, registra a tentativa em log e continua aguardando a resposta final do backend
    • feedback nao dispara metadata.wait_retry_messages, como mensagens do tipo "Voce esta ai?"
    • para finalizacoes esperadas, o runtime aguarda uma janela curta de silencio estavel do cliente, fala o result.content e depois envia stop; se uma nova transcricao final chegar durante a espera, a finalizacao anterior e descartada em favor do novo turno
    • falas de finalizacao esperada (resolvido, nao_resolvido, erro_no_match etc.) nao sao interrompiveis; qualquer fala do cliente durante a finalizacao e descartada
    • nesses casos o runtime nao chama finalizacao remota adicional (end/end_service_once)
    • quando o ready vier com metadata.wait_timeout_seconds, o runtime aguarda esse tempo apos falar a mensagem de ready
    • se tambem vier metadata.wait_retry_messages, o runtime fala cada item do array a cada novo estouro de wait_timeout_seconds; depois do ultimo item, aguarda mais um intervalo igual e envia stop_silencio_longo com reason: "no_user_response"
    • sem metadata.wait_retry_messages, o comportamento continua sendo encerrar direto no primeiro estouro de wait_timeout_seconds
    • enquanto aguarda processamento depois do fim de fala do usuario, o runtime toca somente o audio local longo de conforto; no padrao atual, usa intervalo de 12s e no maximo 6 vezes
    • se a resposta do backend remoto nao chegar em 300s, o runtime encerra com stop_agent_backend_unavailable
  • se o participante do agent LiveKit desconectar sem DONE, o bridge tenta redispatch na mesma room; se o agent nao voltar, encerra com stop_agent_runtime_unavailable

Mapeamento de finalizacoes esperadas de conta:

  • result.type: "resolvido" -> stop_resolvido_e_finalizado
  • result.type: "nao_resolvido" -> stop_nao_resolvido
  • result.type: "resolvido_outros_assuntos" -> stop_outro_assunto
  • result.type: "outros_assuntos" -> stop_outro_assunto
  • result.type: "erro_falha_sistema" -> stop_falha_sistema
  • result.type: "erro_no_match" -> stop_no_match

Contrato SSE atual de conta:

  • o runtime guarda o session_id retornado pelo backend nos eventos ready
  • abre GET /agent/sse?msisdn=...&invoice_id=...&ani=...&protocol_id=...&session_id=...&message_id=...&channelId=ura&uraCallId=... no prepare
  • consome o stream de inicializacao ate ready e ate o termino do prefetch (prefetch_done, prefetch_skipped ou prefetch_failed) ou fechamento da resposta
  • envia cada turno com POST /agent/sse?session_id=...&ani=...&protocol_id=...&message_id=...&channelId=ura&uraCallId=...
  • message_id e um UUID gerado por turno e independente de session_id; nos turnos de acao, session_id continua sendo o identificador de sessao retornado pelo backend de contas
  • body do POST:
    • action: "chat"
    • payload.message
    • payload.message_id com o mesmo UUID do parametro message_id da query string
    • payload.interruption e payload.events quando existirem
  • a resposta do proprio POST e text/event-stream; o runtime consome ready, progress, result e error ate receber o resultado da acao

Contrato SSE atual de oferta:

  • prepare nao abre stream remoto; o primeiro turno do agente chama POST /agent/execute
  • o turno inicial envia message: "inicio_atendimento" para o backend remoto produzir a primeira fala
  • cada turno usa body:
    • messageId gerado por turno como UUID
    • message com inicio_atendimento no primeiro turno ou a transcricao do cliente nos demais
    • context.protocolNumber e context.protocolo vindos de data.protocolo
    • context.gsm vindo de data.gsm
    • context.uraId vindo de data.callIdGed
    • context.callIdGed, context.ani, context.routerCallKey, context.routerCallKeyDay, context.agent e context.assetId quando existirem
  • context.sessionId e enviado a partir de data.session_id/data.sessionId recebido no start; protocolo, callIdGed e routerCallKey nao sao usados como fallback de sessionId
  • headers enviados: Accept: text/event-stream, Content-Type: application/json e Channel-id vindo de channelId ou ura
  • eventos recebidos:
    • schedule_message: fala imediatamente scheduledMessage.message
    • message: fala response
    • done: encerra o stream do turno; a chamada so e finalizada quando o status recebido indicar encerramento terminal
  • de/para do done.additionalInformations.service_status:
    • RESOLVED -> stop_resolvido_e_finalizado
    • UNRESOLVED ou ausente -> stop_nao_resolvido
    • RESOLVED_WITH_NEW_REQUEST -> nao envia stop; a conversa permanece aberta para o novo assunto
  • done.status=transferred e reconhecido, mas ainda nao orquestra transferencia; nesta versao encerra como nao resolvido e preserva metadados de handover para evolucao futura

Endpoint dev fornecido para oferta:

  • host: https://agt-ai-atendimento-ofertas-dev.internal.timbrasil.com.br
  • execute: https://agt-ai-atendimento-ofertas-dev.internal.timbrasil.com.br/agent/execute
  • health: nao configurado por enquanto; a readiness nao deve derivar nem chamar /health para oferta ate essa rota ser fornecida
  • enquanto o certificado interno nao estiver confiavel no ambiente local, REMOTE_AGENT_SSE_TLS_VERIFY_OFERTA=0 permite testar o fluxo ignorando a validacao TLS do httpx
  • pod de referencia: tim-ai-atend-agnt-sales-65764fcf8d-xpzmb
  • IP de referencia: http://10.153.35.23
  • portas: 80:31332/TCP, 443:30635/TCP

Origem desses campos:

  • o bridge extrai esses valores do data recebido em WS /ws/agent
  • o agent local apenas reaproveita esse contexto para chamar o websocket remoto
  • protocol/protocolo e identificador de negocio; session_id e identificador explicito de sessao recebido no start. O runtime nao preenche session_id com protocolo, callIdGed ou routerCallKey.

Configuracao opcional por chamada:

  • o cliente pode enviar callConfig no start, mas esse bloco e opcional
  • objetivo atual: testes, homologacao e overrides tecnicos por sessao
  • callConfig.agentBackend faz override do backend websocket para aquela chamada
  • callConfig.agentBackend tambem pode selecionar o transporte SSE para a sessao
  • callConfig.stt pode ajustar provider, initialPrompt, configOverride e minProbSingleWord
  • callConfig.tts pode ajustar provider, voiceId e modelId
  • valores aceitos hoje em callConfig.agentBackend:
    • remote_ws
    • remote_sse
    • remote_ws_fake
  • hoje os providers suportados no agent local sao:
    • STT: internal_http (Sofya Batch no cliente de teste), fake
    • TTS: elevenlabs, azure, xai, fake

Endpoints atuais

GET /health

Retorna status simples de saude do bridge.

GET /health/resources

Retorna o deep health do bridge com validacao dos recursos usados pelo WS /ws/agent.

Comportamento:

  • 200 quando os checks obrigatorios estao saudaveis
  • 503 quando algum recurso obrigatorio falha
  • inclui active_connections, max_connections, cached, failed_resources e checks
  • os checks atuais cobrem:
    • agent_runtime
    • agent_backend
    • stt
    • tts

GET /health/services

Retorna o health consolidado dos servicos do TIA no formato consumido pela esteira de operacao.

Comportamento:

  • 200 quando todos os servicos monitorados estao saudaveis
  • 503 quando algum servico monitorado falha
  • status no corpo retorna ok ou fail
  • htp_cod_status preserva o status HTTP retornado pelo health do servico quando houver resposta HTTP
  • hhtp_cod_desc preserva a descricao retornada pelo health quando houver; se nao houver descricao no corpo, usa a reason phrase HTTP
  • ignora AGENT_BACKEND=remote_ws_fake e usa as rotas reais configuradas nos envs dos servicos
  • checks cobre apenas os servicos atualmente monitorados:
    • agent_runtime
    • agent_backend.contas: REMOTE_AGENT_HEALTH_URL_CONTA, REMOTE_AGENT_HEALTH_URL_CONTAS ou REMOTE_AGENT_HEALTH_URL
    • agent_backend.oferta: REMOTE_AGENT_HEALTH_URL_OFERTA, REMOTE_AGENT_HEALTH_URL_OFERTAS ou health derivado de REMOTE_AGENT_SSE_URL_OFERTA
    • stt.sofya: STT_HEALTH_URL ou health derivado de STT_URL
    • tts.xAI: provider xAI com XAI_WEBSOCKET_URL/XAI_API_KEY

Para checks sem resposta HTTP, como falha de conexao ou probe por WebSocket, a rota usa fallback 200/500 e a mensagem interna do erro ou sucesso.

Formato:

{
  "status": "ok",
  "checks": {
    "agent_runtime": {
      "htp_cod_status": 200,
      "hhtp_cod_desc": "SUCCESS"
    },
    "agent_backend": {
      "contas": {
        "htp_cod_status": 200,
        "hhtp_cod_desc": "SUCCESS"
      },
      "oferta": {
        "htp_cod_status": 200,
        "hhtp_cod_desc": "SUCCESS"
      }
    },
    "stt": {
      "sofya": {
        "htp_cod_status": 200,
        "hhtp_cod_desc": "SUCCESS"
      }
    },
    "tts": {
      "xAI": {
        "htp_cod_status": 200,
        "hhtp_cod_desc": "SUCCESS"
      }
    }
  }
}

GET /voice-client

Cliente web de teste para capturar microfone, enviar audio para WS /ws/agent, reproduzir o audio do agent e configurar STT, TTS e AGENT por chamada.

WS /fake-agent/ws

Websocket fake para homologacao local do backend remoto.

Uso esperado:

  • usar apenas para homologacao manual do contrato websocket fake
  • manter agent=conta|oferta|cobranca
  • deixar um cliente websocket externo chamar o fake diretamente quando precisar testar esse endpoint

Comportamento:

  • suporta o contrato conta com action/payload
  • suporta o contrato generico com text/stage
  • responde com progressao simples de stages
  • encerra quando recebe textos como encerrar, obrigado ou tchau

WS /ws/agent

Fluxo principal de voz:

  1. cliente conecta
  2. envia mensagem start
  3. bridge valida capacidade e readiness dos recursos obrigatorios
  4. recebe ready ou stop
  5. se receber ready, envia audio binario
  6. recebe audio binario de resposta
  7. recebe stop ao fim da chamada ou em falha terminal

Contrato de inicio da chamada type=start:

  • a primeira mensagem de inicio deve ser um JSON textual com type: "start"; opcionalmente, transferencia_session_id pode chegar antes dela para informar somente o session_id
  • os campos de negocio devem ser enviados em data
  • chaves canonicas obrigatorias em data:
    • agent
    • ani
    • gsm
    • session_id
    • routerCallKey
    • routerCallKeyDay
    • callIdGed
  • chaves obrigatorias por agente:
    • agentData.idFatura quando agent=conta
    • protocolo quando agent=oferta
  • valores canonicos recomendados para agent:
    • conta
    • oferta
    • cobranca

Exemplo recomendado de start para conta:

{
  "type": "start",
  "data": {
    "agent": "conta",
    "ani": "5511999990000",
    "gsm": "5511999990000",
    "session_id": "550e8400-e29b-41d4-a716-446655440000",
    "routerCallKeyDay": "20260409",
    "routerCallKey": "RCK-001",
    "callIdGed": "GED-123456",
    "agentData": {
      "idFatura": "FAT-123"
    }
  },
  "audioFormat": {
    "encoding": "linear16",
    "sampleRateHz": 16000,
    "channels": 1
  },
  "callConfig": {
    "agentBackend": "remote_ws"
  }
}

Exemplo recomendado de start para oferta:

{
  "type": "start",
  "data": {
    "agent": "oferta",
    "ani": "5511999990000",
    "gsm": "5511999990000",
    "session_id": "550e8400-e29b-41d4-a716-446655440000",
    "routerCallKeyDay": "20260409",
    "routerCallKey": "RCK-001",
    "callIdGed": "GED-123456",
    "protocolo": "PRT-20260409-0001"
  },
  "audioFormat": {
    "encoding": "linear16",
    "sampleRateHz": 16000,
    "channels": 1
  },
  "callConfig": {
    "agentBackend": "remote_sse"
  }
}

Mensagem opcional aceita antes do start em cenarios de transferencia:

{
  "type": "transferencia_session_id",
  "data": {
    "session_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

Se essa mensagem chegar antes do start, o bridge usa esse valor apenas para preencher data.session_id quando o start ainda nao trouxer o campo. Em producao, o caminho recomendado e enviar session_id diretamente dentro de data no start.

Contrato de audio:

  • apos o ready, o cliente deve enviar audio binario bruto em PCM16/LINEAR16, 16000 Hz, 1 canal
  • o ready informa os parametros operacionais atuais do bridge:
    • session_id: eco do data.session_id aceito para a chamada
    • sample_rate: 16000
    • channels: 1
    • frame_ms: 20
    • bytes_per_frame: 640
  • no contrato atual, ready significa que o bridge esta pronto para receber os bytes de audio do cliente
  • quando agent_starts_conversation esta habilitado, o agent mantem a entrada do usuario desativada no RoomIO desde antes do StartSession ate o fim da primeira mensagem; esse gate impede que fala ou backlog vindo da URA alcance VAD/STT durante o setup e a saudacao
  • o gate e liberado no fim do primeiro turno do agent e tambem em falha do pipeline, finalizacao ou timeout de setup; chamadas em que o usuario inicia a conversa nao usam esse bloqueio
  • audioFormat no start e opcional e hoje funciona como campo informativo/reservado
  • enviar outro codec, sample rate ou numero de canais nesse campo nao reconfigura o bridge atualmente
  • se houver necessidade de outro formato, homologar com a equipe de desenvolvimento antes da integracao

Mensagens devolvidas pelo servidor:

  • ready quando a sessao foi aceita e o bridge esta pronto para receber audio
  • audio binario PCM16 durante a resposta do agent
  • stop como mensagem terminal em qualquer encerramento do WS /ws/agent

Recuperacao do participante LiveKit do agent:

  • quando o participant identificado como agent desconecta e a chamada ainda nao terminou, o bridge faz redispatch do mesmo AGENT_NAME na mesma room
  • quando o novo participant entra, o bridge reenvia o controle client_audio_enabled e passa a consumir o audio desse novo participant
  • se o redispatch nao trouxer um novo agent dentro do timeout configurado, o bridge envia stop_agent_runtime_unavailable com reason: "agent_disconnected"

Contrato de stop:

  • toda mensagem terminal em WS /ws/agent usa:
{
  "type": "stop",
  "data": {}
}
  • bloqueio antes do ready:
{
  "type": "stop",
  "data": {
    "status": "stop_stt_unavailable",
    "reason": "resource_unhealthy",
    "resource": "stt",
    "failed_resources": ["stt"],
    "phase": "pre_ready"
  }
}
  • falha de recurso durante a sessao:
{
  "type": "stop",
  "data": {
    "status": "stop_agent_backend_unavailable",
    "reason": "resource_unhealthy",
    "resource": "agent_backend",
    "failed_resources": ["agent_backend"],
    "phase": "in_session"
  }
}
  • fim normal da chamada:
{
  "type": "stop",
  "data": {
    "status": "stop_resolvido_e_finalizado",
    "reason": "stage_done",
    "phase": "in_session"
  }
}
  • falha terminal durante a sessao:
{
  "type": "stop",
  "data": {
    "status": "stop_bridge_failed",
    "reason": "bridge_failed",
    "resource": "bridge",
    "phase": "in_session"
  }
}

Status terminais atualmente usados em WS /ws/agent:

  • stop_capacity_tia
  • stop_agent_runtime_unavailable
  • stop_agent_backend_unavailable
  • stop_stt_unavailable
  • stop_tts_unavailable
  • stop_resolvido_e_finalizado
  • stop_nao_resolvido
  • stop_falha_sistema
  • stop_no_match
  • stop_outro_assunto
  • stop_silencio_longo
  • stop_bridge_failed

Regra de interpretacao:

  • phase: "pre_ready" indica bloqueio antes de a sessao aceitar audio
  • phase: "in_session" indica falha terminal ou encerramento depois do ready
  • os status de recurso podem aparecer nas duas fases, dependendo de quando a indisponibilidade foi detectada

Campos opcionais de testes e homologacao:

  • audioFormat pode ser enviado no start, mas hoje nao altera o pipeline de audio
  • callConfig e opcional e existe para testes, smoke test local e homologacao tecnica
  • para integracao produtiva com cliente externo, o contrato pode omitir callConfig

Configuracao de chamada para teste de carga com STT Sofya, agente fake e TTS xAI:

{
  "debugEvents": true,
  "callConfig": {
    "agentBackend": "remote_ws_fake",
    "agentFake": {
      "delayMs": 2500,
      "responses": "Primeira resposta simulada com tamanho intermediario;Segunda resposta simulada com tamanho intermediario;Resposta final encerrando o atendimento simulado"
    },
    "stt": {
      "provider": "internal_http",
      "disableVosk": true
    },
    "tts": {
      "provider": "xai"
    }
  }
}

Regras desse modo:

  • agentFake.responses contem de 2 a 10 frases separadas por ;; espacos laterais sao removidos e cada frase deve ter de 40 a 180 caracteres
  • agentFake.delayMs e aplicado antes de cada resposta, usa 2500 por padrao e aceita valores de 0 a 180000
  • a saudacao inicial continua sendo o intro normal; cada fala posterior reconhecida pelo STT consome uma resposta fake, sem usar o texto transcrito para escolher a resposta
  • respostas anteriores as duas ultimas usam ARGUMENTATION, a penultima usa FORMALIZATION e a ultima usa DONE; chamadas posteriores ao fim recebem novamente o mesmo resultado terminal
  • a sequencia e isolada por sessao
  • debugEvents=true publica pelo websocket somente eventos tecnicos e valores agregados de fala, STT Sofya, TTS xAI e sheds do Bridge; o conteudo integral da conversa nao e incluido nas metricas
  • o endpoint nao possui uma autorizacao adicional especifica para o fake: qualquer cliente ja autorizado a abrir /ws/agent pode selecionar remote_ws_fake por callConfig

WS /ws/text

Fluxo de texto sem audio para testes e integracao basica.

WS /ws/text_stream

Fluxo textual com resposta em streaming.

Fluxo ponta a ponta atual

  1. Cliente abre websocket em /ws/agent
  2. Bridge recebe start e faz parse do contexto da chamada
  3. Bridge valida capacidade e readiness dos recursos
  4. Bridge cria room/token e envia ready
  5. Cliente envia audio para o bridge
  6. Bridge publica audio no LiveKit
  7. Agent recebe audio, STT produz texto
  8. Agent chama o backend de IA configurado
  9. Agent usa TTS para vocalizar resposta
  10. Bridge devolve audio ao cliente
  11. Agent sinaliza DONE ou ocorre erro terminal
  12. Bridge envia stop e fecha a chamada

Dependencias externas relevantes

  • LiveKit
  • STT interno HTTP
  • Vosk
  • ElevenLabs
  • modelo LLM da pipeline
  • API de fidelizacao

Timeline de chamada

O projeto agora gera uma timeline estruturada por chamada em formato jsonl.

Configuracao:

  • CALL_TIMELINE_ENABLED=1 ativa a escrita da timeline
  • CALL_TIMELINE_CONSOLE=1 replica os eventos tambem no stdout
  • CALL_TIMELINE_DIR=./timeline define o diretorio dos arquivos
  • CALL_TIMELINE_QUEUE_MAX=10000 limita eventos pendentes para escrita assincrona
  • CALL_LOG_QUEUE_MAX=20000 limita registros pendentes dos arquivos por chamada
  • ASYNC_IO_WARNING_INTERVAL_S=60 limita a frequencia dos avisos de descarte/erro

A timeline e o arquivo de log por chamada sao gravados por threads de fundo para nao executar I/O de disco no event loop de audio. Quando uma fila atinge o limite, o evento e descartado em vez de bloquear o audio e um warning rate-limited registra o total acumulado. Os limites aceitos ficam entre 1 e 1000000.

Os spans estruturados usam BatchSpanProcessor: span.end() apenas enfileira o span, enquanto o envio OTLP acontece em lote fora da thread chamadora. O provider faz flush e shutdown no encerramento normal do processo.

Fake remoto:

  • remote_ws_fake usa um fake interno em memoria e nao depende de REMOTE_AGENT_WS_FAKE_URL
  • o endpoint ws://127.0.0.1:8000/fake-agent/ws continua disponivel apenas para testes manuais do contrato websocket

Logs de websocket remoto:

  • o adapter remote_ws agora registra no stdout eventos REMOTE_AGENT_WS_CONNECT_OPEN, REMOTE_AGENT_WS_CONNECT_OK, REMOTE_AGENT_WS_REQUEST, REMOTE_AGENT_WS_RESPONSE e falhas *_FAIL
  • os logs incluem instance (hostname/pod), agent, url, host, stage, protocol e metadados do payload para facilitar comparar pods com erro de DNS/host

Mock de encerramento apos primeiro audio:

  • MOCK_STOP_AFTER_FIRST_AUDIO_ENABLED=1 faz o bridge enviar o stop terminal configurado para o reason depois que o primeiro audio real do agente for entregue ao cliente e a saida ficar em silencio pelo intervalo configurado
  • MOCK_STOP_AFTER_FIRST_AUDIO_SILENCE_S=0.35 controla quanto tempo de silencio o bridge espera antes de disparar o stop
  • MOCK_STOP_AFTER_FIRST_AUDIO_REASON=stage_done define o reason exato enviado no stop

Status terminais configuraveis por env:

  • FINAL_STOP_STATUS_RESOLVED=stop_resolvido_e_finalizado
  • FINAL_STOP_STATUS_UNRESOLVED=stop_nao_resolvido
  • FINAL_STOP_STATUS_OTHER_SUBJECT=stop_outro_assunto
  • FINAL_STOP_STATUS_LONG_SILENCE=stop_silencio_longo
  • FINAL_STOP_DEFAULT_KIND=resolved define o fallback quando o reason nao bater em nenhum valor configurado
  • FINAL_STOP_REASON_RESOLVED=stage_done
  • FINAL_STOP_REASON_UNRESOLVED=nao_resolvido
  • FINAL_STOP_REASON_OTHER_SUBJECT=outro_assunto
  • FINAL_STOP_REASON_LONG_SILENCE=no_user_response
  • o mapeamento agora usa comparacao exata do reason, sem aliases

Espera de resposta do backend remoto:

  • REMOTE_AGENT_INFLIGHT_WAIT_INTERVAL_S=12
  • REMOTE_AGENT_INFLIGHT_WAIT_SHORT_AUDIO_DIR=src/app/livekit/assets/comfort/short
  • REMOTE_AGENT_INFLIGHT_WAIT_LONG_AUDIO_DIR=src/app/livekit/assets/comfort/long
  • o primeiro audio de conforto usa um WAV aleatorio da pasta short; a partir do segundo, usa WAVs aleatorios da pasta long
  • o intervalo dos audios de conforto conta a partir do fim do audio anterior
  • REMOTE_AGENT_INFLIGHT_WAIT_TIMEOUT_S=180
  • REMOTE_AGENT_INFLIGHT_WAIT_MAX_NOTICES=0 (0 mantém os confortos sem limite de quantidade até o timeout)
  • REMOTE_AGENT_INFLIGHT_WAIT_TEXT=Um momento, ainda estou consultando para te ajudar.
  • este timeout e tecnico: limita quanto tempo o runtime aguarda o backend remoto processar um turno
  • ele e diferente do timeout de resposta do cliente configurado por metadata.wait_timeout_seconds
  • mensagens feedback, tanto top-level quanto result.type: "feedback", usam a espera tecnica do backend, mas nao contam silencio do cliente nem disparam metadata.wait_retry_messages
  • em um turno normal, depois que o agent termina de falar e o cliente responde, o fim de fala detectado pelo VAD pode antecipar o primeiro conforto curto enquanto o STT conclui a transcricao
  • o conforto antecipado do VAD nao e agendado enquanto o agent esta falando; se outra fala do agent ocupar o say_lock depois do agendamento, o conforto tambem e abortado para nao sair colado ao fim da mensagem
  • se o cliente interromper uma fala interrompivel do agent, o VAD nao enfileira conforto atras dessa fala; o novo pipeline ainda pode usar os confortos normais caso o processamento demore
  • se o cliente falar por mais de 800ms enquanto o backend ja processa outro turno e o agent esta em silencio, o conforto curto especulativo do VAD e suprimido e a transcricao abre uma interrupcao diferida
  • cada interrupcao diferida toca src/app/livekit/assets/comfort/interruption/01.wav, correspondente a "Ouvi o que voce falou, um instante", e invalida a resposta anterior quando ela chegar
  • no pipeline substituto, o conforto dedicado conta como o primeiro aviso ja consumido: o audio short normal nao toca logo depois; se o processamento continuar, o proximo aviso permitido e long e respeita o intervalo configurado
  • o estado de interrupcao e rearmado antes de iniciar o pipeline substituto: cada nova fala valida durante o novo processamento repete o conforto dedicado e substitui novamente a resposta em voo, sem descartar a nova transcricao nem deixar silencio na chamada
  • falas de ate 800ms durante processamento sao tratadas como ruido ou backchannel curto e nao abrem interrupcao diferida
  • os logs principais desse fluxo sao pre_backend_wait_notice_skipped (agent_speaking ou backend_processing), deferred_interruption_accumulated, deferred_interruption_dispatched e os estagios TTS AGENT_BACKEND_WAIT/INTERRUPTION_COMFORT

Recuperacao do participant LiveKit do agent:

  • AGENT_RECONNECT_ENABLED=1
  • AGENT_RECONNECT_MAX_ATTEMPTS=1
  • AGENT_RECONNECT_TIMEOUT_S=10

Protecao de reenvio TTS:

  • TTS_EMPTY_FRAME_RETRY_TIMEOUT_S=3 interrompe uma tentativa de TTS sem primeiro frame de audio ou com gap entre frames no meio da fala apos o timeout e reenvia o mesmo texto uma vez
  • antes do reenvio, toca src/app/livekit/assets/comfort/fails/tts_fail_recovery.wav
  • quando o reenvio tem sucesso, registra um unico envio msg com http_cod_status=200 e erro_msg=TTS_regerado

STT fake:

  • STT_PROVIDER=fake dispensa STT_URL
  • usa FAKE_STT_TRANSCRIPTS como fila de falas por turno
  • FAKE_STT_MODE=repeat_last|cycle controla o comportamento ao consumir a fila

TTS fake:

  • TTS_PROVIDER=fake dispensa credenciais externas
  • gera audio PCM sintetico local para smoke tests do pipeline

Formato:

  • um arquivo por chamada
  • nome do arquivo baseado no room
  • eventos do bridge e do agent entram no mesmo arquivo
  • cada linha contem:
    • ts
    • t_rel_ms
    • component
    • event
    • protocol
    • room
    • campos especificos do evento

Eventos relevantes:

  • bridge:
    • call_start
    • ready_sent
    • dispatch_started
    • livekit_room_connected
    • agent_join
    • client_audio_first_frame_received
    • client_audio_first_frame_published
    • client_audio_enabled
    • done_packet_received
    • stop_sent
    • call_end
  • agent:
    • call_start
    • room_enter
    • session_start_requested
    • stt_recognize_started
    • stt_http_completed
    • user_transcript_final
    • pipeline_run_started
    • pipeline_run_completed
    • remote_agent_request
    • remote_agent_response
    • tts_stage_started
    • tts_stage_result
    • interrupt_marked
    • finalize_started
    • finalize_completed

Uso pratico:

  1. iniciar a chamada normalmente
  2. identificar no terminal o room ou o protocol
  3. abrir o arquivo correspondente em ./timeline
  4. ler os eventos em ordem de t_rel_ms

Ponto de atencao

Durante o refactor, este documento deve continuar descrevendo:

  • comportamento publico
  • contrato do websocket
  • responsabilidades de cada camada

Na versao final, ele deve evoluir para a documentacao oficial da API.