Class: RubyCoded::Chat::State

Inherits:
Object
  • Object
show all
Includes:
MessageAssistant, MessageTokenTracking, Messages, ModelSelection, PlanTracking, Scrollable, TokenCost, ToolConfirmation
Defined in:
lib/ruby_coded/chat/state.rb,
lib/ruby_coded/chat/state/messages.rb,
lib/ruby_coded/chat/state/scrollable.rb,
lib/ruby_coded/chat/state/token_cost.rb,
lib/ruby_coded/chat/state/plan_tracking.rb,
lib/ruby_coded/chat/state/model_selection.rb,
lib/ruby_coded/chat/state/message_assistant.rb,
lib/ruby_coded/chat/state/tool_confirmation.rb,
lib/ruby_coded/chat/state/message_token_tracking.rb

Overview

This class is used to manage the state of the chat

Defined Under Namespace

Modules: MessageAssistant, MessageTokenTracking, Messages, ModelSelection, PlanTracking, Scrollable, TokenCost, ToolConfirmation

Constant Summary collapse

MIN_RENDER_INTERVAL =
0.05

Constants included from TokenCost

TokenCost::CACHE_CREATION_INPUT_MULTIPLIER, TokenCost::UNPRICED_DEFAULTS

Constants included from MessageTokenTracking

MessageTokenTracking::TOKEN_KEYS

Constants included from Messages

Messages::ZERO_TOKEN_USAGE

Instance Attribute Summary collapse

Attributes included from PlanTracking

#clarification_custom_input, #clarification_index, #clarification_input_mode, #clarification_options, #clarification_question

Attributes included from ToolConfirmation

#tool_cv

Attributes included from Messages

#message_generation

Instance Method Summary collapse

Methods included from TokenCost

#init_token_cost, #session_cost_breakdown, #total_session_cost

Methods included from PlanTracking

#activate_plan_mode!, #append_to_clarification_input, #clarification_down, #clarification_up, #clear_plan!, #current_plan, #deactivate_plan_mode!, #delete_last_clarification_char, #enter_plan_clarification!, #exit_plan_clarification!, #has_unsaved_plan?, #init_plan_tracking, #mark_plan_saved!, #plan_clarification?, #plan_mode_active?, #plan_saved?, #selected_clarification_option, #toggle_clarification_input_mode!, #update_current_plan!

Methods included from ToolConfirmation

#auto_approve_tools?, #awaiting_tool_confirmation?, #clear_tool_confirmation!, #disable_auto_approve!, #enable_auto_approve!, #init_tool_confirmation, #pending_tool_args, #pending_tool_name, #request_tool_confirmation!, #resolve_tool_confirmation!, #tool_confirmation_response, #tool_confirmation_response=

Methods included from Scrollable

#scroll_down, #scroll_to_bottom, #scroll_to_top, #scroll_up, #update_scroll_metrics

Methods included from MessageTokenTracking

#token_usage_by_model, #total_input_tokens, #total_output_tokens, #total_thinking_tokens, #update_last_message_tokens

Methods included from MessageAssistant

#ensure_last_is_assistant!, #fail_last_assistant, #last_assistant_empty?, #reset_last_assistant_content, #streaming_append

Methods included from Messages

#add_message, #append_to_last_message, #build_message, #clear_messages!, #init_messages, #messages_snapshot

Methods included from ModelSelection

#append_to_model_filter, #delete_last_filter_char, #enter_model_select!, #exit_model_select!, #filtered_model_list, #model_select?, #model_select_down, #model_select_show_all?, #model_select_up, #selected_model

Constructor Details

#initialize(model:) ⇒ State

Returns a new instance of State.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/ruby_coded/chat/state.rb', line 32

def initialize(model:)
  @model = model
  # String.new: literals like "" are frozen under frozen_string_literal
  @input_buffer = String.new
  @cursor_position = 0
  @input_scroll_offset = 0
  @messages = []
  @streaming = false
  @should_quit = false
  @agentic_mode = false
  @mutex = Mutex.new
  @dirty = true
  @last_render_at = 0.0
  @scroll_offset = 0
  @total_lines = 0
  @visible_height = 0
  @mode = :chat
  @model_list = []
  @model_select_index = 0
  @model_select_filter = String.new
  @model_select_show_all = false
  init_messages
  init_tool_confirmation
  init_plan_tracking
  init_token_cost
  init_plugin_state
end

Instance Attribute Details

#cursor_positionObject (readonly)

Returns the value of attribute cursor_position.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def cursor_position
  @cursor_position
end

#input_bufferObject (readonly)

Returns the value of attribute input_buffer.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def input_buffer
  @input_buffer
end

#input_scroll_offsetObject (readonly)

Returns the value of attribute input_scroll_offset.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def input_scroll_offset
  @input_scroll_offset
end

#messagesObject (readonly)

Returns the value of attribute messages.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def messages
  @messages
end

#modeObject (readonly)

Returns the value of attribute mode.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def mode
  @mode
end

#modelObject

Returns the value of attribute model.



28
29
30
# File 'lib/ruby_coded/chat/state.rb', line 28

def model
  @model
end

#model_listObject (readonly)

Returns the value of attribute model_list.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def model_list
  @model_list
end

#model_select_filterObject (readonly)

Returns the value of attribute model_select_filter.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def model_select_filter
  @model_select_filter
end

#model_select_indexObject (readonly)

