Class: Mneme::L2Runner

Inherits:
Object
  • Object
show all
Defined in:
lib/mneme/l2_runner.rb

Overview

Compresses multiple Level 1 snapshots into a single Level 2 snapshot. L2 snapshots capture days/weeks-scale context from hourly L1 summaries, preventing unbounded snapshot growth via recursive compression.

Triggered from MnemeJob after an L1 snapshot is created, when enough uncovered L1 snapshots have accumulated (configurable via mneme.l2_snapshot_threshold in config.toml).

Examples:

Mneme::L2Runner.new(session).call

Constant Summary collapse

TOOLS =
[
  Tools::SaveSnapshot,
  Tools::EverythingOk
].freeze
SYSTEM_PROMPT =
<<~PROMPT
  You are Mneme, the memory department of an AI agent named Anima.
  Your job is to compress multiple conversation summaries into a single
  higher-level summary.

  You MUST ONLY communicate through tool calls — NEVER output text.

  ──────────────────────────────
  WHAT YOU SEE
  ──────────────────────────────
  Several Level 1 snapshots — hourly conversation summaries.
  Each captures key decisions, goals discussed, and important context
  from a portion of the conversation history.

  ──────────────────────────────
  YOUR TASK
  ──────────────────────────────
  Compress the snapshots into ONE Level 2 summary that captures the
  essential arc across all of them. If the snapshots contain meaningful
  content, call save_snapshot. If they are purely mechanical, call
  everything_ok.

  Preserve:
  - Key decisions and their reasoning
  - Goal progress across the time span
  - Important context shifts or pivots
  - Relationships and patterns across snapshots

  Drop:
  - Redundant details repeated across snapshots
  - Mechanical execution details
  - Interim decisions that were superseded by later ones

  Always finish with exactly ONE tool call: either save_snapshot or everything_ok.
PROMPT

Instance Method Summary collapse

Constructor Details

#initialize(session, client: nil) ⇒ L2Runner

Returns a new instance of L2Runner.

Parameters:

  • session (Session)

    the main session whose L1 snapshots to compress

  • client (LLM::Client, nil) (defaults to: nil)

    injectable LLM client (defaults to fast model)



58
59
60
61
62
63
64
65
# File 'lib/mneme/l2_runner.rb', line 58

def initialize(session, client: nil)
  @session = session
  @client = client || LLM::Client.new(
    model: Anima::Settings.fast_model,
    max_tokens: Anima::Settings.mneme_max_tokens,
    logger: Mneme.logger
  )
end

Instance Method Details

#callString?

Compresses uncovered L1 snapshots into a single L2 snapshot. Returns early if not enough L1 snapshots have accumulated.

Returns:

  • (String, nil)

    LLM response text, or nil when skipped



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/mneme/l2_runner.rb', line 71

def call
  l1_snapshots = eligible_snapshots
  threshold = Anima::Settings.mneme_l2_snapshot_threshold
  sid = @session.id
  snapshot_count = l1_snapshots.size

  if snapshot_count < threshold
    log.debug("session=#{sid} — only #{snapshot_count}/#{threshold} L1 snapshots, skipping L2")
    return
  end

  messages = build_messages(l1_snapshots)
  registry = build_registry(l1_snapshots)

  log.info("session=#{sid} — running L2 compression (#{snapshot_count} L1 snapshots)")

  result = @client.chat_with_tools(
    messages,
    registry: registry,
    session_id: nil,
    system: SYSTEM_PROMPT
  )

  log.info("session=#{sid} — L2 compression done: #{result.to_s.truncate(200)}")
  result
end