Class: SmartPrompt::CompressionEngine

Inherits:
Object
  • Object
show all
Defined in:
lib/smart_prompt/compression_engine.rb

Overview

CompressionEngine handles automatic compression of conversation history through summarization using an LLM adapter

This engine:

  • Generates summaries of older messages to reduce token usage

  • Preserves key facts, decisions, and context in summaries

  • Falls back to truncation strategies when summarization fails

  • Tracks compression metrics for monitoring

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = {}) ⇒ CompressionEngine

Initialize the compression engine

Parameters:

  • config (Hash) (defaults to: {})

    Configuration options

Options Hash (config):

  • :llm_adapter (LLMAdapter)

    LLM adapter for generating summaries

  • :prompt (String)

    Custom summarization prompt template

  • :compression_ratio (Float) — default: 0.5

    Target compression ratio

  • :min_messages_to_compress (Integer) — default: 5

    Minimum messages needed for compression



19
20
21
22
23
24
25
26
# File 'lib/smart_prompt/compression_engine.rb', line 19

def initialize(config = {})
  @config = config
  @llm_adapter = config[:llm_adapter]
  @summarization_prompt = config[:prompt] || default_prompt
  @compression_ratio = config[:compression_ratio] || 0.5
  @min_messages_to_compress = config[:min_messages_to_compress] || 5
  @token_counter = TokenCounter.new
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



11
12
13
# File 'lib/smart_prompt/compression_engine.rb', line 11

def config
  @config
end

Instance Method Details

#compress(session) ⇒ Boolean

Compress a session by identifying and summarizing compressible segments

Parameters:

  • session (Session)

    The session to compress

Returns:

  • (Boolean)

    true if compression was successful



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
109
110
# File 'lib/smart_prompt/compression_engine.rb', line 83

def compress(session)
  return false if session.nil? || session.messages.empty?

  begin
    # Identify compressible message segments
    compressible_segments = identify_compressible_segments(session.messages)

    return false if compressible_segments.empty?

    # Generate summaries for each segment
    summaries = compressible_segments.map { |segment| summarize(segment) }.compact

    return false if summaries.empty?

    # Replace original messages with summaries
    replace_with_summaries(session, compressible_segments, summaries)

    SmartPrompt.logger.info "Session #{session.id} compressed: #{compressible_segments.flatten.count} " \
                            "messages replaced with #{summaries.count} summaries"
    true
  rescue => e
    SmartPrompt.logger.error "Compression failed for session #{session.id}: #{e.message}"
    
    # Fall back to truncation strategy
    fallback_truncate(session)
    false
  end
end

#should_compress?(session) ⇒ Boolean

Check if a session should be compressed based on configuration

Parameters:

  • session (Session)

    The session to evaluate

Returns:

  • (Boolean)

    true if compression is recommended



115
116
117
118
119
120
# File 'lib/smart_prompt/compression_engine.rb', line 115

def should_compress?(session)
  return false if session.nil?
  
  # Check if session has enough messages to warrant compression
  session.message_count > (@min_messages_to_compress * 2)
end

#summarize(messages) ⇒ Message?

Summarize a collection of messages into a single summary message

Parameters:

  • messages (Array<Message>)

    Messages to summarize

Returns:

  • (Message, nil)

    Summary message or nil if summarization fails



31
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/smart_prompt/compression_engine.rb', line 31

def summarize(messages)
  return nil if messages.nil? || messages.empty?
  return nil if messages.length < @min_messages_to_compress

  # Build the content to summarize
  content = messages.map { |msg| "#{msg.role}: #{msg.content}" }.join("\n")
  
  # Create the summarization prompt
  prompt = @summarization_prompt.gsub("{content}", content)

  begin
    # Call LLM to generate summary
    summary_text = if @llm_adapter
      @llm_adapter.send_request([
        { role: "user", content: prompt }
      ])
    else
      # If no LLM adapter, create a simple summary
      create_fallback_summary(messages)
    end

    # Calculate original token count
    original_tokens = messages.sum { |msg| msg.token_count || @token_counter.count(msg.content) }

    # Create summary message
    summary_message = Message.new(
      role: "system",
      content: "[Summary of previous conversation]\n#{summary_text}",
      is_summary: true,
      metadata: {
        original_count: messages.count,
        original_tokens: original_tokens,
        compressed_at: Time.now.iso8601
      }
    )

    # Calculate tokens for the summary
    summary_message.calculate_tokens(@token_counter)

    SmartPrompt.logger.info "Compressed #{messages.count} messages (#{original_tokens} tokens) " \
                            "into summary (#{summary_message.token_count} tokens)"

    summary_message
  rescue => e
    SmartPrompt.logger.error "Summarization failed: #{e.message}\n#{e.backtrace.join("\n")}"
    nil
  end
end