Arquitetura - alexandrebrt14-sys/geo-finops GitHub Wiki

Arquitetura

Última revisão: 08/07/2026, sobre o estado do branch main (v2.0.0).

Visão geral do pipeline

adapters por projeto (papers e caramaschi ativos em 30/09/2026)
        |
        v
geo_finops.track_call
        |
        v
~/.config/geo-finops/calls.db   (SQLite, modo WAL; override via GEO_FINOPS_CONFIG_DIR)
        |
   sync diário 23:50 (Task Scheduler no Windows / cron no Linux e Mac)
        |
        v
Supabase: tabela finops_calls
        |
        v
snapshot JSON (scripts/export_snapshot.py)
        |
        v
alexandrecaramaschi.com/finops  +  /api/finops/llm-usage (ISR 1h)

Camada local (SQLite)

Tabela única llm_calls com: timestamp (ISO 8601 UTC), project, run_id, task_type, model_id, provider (inferido do model_id), tokens_in, tokens_out, cost_usd, success, metadata (JSON), sync_status (pending | synced | error) e synced_at.

Deduplicação garantida por índice único em (timestamp, project, COALESCE(run_id, ''), model_id) combinado com INSERT OR IGNORE.

A evolução de schema é gerenciada por Alembic (alembic.ini + geo_finops/migrations/, baseline 0001 idempotente) — objeto da decisão B-014 (issue #3).

Sincronização com Supabase

  • Worker noturno (geo_finops/sync.py) envia as linhas pending para a tabela finops_calls.
  • Idempotência no destino: Prefer: resolution=ignore-duplicates no POST (PostgREST ON CONFLICT DO NOTHING); HTTP 409 é tratado como sucesso silencioso.
  • Retry: até 3 tentativas com backoff exponencial (1s, 2,5s, 5s) em 5xx e erros de rede; 4xx fatais (401/403/422) não fazem retry.
  • Credenciais (SUPABASE_URL + SUPABASE_KEY ou SUPABASE_SERVICE_ROLE_KEY) vêm de variáveis de ambiente ou de um .env no chain de config.candidate_env_files(). Desde o PR #5 (08/07/2026), .env e .env.* estão no .gitignore.

Estrutura do pacote

Módulo Papel
geo_finops/config.py Paths, parsing de env, credenciais (fonte única)
geo_finops/db.py Schema + conexão WAL
geo_finops/tracker.py track_call + query_calls + inferência de provider
geo_finops/aggregates.py SQL reutilizável (totals, daily_timeseries, top_models)
geo_finops/sync.py Worker noturno → Supabase
geo_finops/migrate.py Migração dos 4 trackers legados (executada em 07/04/2026)
geo_finops/cli.py status / summary / list / migrate / sync
geo_finops/prices.py + prices.yaml Cálculo de custo (5 providers, 11 modelos, versão 2026-04-09, defasada em relação ao parque vigente) — decisão B-020 (issue #4)
geo_finops/digest/ Digest FinOps semanal (cloud estimators, builders, formatters, reporters); desde 10/08/2026 o payload declara valid_for_hours e stale_after
geo_finops/migrations/ Alembic
scripts/ health_check.py (13 dimensões), export_snapshot.py, aggregate_dashboard.py, weekly_digest.py, bootstrap_supabase.py

Adapters por projeto

Cada projeto consumidor tem um adapter thin que importa geo_finops.track_call via sys.path e vira no-op se o pacote não estiver disponível — o tracking jamais quebra a call principal.

Projeto Adapter Estado em 30/09/2026
papers src/finops/unified_adapter.py ativo
caramaschi src/scripts/caramaschi_finops.py ativo
geo-orchestrator src/unified_finops.py removido em 08/09/2026 (refatoração 5.1); o orquestrador mantém o próprio FinOps em output/.finops/
curso-factory src/unified_finops.py arquivo não existe mais

CI de testes

Workflow tests (.github/workflows/test.yml, criado em 09/08/2026 no PR #8): pytest em Python 3.11 e 3.12 a cada push e PR contra main, mais uma sentinela que reprova o build se a coleta cair abaixo de 155 testes. A sentinela existe porque a bateria passou meses sem rodar em servidor e um teste quebrado ficou um mês anotado sem cobrança.

CI de segurança

Workflow Security scan (Python) (agendado semanalmente + workflow_dispatch): bandit, pip-audit e gitleaks. Ficou vermelho de forma crônica até 08/07/2026 por falsos positivos B608 do bandit (queries com where parametrizado), corrigidos no PR #5 com # nosec B608 justificado; os 3 jobs voltaram a ficar verdes na sequência. Gotcha operacional: o GitHub desabilita workflows agendados após cerca de 60 dias sem atividade no repositório (disabled_inactivity).