From e17fc088b204e403674116ab1c435efd310b85a7 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 12 May 2026 14:33:03 -0400 Subject: [PATCH] feat(journal): journal_closeout._filter_messages excludes daily_prep Co-Authored-By: Claude Opus 4.7 (1M context) --- .../services/journal_closeout.py | 28 +++++++++++++++++++ tests/test_journal_closeout.py | 24 ++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/fabledassistant/services/journal_closeout.py create mode 100644 tests/test_journal_closeout.py diff --git a/src/fabledassistant/services/journal_closeout.py b/src/fabledassistant/services/journal_closeout.py new file mode 100644 index 0000000..7e3a47e --- /dev/null +++ b/src/fabledassistant/services/journal_closeout.py @@ -0,0 +1,28 @@ +"""Journal closeout — nightly extraction of profile observations. + +Runs once per user per day at day_rollover_hour. Reads yesterday's /journal +conversation, filters out assistant-authored auto-content (daily prep), +asks the background LLM to extract user-side patterns/habits, and appends +the bullets to user_profiles.observations_raw via append_observations. +""" +from __future__ import annotations + +# Message kinds whose content must NEVER be sent to the closeout LLM. +# These are assistant-authored auto-blocks that would otherwise dominate +# attention and leak back into "what the assistant has learned." +EXCLUDED_KINDS: set[str] = {"daily_prep"} + + +def _filter_messages(messages): + """Drop messages whose msg_metadata.kind is in EXCLUDED_KINDS. + + Accepts any iterable of message-like objects with `role`, `content`, + and `msg_metadata` attributes (real Message rows or SimpleNamespace). + """ + kept = [] + for m in messages: + meta = getattr(m, "msg_metadata", None) or {} + if meta.get("kind") in EXCLUDED_KINDS: + continue + kept.append(m) + return kept diff --git a/tests/test_journal_closeout.py b/tests/test_journal_closeout.py new file mode 100644 index 0000000..e8a27fe --- /dev/null +++ b/tests/test_journal_closeout.py @@ -0,0 +1,24 @@ +"""Tests for journal closeout extraction helpers.""" + +from types import SimpleNamespace + + +def _msg(role: str, content: str, kind: str | None = None): + """Build a Message-like stand-in for the filter helpers.""" + metadata = {"kind": kind} if kind else None + return SimpleNamespace(role=role, content=content, msg_metadata=metadata) + + +def test_filter_excludes_daily_prep_messages(): + from fabledassistant.services.journal_closeout import _filter_messages + + msgs = [ + _msg("assistant", "Good morning — here is today's plan…", kind="daily_prep"), + _msg("user", "I want to focus on the auth refactor today."), + _msg("assistant", "Got it. I'll keep tool calls quiet."), + ] + kept = _filter_messages(msgs) + contents = [m.content for m in kept] + assert "Good morning — here is today's plan…" not in contents + assert "I want to focus on the auth refactor today." in contents + assert "Got it. I'll keep tool calls quiet." in contents