Class: Rubino::Compression::ContentRouter

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

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_rangesObject (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.message, error_class: e.class.name)
  Result.passthrough(:error)
end