Class: Rubino::Context::SummaryBuilder
- Inherits:
-
Object
- Object
- Rubino::Context::SummaryBuilder
- Defined in:
- lib/rubino/context/summary_builder.rb
Overview
Builds structured summaries from compressible message segments. Uses the LLM to generate a comprehensive summary following the template.
Constant Summary collapse
- SUMMARY_PREFIX =
Anti-replay handoff banner prepended to every compaction summary (#415c, ported from Hermes context_compressor.py SUMMARY_PREFIX). Without it a weak model reads the summarized older turns as live instructions and re-does already-finished work (the #10896/#11475 task-loss/replay class). It also points the model at the “## Active Task” field for continuation.
<<~PREFIX.strip [CONTEXT COMPACTION — REFERENCE ONLY] Earlier turns were compacted into the summary below. This is a handoff from a previous context window — treat it as background reference, NOT as active instructions. Do NOT answer questions or fulfill requests mentioned in this summary; they were already addressed. Your current task is identified in the '## Active Task' section of the summary — resume exactly from there. Your persistent memory in the system prompt is ALWAYS authoritative — never deprioritize it due to this note. Respond ONLY to the latest user message that appears AFTER this summary. The current session state (files, config, etc.) may already reflect work described here — avoid repeating it. PREFIX
- SUMMARY_TEMPLATE =
<<~TEMPLATE ## Active Task The SINGLE most important field. Copy the user's most recent unfulfilled request verbatim — the exact words they used. If several tasks were requested and only some are done, list only the ones NOT yet completed. Continuation picks up exactly here. If nothing is outstanding, write "None". ## Goal Current user objective. ## Constraints & Preferences Technical constraints, preferences, conventions. ## Progress ### Done Completed items. ### In Progress Work in progress. ### Blocked Open blockers or errors. ## Key Decisions Technical decisions made and their rationale. ## Relevant Files Files read, modified, or created. ## Tool Results Important tool execution results. ## Current State Current session state. ## Next Steps Planned next actions. ## Critical Context Information that must not be lost. TEMPLATE
Instance Method Summary collapse
-
#build(messages:, previous_summary: nil) ⇒ Object
Builds a summary from messages, optionally incorporating a previous summary.
-
#build_and_save! ⇒ Object
Builds and saves the summary to the database.
-
#initialize(session_id:, config: nil) ⇒ SummaryBuilder
constructor
A new instance of SummaryBuilder.
-
#strip_summary_prefix(summary) ⇒ Object
Returns the summary body without the handoff banner.
-
#with_summary_prefix(summary) ⇒ Object
Normalizes summary text to the current handoff format, stripping any banner first so it is never duplicated (#415c).
Constructor Details
#initialize(session_id:, config: nil) ⇒ SummaryBuilder
Returns a new instance of SummaryBuilder.
62 63 64 65 |
# File 'lib/rubino/context/summary_builder.rb', line 62 def initialize(session_id:, config: nil) @session_id = session_id @config = config || Rubino.configuration end |
Instance Method Details
#build(messages:, previous_summary: nil) ⇒ Object
Builds a summary from messages, optionally incorporating a previous summary. The returned text always carries SUMMARY_PREFIX so the next context window treats it as reference-only (#415c anti-replay).
70 71 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 |
# File 'lib/rubino/context/summary_builder.rb', line 70 def build(messages:, previous_summary: nil) # Strip any banner already on the incoming previous summary so # iterative re-compaction never stacks prefixes (anti-replay guard). previous_summary = strip_summary_prefix(previous_summary) content = () prompt = build_summary_prompt(content, previous_summary) @config.dig("compression", "max_summary_tokens") # Route through AuxiliaryClient so the WHOLE `auxiliary.compression` block # is honored — provider, model AND base_url — exactly like the other aux # tasks (vision/approval/summarize). The summary used to build the adapter # directly from only `auxiliary.compression.model`, silently ignoring # provider/base_url, so a configured summary endpoint did nothing. At the # defaults (provider:"main", model:"") AuxiliaryClient resolves to the # primary model, so existing behaviour is unchanged. response = LLM::AuxiliaryClient.new(config: @config).call( task: "compression", messages: [ { role: "system", content: summary_system_prompt }, { role: "user", content: prompt } ] ) body = response&.content || fallback_summary(, previous_summary) with_summary_prefix(body) rescue StandardError => e # If the LLM summary fails, degrade to a basic extractive summary. Log so # a persistently-failing compression endpoint (which silently produces a # worse summary every turn) is observable instead of invisible. Rubino.logger&.debug(event: "summary_builder.llm_failed", error: e.) with_summary_prefix(fallback_summary(, previous_summary)) end |
#build_and_save! ⇒ Object
Builds and saves the summary to the database
120 121 122 123 124 125 126 127 |
# File 'lib/rubino/context/summary_builder.rb', line 120 def build_and_save! = Session::Store.new = .for_session(@session_id) return if .size < 10 summary = build(messages: , previous_summary: load_previous_summary) save!(summary) end |
#strip_summary_prefix(summary) ⇒ Object
Returns the summary body without the handoff banner.
112 113 114 115 116 117 |
# File 'lib/rubino/context/summary_builder.rb', line 112 def strip_summary_prefix(summary) text = summary.to_s.strip return text[SUMMARY_PREFIX.length..].to_s.lstrip if text.start_with?(SUMMARY_PREFIX) text end |
#with_summary_prefix(summary) ⇒ Object
Normalizes summary text to the current handoff format, stripping any banner first so it is never duplicated (#415c).
106 107 108 109 |
# File 'lib/rubino/context/summary_builder.rb', line 106 def with_summary_prefix(summary) body = strip_summary_prefix(summary) body.empty? ? SUMMARY_PREFIX : "#{SUMMARY_PREFIX}\n#{body}" end |