cost

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Apr 10, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package cost provides cost tracking and reporting for bc agents.

The package supports both SQLite and PostgreSQL backends for persistent storage of cost records and budgets. Use OpenStore to automatically select the backend (Postgres via DATABASE_URL, falling back to SQLite).

Basic Usage

Create and open a cost store:

store := cost.NewStore("/path/to/workspace")
if err := store.Open(); err != nil {
    log.Fatal(err)
}
defer store.Close()

Record a cost entry:

record, err := store.Record("agent-1", "team-alpha", "claude-3-opus",
    1000,  // input tokens
    500,   // output tokens
    0.05,  // cost in USD
)

Get cost summaries:

// By agent
summaries, _ := store.SummaryByAgent()

// By model
summaries, _ := store.SummaryByModel()

// Total workspace cost
total, _ := store.WorkspaceSummary()

Budgets

Set and check budgets:

// Set monthly budget for workspace
store.SetBudget("workspace", cost.BudgetPeriodMonthly, 100.0, 0.8, false)

// Check budget status
status, _ := store.CheckBudget("workspace")
if status.IsNearLimit {
    log.Warn("approaching budget limit")
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CalcCost

func CalcCost(model string, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens int64) float64

CalcCost returns the USD cost for the given token counts and model.

func FindSessionFiles

func FindSessionFiles(claudeProjectsDir string) ([]string, error)

FindSessionFiles returns all .jsonl session file paths under the given Claude projects root directory (~/.claude/projects/).

Types

type AgentDailyCost

type AgentDailyCost struct {
	AgentID      string  `json:"agent_id"`
	Date         string  `json:"date"`
	CostUSD      float64 `json:"cost_usd"`
	TotalTokens  int64   `json:"total_tokens"`
	RecordCount  int64   `json:"record_count"`
	InputTokens  int64   `json:"input_tokens"`
	OutputTokens int64   `json:"output_tokens"`
}

AgentDailyCost represents daily cost data for a specific agent.

type Budget

type Budget struct {
	UpdatedAt time.Time    `json:"updated_at"`
	Period    BudgetPeriod `json:"period"`
	Scope     string       `json:"scope"` // "workspace", "agent:<id>", "team:<id>"
	ID        int64        `json:"id"`
	LimitUSD  float64      `json:"limit_usd"`
	AlertAt   float64      `json:"alert_at"`  // Percentage (0.0-1.0) at which to alert
	HardStop  bool         `json:"hard_stop"` // If true, stop when limit reached
}

Budget represents a cost budget configuration.

type BudgetPeriod

type BudgetPeriod string

BudgetPeriod represents the time period for a budget.

const (
	BudgetPeriodDaily   BudgetPeriod = "daily"
	BudgetPeriodWeekly  BudgetPeriod = "weekly"
	BudgetPeriodMonthly BudgetPeriod = "monthly"
)

type BudgetStatus

type BudgetStatus struct {
	Budget       *Budget `json:"budget"`
	CurrentSpend float64 `json:"current_spend"`
	Remaining    float64 `json:"remaining"`
	PercentUsed  float64 `json:"percent_used"`
	IsOverBudget bool    `json:"is_over_budget"`
	IsNearLimit  bool    `json:"is_near_limit"` // True if >= AlertAt percentage
}

BudgetStatus represents the current status against a budget.

type CostBackend

type CostBackend interface {
	// Lifecycle
	Close() error
	DB() *sql.DB

	// Record operations
	Record(ctx context.Context, agentID, teamID, model string, inputTokens, outputTokens int64, costUSD float64) (*Record, error)
	GetByID(ctx context.Context, id int64) (*Record, error)
	GetByAgent(ctx context.Context, agentID string, limit int) ([]*Record, error)
	GetByAgentWithOffset(ctx context.Context, agentID string, limit, offset int) ([]*Record, error)
	GetByTeam(ctx context.Context, teamID string, limit int) ([]*Record, error)
	GetAll(ctx context.Context, limit int) ([]*Record, error)
	GetAllWithOffset(ctx context.Context, limit, offset int) ([]*Record, error)
	Clear(ctx context.Context) error

	// Summary operations
	SummaryByAgent(ctx context.Context) ([]*Summary, error)
	SummaryByTeam(ctx context.Context) ([]*Summary, error)
	SummaryByModel(ctx context.Context) ([]*Summary, error)
	WorkspaceSummary(ctx context.Context) (*Summary, error)
	AgentSummary(ctx context.Context, agentID string) (*Summary, error)
	TeamSummary(ctx context.Context, teamID string) (*Summary, error)
	GetSummarySince(ctx context.Context, since time.Time) (*Summary, error)
	GetAgentSummarySince(ctx context.Context, since time.Time) ([]*Summary, error)

	// Budget operations
	SetBudget(ctx context.Context, scope string, period BudgetPeriod, limitUSD, alertAt float64, hardStop bool) (*Budget, error)
	GetBudget(ctx context.Context, scope string) (*Budget, error)
	GetAllBudgets(ctx context.Context) ([]*Budget, error)
	DeleteBudget(ctx context.Context, scope string) error
	CheckBudget(ctx context.Context, scope string) (*BudgetStatus, error)

	// Daily cost operations
	GetDailyCosts(ctx context.Context, since time.Time) ([]*DailyCost, error)
	GetAgentDailyCosts(ctx context.Context, since time.Time) ([]*AgentDailyCost, error)

	// Projection
	ProjectCost(ctx context.Context, lookbackDays int, projectDuration time.Duration) (*Projection, error)
}

CostBackend is the storage interface implemented by both SQLiteStore (the existing Store) and PostgresStore. Higher-level wrappers delegate all persistence operations to this interface.

type DailyCost

type DailyCost struct {
	Date         string  `json:"date"`
	CostUSD      float64 `json:"cost_usd"`
	TotalTokens  int64   `json:"total_tokens"`
	RecordCount  int64   `json:"record_count"`
	InputTokens  int64   `json:"input_tokens"`
	OutputTokens int64   `json:"output_tokens"`
}

DailyCost represents aggregated cost data for a single day.

type Importer

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

Importer scans Claude Code JSONL session files and imports token usage into the cost.db store. It tracks which session files have been imported to avoid double-counting.

func NewImporter

func NewImporter(store *Store, workspaceDir string) *Importer

NewImporter creates an Importer for the given workspace.

func (*Importer) ImportAll

func (imp *Importer) ImportAll(ctx context.Context) (int, error)

ImportAll scans all known Claude projects directories and imports new sessions. It is safe to call repeatedly — already-imported sessions are skipped.

type ModelPricing

type ModelPricing struct {
	InputPerM      float64
	OutputPerM     float64
	CacheWritePerM float64 // cache_creation_input_tokens
	CacheReadPerM  float64 // cache_read_input_tokens
}

ModelPricing holds per-token pricing for a Claude model in USD per 1M tokens.

func PricingFor

func PricingFor(model string) ModelPricing

PricingFor returns the pricing for a model (matched by prefix, case-insensitive prefix walk).

type PostgresStore

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

PostgresStore provides Postgres-backed cost tracking. It implements CostBackend with the same API as the SQLite Store.

func NewPostgresStore

func NewPostgresStore(db *sql.DB) *PostgresStore

NewPostgresStore creates a PostgresStore from an existing *sql.DB connection.

func (*PostgresStore) AgentSummary

func (s *PostgresStore) AgentSummary(ctx context.Context, agentID string) (*Summary, error)

AgentSummary returns the cost summary for a specific agent.

func (*PostgresStore) CheckBudget

func (s *PostgresStore) CheckBudget(ctx context.Context, scope string) (*BudgetStatus, error)

CheckBudget returns the current status against a budget.

func (*PostgresStore) Clear

func (s *PostgresStore) Clear(ctx context.Context) error

Clear removes all cost records.

func (*PostgresStore) Close

func (s *PostgresStore) Close() error

Close is a no-op — the shared DB is owned by the caller.

func (*PostgresStore) DB

func (s *PostgresStore) DB() *sql.DB

DB returns the underlying database connection.

func (*PostgresStore) DeleteBudget

func (s *PostgresStore) DeleteBudget(ctx context.Context, scope string) error

DeleteBudget removes a budget for the given scope.

func (*PostgresStore) GetAgentDailyCosts

func (s *PostgresStore) GetAgentDailyCosts(ctx context.Context, since time.Time) ([]*AgentDailyCost, error)

GetAgentDailyCosts returns daily cost totals per agent since the given time.

func (*PostgresStore) GetAgentSummarySince

func (s *PostgresStore) GetAgentSummarySince(ctx context.Context, since time.Time) ([]*Summary, error)

GetAgentSummarySince returns per-agent summaries since the given time.

func (*PostgresStore) GetAll

func (s *PostgresStore) GetAll(ctx context.Context, limit int) ([]*Record, error)

GetAll returns all cost records.

func (*PostgresStore) GetAllBudgets

func (s *PostgresStore) GetAllBudgets(ctx context.Context) ([]*Budget, error)

GetAllBudgets returns all configured budgets.

func (*PostgresStore) GetAllWithOffset

func (s *PostgresStore) GetAllWithOffset(ctx context.Context, limit, offset int) ([]*Record, error)

GetAllWithOffset returns cost records with pagination.

func (*PostgresStore) GetBudget

func (s *PostgresStore) GetBudget(ctx context.Context, scope string) (*Budget, error)

GetBudget returns the budget for a given scope.

func (*PostgresStore) GetByAgent

func (s *PostgresStore) GetByAgent(ctx context.Context, agentID string, limit int) ([]*Record, error)

GetByAgent returns cost records for an agent.

func (*PostgresStore) GetByAgentWithOffset

func (s *PostgresStore) GetByAgentWithOffset(ctx context.Context, agentID string, limit, offset int) ([]*Record, error)

GetByAgentWithOffset returns cost records for an agent with pagination.

func (*PostgresStore) GetByID

func (s *PostgresStore) GetByID(ctx context.Context, id int64) (*Record, error)

GetByID returns a cost record by ID.

func (*PostgresStore) GetByTeam

func (s *PostgresStore) GetByTeam(ctx context.Context, teamID string, limit int) ([]*Record, error)

GetByTeam returns cost records for a team.

func (*PostgresStore) GetDailyCosts

func (s *PostgresStore) GetDailyCosts(ctx context.Context, since time.Time) ([]*DailyCost, error)

GetDailyCosts returns daily cost totals since the given time.

func (*PostgresStore) GetSummarySince

func (s *PostgresStore) GetSummarySince(ctx context.Context, since time.Time) (*Summary, error)

GetSummarySince returns a summary of costs since the given time.

func (*PostgresStore) InitSchema

func (s *PostgresStore) InitSchema() error

InitSchema creates the cost tables in Postgres if they don't exist.

func (*PostgresStore) ProjectCost

func (s *PostgresStore) ProjectCost(ctx context.Context, lookbackDays int, projectDuration time.Duration) (*Projection, error)

ProjectCost calculates a projected cost based on historical daily average.

func (*PostgresStore) Record

func (s *PostgresStore) Record(ctx context.Context, agentID, teamID, model string, inputTokens, outputTokens int64, costUSD float64) (*Record, error)

Record adds a new cost record.

func (*PostgresStore) SetBudget

func (s *PostgresStore) SetBudget(ctx context.Context, scope string, period BudgetPeriod, limitUSD, alertAt float64, hardStop bool) (*Budget, error)

SetBudget creates or updates a budget for the given scope.

func (*PostgresStore) SummaryByAgent

func (s *PostgresStore) SummaryByAgent(ctx context.Context) ([]*Summary, error)

SummaryByAgent returns aggregated costs per agent.

func (*PostgresStore) SummaryByModel

func (s *PostgresStore) SummaryByModel(ctx context.Context) ([]*Summary, error)

SummaryByModel returns aggregated costs per model.

func (*PostgresStore) SummaryByTeam

func (s *PostgresStore) SummaryByTeam(ctx context.Context) ([]*Summary, error)

SummaryByTeam returns aggregated costs per team.

func (*PostgresStore) TeamSummary

func (s *PostgresStore) TeamSummary(ctx context.Context, teamID string) (*Summary, error)

TeamSummary returns the cost summary for a specific team.

func (*PostgresStore) WorkspaceSummary

func (s *PostgresStore) WorkspaceSummary(ctx context.Context) (*Summary, error)

WorkspaceSummary returns the total cost summary for the workspace.

type Projection

type Projection struct {
	Duration        time.Duration `json:"duration"`
	DailyAvgCost    float64       `json:"daily_avg_cost"`
	ProjectedCost   float64       `json:"projected_cost"`
	DaysAnalyzed    int           `json:"days_analyzed"`
	TotalHistorical float64       `json:"total_historical"`
}

Projection represents a cost projection based on historical data.

type Record

type Record struct {
	Timestamp    time.Time `json:"timestamp"`
	AgentID      string    `json:"agent_id"`
	Model        string    `json:"model"`
	TeamID       string    `json:"team_id,omitempty"`
	ID           int64     `json:"id"`
	InputTokens  int64     `json:"input_tokens"`
	OutputTokens int64     `json:"output_tokens"`
	TotalTokens  int64     `json:"total_tokens"`
	CostUSD      float64   `json:"cost_usd"`
}

Record represents a cost entry for an API call.

type SessionEntry

type SessionEntry struct {
	Timestamp           time.Time
	SessionID           string
	Model               string
	CWD                 string
	InputTokens         int64
	OutputTokens        int64
	CacheCreationTokens int64
	CacheReadTokens     int64
}

SessionEntry is a parsed assistant message from a Claude Code JSONL session file.

func ParseSessionFile

func ParseSessionFile(path string) ([]SessionEntry, error)

ParseSessionFile reads a Claude Code JSONL session file and returns all assistant message entries that contain token usage.

type Store

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

Store provides cost tracking backed by SQLite or Postgres. When created via OpenStore, the backend field is set and all operations are delegated to it. When created via NewStore/Open (legacy), the store uses its embedded SQLite connection directly.

func NewStore

func NewStore(workspacePath string) *Store

NewStore creates a new cost store for the given workspace.

func OpenStore

func OpenStore(workspacePath string) (*Store, error)

OpenStore opens the cost store using the shared workspace database. Uses the shared driver type to determine the backend (timescale or sqlite).

func (*Store) AgentSummary

func (s *Store) AgentSummary(ctx context.Context, agentID string) (*Summary, error)

AgentSummary returns the cost summary for a specific agent.

func (*Store) CheckBudget

func (s *Store) CheckBudget(ctx context.Context, scope string) (*BudgetStatus, error)

CheckBudget returns the current status against a budget.

func (*Store) Clear

func (s *Store) Clear(ctx context.Context) error

Clear removes all cost records.

func (*Store) Close

func (s *Store) Close() error

Close is a no-op — the shared DB is owned by the caller. The Postgres backend also uses the shared connection now.

func (*Store) DB

func (s *Store) DB() *sql.DB

DB returns the underlying database connection.

func (*Store) DeleteBudget

func (s *Store) DeleteBudget(ctx context.Context, scope string) error

DeleteBudget removes a budget for the given scope.

func (*Store) GetAgentDailyCosts

func (s *Store) GetAgentDailyCosts(ctx context.Context, since time.Time) ([]*AgentDailyCost, error)

GetAgentDailyCosts returns daily cost totals per agent since the given time.

func (*Store) GetAgentSummarySince

func (s *Store) GetAgentSummarySince(ctx context.Context, since time.Time) ([]*Summary, error)

GetAgentSummarySince returns per-agent summaries since the given time.

func (*Store) GetAll

func (s *Store) GetAll(ctx context.Context, limit int) ([]*Record, error)

GetAll returns all cost records.

func (*Store) GetAllBudgets

func (s *Store) GetAllBudgets(ctx context.Context) ([]*Budget, error)

GetAllBudgets returns all configured budgets.

func (*Store) GetAllWithOffset

func (s *Store) GetAllWithOffset(ctx context.Context, limit, offset int) ([]*Record, error)

GetAllWithOffset returns cost records with pagination support.

func (*Store) GetBudget

func (s *Store) GetBudget(ctx context.Context, scope string) (*Budget, error)

GetBudget returns the budget for a given scope.

func (*Store) GetByAgent

func (s *Store) GetByAgent(ctx context.Context, agentID string, limit int) ([]*Record, error)

GetByAgent returns all cost records for an agent.

func (*Store) GetByAgentWithOffset

func (s *Store) GetByAgentWithOffset(ctx context.Context, agentID string, limit, offset int) ([]*Record, error)

GetByAgentWithOffset returns cost records for an agent with pagination support.

func (*Store) GetByID

func (s *Store) GetByID(ctx context.Context, id int64) (*Record, error)

GetByID returns a cost record by ID.

func (*Store) GetByTeam

func (s *Store) GetByTeam(ctx context.Context, teamID string, limit int) ([]*Record, error)

GetByTeam returns all cost records for a team.

func (*Store) GetDailyCosts

func (s *Store) GetDailyCosts(ctx context.Context, since time.Time) ([]*DailyCost, error)

GetDailyCosts returns daily cost totals since the given time.

func (*Store) GetSummarySince

func (s *Store) GetSummarySince(ctx context.Context, since time.Time) (*Summary, error)

GetSummarySince returns a summary of costs since the given time.

func (*Store) Open

func (s *Store) Open() error

Open initializes the SQLite database.

func (*Store) ProjectCost

func (s *Store) ProjectCost(ctx context.Context, lookbackDays int, projectDuration time.Duration) (*Projection, error)

ProjectCost calculates a projected cost based on historical daily average.

func (*Store) Record

func (s *Store) Record(ctx context.Context, agentID, teamID, model string, inputTokens, outputTokens int64, costUSD float64) (*Record, error)

Record adds a new cost record.

func (*Store) SetBudget

func (s *Store) SetBudget(ctx context.Context, scope string, period BudgetPeriod, limitUSD, alertAt float64, hardStop bool) (*Budget, error)

SetBudget creates or updates a budget for the given scope.

func (*Store) SummaryByAgent

func (s *Store) SummaryByAgent(ctx context.Context) ([]*Summary, error)

SummaryByAgent returns aggregated costs per agent.

func (*Store) SummaryByModel

func (s *Store) SummaryByModel(ctx context.Context) ([]*Summary, error)

SummaryByModel returns aggregated costs per model.

func (*Store) SummaryByTeam

func (s *Store) SummaryByTeam(ctx context.Context) ([]*Summary, error)

SummaryByTeam returns aggregated costs per team.

func (*Store) TeamSummary

func (s *Store) TeamSummary(ctx context.Context, teamID string) (*Summary, error)

TeamSummary returns the cost summary for a specific team.

func (*Store) WorkspaceSummary

func (s *Store) WorkspaceSummary(ctx context.Context) (*Summary, error)

WorkspaceSummary returns the total cost summary for the entire workspace.

type Summary

type Summary struct {
	AgentID      string  `json:"agent_id,omitempty"`
	TeamID       string  `json:"team_id,omitempty"`
	Model        string  `json:"model,omitempty"`
	InputTokens  int64   `json:"input_tokens"`
	OutputTokens int64   `json:"output_tokens"`
	TotalTokens  int64   `json:"total_tokens"`
	TotalCostUSD float64 `json:"total_cost_usd"`
	RecordCount  int64   `json:"record_count"`
}

Summary represents aggregated cost data.

Jump to

Keyboard shortcuts

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