costs

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2026 License: Apache-2.0 Imports: 7 Imported by: 1

Documentation

Overview

Package costs provides a pluggable cost translation layer for ark. It follows the same registration pattern as internal/agent/providers and internal/agent/queue: implementations register via RegisterCostProvider and are selected by name via NewCostProvider.

The default provider ("static") uses hardcoded pricing tables for Anthropic and OpenAI models. Unknown models (e.g. local Ollama) return zero cost rather than an error, so they are tracked as free rather than causing reconcile failures.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Backends

func Backends() []string

Backends returns the sorted names of all registered CostProvider implementations.

func PeriodWindowStart

func PeriodWindowStart(period string) time.Time

PeriodWindowStart returns the start of the current budget window for the given period string. Accepts "daily"/"day", "weekly"/"week", "monthly"/"month".

func RegisterCostProvider

func RegisterCostProvider(name string, f Factory)

RegisterCostProvider makes a CostProvider available under the given name. Typically called from an init() function in the implementation package.

func RegisterSpendStore

func RegisterSpendStore(scheme string, f SpendStoreFactory)

RegisterSpendStore registers a SpendStore factory under a scheme name. Call from an init() function so blank-importing the package activates it.

func SpendStoreBackends

func SpendStoreBackends() []string

SpendStoreBackends returns the sorted names of all registered schemes.

func SumStepCosts

func SumStepCosts(steps []StepCost) float64

SumStepCosts returns the total cost in USD across all steps that have a CostUSD set. Used by flow/phase.go to accumulate run-level cost.

func TruncateToPeriod

func TruncateToPeriod(t time.Time, p Period) time.Time

TruncateToPeriod truncates t to the start of the given period bucket.

Types

type BudgetDecision

type BudgetDecision struct {
	// Status is the outcome: OK, Warning, or Exceeded.
	Status BudgetStatus
	// SpentUSD is the total spend for the period.
	SpentUSD float64
	// LimitUSD is the configured limit.
	LimitUSD float64
	// PctUsed is SpentUSD / LimitUSD expressed as a value from 0 to 100+.
	PctUsed float64
	// Message is a human-readable summary.
	Message string
}

BudgetDecision is the result of a budget policy evaluation.

type BudgetInput

type BudgetInput struct {
	// Selector mirrors ArkBudgetSelector fields.
	Namespace string
	Team      string

	// Period is "daily", "weekly", or "monthly".
	Period string

	// Limit is the maximum spend for the period.
	Limit float64

	// WarnAt is the warn threshold as a percentage (0–100).
	WarnAt int
}

BudgetInput contains everything needed to evaluate a budget, without importing the API types (avoids import cycles).

type BudgetPolicy

type BudgetPolicy interface {
	// Evaluate returns a decision based on current SpendStore data.
	// store and scope are provided by the caller; the policy calls store.Total.
	Evaluate(ctx context.Context, input BudgetInput, store SpendStore) (BudgetDecision, error)
}

BudgetPolicy evaluates current spend against a budget limit.

func DefaultBudgetPolicy

func DefaultBudgetPolicy() BudgetPolicy

DefaultBudgetPolicy returns the standard built-in policy.

type BudgetStatus

type BudgetStatus string

BudgetStatus mirrors arkonisv1alpha1.BudgetStatus to avoid an import cycle.

const (
	BudgetOK       BudgetStatus = "OK"
	BudgetWarning  BudgetStatus = "Warning"
	BudgetExceeded BudgetStatus = "Exceeded"
)

type ConfigMapCostProvider

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

ConfigMapCostProvider reads pricing from a flat key→value map loaded from a Kubernetes ConfigMap. The operator loads this once at startup via NewConfigMapCostProvider and watches the ConfigMap for changes; call Reload(data) whenever the ConfigMap's Data field changes.

ConfigMap format (each key is a model name prefix):

claude-opus-4:    "15.00/75.00"   # input_per_1m/output_per_1m USD
my-custom-model:  "0.50/1.00"
local-model:      "0.00/0.00"

func NewConfigMapCostProvider

func NewConfigMapCostProvider(data map[string]string, fallback CostProvider) (*ConfigMapCostProvider, error)

NewConfigMapCostProvider creates a provider pre-loaded with the given ConfigMap data map. Pass the static provider as fallback so unknown models still get a reasonable cost rather than $0.

func (*ConfigMapCostProvider) Cost

func (p *ConfigMapCostProvider) Cost(model string, inputTokens, outputTokens int64) float64

Cost returns the dollar cost for the given model and token counts. Uses longest-prefix match against the loaded ConfigMap entries. Falls back to the fallback provider if no prefix matches.

func (*ConfigMapCostProvider) Currency

func (p *ConfigMapCostProvider) Currency() string

func (*ConfigMapCostProvider) Reload

