Class: RubynCode::Agent::Conversation
- Inherits:
-
Object
- Object
- RubynCode::Agent::Conversation
- Defined in:
- lib/rubyn_code/agent/conversation.rb
Overview
rubocop:disable Metrics/ClassLength -- message log + incremental token/tool-ID bookkeeping
Instance Attribute Summary collapse
-
#messages ⇒ Object
readonly
Returns the value of attribute messages.
Instance Method Summary collapse
-
#add_assistant_message(content, tool_calls: []) ⇒ Hash
Append an assistant turn to the conversation.
-
#add_tool_result(tool_use_id, _tool_name, output, is_error: false) ⇒ Hash
Append a tool result turn to the conversation.
-
#add_user_message(content) ⇒ Hash
Append a user turn to the conversation.
-
#clear! ⇒ void
Reset the conversation to an empty state.
-
#estimated_json_chars ⇒ Integer
Character length of the JSON-serialized messages array, maintained incrementally on append so per-turn token estimation doesn't have to re-serialize the whole history.
-
#initialize ⇒ Conversation
constructor
A new instance of Conversation.
-
#last_assistant_text ⇒ String?
Extract the text from the most recent assistant message.
- #length ⇒ Integer
-
#refresh_derived_state! ⇒ void
Drops cached serialization/tool-ID bookkeeping.
-
#replace!(new_messages) ⇒ Object
Replace messages with a new array (used after compaction).
-
#to_a ⇒ Object
(also: #to_ary)
Alias for
messages. -
#to_api_format ⇒ Array<Hash>
Return the messages array formatted for the Claude API.
-
#undo_last! ⇒ void
Remove the last user + assistant exchange.
Constructor Details
#initialize ⇒ Conversation
Returns a new instance of Conversation.
10 11 12 13 |
# File 'lib/rubyn_code/agent/conversation.rb', line 10 def initialize @messages = [] reset_derived_state! end |
Instance Attribute Details
#messages ⇒ Object (readonly)
Returns the value of attribute messages.
8 9 10 |
# File 'lib/rubyn_code/agent/conversation.rb', line 8 def @messages end |
Instance Method Details
#add_assistant_message(content, tool_calls: []) ⇒ Hash
Append an assistant turn to the conversation.
34 35 36 37 38 39 40 |
# File 'lib/rubyn_code/agent/conversation.rb', line 34 def (content, tool_calls: []) blocks = normalize_content(content, tool_calls) = { role: 'assistant', content: blocks } @messages << () end |
#add_tool_result(tool_use_id, _tool_name, output, is_error: false) ⇒ Hash
Append a tool result turn to the conversation.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/rubyn_code/agent/conversation.rb', line 49 def add_tool_result(tool_use_id, _tool_name, output, is_error: false) result_block = { type: 'tool_result', tool_use_id: tool_use_id, content: output.to_s } result_block[:is_error] = true if is_error # The Claude API expects tool results as a user message whose content # is an array of tool_result blocks. When the previous message is # already a user/tool_result message we append to it so that multiple # tool results for the same assistant turn are batched together. if @messages.last && @messages.last[:role] == 'user' && (@messages.last) @messages.last[:content] << result_block track_appended_block(result_block) else = { role: 'user', content: [result_block] } @messages << () end result_block end |
#add_user_message(content) ⇒ Hash
Append a user turn to the conversation.
22 23 24 25 26 27 |
# File 'lib/rubyn_code/agent/conversation.rb', line 22 def (content) = { role: 'user', content: content } @messages << () end |
#clear! ⇒ void
This method returns an undefined value.
Reset the conversation to an empty state.
91 92 93 94 |
# File 'lib/rubyn_code/agent/conversation.rb', line 91 def clear! @messages.clear reset_derived_state! end |
#estimated_json_chars ⇒ Integer
Character length of the JSON-serialized messages array, maintained incrementally on append so per-turn token estimation doesn't have to re-serialize the whole history. Matches JSON.generate(messages).length.
101 102 103 104 105 106 107 |
# File 'lib/rubyn_code/agent/conversation.rb', line 101 def estimated_json_chars @json_chars ||= @messages.sum { |msg| JSON.generate(msg).length } return 2 if @messages.empty? # "[" + per-message JSON joined by "," + "]" @json_chars + @messages.length + 1 end |
#last_assistant_text ⇒ String?
Extract the text from the most recent assistant message.
76 77 78 79 80 81 |
# File 'lib/rubyn_code/agent/conversation.rb', line 76 def last_assistant_text assistant_msg = @messages.reverse_each.find { |m| m[:role] == 'assistant' } return nil unless assistant_msg extract_text(assistant_msg[:content]) end |
#length ⇒ Integer
84 85 86 |
# File 'lib/rubyn_code/agent/conversation.rb', line 84 def length @messages.length end |
#refresh_derived_state! ⇒ void
This method returns an undefined value.
Drops cached serialization/tool-ID bookkeeping. Must be called after messages are mutated in place from outside this class (e.g. by Context::MicroCompact); the caches rebuild lazily on next access.
114 115 116 |
# File 'lib/rubyn_code/agent/conversation.rb', line 114 def refresh_derived_state! reset_derived_state! end |
#replace!(new_messages) ⇒ Object
Replace messages with a new array (used after compaction).
156 157 158 159 |
# File 'lib/rubyn_code/agent/conversation.rb', line 156 def replace!() @messages.replace() reset_derived_state! end |
#to_a ⇒ Object Also known as: to_ary
Alias for messages. Common shorthand for callers that want to
treat the conversation as an Array of message hashes.
163 164 165 |
# File 'lib/rubyn_code/agent/conversation.rb', line 163 def to_a @messages end |
#to_api_format ⇒ Array<Hash>
Return the messages array formatted for the Claude API. Ensures proper role alternation and content structure.
122 123 124 125 126 127 128 129 130 131 |
# File 'lib/rubyn_code/agent/conversation.rb', line 122 def to_api_format formatted = @messages.map do |msg| { role: msg[:role], content: format_content(msg[:content]) } end repair_orphaned_tool_uses(formatted) end |
#undo_last! ⇒ void
This method returns an undefined value.
Remove the last user + assistant exchange. Useful for undo. If the last two messages are assistant then user (most recent first), removes both. Otherwise removes only the last message.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/rubyn_code/agent/conversation.rb', line 138 def undo_last! return if @messages.empty? # Walk backwards and remove the most recent user+assistant pair. # The typical pattern is: [..., user, assistant] or # [..., assistant, user(tool_results)]. removed = 0 while @messages.any? && removed < 2 last = @messages.last break if removed == 1 && last[:role] != 'assistant' && last[:role] != 'user' @messages.pop removed += 1 end reset_derived_state! end |