Add AI settings API with key management

GET/PATCH /api/v1/settings/ai return and change provider, base_url,
model, temperature and system_prompt, with read-only api_key_last4 and
key_verified_at. PATCH requires a preset provider, temperature 0-1 in
0.1 steps and a system_prompt of at most 8000 characters.
PUT /settings/ai/api-key seals the key with secretbox and records its
last four characters; POST /settings/ai/verify calls Provider.Verify and
records key_verified_at or returns the provider's typed error;
GET /settings/ai/models lists the live models; POST
/settings/ai/reset-prompt restores household.DefaultPersonaPrompt.

The new internal/ai/aisettings service also builds the harness.Config
for each skill run from the saved settings. presets.ForAccount builds a
provider's ListModels and Verify without a model, so a key can be
checked and a model picked before one is chosen.

Tests scan every response body from these routes for the full key, and
a harness run with the Fake provider uses the PATCHed model and
temperature.

Maestro task: 176b2a30-6e90-4339-b430-5b8967b2fd32

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 08:50:12 -04:00
co-authored by Claude Opus 5.5
parent f25b28b39b
commit 60a92ec78f
17 changed files with 1416 additions and 31 deletions
+7 -1
View File
@@ -13,6 +13,7 @@ import (
"syscall"
"time"
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/ai/aisettings"
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/auth"
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/config"
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/forwarded"
@@ -83,6 +84,11 @@ func run(cfg config.Config, logger *slog.Logger) error {
return err
}
ai, err := aisettings.New(db, box, cfg.PublicURL.String())
if err != nil {
return err
}
sessions, err := auth.NewSessionManager(db, cfg.SessionIdle, cfg.SessionMax)
if err != nil {
return err
@@ -99,7 +105,7 @@ func run(cfg config.Config, logger *slog.Logger) error {
return err
}
router, err := server.NewRouter(logger, version, cfg.TrustedProxy, authFlow, sessions, settings, search, runner, photos, frontend.Assets())
router, err := server.NewRouter(logger, version, cfg.TrustedProxy, authFlow, sessions, settings, search, ai, runner, photos, frontend.Assets())
if err != nil {
return err
}