Class: RubyCoded::Chat::State

Inherits:
Object
  • Object
show all
Includes:
ContextWindow, LoginFlow, 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/login_flow.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/context_window.rb,
lib/ruby_coded/chat/state/model_selection.rb,
lib/ruby_coded/chat/state/login_flow_steps.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: ContextWindow, LoginFlow, LoginFlowSteps, 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 LoginFlow

#login_auth_method, #login_error, #login_items, #login_key_buffer, #login_key_cursor, #login_oauth_result, #login_provider, #login_select_index, #login_step

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 LoginFlow

#append_to_login_key, #delete_last_login_key_char, #enter_login_flow!, #exit_login_flow!, #init_login_flow, #login_active?, #login_advance_to_api_key!, #login_advance_to_auth_method!, #login_advance_to_oauth!, #login_clear_oauth_result!, #login_provider_module, #login_select_down, #login_select_up, #login_selected_item, #login_set_error!, #login_set_oauth_result!

Methods included from ContextWindow

#current_model_context_window, #session_context_tokens_used, #session_context_usage_percentage

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

#last_turn_context_tokens, #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:, command_catalog: nil) ⇒ State

Returns a new instance of State.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/ruby_coded/chat/state.rb', line 36

def initialize(model:, command_catalog: nil)
  @model = model
  @command_catalog = command_catalog
  # 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
  @codex_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

#codex_modeObject

Returns the value of attribute codex_mode.



32
33
34
# File 'lib/ruby_coded/chat/state.rb', line 32

def codex_mode
  @codex_mode
end

#command_catalogObject (readonly)

Returns the value of attribute command_catalog.



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

def command_catalog
  @command_catalog
end

#cursor_positionObject (readonly)

Returns the value of attribute cursor_position.



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

def cursor_position
  @cursor_position
end

#input_bufferObject (readonly)

Returns the value of attribute input_buffer.



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

def input_buffer
  @input_buffer
end

#input_scroll_offsetObject (readonly)

Returns the value of attribute input_scroll_offset.



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

def input_scroll_offset
  @input_scroll_offset
end

#messagesObject (readonly)

Returns the value of attribute messages.



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

def messages
  @messages
end

#modeObject (readonly)

Returns the value of attribute mode.



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

def mode
  @mode
end

#modelObject

Returns the value of attribute model.



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

def model
  @model
end

#model_listObject (readonly)

Returns the value of attribute model_list.



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

def model_list
  @model_list
end

#model_select_filterObject (readonly)

Returns the value of attribute model_select_filter.



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

def model_select_filter
  @model_select_filter
end

#model_select_indexObject (readonly)

Returns the value of attribute model_select_index.



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

def model_select_index
  @model_select_index
end

#mutexObject (readonly)

Returns the value of attribute mutex.



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

def mutex
  @mutex
end

#scroll_offsetObject (readonly)

Returns the value of attribute scroll_offset.



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

def scroll_offset
  @scroll_offset
end

#should_quitObject

Returns the value of attribute should_quit.



32
33
34
# File 'lib/ruby_coded/chat/state.rb', line 32

def should_quit
  @should_quit
end

#streamingObject

Returns the value of attribute streaming.



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

def streaming
  @streaming
end

#tui_suspend_reasonObject (readonly)

Returns the value of attribute tui_suspend_reason.



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

def tui_suspend_reason
  @tui_suspend_reason
end

Instance Method Details

#agentic_mode=(value) ⇒ Object



87
88
89
90
# File 'lib/ruby_coded/chat/state.rb', line 87

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

#agentic_mode?Boolean

Returns:

  • (Boolean)


83
84
85
# File 'lib/ruby_coded/chat/state.rb', line 83

def agentic_mode?
  @agentic_mode
end

#append_to_input(text) ⇒ Object



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

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



194
195
196
197
198
199
# File 'lib/ruby_coded/chat/state.rb', line 194

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

#clear_tui_suspend!Object



223
224
225
226
# File 'lib/ruby_coded/chat/state.rb', line 223

def clear_tui_suspend!
  @tui_suspend_reason = nil
  @tui_suspend_metadata = nil
end

#command_descriptionsObject



96
97
98
99
100
# File 'lib/ruby_coded/chat/state.rb', line 96

def command_descriptions
  return {} unless @command_catalog

  @command_catalog.command_descriptions
end

#consume_input!Object



201
202
203
204
205
206
207
# File 'lib/ruby_coded/chat/state.rb', line 201

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

#delete_last_charObject



152
153
154
155
156
157
158
159
160
# File 'lib/ruby_coded/chat/state.rb', line 152

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)


102
103
104
105
106
107
108
109
110
# File 'lib/ruby_coded/chat/state.rb', line 102

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



112
113
114
115
116
117
# File 'lib/ruby_coded/chat/state.rb', line 112

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

#mark_dirty!Object



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

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

#move_cursor_leftObject



162
163
164
165
166
167
168
# File 'lib/ruby_coded/chat/state.rb', line 162

def move_cursor_left
  return if @cursor_position <= 0

  @cursor_position -= 1
  update_input_scroll_offset
  mark_dirty!
end

#move_cursor_rightObject



170
171
172
173
174
175
176
# File 'lib/ruby_coded/chat/state.rb', line 170

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



186
187
188
189
190
191
192
# File 'lib/ruby_coded/chat/state.rb', line 186

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



178
179
180
181
182
183
184
# File 'lib/ruby_coded/chat/state.rb', line 178

def move_cursor_to_start
  return if @cursor_position == 0

  @cursor_position = 0
  update_input_scroll_offset
  mark_dirty!
end

#request_tui_suspend!(reason, **metadata) ⇒ Object



209
210
211
212
213
# File 'lib/ruby_coded/chat/state.rb', line 209

def request_tui_suspend!(reason, **)
  @tui_suspend_reason = reason
  @tui_suspend_metadata = 
  mark_dirty!
end

#should_quit?Boolean

Returns:

  • (Boolean)


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

def should_quit?
  @should_quit
end

#streaming?Boolean

Returns:

  • (Boolean)


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

def streaming?
  @streaming
end

#tui_suspend_metadataObject



219
220
221
# File 'lib/ruby_coded/chat/state.rb', line 219

def 
  @tui_suspend_metadata || {}
end

#tui_suspend_requested?Boolean

Returns:

  • (Boolean)


215
216
217
# File 'lib/ruby_coded/chat/state.rb', line 215

def tui_suspend_requested?
  !@tui_suspend_reason.nil?
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.



127
128
129
130
131
132
133
134
135
136
# File 'lib/ruby_coded/chat/state.rb', line 127

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).



140
141
142
# File 'lib/ruby_coded/chat/state.rb', line 140

def update_input_visible_width(width)
  @input_visible_width = width
end