Class: Snapshot
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Snapshot
- Defined in:
- app/models/snapshot.rb
Overview
A persisted summary of conversation context created by Mneme before events 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 events (messages + thinks). Level 2 snapshots compress multiple Level 1 snapshots (days/weeks scale). Both levels use the same event 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
Instance Attribute Summary collapse
-
#from_event_id ⇒ Integer
First event ID covered by this snapshot.
-
#level ⇒ Integer
Compression level (1 = from raw events, 2 = from L1 snapshots).
-
#text ⇒ String
The summary text generated by Mneme.
-
#to_event_id ⇒ Integer
Last event ID covered by this snapshot.
-
#token_count ⇒ Integer
Cached token count of the summary text.
Instance Method Summary collapse
-
#token_cost ⇒ Integer
Token cost, using cached count or heuristic estimate.
Instance Attribute Details
#from_event_id ⇒ Integer
Returns first event ID covered by this snapshot.
22 23 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 67 68 69 70 71 72 73 74 75 76 |
# File 'app/models/snapshot.rb', line 22 class Snapshot < ApplicationRecord 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_event_id, presence: true validates :to_event_id, presence: true validates :level, presence: true, numericality: {greater_than: 0} validates :token_count, numericality: {greater_than_or_equal_to: 0}, allow_nil: true validate :from_event_id_not_after_to_event_id scope :for_level, ->(level) { where(level: level) } scope :chronological, -> { order(:from_event_id) } # L1 snapshots whose event 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_event_id <= snapshots.from_event_id " \ "AND l2.to_event_id >= snapshots.to_event_id)" ) } # Snapshots whose source events have fully evicted from the sliding window. # A snapshot is visible when its entire event range precedes the first # event currently in the viewport. # # @param first_event_id [Integer] the first event ID in the sliding window scope :source_events_evicted, ->(first_event_id) { where("to_event_id < ?", first_event_id) } # @return [Integer] token cost, using cached count or heuristic estimate def token_cost token_count.positive? ? token_count : estimate_tokens end private def from_event_id_not_after_to_event_id return unless from_event_id && to_event_id errors.add(:from_event_id, "must be <= to_event_id") if from_event_id > to_event_id end # @return [Integer] estimated token count (at least 1) def estimate_tokens [(text.bytesize / Event::BYTES_PER_TOKEN.to_f).ceil, 1].max end end |
#level ⇒ Integer
Returns compression level (1 = from raw events, 2 = from L1 snapshots).
22 23 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 67 68 69 70 71 72 73 74 75 76 |
# File 'app/models/snapshot.rb', line 22 class Snapshot < ApplicationRecord 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_event_id, presence: true validates :to_event_id, presence: true validates :level, presence: true, numericality: {greater_than: 0} validates :token_count, numericality: {greater_than_or_equal_to: 0}, allow_nil: true validate :from_event_id_not_after_to_event_id scope :for_level, ->(level) { where(level: level) } scope :chronological, -> { order(:from_event_id) } # L1 snapshots whose event 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_event_id <= snapshots.from_event_id " \ "AND l2.to_event_id >= snapshots.to_event_id)" ) } # Snapshots whose source events have fully evicted from the sliding window. # A snapshot is visible when its entire event range precedes the first # event currently in the viewport. # # @param first_event_id [Integer] the first event ID in the sliding window scope :source_events_evicted, ->(first_event_id) { where("to_event_id < ?", first_event_id) } # @return [Integer] token cost, using cached count or heuristic estimate def token_cost token_count.positive? ? token_count : estimate_tokens end private def from_event_id_not_after_to_event_id return unless from_event_id && to_event_id errors.add(:from_event_id, "must be <= to_event_id") if from_event_id > to_event_id end # @return [Integer] estimated token count (at least 1) def estimate_tokens [(text.bytesize / Event::BYTES_PER_TOKEN.to_f).ceil, 1].max end end |
#text ⇒ String
Returns the summary text generated by Mneme.
22 23 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 67 68 69 70 71 72 73 74 75 76 |
# File 'app/models/snapshot.rb', line 22 class Snapshot < ApplicationRecord 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_event_id, presence: true validates :to_event_id, presence: true validates :level, presence: true, numericality: {greater_than: 0} validates :token_count, numericality: {greater_than_or_equal_to: 0}, allow_nil: true validate :from_event_id_not_after_to_event_id scope :for_level, ->(level) { where(level: level) } scope :chronological, -> { order(:from_event_id) } # L1 snapshots whose event 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_event_id <= snapshots.from_event_id " \ "AND l2.to_event_id >= snapshots.to_event_id)" ) } # Snapshots whose source events have fully evicted from the sliding window. # A snapshot is visible when its entire event range precedes the first # event currently in the viewport. # # @param first_event_id [Integer] the first event ID in the sliding window scope :source_events_evicted, ->(first_event_id) { where("to_event_id < ?", first_event_id) } # @return [Integer] token cost, using cached count or heuristic estimate def token_cost token_count.positive? ? token_count : estimate_tokens end private def from_event_id_not_after_to_event_id return unless from_event_id && to_event_id errors.add(:from_event_id, "must be <= to_event_id") if from_event_id > to_event_id end # @return [Integer] estimated token count (at least 1) def estimate_tokens [(text.bytesize / Event::BYTES_PER_TOKEN.to_f).ceil, 1].max end end |
#to_event_id ⇒ Integer
Returns last event ID covered by this snapshot.
22 23 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 67 68 69 70 71 72 73 74 75 76 |
# File 'app/models/snapshot.rb', line 22 class Snapshot < ApplicationRecord 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_event_id, presence: true validates :to_event_id, presence: true validates :level, presence: true, numericality: {greater_than: 0} validates :token_count, numericality: {greater_than_or_equal_to: 0}, allow_nil: true validate :from_event_id_not_after_to_event_id scope :for_level, ->(level) { where(level: level) } scope :chronological, -> { order(:from_event_id) } # L1 snapshots whose event 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_event_id <= snapshots.from_event_id " \ "AND l2.to_event_id >= snapshots.to_event_id)" ) } # Snapshots whose source events have fully evicted from the sliding window. # A snapshot is visible when its entire event range precedes the first # event currently in the viewport. # # @param first_event_id [Integer] the first event ID in the sliding window scope :source_events_evicted, ->(first_event_id) { where("to_event_id < ?", first_event_id) } # @return [Integer] token cost, using cached count or heuristic estimate def token_cost token_count.positive? ? token_count : estimate_tokens end private def from_event_id_not_after_to_event_id return unless from_event_id && to_event_id errors.add(:from_event_id, "must be <= to_event_id") if from_event_id > to_event_id end # @return [Integer] estimated token count (at least 1) def estimate_tokens [(text.bytesize / Event::BYTES_PER_TOKEN.to_f).ceil, 1].max end end |
#token_count ⇒ Integer
Returns cached token count of the summary text.
22 23 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 67 68 69 70 71 72 73 74 75 76 |
# File 'app/models/snapshot.rb', line 22 class Snapshot < ApplicationRecord 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_event_id, presence: true validates :to_event_id, presence: true validates :level, presence: true, numericality: {greater_than: 0} validates :token_count, numericality: {greater_than_or_equal_to: 0}, allow_nil: true validate :from_event_id_not_after_to_event_id scope :for_level, ->(level) { where(level: level) } scope :chronological, -> { order(:from_event_id) } # L1 snapshots whose event 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_event_id <= snapshots.from_event_id " \ "AND l2.to_event_id >= snapshots.to_event_id)" ) } # Snapshots whose source events have fully evicted from the sliding window. # A snapshot is visible when its entire event range precedes the first # event currently in the viewport. # # @param first_event_id [Integer] the first event ID in the sliding window scope :source_events_evicted, ->(first_event_id) { where("to_event_id < ?", first_event_id) } # @return [Integer] token cost, using cached count or heuristic estimate def token_cost token_count.positive? ? token_count : estimate_tokens end private def from_event_id_not_after_to_event_id return unless from_event_id && to_event_id errors.add(:from_event_id, "must be <= to_event_id") if from_event_id > to_event_id end # @return [Integer] estimated token count (at least 1) def estimate_tokens [(text.bytesize / Event::BYTES_PER_TOKEN.to_f).ceil, 1].max end end |
Instance Method Details
#token_cost ⇒ Integer
Returns token cost, using cached count or heuristic estimate.
61 62 63 |
# File 'app/models/snapshot.rb', line 61 def token_cost token_count.positive? ? token_count : estimate_tokens end |