Module: RailsConsoleAi::Storage::DatabaseStorage

Defined in:
lib/rails_console_ai/storage/database_storage.rb

Overview

Thin facade over the AR-backed Skill / Memory tables.

Not a drop-in Storage::Base adapter: the loaders read & write structured records, not opaque Markdown blobs, so we expose a small typed API instead. All methods are safe to call before ‘ai_db_setup` has run — they detect missing tables and return empty results / nil rather than raising.

Class Method Summary collapse

Class Method Details

.active_record_connectionObject



37
38
39
40
41
42
43
44
45
# File 'lib/rails_console_ai/storage/database_storage.rb', line 37

def active_record_connection
  klass = RailsConsoleAi.configuration.connection_class
  if klass
    klass = Object.const_get(klass) if klass.is_a?(String)
    klass.connection
  else
    ::ActiveRecord::Base.connection
  end
end

.agents_available?Boolean

Returns:

  • (Boolean)


20
21
22
# File 'lib/rails_console_ai/storage/database_storage.rb', line 20

def agents_available?
  table_exists?('rails_console_ai_agents')
end

.all_agentsObject

— Agents —



139
140
141
142
143
144
145
# File 'lib/rails_console_ai/storage/database_storage.rb', line 139

def all_agents
  return [] unless agents_available?
  RailsConsoleAi::Agent.alphabetical.map(&:to_hash)
rescue => e
  warn_failure(:all_agents, e)
  []
end

.all_memoriesObject

— Memories —



95
96
97
98
99
100
101
# File 'lib/rails_console_ai/storage/database_storage.rb', line 95

def all_memories
  return [] unless memories_available?
  RailsConsoleAi::Memory.alphabetical.map(&:to_hash)
rescue => e
  warn_failure(:all_memories, e)
  []
end

.all_skillsObject

— Skills —



49
50
51
52
53
54
55
# File 'lib/rails_console_ai/storage/database_storage.rb', line 49

def all_skills
  return [] unless available?
  RailsConsoleAi::Skill.alphabetical.map(&:to_hash)
rescue => e
  warn_failure(:all_skills, e)
  []
end

.available?Boolean

Returns:

  • (Boolean)


12
13
14
# File 'lib/rails_console_ai/storage/database_storage.rb', line 12

def available?
  table_exists?('rails_console_ai_skills')
end

.delete_agent_by_name(name) ⇒ Object



176
177
178
179
180
181
182
# File 'lib/rails_console_ai/storage/database_storage.rb', line 176

def delete_agent_by_name(name)
  return false unless agents_available?
  record = RailsConsoleAi::Agent.where('LOWER(name) = ?', name.to_s.downcase).first
  return false unless record
  record.destroy
  true
end

.delete_memory_by_name(name) ⇒ Object



129
130
131
132
133
134
135
# File 'lib/rails_console_ai/storage/database_storage.rb', line 129

def delete_memory_by_name(name)
  return false unless memories_available?
  record = RailsConsoleAi::Memory.where('LOWER(name) = ?', name.to_s.downcase).first
  return false unless record
  record.destroy
  true
end

.delete_skill_by_name(name) ⇒ Object



85
86
87
88
89
90
91
# File 'lib/rails_console_ai/storage/database_storage.rb', line 85

def delete_skill_by_name(name)
  return false unless available?
  record = RailsConsoleAi::Skill.where('LOWER(name) = ?', name.to_s.downcase).first
  return false unless record
  record.destroy
  true
end

.ensure_tables!(kind) ⇒ Object

Raises:



184
185
186
187
188
189
190
191
192
193
# File 'lib/rails_console_ai/storage/database_storage.rb', line 184

def ensure_tables!(kind)
  ready = case kind
          when :skills   then available?
          when :memories then memories_available?
          when :agents   then agents_available?
          else false
          end
  return if ready
  raise StorageError, "rails_console_ai_#{kind} table does not exist. Run `ai_db_setup` in your console."
end

.find_agent_by_name(name) ⇒ Object



147
148
149
150
151
152
153
154
# File 'lib/rails_console_ai/storage/database_storage.rb', line 147

def find_agent_by_name(name)
  return nil unless agents_available?
  record = RailsConsoleAi::Agent.where('LOWER(name) = ?', name.to_s.downcase).first
  record&.to_hash
rescue => e
  warn_failure(:find_agent_by_name, e)
  nil
end

.find_memory_by_name(name) ⇒ Object



103
104
105
106
107
108
109
110
# File 'lib/rails_console_ai/storage/database_storage.rb', line 103

def find_memory_by_name(name)
  return nil unless memories_available?
  record = RailsConsoleAi::Memory.where('LOWER(name) = ?', name.to_s.downcase).first
  record&.to_hash
rescue => e
  warn_failure(:find_memory_by_name, e)
  nil
end

.find_skill_by_name(name) ⇒ Object



57
58
59
60
61
62
63
64
# File 'lib/rails_console_ai/storage/database_storage.rb', line 57

def find_skill_by_name(name)
  return nil unless available?
  record = RailsConsoleAi::Skill.where('LOWER(name) = ?', name.to_s.downcase).first
  record&.to_hash
rescue => e
  warn_failure(:find_skill_by_name, e)
  nil
end

.memories_available?Boolean

Returns:

  • (Boolean)


16
17
18
# File 'lib/rails_console_ai/storage/database_storage.rb', line 16

def memories_available?
  table_exists?('rails_console_ai_memories')
end

.save_agent(name:, description:, body:, max_rounds: nil, model: nil, tools: [], edited_by: nil, change_note: nil) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/rails_console_ai/storage/database_storage.rb', line 156

def save_agent(name:, description:, body:, max_rounds: nil, model: nil, tools: [], edited_by: nil, change_note: nil)
  ensure_tables!(:agents)
  record = RailsConsoleAi::Agent.where('LOWER(name) = ?', name.to_s.downcase).first
  record ||= RailsConsoleAi::Agent.new
  was_new = record.new_record?
  record.update_with_version!(
    {
      name:        name,
      description: description,
      body:        body,
      max_rounds:  max_rounds,
      model:       model,
      tools:       Array(tools)
    },
    edited_by: edited_by,
    change_note: change_note
  )
  [record, was_new]
end

.save_memory(name:, description:, tags: [], edited_by: nil, change_note: nil) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/rails_console_ai/storage/database_storage.rb', line 112

def save_memory(name:, description:, tags: [], edited_by: nil, change_note: nil)
  ensure_tables!(:memories)
  record = RailsConsoleAi::Memory.where('LOWER(name) = ?', name.to_s.downcase).first
  record ||= RailsConsoleAi::Memory.new
  was_new = record.new_record?
  record.update_with_version!(
    {
      name:        name,
      description: description,
      tags:        Array(tags)
    },
    edited_by: edited_by,
    change_note: change_note
  )
  [record, was_new]
end

.save_skill(name:, description:, body:, tags: [], bypass_guards_for_methods: [], edited_by: nil, change_note: nil) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/rails_console_ai/storage/database_storage.rb', line 66

def save_skill(name:, description:, body:, tags: [], bypass_guards_for_methods: [], edited_by: nil, change_note: nil)
  ensure_tables!(:skills)
  record = RailsConsoleAi::Skill.where('LOWER(name) = ?', name.to_s.downcase).first
  record ||= RailsConsoleAi::Skill.new
  was_new = record.new_record?
  record.update_with_version!(
    {
      name:                      name,
      description:               description,
      body:                      body,
      tags:                      Array(tags),
      bypass_guards_for_methods: Array(bypass_guards_for_methods)
    },
    edited_by: edited_by,
    change_note: change_note
  )
  [record, was_new]
end

.table_exists?(table_name) ⇒ Boolean

Ask the connection directly so we don’t depend on the AR model being autoloaded yet. In a Rails console, the models in app/models are autoloaded lazily — the constant is ‘defined?`-false until first reference. Going through the connection avoids that timing trap.

Returns:

  • (Boolean)


28
29
30
31
32
33
34
35
# File 'lib/rails_console_ai/storage/database_storage.rb', line 28

def table_exists?(table_name)
  return false unless defined?(::ActiveRecord)
  conn = active_record_connection
  return false unless conn
  conn.table_exists?(table_name)
rescue ::ActiveRecord::ActiveRecordError, ::ActiveRecord::NoDatabaseError, NoMethodError
  false
end

.warn_failure(method, error) ⇒ Object



195
196
197
198
# File 'lib/rails_console_ai/storage/database_storage.rb', line 195

def warn_failure(method, error)
  return unless defined?(RailsConsoleAi.logger) && RailsConsoleAi.logger
  RailsConsoleAi.logger.warn("RailsConsoleAi::Storage::DatabaseStorage##{method} failed: #{error.class}: #{error.message}")
end