settings

package
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 22, 2026 License: MIT Imports: 7 Imported by: 0

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

View Source
const (
	ProviderOpenAI    = "openai"
	ProviderAnthropic = "anthropic"
	ProviderGemini    = "gemini"
)

Supported AI providers.

View Source
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.

View Source
const (
	ImageProviderOpenAI = "openai"
	ImageProviderGemini = "gemini"
)

Supported image generation providers. Anthropic isn't in this set because the Messages API has no image-generation surface.

View Source
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

func NewManager(ctx context.Context, store *Store, envSeed AIConfig) (*Manager, error)

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

func (m *Manager) Analyzer() *ai.Analyzer

Analyzer returns the currently configured analyzer, or nil if AI is disabled (no key for the selected provider).

func (*Manager) GeminiCreds

func (m *Manager) GeminiCreds() (apiKey, model string)

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

func (m *Manager) ImageCreds() (provider, apiKey, model string)

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

func (m *Manager) ListModels(ctx context.Context, provider string) ([]string, error)

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

func (m *Manager) OpenAICreds() (apiKey, model string)

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

func (m *Manager) Provider() ai.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) Public

func (m *Manager) Public() AIPublic

Public returns the redacted settings DTO.

func (*Manager) SpeechCreds

func (m *Manager) SpeechCreds() (provider, apiKey, model string)

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

func (m *Manager) Update(ctx context.Context, patch AIUpdate) (AIPublic, error)

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

func (m *Manager) VisionCreds() (provider, apiKey, model string)

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 ProInfo

type ProInfo struct {
	Model  string `json:"model"`
	HasKey bool   `json:"has_key"`
}

ProInfo is the per-provider card content for the settings UI.

type Store

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

Store is a thin key/value wrapper around a *sql.DB.

func OpenStore

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

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.

func (*Store) Close

func (s *Store) Close() error

Close releases the underlying database.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, key string) error

Delete removes a key. No-op if absent.

func (*Store) GetAll

func (s *Store) GetAll(ctx context.Context) (map[string]string, error)

GetAll returns every stored key/value pair.

func (*Store) Set

func (s *Store) Set(ctx context.Context, key, value string) error

Set writes a single value. Empty string is a valid value ("" means "cleared by operator"); callers that want to delete should use Delete instead.

Jump to

Keyboard shortcuts

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