Class: Brute::Middleware::DefaultCompactionPipeline
- Defined in:
- lib/brute/middleware/040_default_compaction_pipeline.rb
Overview
Compacts the conversation once it fills too much of the model's window.
This layer owns the when: before each call it estimates the size of the
conversation, and once it reaches compact_at of the window it asks a
compactor to bring it down to compact_to. What is given up, and in what
order, is the compactor's business.
use Brute::Middleware::Loop::ToolResult
use Brute::Middleware::DefaultCompactionPipeline,
window: 200_000,
summariser: Brute::Completion::OpenRouter.new(config: { access_token: key })
use Brute::Middleware::DefaultToolPipeline, tools: tools
The compactor it builds is the usual ladder, cheapest first:
use Brute::Compaction::Middleware::ToolResults rewrite older tool output
use Brute::Compaction::Middleware::SlidingWindow drop the oldest turns, then steps
run Brute::Compaction::Summarize summarise what is left, until it fits
Pass compactor: to say something else. A Brute::Turn::CompactionPipeline
is the usual thing to pass, but anything answering
#compact(messages, target:) will do. Passing no summariser leaves the
terminal doing nothing, which is how an agent says it would rather live
with a full context than pay to shrink it.
It belongs inside the tool loop rather than around it, so it runs before every call rather than once a turn -- a long run of tool results can fill the window without the turn ever ending.
Sizing anchors on what the provider itself counted for the last call and measures locally only what has been appended since, so the estimate is exact for the bulk of the conversation and approximate only for its tail.
Tool schemas ride in every request and are part of what fills a window,
so they are counted too -- against the trigger when nothing has been
reported yet, and off the target the compactor is given, because what
the schemas occupy is not room the conversation may have. This layer
sits above the tool pipeline, so on the very first call of a turn
env is not set yet: pass tools: to have them counted then.
Compaction is lossy, and this layer keeps no record of what it gave up: it rewrites env and says so with :compacted. An application that keeps a transcript preserves it from there.
agent.on(:compacted) { |env, payload| archive(env, payload) }
Defined Under Namespace
Classes: Declines
Constant Summary
Constants included from Hooks
Hooks::AFTER_LLM_EVENT, Hooks::AFTER_TOOL_EVENT, Hooks::APPROVE_TOOL_EVENT, Hooks::BEFORE_LLM_EVENT, Hooks::BEFORE_TOOL_EVENT, Hooks::COMPACTED_EVENT, Hooks::COMPACT_DURATION_EVENT, Hooks::DURATION_EVENT, Hooks::ENTER_EVENT, Hooks::EXIT_EVENT, Hooks::FARADAY_ERROR_EVENT, Hooks::LLM_DURATION_EVENT, Hooks::LLM_FAILURE_EVENT, Hooks::MIDDLEWARE_ADDED_EVENT, Hooks::OPEN_ROUTER_SERVER_ERROR_EVENT, Hooks::STANDARD_ERROR_EVENT, Hooks::TOOL_DURATION_EVENT, Hooks::TURN_DURATION_EVENT, Hooks::TURN_END_EVENT, Hooks::TURN_START_EVENT
Class Method Summary collapse
-
.compactor(summariser: nil, keep_steps: 2) ⇒ Object
The ladder this layer wires when it is not given one.
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#initialize(app, window:, summariser: nil, compactor: nil, keep_steps: 2, compact_at: 0.7, compact_to: 0.4, token_counter: nil, tools: nil) ⇒ DefaultCompactionPipeline
constructor
A new instance of DefaultCompactionPipeline.
Methods included from Hooks
Constructor Details
#initialize(app, window:, summariser: nil, compactor: nil, keep_steps: 2, compact_at: 0.7, compact_to: 0.4, token_counter: nil, tools: nil) ⇒ DefaultCompactionPipeline
Returns a new instance of DefaultCompactionPipeline.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/brute/middleware/040_default_compaction_pipeline.rb', line 55 def initialize(app, window:, summariser: nil, compactor: nil, keep_steps: 2, compact_at: 0.7, compact_to: 0.4, token_counter: nil, tools: nil) unless compact_to.positive? && compact_to < compact_at && compact_at <= 1 raise ArgumentError, "expected 0 < compact_to < compact_at <= 1, got #{compact_to} and #{compact_at}" end @app = app @compactor = compactor || self.class.compactor(summariser: summariser, keep_steps: keep_steps) @window = window @compact_at = compact_at @compact_to = compact_to @token_counter = token_counter || Brute::TokenCounter.default @tools = tools end |
Class Method Details
.compactor(summariser: nil, keep_steps: 2) ⇒ Object
The ladder this layer wires when it is not given one.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/brute/middleware/040_default_compaction_pipeline.rb', line 78 def self.compactor(summariser: nil, keep_steps: 2) terminal = Declines.new unless summariser.nil? terminal = Brute::Compaction::Summarize.new(summariser, keep_steps: keep_steps) end Brute::Turn::CompactionPipeline.new do use Brute::Compaction::Middleware::ToolResults, keep_steps: 1 use Brute::Compaction::Middleware::SlidingWindow, keep_steps: keep_steps run terminal end end |
Instance Method Details
#call(env) ⇒ Object
70 71 72 73 74 75 |
# File 'lib/brute/middleware/040_default_compaction_pipeline.rb', line 70 def call(env) env[:compactor] = @compactor compact(env) @app.call(env) env end |