Returns the value of attribute model_select_index.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def model_select_index
  @model_select_index
end

#mutexObject (readonly)

Returns the value of attribute mutex.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def mutex
  @mutex
end

#scroll_offsetObject (readonly)

Returns the value of attribute scroll_offset.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def scroll_offset
  @scroll_offset
end

#should_quitObject

Returns the value of attribute should_quit.



28
29
30
# File 'lib/ruby_coded/chat/state.rb', line 28

def should_quit
  @should_quit
end

#streamingObject

Returns the value of attribute streaming.



25
26
27
# File 'lib/ruby_coded/chat/state.rb', line 25

def streaming
  @streaming
end

Instance Method Details

#agentic_mode=(value) ⇒ Object



73
74
75
76
# File 'lib/ruby_coded/chat/state.rb', line 73

def agentic_mode=(value)
  @agentic_mode = value
  mark_dirty!
end

#agentic_mode?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/ruby_coded/chat/state.rb', line 69

def agentic_mode?
  @agentic_mode
end

#append_to_input(text) ⇒ Object



124
125
126
127
128
129
130
# File 'lib/ruby_coded/chat/state.rb', line 124

def append_to_input(text)
  @input_buffer.insert(@cursor_position, text)
  @cursor_position += text.length
  update_input_scroll_offset
  mark_dirty!
  reset_command_completion_index if respond_to?(:reset_command_completion_index, true)
end

#clear_input!Object



174
175
176
177
178
179
# File 'lib/ruby_coded/chat/state.rb', line 174

def clear_input!
  @input_buffer.clear
  @cursor_position = 0
  @input_scroll_offset = 0
  mark_dirty!
end

#consume_input!Object



181
182
183
184
185
186
187
# File 'lib/ruby_coded/chat/state.rb', line 181

def consume_input!
  input = @input_buffer.dup
  @input_buffer.clear
  @cursor_position = 0
  @input_scroll_offset = 0
  input
end

#delete_last_charObject



132
133
134
135
136
137
138
139
140
# File 'lib/ruby_coded/chat/state.rb', line 132

def delete_last_char
  return if @cursor_position <= 0

  @input_buffer.slice!(@cursor_position - 1)
  @cursor_position -= 1
  update_input_scroll_offset
  mark_dirty!
  reset_command_completion_index if respond_to?(:reset_command_completion_index, true)
end

#dirty?Boolean

Returns:

  • (Boolean)


82
83
84
85
86
87
88
89
90
# File 'lib/ruby_coded/chat/state.rb', line 82

def dirty?
  @mutex.synchronize do
    return false unless @dirty
    return true unless @streaming

    now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    (now - @last_render_at) >= MIN_RENDER_INTERVAL
  end
end

#mark_clean!Object



92
93
94
95
96
97
# File 'lib/ruby_coded/chat/state.rb', line 92

def mark_clean!
  @mutex.synchronize do
    @dirty = false
    @last_render_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
  end
end

#mark_dirty!Object



99
100
101
# File 'lib/ruby_coded/chat/state.rb', line 99

def mark_dirty!
  @mutex.synchronize { @dirty = true }
end

#move_cursor_leftObject



142
143
144
145
146
147
148
# File 'lib/ruby_coded/chat/state.rb', line 142

def move_cursor_left
  return if @cursor_position <= 0

  @cursor_position -= 1
  update_input_scroll_offset
  mark_dirty!
end

#move_cursor_rightObject



150
151
152
153
154
155
156
# File 'lib/ruby_coded/chat/state.rb', line 150

def move_cursor_right
  return if @cursor_position >= @input_buffer.length

  @cursor_position += 1
  update_input_scroll_offset
  mark_dirty!
end

#move_cursor_to_endObject



166
167
168
169
170
171
172
# File 'lib/ruby_coded/chat/state.rb', line 166

def move_cursor_to_end
  return if @cursor_position == @input_buffer.length

  @cursor_position = @input_buffer.length
  update_input_scroll_offset
  mark_dirty!
end

#move_cursor_to_startObject



158
159
160
161
162
163
164
# File 'lib/ruby_coded/chat/state.rb', line 158

def move_cursor_to_start
  return if @cursor_position == 0

  @cursor_position = 0
  update_input_scroll_offset
  mark_dirty!
end

#should_quit?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/ruby_coded/chat/state.rb', line 78

def should_quit?
  @should_quit
end

#streaming?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/ruby_coded/chat/state.rb', line 65

def streaming?
  @streaming
end

#update_input_scroll_offsetObject

Updates the horizontal scroll offset of the input area so the cursor is always visible. Call this after every cursor / buffer change. visible_width is set by the renderer each frame via update_input_visible_width.



107
108
109
110
111
112
113
114
115
116
# File 'lib/ruby_coded/chat/state.rb', line 107

def update_input_scroll_offset
  visible = @input_visible_width || 0
  return if visible <= 0

  if @cursor_position < @input_scroll_offset
    @input_scroll_offset = @cursor_position
  elsif @cursor_position >= @input_scroll_offset + visible
    @input_scroll_offset = @cursor_position - visible + 1
  end
end

#update_input_visible_width(width) ⇒ Object

Called by the renderer so the state knows how many characters fit on screen (inner width minus the prompt prefix).



120
121
122
# File 'lib/ruby_coded/chat/state.rb', line 120

def update_input_visible_width(width)
  @input_visible_width = width
end