func (p *ConfigMapCostProvider) Reload(data map[string]string) error

Reload replaces the pricing table with fresh data from a ConfigMap. Call this whenever the ConfigMap's Data field is updated (via a Watch event). Thread-safe; the provider continues serving requests during the reload.

type CostProvider

type CostProvider interface {
	// Cost returns the dollar cost for the given model and token counts.
	// Returns 0.0 for unknown models rather than an error.
	Cost(model string, inputTokens, outputTokens int64) float64

	// Currency returns the ISO 4217 currency code (e.g. "USD").
	Currency() string
}

CostProvider translates token usage into a dollar cost for a given model.

func Default

func Default() CostProvider

Default returns the static cost provider. Convenience wrapper for callers that don't need configurability — zero cost on unknown models, no error path.

func NewCostProvider

func NewCostProvider(name string) (CostProvider, error)

NewCostProvider returns the named CostProvider, or the "static" default when name is empty. Returns an error only if the name is non-empty and unknown.

type Factory

type Factory func() CostProvider

Factory constructs a CostProvider.

type NoopSpendStore

type NoopSpendStore struct{}

NoopSpendStore silently discards all writes. Useful for deployments that only want cost-per-run tracking (Phase 1) without full spend history.

func (NoopSpendStore) Record

func (NoopSpendStore) Rollup

func (NoopSpendStore) Total

type Period

type Period string

Period controls the time bucket granularity for rollup queries.

const (
	PeriodDay   Period = "day"
	PeriodWeek  Period = "week"
	PeriodMonth Period = "month"
)

type RollupEntry

type RollupEntry struct {
	Date         time.Time // start of the bucket (truncated to Period)
	Namespace    string
	Team         string
	Model        string
	TotalCostUSD float64
	InputTokens  int64
	OutputTokens int64
	RunCount     int64
}

RollupEntry is one time-bucket of aggregated spend.

type SpendEntry

type SpendEntry struct {
	Timestamp    time.Time
	Namespace    string
	Team         string
	RunName      string
	StepName     string
	Model        string
	InputTokens  int64
	OutputTokens int64
	CostUSD      float64
}

SpendEntry records the cost of a single pipeline step.

type SpendScope

type SpendScope struct {
	Namespace string // empty = all namespaces
	Team      string // empty = all teams
	Model     string // empty = all models
}

SpendScope filters queries to a subset of entries. Empty strings mean "all values".

type SpendStore

type SpendStore interface {
	// Record saves one step's spend. Called after each step succeeds.
	Record(ctx context.Context, entry SpendEntry) error

	// Rollup returns spend aggregated into period buckets for a scope.
	Rollup(ctx context.Context, scope SpendScope, period Period, since time.Time) ([]RollupEntry, error)

	// Total returns the sum of CostUSD for a scope since the given time.
	// Used by BudgetPolicy to compare against a spend limit.
	Total(ctx context.Context, scope SpendScope, since time.Time) (float64, error)
}

SpendStore records and queries historical pipeline spend. All writes are best-effort; implementations should log errors but must not return errors that would fail the reconcile loop.

func NewSpendStore

func NewSpendStore(url string) (SpendStore, error)

NewSpendStore returns a SpendStore for the given URL. The URL scheme selects the backend (e.g. "redis://localhost:6379"). Returns a NoopSpendStore when url is empty.

type SpendStoreFactory

type SpendStoreFactory func(url string) (SpendStore, error)

SpendStoreFactory constructs a SpendStore from a connection URL. The URL scheme selects the backend (e.g. "redis://...").

type StandardBudgetPolicy

type StandardBudgetPolicy struct{}

StandardBudgetPolicy is the built-in budget policy. It calls SpendStore.Total for the current period window and compares it against the limit.

func (*StandardBudgetPolicy) Evaluate

Evaluate checks current spend for the period and returns OK / Warning / Exceeded.

type StaticCostProvider

type StaticCostProvider struct{}

StaticCostProvider uses the hardcoded pricing table above. Unknown models return 0.0 (treated as free).

func (*StaticCostProvider) Cost

func (p *StaticCostProvider) Cost(model string, inputTokens, outputTokens int64) float64

func (*StaticCostProvider) Currency

func (p *StaticCostProvider) Currency() string

type StepCost

type StepCost struct {
	CostUSD float64
}

StepCost is a minimal struct used to pass step cost data into SumStepCosts without importing the api/v1alpha1 package (avoids import cycles).

Directories

Path Synopsis
Package memstore provides an in-memory SpendStore for local development, ark run, and unit tests.
Package memstore provides an in-memory SpendStore for local development, ark run, and unit tests.
Package redisstore registers a Redis-backed SpendStore.
Package redisstore registers a Redis-backed SpendStore.

Jump to

Keyboard shortcuts

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