Gateway de IA Gerenciado
Esta página documenta a arquitetura e as decisões por trás do modelo SaaS gerenciado da ThairaAI, no qual todas as chamadas de IA passam por um gateway de nuvem centralizado, autenticado e medido — em vez de cada usuário fornecer suas próprias chaves de provedor.
Por quê
Seção intitulada “Por quê”Historicamente a ThairaAI é um app desktop gratuito em que cada usuário cola suas próprias chaves de provedor (IProvider.apiKey, armazenadas localmente) e cada chamada de IA vai direto do desktop ao provedor. O modelo SaaS gerenciado muda isso para pequenas e médias empresas:
- A ThairaAI mantém as credenciais de IA centralmente; usuários nunca veem uma chave de provedor.
- Empresas pagam uma assinatura; o uso é medido e limitado por empresa.
- Todo o tráfego de IA passa por um único gateway de nuvem sob nosso controle.
Decisões definidas
Seção intitulada “Decisões definidas”| Decisão | Escolha | Justificativa |
|---|---|---|
| Propriedade das chaves | Fornecidas pela ThairaAI (SaaS gerenciado) | Nós mantemos as credenciais; uso medido + limitado por empresa |
| Multilocação | Gateway único multi-tenant no GCP Cloud Run | Operação mais barata, escala de forma limpa |
| Autenticação | Firebase Authentication (Google + e-mail/senha + SSO futuro) | Atende empresas com e sem Google (Microsoft 365) sem assumirmos a segurança de senhas |
| Motor upstream | OpenRouter no MVP | Zero infra, $0 ocioso, retorna o custo real por requisição. O 9router (auto-hospedado, margem por arbitragem de assinaturas) é uma troca futura — o upstream do gateway é uma costura de configuração |
Arquitetura
Seção intitulada “Arquitetura”desktop ThairaAI ──(1 login Firebase)──► Firebase Auth ──► ID token (JWT, ~1h) │ │ (2) chamada /v1/*, Bearer <ID token Firebase>, compatível com OpenAI ▼┌──────────────────────────────────────────────────────────────┐│ Thaira Gateway (Cloud Run, stateless, escala 0→N) ││ verifica JWT → resolve usuário→empresa→plano → (cota) → ││ encaminha → mede uso + custo real ││ Cloud SQL (Postgres): tabelas de tenant + uso │└───────────────┬────────────────────────────────────────────────┘ │ (3) Bearer <chave do upstream> ▼ OpenRouter ──► provedores (Anthropic, OpenAI, Gemini, …)
Stripe ──(webhook)──► Gateway: atualiza plano/permissões da empresa [Fase 2]O gateway é agnóstico ao upstream: apontá-lo para o OpenRouter ou para um 9router auto-hospedado é uma mudança de configuração (UPSTREAM_URL / UPSTREAM_API_KEY), não de código.
Componentes
Seção intitulada “Componentes”| Componente | Responsabilidade | Runtime |
|---|---|---|
| Thaira Gateway | Verificar o JWT do Firebase, resolver o tenant, encaminhar ao upstream, medir tokens + custo real | Node 22 + TypeScript + Hono, Cloud Run |
| Cloud SQL (Postgres) | Fonte de verdade: empresas, usuários, permissões, uso | Cloud SQL (reutiliza uma instância existente) |
| OpenRouter | Upstream compatível com OpenAI; roteamento/fallback entre provedores; retorna custo real | Gerenciado (externo) |
| Firebase Auth | Identidade (Google + e-mail/senha + SSO), emite ID tokens | Gerenciado (GCP) |
| Secret Manager | Chave do upstream, URL do banco, credenciais do Firebase Admin | GCP |
| Stripe (Fase 2) | Assinatura por empresa + cobrança por uso | Gerenciado |
Fluxo de autenticação
Seção intitulada “Fluxo de autenticação”O desktop conduz a autenticação Firebase a partir do processo principal via API REST do Identity Toolkit (sem o SDK web — adequado ao Electron):
- O usuário entra (e-mail/senha; Google/SSO depois) → o Firebase retorna um ID token + refresh token.
- O refresh token é armazenado criptografado (
safeStoragedo Electron); o ID token de curta duração fica em memória e é renovado automaticamente (~1h) sob demanda. - Cada requisição ao gateway leva
Authorization: Bearer <ID token>. - O gateway verifica o token (assinatura +
aud+exp) via Firebase Admin SDK e mapeiafirebase_uid → usuário → empresa → plano.
Modelo de dados (Cloud SQL / Postgres)
Seção intitulada “Modelo de dados (Cloud SQL / Postgres)”O schema é intencionalmente mínimo e não é documentado aqui. Em alto nível, cobre:
- Registros de tenant + identidade (empresas, usuários) indexados pelo Firebase UID.
- Permissões por plano (limites, rate limits, lista de modelos permitidos); campos não definidos significam ilimitado.
- Eventos de uso somente-inclusão (modelo, endpoint, contagem de tokens, custo real, status) — nenhum conteúdo de prompt/resposta é armazenado.
- Agregados de uso por período usados para checagens rápidas de cota.
Onboarding da Fase 1: por convite, com adesão automática opcional por domínio de e-mail; caso contrário, uma empresa pessoal é criada por usuário.
Medição (custo real)
Seção intitulada “Medição (custo real)”O gateway injeta usage: { include: true } (e stream_options.include_usage para streams) para que o OpenRouter retorne o custo real em USD por requisição além das contagens de tokens.
- Sem streaming: a resposta é bufferizada e o
usageé parseado (prompt/completion_tokens+cost). - Com streaming: o corpo do upstream é duplicado com
tee()— um ramo transmite ao cliente sem alteração (sem latência extra), o outro é varrido pelo chunk final deusage(que carrega ocost). - Armazenado no evento de uso e somado no agregado por período. A medição é best-effort e nunca quebra a requisição do usuário. Nenhum conteúdo de prompt/resposta é armazenado.
Objeto usage do OpenRouter (verificado contra a API real):
{ "prompt_tokens": 13, "completion_tokens": 6, "total_tokens": 19, "cost": 0.00000555, "cost_details": { "upstream_inference_cost": 0.00000555 } }Custos por requisição são da ordem de 1e-6 USD, então as colunas de custo usam um tipo NUMERIC de alta escala.
Integração com o desktop
Seção intitulada “Integração com o desktop”O desktop já estava “pronto para gateway” (IProvider carrega baseUrl + apiKey, com a flag isGateway). O modelo gerenciado adiciona:
- Bridge IPC
cloudAuth+CloudAuthService(src/process/services/cloudAuth/) — login/cadastro Firebase, armazenamento criptografado do token, renovação automática. - Injeção de token no lado do processo — o agente interativo (
AionrsManager) busca um token Firebase novo no processo principal (cloudAuthService.getToken()) e o define como oapiKeydo modelo antes de o agente rodar. O SaaS gerenciado é exclusivo do gateway, então isso é incondicional (sem ramoisGateway); osafeStoragesó existe no processo principal, então o token é injetado aqui em vez de em processos derivados. Sem tokens obsoletos, nada persistido na config. - Provedor provisionado automaticamente — no login, um provedor de gateway embutido
Thaira(baseUrl = THAIRA_GATEWAY_URL, modelos de/v1/models) é criado/atualizado e habilitado; desabilitado no logout. - UI de login — uma aba “Thaira Account” em Configurações (
CloudAccountModalContent.tsx).
Canais (WhatsApp / Telegram)
Seção intitulada “Canais (WhatsApp / Telegram)”As respostas de modo-negócio dos canais são exclusivamente via gateway — BYOK e provedores locais nunca são usados ali, então toda mensagem de WhatsApp/Telegram é medida e limitada como qualquer outra chamada ao gateway.
ThairaGatewayAgent(src/process/task/directAgent/ThairaGatewayAgent.ts, tipo de conversathaira-gateway) é o recepcionista do cliente-empresa: um agente leve, sem CLI, que fala/v1/chat/completionscompatível com OpenAI ao gateway com um token Firebase novo. É o sucessor comportamento-por-comportamento do removidoGeminiDirectAgentnativo (BYOK) — mesma persona de recepcionista, streaming, histórico (limitado a 40 turnos) e function-calling do Google Calendar (pelo protocolo de tools da OpenAI, limitado aMAX_TOOL_TURNSe auto-executado, sem confirmação interativa). A fábrica de agentes o seleciona para conversas de canal do tipothaira-gateway, que o despachante do gateway atribui sempre que o papel do usuário do canal érole === 'client'.- Resolução de modelo —
getChannelDefaultModel()/resolveChannelGatewayModel()sempre retornam o provedor de gateway gerenciado. O modelo é a escolha explícita do usuário para o canal, ouDEFAULT_CHANNEL_MODEL(um padrão de baixo custo) quando nada foi definido. Deslogado, retornam um placeholder para que o agente mostre uma mensagem clara de “entre na Thaira” em vez de cair silenciosamente em uma chave BYOK. - Modelo sempre atual — uma conversa de canal do tipo
thaira-gatewayre-resolve seu modelo a partir do padrão salvo do canal a cada mensagem, então uma troca de modelo em Configurações (ou no cabeçalho do chat) passa a valer imediatamente. (As linhas legadas de cliente do tipogeminiforam migradas parathaira-gatewaypela migração de dados v28.) - Seletores exclusivos de gateway — os seletores de modelo de WhatsApp/Telegram (Configurações e o cabeçalho do chat) são filtrados para mostrar apenas modelos do gateway; selecionar um o persiste como padrão do canal. O seletor lê
/v1/modelscom o token Firebase e cai de volta para a lista populada no login, então um usuário logado sempre vê os modelos do gateway.
Variáveis de ambiente do desktop para uso real (.env):
THAIRA_GATEWAY_URL— URL base do gateway (dev local:http://localhost:8080; depois a URL do Cloud Run).FIREBASE_WEB_API_KEY— uma chave do projeto sem restrição de referer (a chave de navegador criada automaticamente pelo Firebase é restrita por referer e é bloqueada no Electron).
Implantação
Seção intitulada “Implantação”- Gateway → Cloud Run em
southamerica-east1(stateless, escala 0→N). Segredos (UPSTREAM_API_KEY,DATABASE_URL) no Secret Manager. O Firebase Admin usa a service account via Application Default Credentials (sem arquivo de chave na imagem). - Banco de dados → reutilizar uma instância Cloud SQL Postgres 15 existente com um banco + usuário novos e isolados para o gateway (sem custo de nova instância).
- Migrações → rodar
npm run migrateuma vez antes do primeiro tráfego. - Para privacidade de PMEs, configurar as preferências de retenção de dados / retenção zero do OpenRouter por provedor.
Segurança
Seção intitulada “Segurança”- A chave do upstream existe apenas no gateway (Secret Manager) — nunca no build do desktop.
- JWT do Firebase verificado (assinatura + audience + expiração) em cada requisição.
- Sem registro de prompt/resposta — privacidade para clientes PME.
- Limites de taxa por empresa + por usuário (Fase 2) protegem contra abuso e custos descontrolados.
Status da implementação
Seção intitulada “Status da implementação”Pronto e verificado (ponta a ponta localmente, contra OpenRouter + Firebase + Postgres reais):
- Gateway: autenticação → resolução de tenant → encaminhamento → medição de custo real. Endpoints
/healthz,/v1/chat/completions,/v1/messages,/v1/models. - Desktop: bridge + serviço
cloudAuth, injeção de token no lado do processo, provedorThairaprovisionado automaticamente, UI de login “Thaira Account” (en + pt-BR). - Canais: respostas de modo-negócio de WhatsApp + Telegram roteadas pelo gateway via
ThairaGatewayAgent(compatível com OpenAI, token novo), seleção de modelo exclusiva do gateway com padrão de baixo custo e resolução sempre-atual a cada mensagem. Verificado de ponta a ponta, incluindo chamadas de tool do Google Calendar.
Pendente:
- Implantar o gateway no Cloud Run + provisionar o banco dedicado na instância existente.
- Fase 2: cota/limite de taxa com Redis (Memorystore); cobrança de assinatura via Stripe sobre o custo real capturado; lista de modelos permitidos por plano em
/v1/models. - Troca/híbrido futuro opcional: 9router auto-hospedado atrás do gateway para margem por arbitragem de assinaturas.