Prefs: owner defaults (model, backend, persona_id, reel span/length)

Closes #127.

Grows user_ai_prefs with five nullable owner-default columns and makes
them the server-side fallback layer: request param -> stored pref ->
hard default, at insight generation (agentic + standard), all three
insight-chat turn variants, and reel creation.

- PUT /prefs accepts the fields with the existing three-state partial
  update semantics (TriState generalized to TriState<T> for the numeric
  reel_max_segments); validation (canonical BackendKind::parse,
  MemoriesSpan::from_str) runs before any write.
- GET /prefs returns the new fields.
- capture_prefs reads the existing row and carries the AI owner-defaults
  through unchanged (the upsert overwrites all columns), mirroring only
  the effective reel span/segments.
- MemoriesSpan gains Display + FromStr so stored spans round-trip
  losslessly.

1463/0 tests (+27), clippy clean.
This commit is contained in:
Cameron Cordes committed 2026-09-27 19:48:45 -04:00
1 parent aa3c58f051
commit a699c940b8
10 files changed
+863 -85

No files matched your search

+18
View File
@@ -579,6 +579,19 @@ pub struct UserAiPrefs {
/// default (`camera_tz::DEFAULT_DISPLAY_TZ`). The legacy
/// `tz_offset_minutes` column stays frozen for rollback.
pub display_tz: Option<String>,
/// Last/explicit insight + chat model choice (local id or OpenRouter id).
/// NULL = current per-request / env default behavior.
pub default_model: Option<String>,
/// `"local"` | `"hybrid"` — insight/chat backend. NULL = per-request /
/// hard default ("local").
pub default_backend: Option<String>,
/// Active persona for persona chat. NULL = hard default ("default").
pub default_persona_id: Option<String>,
/// Reel span: `"day"` | `"week"` | `"month"`. NULL = per-request /
/// hard default (day).
pub reel_span: Option<String>,
/// Reel segment cap. NULL = per-request / hard default.
pub reel_max_segments: Option<i32>,
}
#[derive(Insertable, Debug, Clone, serde::Deserialize, serde::Serialize)]
@@ -589,4 +602,9 @@ pub struct UpsertUserAiPrefs {
pub library: Option<String>,
pub updated_at: i64,
pub display_tz: Option<String>,
pub default_model: Option<String>,
pub default_backend: Option<String>,
pub default_persona_id: Option<String>,
pub reel_span: Option<String>,
pub reel_max_segments: Option<i32>,
}