Class: Snapshot

Inherits:
ApplicationRecord show all
Includes:
TokenEstimation
Defined in:
app/models/snapshot.rb

Overview

A persisted summary of conversation context created by Mneme before messages evict from the viewport. Snapshots capture the “gist” of what happened so the agent retains awareness of past context.

Level 1 snapshots are created from raw messages (conversation + thinks). Level 2 snapshots compress multiple Level 1 snapshots (days/weeks scale). Both levels use the same message ID range tracking — an L2 snapshot’s range is the union of its constituent L1 snapshots.

Constant Summary collapse

MAX_TEXT_BYTES =

32KB — generous upper bound (~8K tokens). The LLM tool description advises a tighter limit (mneme_max_tokens), but this hard cap prevents unbounded storage.

32_768

Constants included from TokenEstimation

TokenEstimation::BYTES_PER_TOKEN

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from TokenEstimation

estimate_token_count, #estimate_tokens

Instance Attribute Details

#from_message_idInteger

Returns first message ID covered by this snapshot.

Returns:

  • (Integer)

    first message ID covered by this snapshot



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/models/snapshot.rb', line 24

class Snapshot < ApplicationRecord
  include TokenEstimation

  belongs_to :session

  # 32KB — generous upper bound (~8K tokens). The LLM tool description advises
  # a tighter limit (mneme_max_tokens), but this hard cap prevents unbounded storage.
  MAX_TEXT_BYTES = 32_768

  validates :text, presence: true, length: {maximum: MAX_TEXT_BYTES}
  validates :from_message_id, presence: true
  validates :to_message_id, presence: true
  validates :level, presence: true, numericality: {greater_than: 0}
  validates :token_count, numericality: {greater_than_or_equal_to: 0}
  validate :from_message_id_not_after_to_message_id

  scope :for_level, ->(level) { where(level: level) }
  scope :chronological, -> { order(:from_message_id) }

  # @return [String] summary text used for token estimation and remote counting
  def tokenization_text
    text.to_s
  end

  # L1 snapshots whose message range is NOT fully contained within any L2 snapshot.
  # Used to determine which L1 snapshots are still "live" in the viewport.
  scope :not_covered_by_l2, -> {
    where.not(
      "EXISTS (SELECT 1 FROM snapshots l2 " \
      "WHERE l2.session_id = snapshots.session_id " \
      "AND l2.level = 2 " \
      "AND l2.from_message_id <= snapshots.from_message_id " \
      "AND l2.to_message_id >= snapshots.to_message_id)"
    )
  }

  private

  def from_message_id_not_after_to_message_id
    return unless from_message_id && to_message_id
    errors.add(:from_message_id, "must be <= to_message_id") if from_message_id > to_message_id
  end
end

#levelInteger

Returns compression level (1 = from raw messages, 2 = from L1 snapshots).

Returns:

  • (Integer)

    compression level (1 = from raw messages, 2 = from L1 snapshots)



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/models/snapshot.rb', line 24

class Snapshot < ApplicationRecord
  include TokenEstimation

  belongs_to :session

  # 32KB — generous upper bound (~8K tokens). The LLM tool description advises
  # a tighter limit (mneme_max_tokens), but this hard cap prevents unbounded storage.
  MAX_TEXT_BYTES = 32_768

  validates :text, presence: true, length: {maximum: MAX_TEXT_BYTES}
  validates :from_message_id, presence: true
  validates :to_message_id, presence: true
  validates :level, presence: true, numericality: {greater_than: 0}
  validates :token_count, numericality: {greater_than_or_equal_to: 0}
  validate :from_message_id_not_after_to_message_id

  scope :for_level, ->(level) { where(level: level) }
  scope :chronological, -> { order(:from_message_id) }

  # @return [String] summary text used for token estimation and remote counting
  def tokenization_text
    text.to_s
  end

  # L1 snapshots whose message range is NOT fully contained within any L2 snapshot.
  # Used to determine which L1 snapshots are still "live" in the viewport.
  scope :not_covered_by_l2, -> {
    where.not(
      "EXISTS (SELECT 1 FROM snapshots l2 " \
      "WHERE l2.session_id = snapshots.session_id " \
      "AND l2.level = 2 " \
      "AND l2.from_message_id <= snapshots.from_message_id " \
      "AND l2.to_message_id >= snapshots.to_message_id)"
    )
  }

  private

  def from_message_id_not_after_to_message_id
    return unless from_message_id && to_message_id
    errors.add(:from_message_id, "must be <= to_message_id") if from_message_id > to_message_id
  end
end

#textString

Returns the summary text generated by Mneme.

Returns:

  • (String)

    the summary text generated by Mneme



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/models/snapshot.rb', line 24

