233 lines
7.0 KiB
Markdown
233 lines
7.0 KiB
Markdown
# Auri/EarLLM — Contexto Completo para Análise Matemática
|
||
|
||
## Visão Geral do Sistema
|
||
|
||
**Projeto**: Auri v2.5.0 (EarLLM One)
|
||
**Localização**: `C:\Users\renat\earbudllm`
|
||
**Tipo**: Android app multi-módulo (Kotlin + Jetpack Compose)
|
||
**Função**: Pipeline de voz → STT → LLM → TTS via Bluetooth earbuds
|
||
|
||
---
|
||
|
||
## Arquitetura de Módulos
|
||
|
||
```
|
||
app (orquestrador)
|
||
├── core-logging (cross-cutting: logging, métricas)
|
||
├── bluetooth (conectividade BT — A2DP, HFP, SCO)
|
||
├── audio (captura PCM, roteamento, botões hardware)
|
||
├── voice (STT, TTS, pipeline de voz)
|
||
├── llm (clients: OpenAI, Claude, Gemini, Ollama, RPA)
|
||
└── integrations (Gmail OAuth2)
|
||
```
|
||
|
||
### Grafo de Dependências (formal)
|
||
```
|
||
G = (V, E) onde:
|
||
V = {app, bluetooth, audio, voice, llm, integrations, core-logging}
|
||
E = {
|
||
app → bluetooth,
|
||
app → audio,
|
||
app → voice,
|
||
app → llm,
|
||
app → integrations,
|
||
app → core-logging,
|
||
audio → core-logging,
|
||
voice → audio,
|
||
voice → core-logging,
|
||
llm → core-logging,
|
||
integrations → core-logging
|
||
}
|
||
|
||
Propriedades:
|
||
- Acíclico: SIM (DAG) ✅
|
||
- core-logging: nó sink (grau de saída = 0)
|
||
- app: nó source (grau de entrada = 0 de outros módulos)
|
||
- Componentes fortemente conectados: cada módulo é seu próprio SCC (DAG)
|
||
```
|
||
|
||
---
|
||
|
||
## Máquina de Estados Principal
|
||
|
||
### VoicePipeline States
|
||
```
|
||
S = {IDLE, RECORDING, TRANSCRIBING, QUERYING_LLM, SPEAKING, ERROR}
|
||
|
||
Transições δ:
|
||
IDLE + startRecording → RECORDING
|
||
RECORDING + stopRecording → TRANSCRIBING
|
||
RECORDING + error → ERROR
|
||
TRANSCRIBING + sttResult(text) → QUERYING_LLM
|
||
TRANSCRIBING + sttResult(empty) → ERROR (auto-reset em 3s → IDLE)
|
||
TRANSCRIBING + error → ERROR
|
||
QUERYING_LLM + llmResult → SPEAKING
|
||
QUERYING_LLM + error → ERROR
|
||
SPEAKING + ttsComplete → IDLE
|
||
ERROR + timeout(3s) → IDLE
|
||
ERROR + userReset → IDLE
|
||
|
||
Propriedades verificadas:
|
||
✅ Sem estados inalcançáveis (todos estados têm caminho de IDLE)
|
||
✅ Sem deadlocks (todos estados têm transição de saída)
|
||
✅ Auto-healing: ERROR sempre resolve para IDLE
|
||
⚠️ SPEAKING não tem cancel — bloqueio possível se TTS travar
|
||
```
|
||
|
||
### BluetoothController States
|
||
```
|
||
S = {DISCONNECTED, SCANNING, CONNECTING, CONNECTED, SCO_CONNECTING, SCO_ACTIVE, ERROR}
|
||
|
||
Prioridade de fonte de áudio (função monotônica):
|
||
priority: AudioSource → ℤ
|
||
BLE_AUDIO → 5
|
||
BT_SCO → 4
|
||
USB_MIC → 3
|
||
WIRED → 2
|
||
BUILTIN → 1
|
||
|
||
Invariante: currentSource = argmax{priority(s) | s ∈ availableSources}
|
||
```
|
||
|
||
---
|
||
|
||
## Análise de Concorrência
|
||
|
||
### Coroutine Scopes
|
||
```
|
||
viewModelScope (MainViewModel):
|
||
- Lifecyle: vinculado ao ViewModel, cancelado onCleared()
|
||
- Dispatchers: Main para UI, IO para rede/disk, Default para CPU
|
||
|
||
Padrões identificados:
|
||
- StateFlow<VoicePipelineState> como bus de eventos centralizdo
|
||
- collect { } em LaunchedEffect nas telas Compose
|
||
- MutableStateFlow com atomic updates (thread-safe)
|
||
|
||
Riscos potenciais:
|
||
- SharedFlow sem replay: eventos podem ser perdidos se collector lento
|
||
- launch { } sem supervisorScope: falha cancela todos os filhos
|
||
- withContext(Dispatchers.IO) aninhado: overhead desnecessário de contexto
|
||
```
|
||
|
||
### AuriToolExecutor — Análise de Idempotência
|
||
```
|
||
9 ferramentas:
|
||
1. alarm — NÃO idempotente (cria alarmes duplicados)
|
||
2. calendar — NÃO idempotente (cria eventos duplicados)
|
||
3. reminder — NÃO idempotente
|
||
4. time — Idempotente (read-only)
|
||
5. email — NÃO idempotente (pode enviar duplicado)
|
||
6. draft — Quase idempotente (draft com mesmo conteúdo)
|
||
7. call — NÃO idempotente (inicia chamada)
|
||
8. whatsapp — NÃO idempotente
|
||
9. app — Idempotente se app já aberto
|
||
|
||
Risco: sem deduplicação, retry logic pode causar ações duplas
|
||
Recomendação: implementar idempotency keys por ferramenta
|
||
```
|
||
|
||
---
|
||
|
||
## Análise de Performance
|
||
|
||
### Pipeline de Latência (E2E medido no A04)
|
||
```
|
||
Componente Latência típica Modelo
|
||
──────────────────────────────────────────────
|
||
Audio capture ~100ms determinístico
|
||
STT (online) 200-800ms distribuição log-normal
|
||
STT (Vosk offline) N/A (stub) —
|
||
LLM (Ollama A04) 10-15s alta variância (~3 tok/s)
|
||
LLM (OpenAI API) 1-3s distribuição gamma
|
||
TTS 50-200ms determinístico
|
||
|
||
E2E latência total (Ollama A04): μ ≈ 12s, σ ≈ 3s
|
||
E2E latência total (OpenAI): μ ≈ 2.5s, σ ≈ 0.8s
|
||
|
||
Modelo de fila M/M/1 para pipeline LLM:
|
||
- λ (taxa de requisições): ~0.1 req/s (1 a cada 10s em uso típico)
|
||
- μ (taxa de serviço Ollama A04): ~0.08 req/s
|
||
- ρ = λ/μ = 1.25 > 1 → INSTÁVEL sob carga contínua!
|
||
- ρ (OpenAI) ≈ 0.3 → ESTÁVEL com buffer adequado
|
||
```
|
||
|
||
### Consumo de Memória
|
||
```
|
||
Estimativa por componente:
|
||
- App base: ~50MB
|
||
- Bluetooth stack: ~5MB
|
||
- Audio buffer (PCM, 16kHz, 16-bit, 5s): ~160KB
|
||
- STT model (Android): ~2MB (online) / ~50MB (Vosk)
|
||
- LLM context (OpenAI/Claude): apenas tokens (rede)
|
||
- LLM local (llama3.2:1b): ~800MB RAM
|
||
|
||
Total com Ollama local: ~850MB → crítico em dispositivos 2GB RAM
|
||
```
|
||
|
||
---
|
||
|
||
## Análise de Segurança
|
||
|
||
### Superfície de Ataque
|
||
```
|
||
1. API Keys: EncryptedSharedPreferences (AES-256-GCM) ✅
|
||
2. Bluetooth SCO: comunicação de voz sem criptografia (design limitation) ⚠️
|
||
3. HTTP cleartext (Ollama localhost): permitido explicitamente via network_security_config ⚠️
|
||
4. LAN access: cleartext permitido para 192.168.*.* — risco em redes públicas ❌
|
||
5. Gmail OAuth2 tokens: persistidos em token store — verificar criptografia
|
||
6. Audio recording: exige permissão RECORD_AUDIO — verificar escopo temporal
|
||
```
|
||
|
||
---
|
||
|
||
## Pontos de Alta Complexidade
|
||
|
||
### LlmClientFactory (complexidade ciclomática alta)
|
||
```
|
||
Função: factory(provider, context) → LlmClient
|
||
Branches:
|
||
- 11 providers (OPENAI, CLAUDE, GEMINI, AI_STUDIO, OLLAMA, STUB + 5 RPA variants)
|
||
- Context nullable vs non-null
|
||
- Config (base_url, model) presente vs ausente
|
||
|
||
Complexidade ciclomática estimada: CC ≈ 15-20
|
||
Recomendação: refatorar para Strategy + Registry pattern
|
||
```
|
||
|
||
### MainViewModel (God Object potential)
|
||
```
|
||
Responsabilidades identificadas:
|
||
1. Orquestração de VoicePipeline
|
||
2. Gerenciamento de LLM provider selection
|
||
3. Estado de Bluetooth
|
||
4. Histórico de conversas
|
||
5. Tool execution proxy
|
||
6. Settings sync
|
||
|
||
Violação do SRP (Single Responsibility Principle)
|
||
Solução: decomposição em sub-ViewModels especializados
|
||
```
|
||
|
||
---
|
||
|
||
## Invariantes Globais do Sistema
|
||
|
||
```
|
||
GLOBAL-INV-01:
|
||
Em todo momento, no máximo 1 foreground service ativo para recording
|
||
Formalmente: |{s ∈ Services | s.isRecording = true}| ≤ 1
|
||
|
||
GLOBAL-INV-02:
|
||
API key nunca é transmitida em logs
|
||
Formalmente: ∀ log entry l: ¬contains(l.text, apiKey)
|
||
|
||
GLOBAL-INV-03:
|
||
SCO connection existe sse isRecording = true E source = BT_SCO
|
||
Formalmente: scoActive ↔ (isRecording ∧ audioSource = BT_SCO)
|
||
|
||
GLOBAL-INV-04:
|
||
Pipeline sempre em estado definido (sem estado undefined/null)
|
||
Formalmente: pipelineState ∈ S (definido acima, sem null)
|
||
```
|