Pular para o conteúdo

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.

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ãoEscolhaJustificativa
Propriedade das chavesFornecidas pela ThairaAI (SaaS gerenciado)Nós mantemos as credenciais; uso medido + limitado por empresa
MultilocaçãoGateway único multi-tenant no GCP Cloud RunOperação mais barata, escala de forma limpa
AutenticaçãoFirebase Authentication (Google + e-mail/senha + SSO futuro)Atende empresas com e sem Google (Microsoft 365) sem assumirmos a segurança de senhas
Motor upstreamOpenRouter no MVPZero 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
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.

ComponenteResponsabilidadeRuntime
Thaira GatewayVerificar o JWT do Firebase, resolver o tenant, encaminhar ao upstream, medir tokens + custo realNode 22 + TypeScript + Hono, Cloud Run
Cloud SQL (Postgres)Fonte de verdade: empresas, usuários, permissões, usoCloud SQL (reutiliza uma instância existente)
OpenRouterUpstream compatível com OpenAI; roteamento/fallback entre provedores; retorna custo realGerenciado (externo)
Firebase AuthIdentidade (Google + e-mail/senha + SSO), emite ID tokensGerenciado (GCP)
Secret ManagerChave do upstream, URL do banco, credenciais do Firebase AdminGCP
Stripe (Fase 2)Assinatura por empresa + cobrança por usoGerenciado

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):

  1. O usuário entra (e-mail/senha; Google/SSO depois) → o Firebase retorna um ID token + refresh token.
  2. O refresh token é armazenado criptografado (safeStorage do Electron); o ID token de curta duração fica em memória e é renovado automaticamente (~1h) sob demanda.
  3. Cada requisição ao gateway leva Authorization: Bearer <ID token>.
  4. O gateway verifica o token (assinatura + aud + exp) via Firebase Admin SDK e mapeia firebase_uid → usuário → empresa → plano.

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.

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 de usage (que carrega o cost).
  • 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.

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 o apiKey do modelo antes de o agente rodar. O SaaS gerenciado é exclusivo do gateway, então isso é incondicional (sem ramo isGateway); o safeStorage só 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).

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 conversa thaira-gateway) é o recepcionista do cliente-empresa: um agente leve, sem CLI, que fala /v1/chat/completions compatível com OpenAI ao gateway com um token Firebase novo. É o sucessor comportamento-por-comportamento do removido GeminiDirectAgent nativo (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 a MAX_TOOL_TURNS e auto-executado, sem confirmação interativa). A fábrica de agentes o seleciona para conversas de canal do tipo thaira-gateway, que o despachante do gateway atribui sempre que o papel do usuário do canal é role === 'client'.
  • Resolução de modelogetChannelDefaultModel() / resolveChannelGatewayModel() sempre retornam o provedor de gateway gerenciado. O modelo é a escolha explícita do usuário para o canal, ou DEFAULT_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-gateway re-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 tipo gemini foram migradas para thaira-gateway pela 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/models com 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).
  • 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 migrate uma 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.
  • 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.

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, provedor Thaira provisionado 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.