Voltar para a documentação
Guia 00

Quotas e custos — os limites de uso e de despesa

O motor de quota cobre três géneros: a quota de tokens e a quota de armazenamento (Phase 207) mais a quota de custo (Phase 216, hold-and-settle sobre a matemática do dinheiro real). As quotas são a ferramenta do operador para os limites de uso em instala…

5 min de leitura

O motor de quota cobre três géneros: a quota de tokens e a quota de armazenamento (Phase 207) mais a quota de custo (Phase 216, hold-and-settle sobre a matemática do dinheiro real). As quotas são a ferramenta do operador para os limites de uso em instalações multi-utilizador; vinculam cada utilizador com uma quota configurada — administradores incluídos — e uma quota por configurar = ilimitado (fail-open: zero mudança de comportamento para as instalações existentes).

Funcionalidade Community (a ferramenta do operador). Fontes: docs/QUOTAS.md (Phase 207 + 216), docs/MODEL_COSTS.md, docs/API.md (a secção Costs, quotas, and per-user model policy). Linhas de proveniência: FACT-CENSUS.md F-QUOTA-1.

As invariantes de fundamento

  • Nenhum segundo contador. A verdade do consumo (tokens/custo) fica no ledger existente WorkspaceTokenUsage (packages/server/src/services/quotaService.ts). QuotaBalance / QuotaHold / QuotaReset = estado derivado e ancorado, nunca uma tabela de contagem.
  • Reset = a âncora do ledger. Um reset escreve de novo QuotaReset(kind, at) que delimita a janela rolante — o histórico de uso/custo nunca é apagado nem zerado.
  • A barreira ao nível do serviço, pre-turn — NÃO o middleware de auth (o controlo precisa de dados do domínio, não da forma do pedido). As barreiras arrancam antes da primeira chamada LLM no orchestrator e antes da ingestão nas vias de carregamento.
  • Fail-open em quotas por configurar. A cadeia de resolução termina em "ilimitado" quando nada está configurado (D-08).
  • A matemática do dinheiro é Decimal→cents. TODA a aritmética de custos passa por Prisma.Decimal e aterra em centavos inteiros — nunca float (costQuotaService.ts, a doutrina COST-01).

A cadeia de resolução (D-08)

NívelFonteNotas
1O override por utilizadorUser.tokenQuotaLimit / User.storageQuotaGb / as linhas de custo por utilizador — via PUT /api/quota/:userId (admin)
2O preset da instalaçãoAs chaves SystemConfig (abaixo) — DB > ENV > predefinição
3IlimitadoO valor sentinela "0" / "" / unset = não configurado → fail-open

As chaves de configuração (SystemConfig, editáveis via Settings)

ChavePredefiniçãoSignificado
QUOTA_TOKEN_DEFAULTo sentinela unsetO limite de tokens da instalação para cada utilizador sem override
QUOTA_STORAGE_GB_DEFAULTo sentinela unsetO limite de armazenamento da instalação (GB)
QUOTA_COST_EUR_DEFAULTo sentinela unset ("0"/"" = ilimitado)O limite de CUSTO da instalação — cadeia decimal EUR
QUOTA_COST_FALLBACK_INPUT_CENTS_PER_1Mum piso integrado não nuloA tarifa de recurso SC-4 para modelos SEM preço — input, centavos inteiros por 1M tokens
QUOTA_COST_FALLBACK_OUTPUT_CENTS_PER_1Mum piso integrado não nuloA tarifa de recurso SC-4 para modelos SEM preço — output

A doutrina SC-4: um modelo sem preço nunca contorna a quota de custo — as tarifas de recurso aplicam-se com warn-once (warnFallbackOnce): um modelo sem preço nunca pode resultar grátis.

As quotas de tokens (quotaService.ts)

  • resolveTokenQuota(userId) → { limit, source } — limit: null = ilimitado; source diz que nível da D-08 decidiu.
  • A janela rolante parte da última âncora QuotaReset para (userId, kind: "token") (ou da época se nunca reiniciado); o uso na janela soma-se a partir do ledger WorkspaceTokenUsage.
  • Ultrapassar a quota lança QuotaError (status + payload) — o campo quota do payload é o contrato que frontend, widget e os handlers dos conectors leem. Não há middleware que contorne: a barreira corre DENTRO da via do serviço.

As quotas de custo (costQuotaService.ts, Phase 216) — hold-and-settle

