Class: RubynCode::Agent::Conversation

Inherits:
Object
  • Object
show all
Defined in:
lib/rubyn_code/agent/conversation.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConversation

Returns a new instance of Conversation.



8
9
10
# File 'lib/rubyn_code/agent/conversation.rb', line 8

def initialize
  @messages = []
end

Instance Attribute Details

#messagesObject (readonly)

Returns the value of attribute messages.



6
7
8
# File 'lib/rubyn_code/agent/conversation.rb', line 6

def messages
  @messages
end

Instance Method Details

#add_assistant_message(content, tool_calls: []) ⇒ Hash

Append an assistant turn to the conversation.

Parameters:

  • content (Array<Hash>, String, nil)

    text blocks from the response

  • tool_calls (Array<Hash>) (defaults to: [])

    tool_use blocks from the response

Returns:

  • (Hash)

    the appended message



27
28
29
30
31
32
# File 'lib/rubyn_code/agent/conversation.rb', line 27

def add_assistant_message(content, tool_calls: [])
  blocks = normalize_content(content, tool_calls)
  message = { role: 'assistant', content: blocks }
  @messages << message
  message
end

#add_tool_result(tool_use_id, _tool_name, output, is_error: false) ⇒ Hash

Append a tool result turn to the conversation.

Parameters:

  • tool_use_id (String)
  • tool_name (String)
  • output (String)
  • is_error (Boolean) (defaults to: false)

Returns:

  • (Hash)

    the appended message



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/rubyn_code/agent/conversation.rb', line 41

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' && tool_result_message?(@messages.last)
    @messages.last[:content] << result_block
  else
    @messages << { role: 'user', content: [result_block] }
  end

  result_block
end

#add_user_message(content) ⇒ Hash

Append a user turn to the conversation.

Parameters:

  • content (String)

Returns:

  • (Hash)

    the appended message



16
17
18
19
20
# File 'lib/rubyn_code/agent/conversation.rb', line 16

def add_user_message(content)
  message = { role: 'user', content: content }
  @messages << message
  message
end

#clear!void

This method returns an undefined value.

Reset the conversation to an empty state.



80
81
82
# File 'lib/rubyn_code/agent/conversation.rb', line 80

def clear!
  @messages.clear
end

#last_assistant_textString?

Extract the text from the most recent assistant message.

Returns:

  • (String, nil)


65
66
67
68
69
70
# File 'lib/rubyn_code/agent/conversation.rb', line 65

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

#lengthInteger

Returns:

  • (Integer)


73
74
75
# File 'lib/rubyn_code/agent/conversation.rb', line 73

def length
  @messages.length
end

#replace!(new_messages) ⇒ Object

Replace messages with a new array (used after compaction).



121
122
123
# File 'lib/rubyn_code/agent/conversation.rb', line 121

def replace!(new_messages)
  @messages.replace(new_messages)
end

#to_api_formatArray<Hash>

Return the messages array formatted for the Claude API. Ensures proper role alternation and content structure.

Returns:

  • (Array<Hash>)


88
89
90
91
92
93
94
95
96
97
# File 'lib/rubyn_code/agent/conversation.rb', line 88

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.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/rubyn_code/agent/conversation.rb', line 104

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
end