feat: structured user profile with LLM-learned preferences

Replaces the freeform briefing-profile note with a DB-backed user_profiles
table. Users can edit job/industry/expertise/response preferences/interests/
work schedule via a new Settings → Profile tab. The LLM appends nightly
observations; at 14+ entries they are auto-consolidated into a learned_summary.
Profile context is injected into both briefing and chat system prompts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-30 14:17:30 -04:00
parent 9f3b9e45c6
commit dba41879ed
11 changed files with 667 additions and 9 deletions
@@ -357,7 +357,7 @@ async def run_compilation(
if model is None:
model = await get_setting(user_id, "default_model", Config.OLLAMA_MODEL)
from fabledassistant.services.briefing_profile import get_profile_body
from fabledassistant.services.user_profile import build_profile_context
from fabledassistant.services.briefing_preferences import (
load_topic_preferences,
load_topic_reaction_scores,
@@ -365,8 +365,8 @@ async def run_compilation(
)
from fabledassistant.services.weather import parse_weather_card_data, get_cached_weather_rows
profile_body, temp_unit = await asyncio.gather(
get_profile_body(user_id),
profile_context, temp_unit = await asyncio.gather(
build_profile_context(user_id),
_get_temp_unit(user_id),
)
@@ -424,7 +424,7 @@ async def run_compilation(
}
briefing_text = await _llm_synthesise(
_unified_system_prompt(profile_body),
_unified_system_prompt(profile_context),
_unified_user_prompt(internal_data_filtered, external_data_filtered, slot, temp_unit),
model,
)