# TIA Voice API

Este repositorio contem a stack de voz-to-voz do projeto TIA.

## Estrutura

- `src/app`: gateway WebSocket, runtime LiveKit, providers e utilitarios
- `src/agent`: pipeline e estagios do agente
- `tests`: testes automatizados
- `docs`: documentacao viva do projeto
- `k8s/livekit`: imagem e manifest do pod dedicado do LiveKit
- `k8s/tia`: imagem e manifest do pod da aplicacao com `bridge` e `agent`
- `requirements.txt`: dependencias Python da aplicacao
- `livekit.yaml`: configuracao local do servidor LiveKit

## Setup local

```bash
make setup
```

O alvo `make setup` faz o bootstrap do ambiente local:

- cria `.venv` priorizando `python3.13`, `python3.12`, `python3.11`, `python3.10`, `python3.9`, `python3` e `python`
- instala as dependencias de `requirements.txt`
- cria `.env.dev` a partir de `.env.example` se o arquivo ainda nao existir

O projeto hoje exige Python `3.9` ate `3.13`. Python `3.14` nao e aceito pelas dependencias atuais de `livekit-agents==1.3.10`.

Se voce tiver mais de um Python instalado e quiser forcar uma versao especifica no bootstrap:

```bash
make BOOTSTRAP_PY=python3.12 setup
```

Se voce preferir fazer manualmente ou quiser depurar o bootstrap, use:

```bash
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
cp .env.example .env.dev
```

Se `make test`, `make agent` ou `make bridge` falharem com erro de virtualenv ausente, rode `make setup` primeiro.

## Teste local

```bash
make test
```

O `makefile` executa os testes com `.venv/bin/python` e injeta `PYTHONPATH=src`, entao o comando deve ser chamado a partir da raiz do repositorio.

## Execucao local

O `.env.dev` de desenvolvimento pode ser configurado em dois modos:

- smoke test local: `AGENT_BACKEND=remote_ws_fake`, `STT_PROVIDER=fake` e `TTS_PROVIDER=fake`
- integracao real: ajuste `AGENT_BACKEND`, `STT_PROVIDER`, `TTS_PROVIDER` e as credenciais externas necessarias

No modo fake, o agent nao depende de STT HTTP, ElevenLabs ou backend LLM externo. O `STT fake` consome uma sequencia configurada em `FAKE_STT_TRANSCRIPTS` e o `TTS fake` gera um tom PCM local para validar o pipeline de audio.

Importante: `LIVEKIT_API_KEY` e `LIVEKIT_API_SECRET` do `.env.dev` precisam ser identicos ao bloco `keys` de `livekit.yaml`. Se esses valores divergirem, o bridge falha ao conectar/publicar no LiveKit e o dispatch do agent retorna erro de autenticacao.

Para usar o STT HTTP real, configure no `.env.dev`:

- `STT_PROVIDER=internal_http`
- `STT_URL=http://10.152.95.27:8100`
- `STT_KEY=...` se o servico exigir o header `x-api-key`
- `STT_LANG=portuguese`

Observacao: o provider faz `POST` exatamente na URL configurada em `STT_URL`. Se o seu servico expuser uma rota especifica, use a URL completa, por exemplo `http://10.152.95.27:8100/transcribe`.

Para usar Azure Speech TTS com o plugin oficial do LiveKit, configure no `.env.dev`:

- `TTS_PROVIDER=azure`
- `AZURE_SPEECH_KEY=...`
- `AZURE_SPEECH_REGION=...` ou `AZURE_SPEECH_ENDPOINT=https://...cognitiveservices.azure.com/`
- `AZURE_SPEECH_VOICE=pt-BR-FranciscaNeural`
- `AZURE_SPEECH_LANGUAGE=pt-BR`
- `AZURE_SPEECH_DEPLOYMENT_ID=...` apenas para Custom Voice

Para usar xAI TTS, configure no `.env.dev`:

- `TTS_PROVIDER=xai`
- `XAI_API_KEY=...`
- `XAI_WEBSOCKET_URL=https://cloud9.api.x.ai/v1/tts`
- `XAI_TTS_READINESS_MODE=connect` opcional para validar apenas o handshake do WebSocket; default `synthesize`
- `XAI_TTS_VOICE=ara` opcional
- `XAI_TTS_LANGUAGE=pt-BR` opcional
- `TTS_FRAME_GAP_TIMEOUT_S=2` limite entre deltas de audio do xAI; quando estoura, o runtime registra `Falha TTS`, toca o audio de conforto e reenvia o texto

Para enviar logs estruturados ao Google Pub/Sub, configure:

