lore: bound pamiec.json by summarising old memory into "Legendy Baru"
CI / compile (pull_request) Successful in 9s
CI / unit (pull_request) Successful in 17s
CI / integration (pull_request) Successful in 10s

The conversation memory file grows forever (every chat appends a user+assistant
pair), so startup load gets slower and the disk fills. New always-loaded cog
lore_commands turns that growth into content: a background task summarises the
oldest slice into one in-character "legend" via the ACTIVE AI backend, replaces
those old messages with the summary (bounding the file, keeping continuity for
the next startup's context), archives it to legendy.json, and announces it on
#legendy.

Safety: the compaction transforms (build_transcript, apply_compaction) are pure
and unit-tested. The file rewrite is re-read -> back up -> atomic write with no
await in between, so a handle_response append that lands while the summary is
being generated can neither be lost (it's in the preserved tail) nor corrupt
the file (single-threaded, no interleave). A .bak is kept. Scope note: this
bounds the on-disk file (startup/disk); the in-RAM MESSAGE_TABLE is a separate
concern left untouched to avoid yanking context from a live conversation.

Commands: $zapisz_legende (Vykidailo) forces a compaction now; $legendy recalls
a random past legend. All thresholds env-overridable (CONJURER_MEMORY_COMPACT_*,
CONJURER_LEGENDS_CHANNEL). constants gains LEGENDS_FILE + config + seed; bot.py
registers the cog.

Verified: tests/unit/test_lore_commands.py covers prefix-replace/tail-keep,
preservation of appends made during summarisation, and transcript formatting +
head/tail truncation. Unit job 32 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-31 17:00:34 +02:00
parent d40c93ab3f
commit 868ea96e15
5 changed files with 369 additions and 0 deletions
+16
View File
@@ -203,6 +203,21 @@ TRANSCRIPTS_PATH = os.getenv(
os.path.join(os.path.dirname(LOGFILE) or ".", "transcripts") + os.sep,
)
# "Legendy Baru": archive of AI-written summaries of old conversation memory
# (lore_commands). Lives next to pamiec.json by default; seeded empty.
LEGENDS_FILE = os.getenv(
"CONJURER_LEGENDS_FILE",
os.path.join(os.path.dirname(MEMORY_FIVE_SIARA) or ".", "legendy.json"),
)
# Channel the compacted "legends" are announced to (the #legendy channel).
LEGENDS_CHANNEL_ID = int(os.getenv("CONJURER_LEGENDS_CHANNEL", "1084448332841230388"))
# Memory compaction: when pamiec.json grows past THRESHOLD messages, summarise
# everything except the last KEEP_RECENT into one legend. HOURS is how often the
# background task checks.
MEMORY_COMPACT_THRESHOLD = int(os.getenv("CONJURER_MEMORY_COMPACT_THRESHOLD", "400"))
MEMORY_KEEP_RECENT = int(os.getenv("CONJURER_MEMORY_KEEP_RECENT", "200"))
MEMORY_COMPACT_HOURS = float(os.getenv("CONJURER_MEMORY_COMPACT_HOURS", "6"))
FILE_SERVICE_ADDRESS = os.getenv("CONJURER_FILE_SERVICE", "http://192.168.1.15:5000")
RADIO_HARBOR_ADDRESS = os.getenv("CONJURER_RADIO_HARBOR", "http://192.168.1.15:54321")
# Betoniarka (radio-operator service colocated with Liquidsoap). Falls back to
@@ -272,6 +287,7 @@ def _ensure_runtime_layout() -> None:
_seed_file(SETTINGS_FILE, "settings.json", "{}")
_seed_file(SYSTEM_GPT_SETTINGS, "system_gpt_settings.json", "{}")
_seed_file(MEMORY_FIVE_SIARA, "pamiec.json", "[]")
_seed_file(LEGENDS_FILE, "legendy.json", "[]")
_seed_file(MEMORY_FIVE_MUZYKA, "pamiec_muzyki.json", "[]")
_seed_file(ACCIDENT_LOG, "accident_log.json", "[]")