Documentation
¶
Overview ¶
Package ai hosts the hot-swappable LLM provider adapters (OpenAI, Anthropic, Google Gemini) used across Rivolt: weekly digest, trip planning, charging strategy, and anomaly explanations.
The code in this file is provider-agnostic. Domain features (digest, planner, coach) live in their own files and build on top of Provider.
Anthropic Claude Messages API provider.
Docs: https://docs.anthropic.com/en/api/messages
Google Gemini generateContent API provider.
Docs: https://ai.google.dev/api/generate-content
Model listing helpers — one per provider. Each returns the set of model IDs that can serve generateContent / chat completions, so the UI can offer a dropdown instead of asking the operator to type a model name.
These are plain functions (not methods on *Provider) because the settings UI needs to list models even when the selected model would otherwise be invalid — we don't want to construct a full provider just to list.
OpenAI Chat Completions provider. No SDK — one tiny HTTP call keeps the dependency surface small and makes auditing trivial.
Index ¶
- func ComputeCost(provider, model string, inTokens, outTokens int64) float64
- func ListAnthropicModels(ctx context.Context, apiKey string) ([]string, error)
- func ListGeminiModels(ctx context.Context, apiKey string) ([]string, error)
- func ListOpenAIModels(ctx context.Context, apiKey string) ([]string, error)
- func SplitModelName(name string) (provider, model string)
- type Analyzer
- type AnthropicConfig
- type AnthropicProvider
- type CostBreak
- type GeminiConfig
- type GeminiProvider
- type OpenAIConfig
- type OpenAIProvider
- type Provider
- type Record
- type Recorder
- type TokenUsage
- type UsageSummary
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ComputeCost ¶
ComputeCost returns the USD cost of a call using real token counts and the provider's published per-1M-token price. Unknown models fall back to a conservative rate so the dashboard shows *something*.
func ListAnthropicModels ¶
ListAnthropicModels returns every model the Anthropic Messages API advertises. All of them support messages, so no client-side filtering.
func ListGeminiModels ¶
ListGeminiModels returns the Gemini models that support generateContent, with the "models/" prefix stripped. Gemma and preview-only variants are included too — the user can pick, we don't gatekeep.
func ListOpenAIModels ¶
ListOpenAIModels returns the subset of OpenAI models usable with chat completions. We filter client-side since /v1/models returns every model including embeddings, moderation, TTS, etc.
func SplitModelName ¶
SplitModelName decomposes a Provider.Name() result (e.g. "anthropic:claude-haiku-4-5") into provider and model. Unknown shapes return (name, "").
Types ¶
type Analyzer ¶
type Analyzer struct {
// contains filtered or unexported fields
}
Analyzer wraps a Provider. It exists so call sites can hot-swap the backing provider without plumbing the raw Provider through every handler. Settings manager owns the single *Analyzer instance and rebuilds it when the operator changes API key / provider / model.
func NewAnalyzer ¶
NewAnalyzer returns an analyzer backed by p. p must be non-nil; the settings manager guards against that before calling.
func (*Analyzer) Complete ¶
Complete is a convenience passthrough so domain code doesn't have to reach into Provider directly.
type AnthropicConfig ¶
type AnthropicConfig struct {
APIKey string // required
Model string // default "claude-3-5-haiku-latest"
Endpoint string
Version string // x-api-key header version, default "2023-06-01"
Timeout time.Duration
}
AnthropicConfig configures the provider. Zero values pick sensible defaults.
type AnthropicProvider ¶
type AnthropicProvider struct {
// contains filtered or unexported fields
}
AnthropicProvider calls the Anthropic Messages API.
func NewAnthropic ¶
func NewAnthropic(cfg AnthropicConfig) (*AnthropicProvider, error)
NewAnthropic constructs a provider. Returns an error if no API key is set.
func (*AnthropicProvider) Complete ¶
func (p *AnthropicProvider) Complete(ctx context.Context, system, user string) (string, TokenUsage, error)
Complete sends a system+user prompt pair and returns the assistant text along with real token usage parsed from the Anthropic response.
func (*AnthropicProvider) Name ¶
func (p *AnthropicProvider) Name() string
Name reports a stable identifier for cache keying.
type CostBreak ¶
type CostBreak struct {
Calls int `json:"calls"`
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
CostUSD float64 `json:"cost_usd"`
LastUsedUnix int64 `json:"last_used_unix,omitempty"`
}
CostBreak is the per-slice rollup shown in the dashboard.
type GeminiConfig ¶
type GeminiConfig struct {
APIKey string // required
Model string // default "gemini-1.5-flash"
Endpoint string // v1beta base URL; default official endpoint
Timeout time.Duration
}
GeminiConfig configures the provider.
type GeminiProvider ¶
type GeminiProvider struct {
// contains filtered or unexported fields
}
GeminiProvider calls the Google Generative Language API.
func NewGemini ¶
func NewGemini(cfg GeminiConfig) (*GeminiProvider, error)
NewGemini constructs a provider. Returns an error if no API key is set.
func (*GeminiProvider) Complete ¶
func (p *GeminiProvider) Complete(ctx context.Context, system, user string) (string, TokenUsage, error)
Complete sends the prompt pair and returns the assistant text along with real token usage from the response.
Gemini has no separate "system" role; we fold it into the request as system_instruction which is the documented equivalent.
func (*GeminiProvider) Name ¶
func (p *GeminiProvider) Name() string
Name reports a stable identifier for cache keying.
type OpenAIConfig ¶
type OpenAIConfig struct {
APIKey string // required
Model string // default "gpt-4o-mini"
Endpoint string // default official OpenAI chat endpoint
Timeout time.Duration
}
OpenAIConfig configures the provider. Zero values pick sensible defaults.
type OpenAIProvider ¶
type OpenAIProvider struct {
// contains filtered or unexported fields
}
OpenAIProvider calls the OpenAI Chat Completions API.
func NewOpenAI ¶
func NewOpenAI(cfg OpenAIConfig) (*OpenAIProvider, error)
NewOpenAI constructs a provider. Returns an error if no API key is set.
func (*OpenAIProvider) Complete ¶
func (p *OpenAIProvider) Complete(ctx context.Context, system, user string) (string, TokenUsage, error)
Complete sends a system+user prompt pair and returns the assistant text along with real token usage from the response.
func (*OpenAIProvider) Name ¶
func (p *OpenAIProvider) Name() string
Name reports a stable identifier for cache keying.
type Provider ¶
type Provider interface {
// Complete sends a system+user prompt pair and returns the assistant
// text along with real token usage parsed from the provider response.
// Implementations MUST return usage whenever the API gives it to them;
// a zero-valued TokenUsage is only acceptable when the upstream
// response omitted the counts (fall back to zeros, never estimate).
Complete(ctx context.Context, system, user string) (string, TokenUsage, error)
// Name returns a short identifier (e.g. "openai:gpt-4o-mini") used
// for cache keys and usage accounting. Changing model invalidates
// downstream caches automatically.
Name() string
}
Provider is the minimal contract every LLM backend must implement.
type Record ¶
type Record struct {
Time time.Time
Provider string // openai, anthropic, gemini
Model string // gpt-4o-mini, claude-haiku-4-5, ...
Feature string // analyze, coach, compare, digest, ask, name, transcribe, image
InputTokens int64
OutputTokens int64
DurationMs int64
ShotID string
OK bool
Err string
}
Record is the event we log for each LLM call.
type Recorder ¶
type Recorder struct {
// contains filtered or unexported fields
}
Recorder persists AI call metadata to SQLite.
func NewRecorder ¶
NewRecorder wires a Recorder to an already-open *sql.DB (we reuse the shots database to avoid a second file).
type TokenUsage ¶
type TokenUsage struct {
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
}
TokenUsage is the real input/output token count reported by a provider for a single call. Zero values mean the provider didn't report counts.
type UsageSummary ¶
type UsageSummary struct {
Since time.Time `json:"since"`
TotalCalls int `json:"total_calls"`
TotalCostUSD float64 `json:"total_cost_usd"`
ByProvider map[string]CostBreak `json:"by_provider"`
ByFeature map[string]CostBreak `json:"by_feature"`
ByModel map[string]CostBreak `json:"by_model"`
Recent []Record `json:"recent"` // newest first
}
UsageSummary aggregates recent activity for the dashboard.