- `GCP_PROJECT_ID=...`
- `AGENT_PUBSUB_TOPIC=agent-logs` ou `projects/.../topics/agent-logs`

Se uma dessas variaveis nao existir, os eventos estruturados continuam indo apenas para o log local. O ambiente tambem precisa ter credenciais Google disponiveis via Application Default Credentials ou service account com permissao de publicacao no topico.

Para subir o fluxo local de voz:

```bash
make local-up
```

Depois abra `http://127.0.0.1:8000/voice-client`. O cliente web agora vem com `remote_ws_fake`, `fake` STT e `fake` TTS por padrao para smoke test local.

Para rodar o teste operacional com STT Sofya, TTS xAI, Bridge e LiveKit reais:

```bash
make local-stresstest
```

O alvo sobe o ambiente local se necessario, executa `python -m app.tools.local_stresstest` e gera os artefatos em `.run/local-stresstest/`:

- `report.md`: resumo em Markdown com tabelas e diagramas Mermaid
- `summary.json`: resultado estruturado completo
- `stt_results.csv`, `tts_results.csv`, `e2e_results.csv`: planilhas dos cenarios
- `timeline_excerpt.jsonl`: eventos relevantes da timeline da chamada E2E
- `diagrams/*.svg`: imagens estaticas dos fluxos, boas para preview no VSCode
- `diagrams/*.mmd`: fontes Mermaid dos fluxos
- `stt_dumps/*.wav` e `stt_dumps/*.pcm`: audio efetivamente enviado ao STT
- WAVs gerados para baseline, variacoes, TTS e audio recebido do Bridge

Por padrao, o alvo habilita dumps de audio do STT e logs de VAD do agent
(`FLOW_LOG_VAD_DECISIONS=1`, `FLOW_LOG_VAD_ACTIVITY=1`). O agent local e reiniciado
antes do teste para garantir que esses flags entrem no processo.

Se `STRESS_AUDIO` nao for informado, o runner gera um baseline sintetico com xAI TTS usando `STRESS_SYNTH_TEXT`, ou `STRESS_EXPECTED_TEXT` quando `STRESS_SYNTH_TEXT` nao for definido. Quando houver uma gravacao humana, rode:

```bash
STRESS_AUDIO=/caminho/audio_16k_mono.wav make local-stresstest
```

Variaveis uteis:

- `STRESS_EXPECTED_TEXT`: frase esperada para calculo de WER
- `STRESS_SYNTH_TEXT`: frase usada na sintese do baseline xAI TTS, default igual a `STRESS_EXPECTED_TEXT`
- `STRESS_CRITICAL_TERMS`: termos obrigatorios separados por virgula
- `STRESS_WER_THRESHOLD`: limite de WER, default `0.20`
- `STRESS_REPEAT`: repeticoes dos cenarios STT, default `1`; com os 25 cenarios atuais, gera 25 chamadas ao STT. Use `STRESS_REPEAT=2` para 50 chamadas
- `STRESS_CONCURRENCY`: concorrencia dos cenarios STT, default `2`
- `STRESS_PREFIX_TEXT`: prefixo que precisa aparecer no inicio da transcricao, default primeiras palavras de `STRESS_EXPECTED_TEXT`
- `STRESS_PREFIX_WORDS`: quantidade de palavras usadas no prefixo automatico, default `2`
- `STRESS_VAD_PROXY_DBFS`: limiar RMS usado na analise local de VAD proxy, default `-45`
- `STRESS_VAD_PREFIX_PADDING_MS`: padding usado na analise local de VAD proxy, default vem de `VAD_PREFIX_PADDING_DURATION`
- `STRESS_VAD_MIN_SPEECH_MS`: fala minima usada na analise local de VAD proxy, default `100`
- `VAD_PREFIX_PADDING_DURATION`: pre-roll do LiveKit VAD, default `2.0` segundos
- `VAD_PREFIX_PADDING_MIN_DURATION`: piso aplicado tambem sobre override de `call_config`, default `2.0` segundos
- `STT_INPUT_PREFIX_PADDING_MS`: silencio curto adicionado antes do WAV enviado ao STT HTTP, default `250`
- `STT_DUMP_DIR`: diretorio dos WAV/PCM enviados ao STT, default `.run/local-stresstest/stt_dumps`
- `FLOW_LOG_VAD_DECISIONS`: habilita logs `vad_speech_start`/`vad_speech_end`, default `1` no alvo
- `FLOW_LOG_VAD_ACTIVITY`: habilita logs `vad_activity`, default `1` no alvo
- `STRESS_RESTART_AGENT_FOR_DIAGNOSTICS`: reinicia o agent antes do teste, default `1`
- `STRESS_STARTUP_WAIT_S`: tempo maximo para aguardar Bridge e agent runtime, default `180`
- `STRESS_STARTUP_POLL_S`: intervalo entre probes de prontidao, default `3`
- `STRESS_SKIP_LOCAL_WAIT`: use `1` para pular a espera inicial
- `STRESS_BRIDGE_HEALTH_URL`: URL de health do Bridge, derivada de `STRESS_BRIDGE_URL` por padrao
- `STRESS_AGENT_HEALTH_URL`: URL de health do agent runtime, default `http://127.0.0.1:18081/`
- `STRESS_REPORT_DIR`: diretorio de saida, default `.run/local-stresstest`

