Documentation
¶
Overview ¶
Package storage provides eyrie's persistence layer: virtual keys and an SQLite-backed budget store (BudgetStore / SQLiteStore) for tracking usage and spend limits.
Index ¶
- Variables
- type Alias
- type AnalyticsStore
- type BudgetStore
- func (s *BudgetStore) CheckBudget(ctx context.Context, virtualKey string, estCostUSD float64) error
- func (s *BudgetStore) Close() error
- func (s *BudgetStore) CreateVirtualKey(ctx context.Context, id, name, provider string, limitUSD float64) error
- func (s *BudgetStore) Get(ctx context.Context, virtualKey string) (*VirtualKey, error)
- func (s *BudgetStore) ProviderSecret(ctx context.Context, virtualKey, provider string) (string, error)
- func (s *BudgetStore) RecordUsage(ctx context.Context, virtualKey string, costUSD float64, ...) error
- func (s *BudgetStore) SetProviderSecret(ctx context.Context, virtualKey, provider, apiKey string) error
- type CostRecord
- type DAG
- func (d *DAG) Append(ctx context.Context, parentID string, role string, content string) (*DAGNode, error)
- func (d *DAG) Branches(ctx context.Context, nodeID string) ([]*DAGNode, error)
- func (d *DAG) Close() error
- func (d *DAG) Fork(ctx context.Context, nodeID string) (*DAGNode, error)
- func (d *DAG) Head(ctx context.Context) (*DAGNode, error)
- func (d *DAG) History(ctx context.Context, nodeID string) ([]*DAGNode, error)
- func (d *DAG) Prune(ctx context.Context, nodeID string) error
- func (d *DAG) SetHead(ctx context.Context, nodeID string) error
- type DAGNode
- type Node
- type NodeType
- type Option
- type ProviderHealthRecord
- type SQLiteAnalyticsStore
- func (a *SQLiteAnalyticsStore) GetCostSummary(ctx context.Context, since time.Time) ([]UsageStats, error)
- func (a *SQLiteAnalyticsStore) GetProviderHealth(ctx context.Context, since time.Time) ([]ProviderHealthRecord, error)
- func (a *SQLiteAnalyticsStore) GetUsageStats(ctx context.Context, since time.Time) ([]UsageStats, error)
- func (a *SQLiteAnalyticsStore) MigrateAnalytics() error
- func (a *SQLiteAnalyticsStore) RecordCost(ctx context.Context, rec *CostRecord) error
- type SQLiteStore
- func (s *SQLiteStore) Close() error
- func (s *SQLiteStore) CreateAlias(ctx context.Context, alias, nodeID string) error
- func (s *SQLiteStore) CreateNode(ctx context.Context, node *Node) error
- func (s *SQLiteStore) DeleteAlias(ctx context.Context, alias string) error
- func (s *SQLiteStore) DeleteNode(ctx context.Context, id string) error
- func (s *SQLiteStore) GetAncestors(ctx context.Context, id string) ([]*Node, error)
- func (s *SQLiteStore) GetNode(ctx context.Context, id string) (*Node, error)
- func (s *SQLiteStore) GetNodeByAlias(ctx context.Context, alias string) (*Node, error)
- func (s *SQLiteStore) GetNodeByPrefix(ctx context.Context, prefix string) (*Node, error)
- func (s *SQLiteStore) GetNodeChildren(ctx context.Context, id string) ([]*Node, error)
- func (s *SQLiteStore) GetOrphanedToolUses(ctx context.Context, ancestorIDs []string) (map[string][]string, error)
- func (s *SQLiteStore) GetSubtree(ctx context.Context, id string) ([]*Node, error)
- func (s *SQLiteStore) IndexToolIDs(ctx context.Context, nodeID string, toolIDs []string, role string) error
- func (s *SQLiteStore) ListAliases(ctx context.Context, nodeID string) ([]Alias, error)
- func (s *SQLiteStore) ListRootNodes(ctx context.Context) ([]*Node, error)
- func (s *SQLiteStore) UpdateNode(ctx context.Context, node *Node) error
- type Store
- type UsageStats
- type VirtualKey
Constants ¶
This section is empty.
Variables ¶
var ( ErrBudgetExceeded = errors.New("eyrie: virtual key budget exceeded") ErrUnknownVirtualKey = errors.New("eyrie: unknown virtual key") )
Budget sentinel errors. These mirror the client package's errors by message so callers can match on either; the BudgetProvider depends only on the structural method set, not on these specific values.
Functions ¶
This section is empty.
Types ¶
type AnalyticsStore ¶
type AnalyticsStore interface {
// RecordCost persists a cost record for a completed request.
RecordCost(ctx context.Context, rec *CostRecord) error
// GetUsageStats returns aggregated usage stats grouped by provider/model,
// filtered to records created after the given time.
GetUsageStats(ctx context.Context, since time.Time) ([]UsageStats, error)
// GetCostSummary returns total cost grouped by provider/model since the given time.
GetCostSummary(ctx context.Context, since time.Time) ([]UsageStats, error)
// GetProviderHealth returns health metrics derived from actual request data.
GetProviderHealth(ctx context.Context, since time.Time) ([]ProviderHealthRecord, error)
}
AnalyticsStore provides persistent cost tracking and usage analytics. It extends the base Store with analytics-specific tables and queries.
type BudgetStore ¶
type BudgetStore struct {
// contains filtered or unexported fields
}
BudgetStore is a SQLite-backed store for virtual keys, their budgets, and a per-request cost ledger. It satisfies the client.BudgetStore interface structurally (CheckBudget + RecordUsage) without importing the client package.
It is safe for concurrent use (single underlying connection, like SQLiteStore).
func OpenBudgetStore ¶
func OpenBudgetStore(path string) (*BudgetStore, error)
OpenBudgetStore opens (or creates) a SQLite database at path and ensures the budget schema exists. The database file is set to 0o600 because the virtual_key_secrets table stores provider API keys.
func (*BudgetStore) CheckBudget ¶
CheckBudget implements the client.BudgetStore contract: it returns ErrBudgetExceeded if charging estCostUSD would exceed the key's limit, ErrUnknownVirtualKey if the key is unknown, or nil otherwise.
func (*BudgetStore) Close ¶
func (s *BudgetStore) Close() error
Close releases the underlying database.
func (*BudgetStore) CreateVirtualKey ¶
func (s *BudgetStore) CreateVirtualKey(ctx context.Context, id, name, provider string, limitUSD float64) error
CreateVirtualKey inserts a virtual key with the given budget. A non-positive limitUSD means unlimited spend.
func (*BudgetStore) Get ¶
func (s *BudgetStore) Get(ctx context.Context, virtualKey string) (*VirtualKey, error)
Get returns the current state of a virtual key.
func (*BudgetStore) ProviderSecret ¶
func (s *BudgetStore) ProviderSecret(ctx context.Context, virtualKey, provider string) (string, error)
ProviderSecret returns the real API key for a virtual key + provider.
func (*BudgetStore) RecordUsage ¶
func (s *BudgetStore) RecordUsage(ctx context.Context, virtualKey string, costUSD float64, tokensIn, tokensOut int) error
RecordUsage implements the client.BudgetStore contract: it appends a ledger row and increments the running totals atomically.
func (*BudgetStore) SetProviderSecret ¶
func (s *BudgetStore) SetProviderSecret(ctx context.Context, virtualKey, provider, apiKey string) error
SetProviderSecret associates a real provider API key with a virtual key.
type CostRecord ¶
type CostRecord struct {
ID int64 `json:"id"`
Provider string `json:"provider"`
Model string `json:"model"`
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
CacheReadTokens int `json:"cache_read_tokens"`
CacheCreationTokens int `json:"cache_creation_tokens"`
CostUSD float64 `json:"cost_usd"`
LatencyMs int `json:"latency_ms"`
CreatedAt time.Time `json:"created_at"`
}
CostRecord represents a single persisted cost entry for an API request.
type DAG ¶
type DAG struct {
// contains filtered or unexported fields
}
DAG is a conversation stored as a directed acyclic graph.
func NewDAGFromStore ¶
NewDAGFromStore creates a DAG using an existing store, which is useful in tests and callers that manage store lifetime outside the DAG wrapper.
func (*DAG) Append ¶
func (d *DAG) Append(ctx context.Context, parentID string, role string, content string) (*DAGNode, error)
Append adds a new node as a child of the given parent and advances the head.
type DAGNode ¶
type DAGNode struct {
ID string
ParentID string
Role string
Content string
Model string
CreatedAt time.Time
Metadata map[string]string
}
DAGNode is a single message in the conversation DAG.
type Node ¶
type Node struct {
ID string `json:"id"`
ParentID string `json:"parent_id,omitempty"`
RootID string `json:"root_id,omitempty"`
Sequence int `json:"sequence"`
NodeType NodeType `json:"node_type"`
Content string `json:"content"`
Provider string `json:"provider,omitempty"`
Model string `json:"model,omitempty"`
TokensIn int `json:"tokens_in,omitempty"`
TokensOut int `json:"tokens_out,omitempty"`
TokensCacheRead int `json:"tokens_cache_read,omitempty"`
TokensCacheCreation int `json:"tokens_cache_creation,omitempty"`
TokensReasoning int `json:"tokens_reasoning,omitempty"`
LatencyMs int `json:"latency_ms,omitempty"`
StopReason string `json:"stop_reason,omitempty"`
OutputGroupID string `json:"output_group_id,omitempty"`
Status string `json:"status,omitempty"`
Title string `json:"title,omitempty"`
SystemPrompt string `json:"system_prompt,omitempty"`
CreatedAt time.Time `json:"created_at"`
Metadata json.RawMessage `json:"metadata,omitempty"`
}
Node is a single persisted entry in the conversation DAG.
type Option ¶
type Option func(*openConfig)
Option configures a SQLiteStore at Open time.
func WithMaxOpenConns ¶
WithMaxOpenConns overrides the database connection pool size. The default (1) serializes all access, which is safe for single-agent use; raise it for concurrent (e.g. HTTP server) workloads. SQLite still serializes writes via the busy_timeout pragma, so concurrent access remains bounded by WAL readers plus a single writer.
type ProviderHealthRecord ¶
type ProviderHealthRecord struct {
Provider string `json:"provider"`
RequestCount int `json:"request_count"`
ErrorCount int `json:"error_count"`
ErrorRate float64 `json:"error_rate"`
AvgLatencyMs float64 `json:"avg_latency_ms"`
P50LatencyMs float64 `json:"p50_latency_ms"`
P95LatencyMs float64 `json:"p95_latency_ms"`
P99LatencyMs float64 `json:"p99_latency_ms"`
LastRequestAt time.Time `json:"last_request_at"`
}
ProviderHealthRecord holds per-provider health data from the nodes table.
type SQLiteAnalyticsStore ¶
type SQLiteAnalyticsStore struct {
// contains filtered or unexported fields
}
SQLiteAnalyticsStore implements AnalyticsStore backed by SQLite.
func NewSQLiteAnalyticsStore ¶
func NewSQLiteAnalyticsStore(s *SQLiteStore) *SQLiteAnalyticsStore
NewSQLiteAnalyticsStore wraps an existing SQLiteStore's database connection for analytics operations. The caller must ensure the underlying DB has been migrated (i.e. the SQLiteStore was opened via Open).
func NewSQLiteAnalyticsStoreFromDB ¶
func NewSQLiteAnalyticsStoreFromDB(db *sql.DB) *SQLiteAnalyticsStore
NewSQLiteAnalyticsStoreFromDB creates an analytics store from a raw DB handle.
func (*SQLiteAnalyticsStore) GetCostSummary ¶
func (a *SQLiteAnalyticsStore) GetCostSummary(ctx context.Context, since time.Time) ([]UsageStats, error)
GetCostSummary returns total cost grouped by provider/model from cost_records.
func (*SQLiteAnalyticsStore) GetProviderHealth ¶
func (a *SQLiteAnalyticsStore) GetProviderHealth(ctx context.Context, since time.Time) ([]ProviderHealthRecord, error)
GetProviderHealth returns health metrics derived from actual request data in the nodes table, computed over the given time window.
func (*SQLiteAnalyticsStore) GetUsageStats ¶
func (a *SQLiteAnalyticsStore) GetUsageStats(ctx context.Context, since time.Time) ([]UsageStats, error)
GetUsageStats returns aggregated usage statistics from the nodes table, grouped by provider and model, for nodes created after 'since'.
func (*SQLiteAnalyticsStore) MigrateAnalytics ¶
func (a *SQLiteAnalyticsStore) MigrateAnalytics() error
MigrateAnalytics creates the analytics-specific tables. It is safe to call multiple times (uses IF NOT EXISTS).
func (*SQLiteAnalyticsStore) RecordCost ¶
func (a *SQLiteAnalyticsStore) RecordCost(ctx context.Context, rec *CostRecord) error
RecordCost persists a cost record.
type SQLiteStore ¶
type SQLiteStore struct {
// contains filtered or unexported fields
}
func Open ¶
func Open(path string, opts ...Option) (*SQLiteStore, error)
Open opens the SQLite conversation DAG store at path. The store is opened in WAL mode with foreign keys enabled. Without options the connection pool is limited to a single connection (serialized access); pass WithMaxOpenConns to raise the pool size for concurrent workloads.
func (*SQLiteStore) Close ¶
func (s *SQLiteStore) Close() error
func (*SQLiteStore) CreateAlias ¶
func (s *SQLiteStore) CreateAlias(ctx context.Context, alias, nodeID string) error
func (*SQLiteStore) CreateNode ¶
func (s *SQLiteStore) CreateNode(ctx context.Context, node *Node) error
func (*SQLiteStore) DeleteAlias ¶
func (s *SQLiteStore) DeleteAlias(ctx context.Context, alias string) error
func (*SQLiteStore) DeleteNode ¶
func (s *SQLiteStore) DeleteNode(ctx context.Context, id string) error
func (*SQLiteStore) GetAncestors ¶
func (*SQLiteStore) GetNodeByAlias ¶
func (*SQLiteStore) GetNodeByPrefix ¶
func (*SQLiteStore) GetNodeChildren ¶
func (*SQLiteStore) GetOrphanedToolUses ¶
func (*SQLiteStore) GetSubtree ¶
func (*SQLiteStore) IndexToolIDs ¶
func (*SQLiteStore) ListAliases ¶
func (*SQLiteStore) ListRootNodes ¶
func (s *SQLiteStore) ListRootNodes(ctx context.Context) ([]*Node, error)
func (*SQLiteStore) UpdateNode ¶
func (s *SQLiteStore) UpdateNode(ctx context.Context, node *Node) error
type Store ¶
type Store interface {
CreateNode(ctx context.Context, node *Node) error
GetNode(ctx context.Context, id string) (*Node, error)
GetNodeByPrefix(ctx context.Context, prefix string) (*Node, error)
GetNodeChildren(ctx context.Context, id string) ([]*Node, error)
GetSubtree(ctx context.Context, id string) ([]*Node, error)
GetAncestors(ctx context.Context, id string) ([]*Node, error)
ListRootNodes(ctx context.Context) ([]*Node, error)
UpdateNode(ctx context.Context, node *Node) error
DeleteNode(ctx context.Context, id string) error
CreateAlias(ctx context.Context, alias, nodeID string) error
DeleteAlias(ctx context.Context, alias string) error
GetNodeByAlias(ctx context.Context, alias string) (*Node, error)
ListAliases(ctx context.Context, nodeID string) ([]Alias, error)
IndexToolIDs(ctx context.Context, nodeID string, toolIDs []string, role string) error
// GetOrphanedToolUses returns tool_use IDs on ancestorIDs with no matching
// tool_result on the same path. Map keys are node IDs.
GetOrphanedToolUses(ctx context.Context, ancestorIDs []string) (map[string][]string, error)
Close() error
}
Store captures the persistence operations used by the DAG wrapper and API layers so tests can swap implementations without changing call sites.
type UsageStats ¶
type UsageStats struct {
Provider string `json:"provider"`
Model string `json:"model"`
RequestCount int `json:"request_count"`
TotalInputTokens int `json:"total_input_tokens"`
TotalOutputTokens int `json:"total_output_tokens"`
TotalCacheRead int `json:"total_cache_read_tokens"`
TotalCacheCreation int `json:"total_cache_creation_tokens"`
ErrorCount int `json:"error_count"`
ErrorRate float64 `json:"error_rate"`
AvgLatencyMs float64 `json:"avg_latency_ms"`
TotalCostUSD float64 `json:"total_cost_usd"`
}
UsageStats holds aggregated usage statistics for a provider/model pair.