orgusage

package
v0.47.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package orgusage meters per-org (tenant) monthly run launches and LLM spend, and enforces the launch-time caps. It is the billing source of truth in cloud mode: Prometheus counters stay global (no tenant labels — cardinality discipline), while these Mongo-backed counters answer "how much did this org consume this month".

The shape deliberately mirrors pkg/webhooks' Counter (same CAS strategy, same month-bucketed document ids) so operators reasoning about one quota system can reason about the other.

Index

Constants

View Source
const RetentionDays = 400

RetentionDays bounds how long monthly usage documents are retained (Mongo TTL). 400 days keeps a full year of history plus margin for an annual billing cycle.

Variables

This section is empty.

Functions

func CostToMillis

func CostToMillis(usd float64) int64

CostToMillis converts a USD amount to integer thousandths so the Mongo $inc stays integral (float $inc would accumulate drift) and the launch gate can express Team.MonthlyCostCapUSD in the counter's native unit.

func EnsureSchema

func EnsureSchema(ctx context.Context, db *mongo.Database) error

EnsureSchema creates the usage indexes idempotently.

Types

type Counter

type Counter interface {
	// AllowRun atomically increments the month's launched-run counter
	// and checks BOTH launch-time caps against the post-increment
	// document in one round trip: maxRuns on the run count and
	// maxCostMillis on the accumulated spend (each 0 = no cap; with no
	// caps the increment still happens — that is the metering). A
	// denied call rolls the increment back and reports which cap hit.
	// The cost check is a soft cap by nature (a run's future spend is
	// unknowable) — in-flight runs finish, new launches are denied.
	AllowRun(ctx context.Context, tenantID string, when time.Time, maxRuns int, maxCostMillis int64) (DenyReason, error)
	// AddSpend accumulates post-hoc LLM cost/token usage for the
	// month. Never gates — AllowRun enforces the cap pre-launch.
	AddSpend(ctx context.Context, tenantID string, when time.Time, costUSD float64, inputTokens, outputTokens int64) error
	// ReleaseRun undoes one AllowRun admission whose launch was
	// ultimately abandoned without any run being created (e.g. the
	// loser of two concurrent duplicate webhook deliveries). Decrements
	// the month's run counter; a missing month document is a no-op.
	ReleaseRun(ctx context.Context, tenantID string, when time.Time) error
	// Usage returns the month's counters for the org. A month with no
	// activity returns the zero value (Month still filled).
	Usage(ctx context.Context, tenantID string, when time.Time) (MonthlyUsage, error)
}

Counter is the per-org monthly metering + enforcement surface. Implementations: MongoCounter (production, atomic CAS) and MemoryCounter (tests/local). Keep semantics in lock-step.

type DenyReason

type DenyReason string

DenyReason qualifies an AllowRun refusal so the launch gate can map it onto the right stable denial token.

const (
	// DenyNone — the launch is allowed (and metered).
	DenyNone DenyReason = ""
	// DenyRuns — the monthly run quota is exhausted.
	DenyRuns DenyReason = "runs"
	// DenyCost — the month's accumulated LLM spend has reached the cap.
	DenyCost DenyReason = "cost"
)

type MemoryCounter

type MemoryCounter struct {
	// contains filtered or unexported fields
}

MemoryCounter is the in-process Counter for tests and local mode. Keep its semantics in lock-step with MongoCounter.

func NewMemoryCounter

func NewMemoryCounter() *MemoryCounter

func (*MemoryCounter) AddSpend

func (c *MemoryCounter) AddSpend(_ context.Context, tenantID string, when time.Time, costUSD float64, inputTokens, outputTokens int64) error

func (*MemoryCounter) AllowRun

func (c *MemoryCounter) AllowRun(_ context.Context, tenantID string, when time.Time, maxRuns int, maxCostMillis int64) (DenyReason, error)

func (*MemoryCounter) ReleaseRun added in v0.40.0

func (c *MemoryCounter) ReleaseRun(_ context.Context, tenantID string, when time.Time) error

func (*MemoryCounter) Usage

func (c *MemoryCounter) Usage(_ context.Context, tenantID string, when time.Time) (MonthlyUsage, error)

type MongoCounter

type MongoCounter struct {
	// contains filtered or unexported fields
}

MongoCounter is the production Counter. One document per (org, month); all increments go through findOneAndUpdate / $inc so the allow/deny decision is atomic per call (same CAS strategy as webhooks.MongoCounter).

func NewMongoCounter

func NewMongoCounter(db *mongo.Database) *MongoCounter

func (*MongoCounter) AddSpend

func (c *MongoCounter) AddSpend(ctx context.Context, tenantID string, when time.Time, costUSD float64, inputTokens, outputTokens int64) error

func (*MongoCounter) AllowRun

func (c *MongoCounter) AllowRun(ctx context.Context, tenantID string, when time.Time, maxRuns int, maxCostMillis int64) (DenyReason, error)

func (*MongoCounter) ReleaseRun added in v0.40.0

func (c *MongoCounter) ReleaseRun(ctx context.Context, tenantID string, when time.Time) error

ReleaseRun mirrors the deny-path rollback in AllowRun: one $inc -1 on the month's run counter, no upsert (a TTL-evicted or never-created month document stays absent — decrementing a fresh doc below zero would corrupt the metering).

func (*MongoCounter) Usage

func (c *MongoCounter) Usage(ctx context.Context, tenantID string, when time.Time) (MonthlyUsage, error)

type MonthlyUsage

type MonthlyUsage struct {
	// Month is the UTC bucket key, e.g. "2026-06".
	Month string `json:"month"`
	// Runs counts run launches accepted this month (REST + webhook +
	// resume all consume the same budget — a resume re-enters the
	// engine and spends like a launch).
	Runs int `json:"runs"`
	// CostUSD is the metered LLM spend accumulated by runners. Claw
	// (in-process) nodes are metered precisely; delegate backends
	// (claude_code) report tokens without a price table, so their
	// cost contribution is zero — treat this as a floor, not an
	// exact invoice.
	CostUSD      float64 `json:"cost_usd"`
	InputTokens  int64   `json:"input_tokens"`
	OutputTokens int64   `json:"output_tokens"`
}

MonthlyUsage is the read view of one org's current-month counters.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL