Module: RubynCode::Context::ContextCollapse

Defined in:
lib/rubyn_code/context/context_collapse.rb

Overview

Lightweight context reduction that removes old conversation turns without calling the LLM. Runs before auto-compact — if collapse alone brings the context under threshold, the expensive LLM summarization is skipped.

Keeps the first message (initial user request), the most recent N exchanges, and replaces everything in between with a “[earlier conversation snipped]” marker.

Constant Summary collapse

SNIP_MARKER =
'[%d earlier messages snipped for context efficiency]'
CHARS_PER_TOKEN =
4

Class Method Summary collapse

Class Method Details

.call(messages, threshold:, keep_recent: 6) ⇒ Array<Hash>?

Returns a collapsed copy of messages if doing so brings the estimated token count under threshold. Returns nil if collapse isn’t sufficient (caller should fall through to full auto-compact).

Parameters:

  • messages (Array<Hash>)

    conversation messages

  • threshold (Integer)

    target token count

  • keep_recent (Integer) (defaults to: 6)

    number of recent messages to preserve

Returns:

  • (Array<Hash>, nil)

    collapsed messages or nil if not sufficient



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/rubyn_code/context/context_collapse.rb', line 23

def self.call(messages, threshold:, keep_recent: 6)
  return nil if messages.size <= keep_recent + 2

  # Always preserve the very first message (may contain critical
  # system-level context like auth shims) AND the first real user
  # message so the agent retains the user's original request.
  anchors = build_anchors(messages)

  recent = messages.last(keep_recent)
  snipped_count = messages.size - keep_recent - anchors.size

  collapsed = [
    *anchors,
    { role: 'user', content: format(SNIP_MARKER, snipped_count) },
    *recent
  ]

  # Only use collapse if it gets us under threshold
  estimated = (JSON.generate(collapsed).length.to_f / CHARS_PER_TOKEN).ceil
  estimated <= threshold ? collapsed : nil
rescue JSON::GeneratorError
  nil
end

.first_real_user_message(messages) ⇒ Object



66
67
68
69
70
# File 'lib/rubyn_code/context/context_collapse.rb', line 66

def self.first_real_user_message(messages)
  messages[1..].find do |msg|
    msg[:role] == 'user' && !system_injection?(msg)
  end
end

.system_injection?(msg) ⇒ Boolean

Returns:

  • (Boolean)


61
62
63
64
# File 'lib/rubyn_code/context/context_collapse.rb', line 61

def self.system_injection?(msg)
  content = msg[:content]
  content.is_a?(String) && content.start_with?('[system]')
end