Class: RubyCode::Memory

Inherits:
RubyCode::Models::Base show all
Defined in:
lib/rubycode/models/memory.rb

Overview

Manages conversation memory using shared database connection

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from RubyCode::Models::Base

all, count, dataset, delete, first, last, latest, oldest, order, where

Class Method Details

.table_name ⇒ Object



7
8
9
# File 'lib/rubycode/models/memory.rb', line 7

def table_name
  :messages
end

Instance Method Details

#add_message(message = nil, role: nil, content: nil, tool_calls: nil) ⇒ Object

Accept either Message objects or keyword arguments for backwards compatibility



13
14
15
16
17
18
19
20
21
# File 'lib/rubycode/models/memory.rb', line 13

def add_message(message = nil, role: nil, content: nil, tool_calls: nil)
  if message.is_a?(Message)
    insert_message(message.role, message.content, message.tool_calls)
  elsif role && content
    insert_message(role, content, tool_calls)
  else
    raise ArgumentError, "Must provide either a Message object or role: and content: keyword arguments"
  end
end

#build_message_list(first_msg, middle, recent, prune_tool_results) ⇒ Object



49
50
51
52
53
54
# File 'lib/rubycode/models/memory.rb', line 49

def build_message_list(first_msg, middle, recent, prune_tool_results)
  return [first_msg] + recent unless should_prune_middle?(prune_tool_results, middle)

  pruned_middle = prune_tool_results_from_middle(middle)
  [first_msg] + pruned_middle + recent
end

#build_windowed_format(all_messages, window_size, prune_tool_results) ⇒ Object



40
41
42
43
44
45
46
47
# File 'lib/rubycode/models/memory.rb', line 40

def build_windowed_format(all_messages, window_size, prune_tool_results)
  first_msg, recent, middle = partition_messages(all_messages, window_size)

  return recent.map(&:to_h) if recent.include?(first_msg)

  result_messages = build_message_list(first_msg, middle, recent, prune_tool_results)
  result_messages.map(&:to_h)
end

#clear ⇒ Object



81
82
83
# File 'lib/rubycode/models/memory.rb', line 81

def clear
  db[:messages].delete
end

#last_assistant_message ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/rubycode/models/memory.rb', line 96

def last_assistant_message
  row = self.class.latest.where(role: "assistant").first
  return nil unless row

  Message.new(
    role: row[:role],
    content: row[:content],
    tool_calls: deserialize_tool_calls(row[:tool_calls])
  )
end

#last_user_message ⇒ Object



85
86
87
88
89
90
91
92
93
94
# File 'lib/rubycode/models/memory.rb', line 85

def last_user_message
  row = self.class.latest.where(role: "user").first
  return nil unless row

  Message.new(
    role: row[:role],
    content: row[:content],
    tool_calls: deserialize_tool_calls(row[:tool_calls])
  )
end

#messages ⇒ Object



23
24
25
26
27
28
29
30
31
# File 'lib/rubycode/models/memory.rb', line 23

def messages
  db[:messages].order(:id).map do |row|
    Message.new(
      role: row[:role],
      content: row[:content],
      tool_calls: deserialize_tool_calls(row[:tool_calls])
    )
  end
end

#partition_messages(all_messages, window_size) ⇒ Object



56
57
58
59
60
61
# File 'lib/rubycode/models/memory.rb', line 56

def partition_messages(all_messages, window_size)
  first_msg = all_messages.first
  recent = all_messages.last(window_size)
  middle = all_messages[1..-(window_size + 1)] || []
  [first_msg, recent, middle]
end

#prune_tool_results_from_middle(middle) ⇒ Object



71
72
73
74
75
76
77
78
79
# File 'lib/rubycode/models/memory.rb', line 71

def prune_tool_results_from_middle(middle)
  middle.map do |msg|
    if tool_result_message?(msg)
      Message.new(role: msg.role, content: "[Tool result cleared]")
    else
      msg
    end
  end
end

#should_prune_middle?(prune_tool_results, middle) ⇒ Boolean

Returns:

  • (Boolean)


63
64
65
# File 'lib/rubycode/models/memory.rb', line 63

def should_prune_middle?(prune_tool_results, middle)
  prune_tool_results && middle.any? { |msg| tool_result_message?(msg) }
end

#to_llm_format(window_size: 10, prune_tool_results: true) ⇒ Object



33
34
35
36
37
38
# File 'lib/rubycode/models/memory.rb', line 33

def to_llm_format(window_size: 10, prune_tool_results: true)
  all_messages = messages
  return all_messages.map(&:to_h) if all_messages.length <= window_size

  build_windowed_format(all_messages, window_size, prune_tool_results)
end

#tool_result_message?(message) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/rubycode/models/memory.rb', line 67

def tool_result_message?(message)
  message.role == "user" && message.content.start_with?("Tool '")
end