Class: Botiasloop::Conversation
- Inherits:
-
Object
- Object
- Botiasloop::Conversation
- Defined in:
- lib/botiasloop/conversation.rb
Overview
Conversation model - represents a chat conversation with messages Direct Sequel model with all database and business logic combined
Defined Under Namespace
Classes: Message
Constant Summary collapse
- LABEL_REGEX =
Valid label format: alphanumeric, dashes, and underscores
/\A[a-zA-Z0-9_-]+\z/
Instance Method Summary collapse
-
#add(role, content, input_tokens: 0, output_tokens: 0) ⇒ Object
Add a message to the conversation.
-
#archive! ⇒ Conversation
Archive this conversation.
-
#archived? ⇒ Boolean
Check if this conversation is archived.
-
#before_create ⇒ Object
Auto-generate human-readable ID before creation if not provided.
-
#compact!(summary, recent_messages) ⇒ Object
Compact conversation by replacing old messages with a summary.
-
#history ⇒ Array<Hash>
Get conversation history as array of message hashes.
-
#label=(value) ⇒ Object
Set the label for this conversation.
-
#label? ⇒ Boolean
Check if this conversation has a label.
-
#last_activity ⇒ String?
Get the timestamp of the last activity in the conversation.
-
#message_count ⇒ Integer
Get the number of messages in the conversation.
-
#reset! ⇒ Object
Reset conversation - clear all messages and reset token counts.
-
#system_prompt ⇒ String
Generate the system prompt for this conversation Includes current date/time and environment info.
-
#total_tokens ⇒ Integer
Get total tokens (input + output) for the conversation.
-
#update_token_totals(input_tokens, output_tokens) ⇒ Object
Update conversation-level token totals.
-
#uuid ⇒ String
Human-readable ID of the conversation.
-
#validate ⇒ Object
Validations.
Instance Method Details
#add(role, content, input_tokens: 0, output_tokens: 0) ⇒ Object
Add a message to the conversation
124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/botiasloop/conversation.rb', line 124 def add(role, content, input_tokens: 0, output_tokens: 0) Message.create( conversation_id: id, role: role, content: content, input_tokens: input_tokens || 0, output_tokens: output_tokens || 0, timestamp: Time.now.utc ) # Update conversation token totals update_token_totals(input_tokens, output_tokens) end |
#archive! ⇒ Conversation
Archive this conversation
90 91 92 93 |
# File 'lib/botiasloop/conversation.rb', line 90 def archive! update(archived: true) self end |
#archived? ⇒ Boolean
Check if this conversation is archived
74 75 76 |
# File 'lib/botiasloop/conversation.rb', line 74 def archived? archived == true end |
#before_create ⇒ Object
Auto-generate human-readable ID before creation if not provided
48 49 50 51 |
# File 'lib/botiasloop/conversation.rb', line 48 def before_create self.id ||= HumanId.generate super end |
#compact!(summary, recent_messages) ⇒ Object
Compact conversation by replacing old messages with a summary
173 174 175 176 177 178 179 |
# File 'lib/botiasloop/conversation.rb', line 173 def compact!(summary, ) reset! add("system", summary) .each do |msg| add(msg[:role], msg[:content]) end end |
#history ⇒ Array<Hash>
Get conversation history as array of message hashes
151 152 153 |
# File 'lib/botiasloop/conversation.rb', line 151 def history .order(:timestamp).map(&:to_hash) end |
#label=(value) ⇒ Object
Set the label for this conversation
81 82 83 84 85 |
# File 'lib/botiasloop/conversation.rb', line 81 def label=(value) # Allow empty string to be treated as nil (clearing the label) value = nil if value.to_s.empty? super end |
#label? ⇒ Boolean
Check if this conversation has a label
67 68 69 |
# File 'lib/botiasloop/conversation.rb', line 67 def label? !label.nil? && !label.to_s.empty? end |
#last_activity ⇒ String?
Get the timestamp of the last activity in the conversation
98 99 100 101 102 |
# File 'lib/botiasloop/conversation.rb', line 98 def last_activity return nil if .empty? .order(:timestamp).last..utc.iso8601 end |
#message_count ⇒ Integer
Get the number of messages in the conversation
107 108 109 |
# File 'lib/botiasloop/conversation.rb', line 107 def .count end |
#reset! ⇒ Object
Reset conversation - clear all messages and reset token counts
162 163 164 165 166 167 |
# File 'lib/botiasloop/conversation.rb', line 162 def reset! .delete self.input_tokens = 0 self.output_tokens = 0 save end |
#system_prompt ⇒ String
Generate the system prompt for this conversation Includes current date/time and environment info
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/botiasloop/conversation.rb', line 185 def system_prompt skills_registry = Skills::Registry.new prompt = <<~PROMPT You are Botias, an autonomous AI agent. Environment: - OS: #{RUBY_PLATFORM} - Shell: #{ENV.fetch("SHELL", "unknown")} - Working Directory: #{Dir.pwd} - Date: #{Time.now.strftime("%Y-%m-%d")} - Time: #{Time.now.strftime("%H:%M:%S %Z")} You operate in a ReAct loop: Reason about the task, Act using tools, Observe results. PROMPT if skills_registry.skills.any? prompt += <<~SKILLS Available Skills: #{skills_registry.skills_table} To use a skill, read its SKILL.md file at the provided path using the shell tool (e.g., `cat ~/skills/skill-name/SKILL.md`). Skills follow progressive disclosure: only metadata is shown above. Full instructions are loaded on demand. SKILLS end prompt += build_operator_section prompt += build_identity_section prompt end |
#total_tokens ⇒ Integer
Get total tokens (input + output) for the conversation
114 115 116 |
# File 'lib/botiasloop/conversation.rb', line 114 def total_tokens (input_tokens || 0) + (output_tokens || 0) end |
#update_token_totals(input_tokens, output_tokens) ⇒ Object
Update conversation-level token totals
142 143 144 145 146 |
# File 'lib/botiasloop/conversation.rb', line 142 def update_token_totals(input_tokens, output_tokens) self.input_tokens = (self.input_tokens || 0) + (input_tokens || 0) self.output_tokens = (self.output_tokens || 0) + (output_tokens || 0) save if modified? end |
#uuid ⇒ String
Returns Human-readable ID of the conversation.
156 157 158 159 |
# File 'lib/botiasloop/conversation.rb', line 156 def uuid # Return existing id or generate a new one for unsaved records self.id ||= HumanId.generate end |
#validate ⇒ Object
Validations
54 55 56 57 58 59 60 61 62 |
# File 'lib/botiasloop/conversation.rb', line 54 def validate super return unless label && !label.to_s.empty? validates_format LABEL_REGEX, :label, message: "Invalid label format. Use only letters, numbers, dashes, and underscores." validates_unique :label, message: "Label '#{label}' already in use by another conversation" end |