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 |
# 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.compression_max_summary_tokens # Use the auxiliary compression model if configured model = compression_model adapter = LLM::RubyLLMAdapter.new(model_id: model) response = adapter.chat(messages: [ { role: "system", content: summary_system_prompt }, { role: "user", content: prompt } ]) body = response&.content || fallback_summary(, previous_summary) with_summary_prefix(body) rescue StandardError # If LLM fails, produce a basic extractive summary with_summary_prefix(fallback_summary(, previous_summary)) end |
#build_and_save! ⇒ Object
Builds and saves the summary to the database
111 112 113 114 115 116 117 118 |
# File 'lib/rubino/context/summary_builder.rb', line 111 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.
103 104 105 106 107 108 |
# File 'lib/rubino/context/summary_builder.rb', line 103 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).
97 98 99 100 |
# File 'lib/rubino/context/summary_builder.rb', line 97 def with_summary_prefix(summary) body = strip_summary_prefix(summary) body.empty? ? SUMMARY_PREFIX : "#{SUMMARY_PREFIX}\n#{body}" end |