Comandos auxiliares do modo local:

```bash
make local-status
make local-logs
make bridge-logs
make agent-logs
make local-down
```

Se o `agent` falhar com `worker process is not responding.. worker crashed?` e o log mostrar erro de bind na porta `18081`, existe um worker antigo preso nessa porta. Nesse caso, rode `make agent-down` e depois `make agent-up` ou `make local-up` novamente.

## Kubernetes

A pasta `k8s` agora esta organizada em dois blocos:

- `k8s/livekit/Dockerfile`: usa a imagem oficial `livekit/livekit-server` como base para publicacao no registro privado da empresa
- `k8s/livekit/deployment.yaml`: deployment e service do pod exclusivo do LiveKit
- `k8s/tia/Dockerfile`: mesma imagem da aplicacao Python da raiz do repo, mantida ao lado do manifest para facilitar pipeline de build/publicacao
- `k8s/tia/deployment.yaml`: deployment e service do pod `tia-app`, com dois containers na mesma imagem: `tia-bridge` e `tia-agent`

Os dois `Dockerfile`s devem ser buildados a partir da raiz do repositorio, porque dependem do contexto raiz para copiar `requirements.txt`, `src/` e `livekit.yaml`.

Exemplos:

```bash
docker build -f k8s/livekit/Dockerfile -t registry.example.com/tia/livekit:TAG .
docker build -f k8s/tia/Dockerfile -t registry.example.com/tia/app:TAG .
```

Depois do push para o registry privado, ajuste as imagens em `k8s/livekit/deployment.yaml` e `k8s/tia/deployment.yaml` ou substitua os placeholders via pipeline:

- `LIVEKIT_IMAGE_REPOSITORY:LIVEKIT_IMAGE_TAG`
- `TIA_IMAGE_REPOSITORY:TIA_IMAGE_TAG`

Aplicacao dos manifests:

```bash
kubectl apply -f k8s/livekit/deployment.yaml
kubectl apply -f k8s/tia/deployment.yaml
```

Observacoes:

- `k8s/livekit/Dockerfile` embute o arquivo `livekit.yaml` dentro da imagem; se a configuracao do LiveKit mudar, a imagem precisa ser rebuildada
- `k8s/tia/deployment.yaml` ainda usa envs inline de placeholder para `LIVEKIT_API_KEY` e `LIVEKIT_API_SECRET`; o passo seguinte natural e migrar isso para `Secret` e demais configs para `ConfigMap`
- o `bridge` atende na porta `8000` e o `agent` expõe a porta interna `18081`

## Comandos uteis

```bash
make test
make agent
make bridge
make livekit
```

Todos os comandos assumem o codigo em `src`, portanto a raiz do projeto deve ser usada como diretorio de execucao.

## Documentacao

- `docs/README.md`: indice das docs do projeto
- `docs/refactor-plan.md`: plano do refactor incremental
- `docs/refactor-log.md`: registro das mudancas por etapa
- `docs/api-overview.md`: documentacao viva da API e do comportamento atual

---

## Regional xAI TTS pool (Kubernetes)

Esta versão inclui uma arquitetura opcional para alta disponibilidade do TTS xAI com Pods TIA regionais, pool WebSocket pré-aquecido por Pod, readiness orientada a capacidade e failover via Service/Load Balancer.

Documentação:

- `docs/regional/ARQUITETURA_TIA_XAI_REGIONAL.md`
- `docs/regional/DEPLOYMENT_TIA_XAI_REGIONAL.md`
- `docs/regional/TESTES_TIA_XAI_REGIONAL.md`

Código principal:

- `src/app/livekit/adapters/xai_pool_proxy.py`

Manifests e scripts:

- `k8s/regional/`
- `scripts/render-regional-k8s.sh`
- `scripts/validate-regional-k8s.sh`
- `scripts/deploy-regional-k8s.sh`

O modo antigo de conexão direta com xAI foi preservado. O deployment regional é opt-in.