class Snapshot < ApplicationRecord
  include TokenEstimation

  belongs_to :session

  # 32KB — generous upper bound (~8K tokens). The LLM tool description advises
  # a tighter limit (mneme_max_tokens), but this hard cap prevents unbounded storage.
  MAX_TEXT_BYTES = 32_768

  validates :text, presence: true, length: {maximum: MAX_TEXT_BYTES}
  validates :from_message_id, presence: true
  validates :to_message_id, presence: true
  validates :level, presence: true, numericality: {greater_than: 0}
  validates :token_count, numericality: {greater_than_or_equal_to: 0}
  validate :from_message_id_not_after_to_message_id

  scope :for_level, ->(level) { where(level: level) }
  scope :chronological, -> { order(:from_message_id) }

  # @return [String] summary text used for token estimation and remote counting
  def tokenization_text
    text.to_s
  end

  # L1 snapshots whose message range is NOT fully contained within any L2 snapshot.
  # Used to determine which L1 snapshots are still "live" in the viewport.
  scope :not_covered_by_l2, -> {
    where.not(
      "EXISTS (SELECT 1 FROM snapshots l2 " \
      "WHERE l2.session_id = snapshots.session_id " \
      "AND l2.level = 2 " \
      "AND l2.from_message_id <= snapshots.from_message_id " \
      "AND l2.to_message_id >= snapshots.to_message_id)"
    )
  }

  private

  def from_message_id_not_after_to_message_id
    return unless from_message_id && to_message_id
    errors.add(:from_message_id, "must be <= to_message_id") if from_message_id > to_message_id
  end
end

#to_message_idInteger

Returns last message ID covered by this snapshot.

Returns:

  • (Integer)

    last message ID covered by this snapshot



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/models/snapshot.rb', line 24

class Snapshot < ApplicationRecord
  include TokenEstimation

  belongs_to :session

  # 32KB — generous upper bound (~8K tokens). The LLM tool description advises
  # a tighter limit (mneme_max_tokens), but this hard cap prevents unbounded storage.
  MAX_TEXT_BYTES = 32_768

  validates :text, presence: true, length: {maximum: MAX_TEXT_BYTES}
  validates :from_message_id, presence: true
  validates :to_message_id, presence: true
  validates :level, presence: true, numericality: {greater_than: 0}
  validates :token_count, numericality: {greater_than_or_equal_to: 0}
  validate :from_message_id_not_after_to_message_id

  scope :for_level, ->(level) { where(level: level) }
  scope :chronological, -> { order(:from_message_id) }

  # @return [String] summary text used for token estimation and remote counting
  def tokenization_text
    text.to_s
  end

  # L1 snapshots whose message range is NOT fully contained within any L2 snapshot.
  # Used to determine which L1 snapshots are still "live" in the viewport.
  scope :not_covered_by_l2, -> {
    where.not(
      "EXISTS (SELECT 1 FROM snapshots l2 " \
      "WHERE l2.session_id = snapshots.session_id " \
      "AND l2.level = 2 " \
      "AND l2.from_message_id <= snapshots.from_message_id " \
      "AND l2.to_message_id >= snapshots.to_message_id)"
    )
  }

  private

  def from_message_id_not_after_to_message_id
    return unless from_message_id && to_message_id
    errors.add(:from_message_id, "must be <= to_message_id") if from_message_id > to_message_id
  end
end

#token_countInteger

Returns token count of the summary text. Seeded with a local estimate on create and later refined by CountTokensJob using the real Anthropic tokenizer.

Returns:

  • (Integer)

    token count of the summary text. Seeded with a local estimate on create and later refined by CountTokensJob using the real Anthropic tokenizer.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'app/models/snapshot.rb', line 24

class Snapshot < ApplicationRecord
  include TokenEstimation

  belongs_to :session

  # 32KB — generous upper bound (~8K tokens). The LLM tool description advises
  # a tighter limit (mneme_max_tokens), but this hard cap prevents unbounded storage.
  MAX_TEXT_BYTES = 32_768

  validates :text, presence: true, length: {maximum: MAX_TEXT_BYTES}
  validates :from_message_id, presence: true
  validates :to_message_id, presence: true
  validates :level, presence: true, numericality: {greater_than: 0}
  validates :token_count, numericality: {greater_than_or_equal_to: 0}
  validate :from_message_id_not_after_to_message_id

  scope :for_level, ->(level) { where(level: level) }
  scope :chronological, -> { order(:from_message_id) }

  # @return [String] summary text used for token estimation and remote counting
  def tokenization_text
    text.to_s
  end

  # L1 snapshots whose message range is NOT fully contained within any L2 snapshot.
  # Used to determine which L1 snapshots are still "live" in the viewport.
  scope :not_covered_by_l2, -> {
    where.not(
      "EXISTS (SELECT 1 FROM snapshots l2 " \
      "WHERE l2.session_id = snapshots.session_id " \
      "AND l2.level = 2 " \
      "AND l2.from_message_id <= snapshots.from_message_id " \
      "AND l2.to_message_id >= snapshots.to_message_id)"
    )
  }

  private

  def from_message_id_not_after_to_message_id
    return unless from_message_id && to_message_id
    errors.add(:from_message_id, "must be <= to_message_id") if from_message_id > to_message_id
  end
end

Instance Method Details

#tokenization_textString

Returns summary text used for token estimation and remote counting.

Returns:

  • (String)

    summary text used for token estimation and remote counting



44
45
46
# File 'app/models/snapshot.rb', line 44

def tokenization_text
  text.to_s
end