Class: Rubino::Compression::ContentRouter
- Inherits:
-
Object
- Object
- Rubino::Compression::ContentRouter
- Defined in:
- lib/rubino/compression/content_router.rb
Overview
The SINGLE content-routed seam every tool output passes through when compression is enabled. Given a piece of tool output plus light context (the tool name, a ‘compress_hint` the tool optionally emitted — e.g. read’s source_path: — and the per-call ‘compress` opt-out flag), it DETECTS the content type and dispatches to a compressor STRATEGY:
:log → LogCompressor (test/build/lint/shell dumps)
:code → Compressor(:code) (Ruby source from a WHOLE-file read → skeleton)
:diff → DiffCompressor (unified diff → trimmed context + lock elision)
:json → JsonCompressor (whole-output JSON → schema-fold / row select)
:grep / :short / :other → PASSTHROUGH (no compression)
The DIFF channel is special: a diff is the “show me the diff” view, and the human sees the FULL coloured diff in the tool ‘:body` (scrollback), which is rendered SEPARATELY at the executor and NEVER routed here. Only the model’s ‘:output` reaches this seam. The DiffCompressor trims far context and elides generated/lock files but keeps every +/- line and every file/hunk header; behind its saving guard a small/tight diff passes through BYTE-IDENTICAL, so the common “show me” case is untouched. Grep / short still pass through verbatim (their value is exact-string anchors edit/grep rely on).
JSON is special: a tool output whose WHOLE content parses as a JSON array/object routes to the JsonCompressor — and that detection runs BEFORE the log channel, so a ‘shell` JSON dump (`kubectl get -o json`, `gh api`) is folded as a table, never mis-compressed as a log. Small JSON passes through byte-identical via the compressor’s own saving guard.
The router NEVER raises into the caller: any strategy error falls back to a no-op result whose ‘text` is meaningless, so the executor sends the original. Compression must never break a tool call.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- SHORT_MAX_LINES =
Below this many lines nothing is worth the pointer indirection; the type detectors short-circuit short output to passthrough before any strategy.
5- GREP_LINE_RE =
A grep / ripgrep / search hit: ‘path:line:` or `path:line:col:`. If most non-blank lines look like this, the output is a search result — its value is the exact path:line anchors a follow-up read/edit consumes, so it must pass through verbatim. (Sampled, majority-rules, so a stray prose line in a grep dump doesn’t flip the verdict.)
/\A[^\s:]+:\d+:/- DIFF_RE =
A unified-diff body: ‘diff –git`, `@@ -a,b c,d @@`, or the `— ` / `++ ` file headers. Compressing a diff would drop the +/- context the human and the apply path both need.
/^(?:diff --git |@@ [-+]|\+\+\+ |--- )/- LOG_TOOLS =
Tools whose default output is command/log text — the LogCompressor channel. Anything not listed (and not matching a more specific shape) is :other and passes through.
%w[shell test shell_output shell_tail].freeze
Instance Attribute Summary collapse
-
#last_elided_ranges ⇒ Object
readonly
The elided (first_line, count) ranges from the most recent :code route, so the read tool can record them for drill-in detection without the router threading another return value.
Instance Method Summary collapse
-
#initialize(config) ⇒ ContentRouter
constructor
A new instance of ContentRouter.
-
#route(text, tool_name:, compress_hint: nil, compress: true) ⇒ Object
text — the model-facing tool output (post-redaction, pre-truncate) tool_name — the calling tool (“read”, “shell”, “test”, …) compress_hint — optional Hash a tool emits for routing context: read → { full_file:, source_path:, stream_kind: } shell/test → { stream_kind: } (:diff suppresses logs) compress — the per-call opt-out: false ⇒ unconditional passthrough.
Constructor Details
#initialize(config) ⇒ ContentRouter
Returns a new instance of ContentRouter.
66 67 68 |
# File 'lib/rubino/compression/content_router.rb', line 66 def initialize(config) @config = config end |
Instance Attribute Details
#last_elided_ranges ⇒ Object (readonly)
The elided (first_line, count) ranges from the most recent :code route, so the read tool can record them for drill-in detection without the router threading another return value. Cleared to [] on every route().
237 238 239 |
# File 'lib/rubino/compression/content_router.rb', line 237 def last_elided_ranges @last_elided_ranges end |
Instance Method Details
#route(text, tool_name:, compress_hint: nil, compress: true) ⇒ Object
text — the model-facing tool output (post-redaction, pre-truncate) tool_name — the calling tool (“read”, “shell”, “test”, …) compress_hint — optional Hash a tool emits for routing context:
read → { full_file:, source_path:, stream_kind: }
shell/test → { stream_kind: } (:diff suppresses logs)
compress — the per-call opt-out: false ⇒ unconditional passthrough.
Returns a Result. applied? == false ⇒ send the original text.
78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/rubino/compression/content_router.rb', line 78 def route(text, tool_name:, compress_hint: nil, compress: true) @last_elided_ranges = [] return Result.passthrough(:disabled) unless enabled? return Result.passthrough(:opt_out) unless compress return Result.passthrough(:empty) if text.nil? || text.empty? type = detect(text, tool_name, compress_hint || {}) dispatch(type, text, compress_hint || {}) rescue StandardError => e Rubino.logger&.warn(event: "compression.routing_failed", tool: tool_name, error: e., error_class: e.class.name) Result.passthrough(:error) end |