A reserva em duas fases:

  1. Reserve (antes da chamada LLM) — reserveCostQuota(principal, estimateCents) executa UMA transação interativa: um updateMany condicional cujo predicado (spentCents + estimate <= limitCents) o Postgres avalia com row-lock, de modo que N barreiras concorrentes se SERIALIZAM em vez de todas passar sobre um controlo velho (SC-2) + o insert de QuotaHold na mesma transação.
  2. Settle (ao segmento de uso) — settleCostHold encerra o hold e reembolsa estimate − actual. O overshoot em voo (actual > estimate) = a janela aceite — nenhum kill de um stream em curso.
  3. Em repouso vale a invariante: settledSpent + Σ(lead abertas) ≤ limite.
  4. A fonte do preço por linha: o snapshot FROZEN da Phase 203 onde existe; senão as tarifas de recurso SC-4 (warn-once); as linhas em moeda fora da dominante ficam mesmo assim precificadas (nunca silenciosamente grátis, nunca excluídas).
  5. Reset — resetCostQuota escreve a âncora Quota(kind "cost") e RECONSTRÓI o saldo a partir de ledger-soma + holds abertos (D-02).

As quotas de armazenamento

checkStorageQuota / resolveStorageQuota aplicam o override por utilizador storageQuotaGb, o preset QUOTA_STORAGE_GB_DEFAULT ou o ilimitado. As barreiras arrancam nas vias de carregamento antes do dispatch ao collector.

O principal para os widgets

Os visitantes anónimos do widget não são utilizadores — resolveWidgetQuotaPrincipal liga o tráfego widget ao principal da organização do widget: o consumo do widget pesa na cadeia de quotas da organização, não em identidades por visitante.

Onde as barreiras arrancam (os sítios de consumo)

SuperfícieBarreiraFicheiro
Chat (JWT, widget proxy, conectors — um orchestrator)barreira de tokens + custo pre-turn / pre-LLMpackages/server/src/agent/orchestrator.ts
Carregamento de documentoa barreira de armazenamento pre-ingestpackages/server/src/routes/uploads.ts, routes/documents.ts
A varredura de reseto reset horário das quotas de tokens (pg-boss cron: initQuotaResetScheduler)services/quotaResetJob.ts
As leituras self-serviceGET /api/quota/me, rollups, reset manualroutes/quota.ts

O fluxo do operador

  1. A predefinição da instalação — a UI de Settings → as chaves QUOTA_* (ou PUT /api/system/settings).
  2. O override por utilizador — PUT /api/quota/:userId (admin) ou a UI de quota.
  3. Os preços por utilizador — a família COSTS (Phase 209: /api/users/:userId/pricing, as chaves de fornecedores próprios sob /api/me/provider-keys) alimenta o mesmo motor.
  4. O reset manual a qualquer momento: POST /api/quota/:userId/reset; a varredura horária sustenta o estado estacionário.

Os preços dos modelos (MODEL_COSTS)

Configuração: Settings → Providers → escolher o fornecedor → a ação pricing do modelo → o custo por-1M-token input/output (p. ex. 1.50 e 6.00) + a moeda → Save. A fórmula: cost = tokens × rate, calculada server-side em cada escrita de uso, com snapshot congelado ao tempo de execução (uma mudança posterior da tarifa nunca reescreve o histórico). Moedas: USD, EUR, GBP, JPY, CNY, INR — totais por moeda, nenhuma conversão na v1. N/A = preço por configurar (os modelos cloud unset; a UI mostra "N/A" — a administração tem de configurar); $0.00 = explicitamente grátis (os modelos locais/Ollama foram preenchidos a 0 na migração; override possível). Os limites da v1: nenhuma conversão entre moedas; só o custo da iteração principal (as sub-pipelines tool/MCP/synthesis excluídas); o custo fica disponível no evento done (não a meio do stream).

A referência de endpoints

A tabela completa vive em API reference → Costs, quotas, and per-user model policy.

Ver também

  • Scaling — o comportamento das quotas sob implantação multi-instância

Este guia cobre a superfície operacional das quotas e dos custos; a profundidade técnica vive no post gémeo:

Provenance

SecçãoFonte
Invariantes / a cadeia de resolução / as chavesdocs/QUOTAS.md (core invariants, D-08, as chaves — Phase 207/203) — F-QUOTA-1
As quotas de custo / hold-and-settledocs/QUOTAS.md (§ Cost quotas — Phase 216) + costQuotaService.ts
Os preços dos modelosdocs/MODEL_COSTS.md (Phase 203/209/216)
Os sítios das barreirasdocs/QUOTAS.md (§ Where the gates fire)

---

Referência operativa — Simmetric Chat v0.28.1 (documentos atualizados a 2026-10-02, SHA do census `aca5b8ff`).