Documentation
¶
Overview ¶
Package settings persists app-level configuration (currently just the AI provider/model/API keys) in the same SQLite database used for shot history.
We keep this in a trivial key/value table rather than a typed schema because the surface is small and is expected to grow (auth, UI prefs, …).
Index ¶
- Constants
- type AIConfig
- type AIImagePublic
- type AIPublic
- type AISpeechPublic
- type AIUpdate
- type Manager
- func (m *Manager) Analyzer() *ai.Analyzer
- func (m *Manager) GeminiCreds() (apiKey, model string)
- func (m *Manager) ImageCreds() (provider, apiKey, model string)
- func (m *Manager) ListModels(ctx context.Context, provider string) ([]string, error)
- func (m *Manager) OpenAICreds() (apiKey, model string)
- func (m *Manager) Provider() ai.Provider
- func (m *Manager) Public() AIPublic
- func (m *Manager) SpeechCreds() (provider, apiKey, model string)
- func (m *Manager) Update(ctx context.Context, patch AIUpdate) (AIPublic, error)
- func (m *Manager) VisionCreds() (provider, apiKey, model string)
- type ProInfo
- type Store
Constants ¶
const ( ProviderOpenAI = "openai" ProviderAnthropic = "anthropic" ProviderGemini = "gemini" )
Supported AI providers.
const ( DefaultOpenAIModel = "gpt-4o-mini" DefaultAnthropicModel = "claude-haiku-4-5-20251001" DefaultGeminiModel = "gemini-2.5-flash" DefaultOpenAIImageModel = "gpt-image-1" DefaultGeminiImageModel = "gemini-2.5-flash-image" // Speech-to-text defaults. Whisper still has a single public model; // Gemini uses whatever text model you point at /generateContent with // audio inline. gemini-2.5-flash is the cheapest multimodal tier. DefaultOpenAISpeechModel = "whisper-1" DefaultGeminiSpeechModel = "gemini-2.5-flash" )
Defaults used when nothing is stored and no env override is present.
const ( ImageProviderOpenAI = "openai" ImageProviderGemini = "gemini" )
Supported image generation providers. Anthropic isn't in this set because the Messages API has no image-generation surface.
const ( SpeechProviderOpenAI = "openai" SpeechProviderGemini = "gemini" )
Supported speech-to-text providers. Same caveat as image: Anthropic has no audio-input API yet.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AIConfig ¶
type AIConfig struct {
Provider string // "", "openai", "anthropic", "gemini"
OpenAIModel string
OpenAIKey string
AnthropicModel string
AnthropicKey string
GeminiModel string
GeminiKey string
// Image generation is a separate pipeline from text analysis and so
// has its own provider + per-provider model overrides. The API keys
// are shared with the text providers above.
ImageProvider string // "", "openai", "gemini"
ImageOpenAIModel string
ImageGeminiModel string
// Speech-to-text pipeline (voice notes). Same shape as Image.
SpeechProvider string // "", "openai", "gemini"
SpeechOpenAIModel string
SpeechGeminiModel string
}
AIConfig is the raw persisted AI configuration. Keys are never returned from handlers; this struct is the internal representation.
type AIImagePublic ¶
type AIImagePublic struct {
Provider string `json:"provider"` // "", openai|gemini
Effective string `json:"effective,omitempty"` // what auto-selection picked
// Per-provider model overrides. Empty string means "use the default".
OpenAIModel string `json:"openai_model,omitempty"`
GeminiModel string `json:"gemini_model,omitempty"`
Ready bool `json:"ready"`
}
AIImagePublic is the subset surfaced to the Settings UI. Per-provider key presence is derived from the top-level Providers map, so we only repeat the model choices and the selected provider here.
type AIPublic ¶
type AIPublic struct {
Provider string `json:"provider"` // "", openai|anthropic|gemini
Effective string `json:"effective_provider,omitempty"`
Model string `json:"effective_model,omitempty"`
Providers map[string]ProInfo `json:"providers"`
Ready bool `json:"ready"`
// Image is the public view of the image-generation configuration.
Image AIImagePublic `json:"image"`
// Speech is the public view of the voice-transcription configuration.
Speech AISpeechPublic `json:"speech"`
}
AIPublic is the redacted view returned to the UI. Keys are reported only as "set" / "not set"; the actual secret never leaves the server.
type AISpeechPublic ¶
type AISpeechPublic struct {
Provider string `json:"provider"`
Effective string `json:"effective,omitempty"`
OpenAIModel string `json:"openai_model,omitempty"`
GeminiModel string `json:"gemini_model,omitempty"`
Ready bool `json:"ready"`
}
AISpeechPublic mirrors AIImagePublic for the speech-to-text pipeline.
type AIUpdate ¶
type AIUpdate struct {
Provider *string `json:"provider,omitempty"`
OpenAIModel *string `json:"openai_model,omitempty"`
OpenAIKey *string `json:"openai_api_key,omitempty"`
AnthropicModel *string `json:"anthropic_model,omitempty"`
AnthropicKey *string `json:"anthropic_api_key,omitempty"`
GeminiModel *string `json:"gemini_model,omitempty"`
GeminiKey *string `json:"gemini_api_key,omitempty"`
// Image generation settings.
ImageProvider *string `json:"image_provider,omitempty"`
ImageOpenAIModel *string `json:"image_openai_model,omitempty"`
ImageGeminiModel *string `json:"image_gemini_model,omitempty"`
// Speech-to-text settings.
SpeechProvider *string `json:"speech_provider,omitempty"`
SpeechOpenAIModel *string `json:"speech_openai_model,omitempty"`
SpeechGeminiModel *string `json:"speech_gemini_model,omitempty"`
}
AIUpdate is the JSON body accepted by PUT /api/settings/ai. A nil pointer means "leave this field alone"; an explicit empty string clears a key.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager owns the current AI configuration and the corresponding Analyzer. It is concurrency-safe; handlers call Analyzer() on every request.
func NewManager ¶
NewManager loads any persisted config and builds the initial Analyzer. envSeed is merged in for values that are not yet stored, so first-boot behaviour matches the previous env-only configuration.
func (*Manager) Analyzer ¶
Analyzer returns the currently configured analyzer, or nil if AI is disabled (no key for the selected provider).
func (*Manager) GeminiCreds ¶
GeminiCreds returns the currently stored Gemini API key and model. The caller must treat the key as a secret and never log it. Used by the profile-image generation endpoint which doesn't go through Analyzer.
func (*Manager) ImageCreds ¶
ImageCreds returns the provider, API key, and model to use for image generation. Provider is one of "openai" or "gemini", or the empty string when no image-capable provider has a key configured. Respects the user's explicit ImageProvider setting and falls back to auto-selection otherwise.
func (*Manager) ListModels ¶
ListModels fetches the catalogue of usable models for a provider, using the stored API key. The caller is expected to have already saved a key; we deliberately don't accept an ad-hoc key here so the UI has one place (PUT /api/settings/ai) that persists secrets.
func (*Manager) OpenAICreds ¶
OpenAICreds returns the stored OpenAI API key and text model. Used by non-Analyzer endpoints (e.g. Whisper transcription) that need OpenAI auth directly.
func (*Manager) Provider ¶
Provider returns the active ai.Provider, or nil when AI is disabled. Features outside of shot analysis (coach, comparator, ask, digest, profile-name) call this directly so they can wrap the provider in their own task-specific helper.
func (*Manager) SpeechCreds ¶
SpeechCreds returns the provider, API key, and model to use for voice transcription. Same contract as ImageCreds: empty provider means "no speech-capable provider has a key configured".
func (*Manager) Update ¶
Update applies the patch, persists it, and rebuilds the analyzer. Returns the new public view (or an error if the patch is invalid).
func (*Manager) VisionCreds ¶
VisionCreds returns the provider, API key and model to use for image UNDERSTANDING (vision input, e.g. scanning a coffee bag). This is distinct from ImageCreds, which generates images. Vision follows the main chat provider so the bag gets read by whichever LLM you've chosen to analyse your shots. Supported providers: "openai" and "gemini". Anthropic Claude supports vision but isn't wired here yet; if the user's chat provider is Anthropic we fall back to whichever of OpenAI/Gemini has a key. Returns empty provider when no vision- capable key is set.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a thin key/value wrapper around a *sql.DB.
func OpenStore ¶
OpenStore opens (or creates) a SQLite database at path. It is safe to point this at the same file the shots package uses; tables don't collide.