Class: Phronomy::Context::CompactionContext

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/context/compaction_context.rb

Overview

Context object passed to the +on_compact+ callback registered on an agent.

The callback calls #compact one or more times to specify which ranges of messages to replace with a summary. Each call:

  1. Yields the selected message elements to the block.
  2. Receives the block's return value as the summary text.
  3. Persists a compaction record to the memory store (if available).
  4. Updates #result_messages so that the compacted range is replaced by a single +:system+ summary message.

The agent reads #result_messages after the callback returns and uses it as the new message list for this invocation.

Examples:

Summarise the oldest half of the conversation

on_compact do |ctx|
  half = ctx.message_elements.length / 2
  ctx.compact(0...half) do |elements|
    texts = elements.map { |e| "#{e[:role]}: #{e[:message].content}" }.join("\n")
    "Summary of earlier conversation:\n#{texts}"
  end
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(message_elements:, budget:, thread_id: nil, memory: nil) ⇒ CompactionContext

Returns a new instance of CompactionContext.

Parameters:

  • message_elements (Array<Hash>)

    each element: { seq: Integer, message: Object, tokens: Integer, role: Symbol }

  • budget (Phronomy::Context::TokenBudget, nil)
  • thread_id (String, nil) (defaults to: nil)

    used when saving compaction records

  • memory (Object, nil) (defaults to: nil)

    memory object; must respond to #save_compaction for compaction records to be persisted



50
51
52
53
54
55
56
57
# File 'lib/phronomy/context/compaction_context.rb', line 50

def initialize(message_elements:, budget:, thread_id: nil, memory: nil)
  @message_elements = message_elements.dup
  @budget = budget
  @total_tokens = message_elements.sum { |e| e[:tokens] }
  @thread_id = thread_id
  @memory = memory
  @result_messages = @message_elements.map { |e| e[:message] }
end

Instance Attribute Details

#budgetPhronomy::Context::TokenBudget? (readonly)



33
34
35
# File 'lib/phronomy/context/compaction_context.rb', line 33

def budget
  @budget
end

#message_elementsArray<Hash> (readonly)

Returns message elements at compaction time.

Returns:

  • (Array<Hash>)

    message elements at compaction time



30
31
32
# File 'lib/phronomy/context/compaction_context.rb', line 30

def message_elements
  @message_elements
end

#result_messagesArray (readonly)

The current message list to be used after all compact calls have been made. Updated by each call to #compact.

Returns:

  • (Array)


42
43
44
# File 'lib/phronomy/context/compaction_context.rb', line 42

def result_messages
  @result_messages
end

#total_tokensInteger (readonly)

Returns total estimated token count before compaction.

Returns:

  • (Integer)

    total estimated token count before compaction



36
37
38
# File 'lib/phronomy/context/compaction_context.rb', line 36

def total_tokens
  @total_tokens
end

Instance Method Details

#compact(range) {|elements| ... } ⇒ Array

Replace a range of messages with a summary produced by the block.

The block receives the selected Array elements and must return a String that serves as the summary text. After the call, #result_messages reflects the replacement.

If the memory object responds to #save_compaction, a compaction record { start_seq:, end_seq:, summary_text: } is persisted for auditability.

Parameters:

  • range (Range, Integer)

    index range into message_elements (0-based)

Yield Parameters:

  • elements (Array<Hash>)

    the selected message elements

Yield Returns:

  • (String)

    summary text to replace the selected messages

Returns:

  • (Array)

    the updated result_messages array



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/phronomy/context/compaction_context.rb', line 72

def compact(range)
  # Normalise: Integer index → single-element Array; Range → Array slice.
  raw = @message_elements[range]
  elements = if raw.is_a?(Array)
    raw
  elsif raw.nil?
    []
  else
    [raw]
  end
  return @result_messages if elements.empty?

  summary_text = yield(elements).to_s

  start_seq = elements.first[:seq]
  end_seq = elements.last[:seq]

  if @memory && @thread_id && @memory.respond_to?(:save_compaction)
    @memory.save_compaction(
      thread_id: @thread_id,
      start_seq: start_seq,
      end_seq: end_seq,
      summary_text: summary_text
    )
  end

  # Compute the last included index in the original @message_elements array.
  last_idx = if range.is_a?(Range)
    range.exclude_end? ? range.last - 1 : range.last
  else
    range.to_i
  end

  remaining = (@message_elements[(last_idx + 1)..] || []).map { |e| e[:message] }
  summary_msg = OpenStruct.new(role: :system, content: summary_text)
  @result_messages = [summary_msg] + remaining
end