Module: DataRedactor::Integrations::RubyLLM

Defined in:
lib/data_redactor/integrations/ruby_llm.rb

Overview

Transparent outbound redaction for the ruby_llm gem (crmne/ruby_llm).

Calling RubyLLM.install! prepends a small module onto RubyLLM::Protocol that deep-redacts the rendered request payload before it is posted to any provider. Protocol#render is the single point where every provider (Anthropic, OpenAI/chat_completions, Gemini, Bedrock/Converse, Responses) has assembled its final request Hash, so one hook covers them all without knowing any provider-specific shape.

Because the payload is walked with DataRedactor.redact_deep, this scrubs every String leaf in the request: the user prompt, the system prompt, tool definitions, and — crucially — any file contents or shell-command output that an agent fed back in as a tool result, since those are already inlined as strings in messages by the time render runs.

This is a monkeypatch (a prepend onto a private internal class). It is opt-in and pinned: RubyLLM.install! raises unless a supported ruby_llm version is loaded and RubyLLM::Protocol#render still exists, so an upstream refactor fails loudly at install time rather than silently leaking data. Prefer this only when you need redaction to be transparent; otherwise redact per call with DataRedactor.redact before chat.ask.

What is NOT redacted

  • Base64 attachments (PDFs, images, audio sent inline as base64) — the sensitive bytes are encoded, so patterns cannot see into them.
  • URL-referenced files/images — the content lives on a remote server and never enters the payload.

Examples:

Make every ruby_llm request redacted, app-wide

require "data_redactor/integrations/ruby_llm"
DataRedactor::Integrations::RubyLLM.install!

chat = RubyLLM.chat(model: "claude-opus-4-8")
chat.ask("my card is 4111111111111111")  # sent as "my card is [REDACTED]"

Scope the redaction with the usual filters

DataRedactor::Integrations::RubyLLM.install!(only: [:financial, :contact])

Defined Under Namespace

Modules: PayloadPatch

Constant Summary collapse

SUPPORTED_VERSION =

ruby_llm versions whose Protocol#render chokepoint this integration has been verified against. Bump (and re-verify) on each ruby_llm release.

"~> 1.16"

Class Method Summary collapse

Class Method Details

.install!(only: nil, except: nil, placeholder: DataRedactor::PLACEHOLDER_DEFAULT) ⇒ void

This method returns an undefined value.

Prepend the redaction patch onto RubyLLM::Protocol. Idempotent: a second call with the patch already installed is a no-op (the filter options from the first successful install are kept).

The only:/except:/placeholder: filters are captured here and applied to every subsequent request.

Parameters:

Raises:

  • (RuntimeError)

    if ruby_llm is not loaded, the loaded version is outside SUPPORTED_VERSION, or RubyLLM::Protocol#render is missing (i.e. an upstream refactor moved the chokepoint).



63
64
65
66
67
68
69
70
# File 'lib/data_redactor/integrations/ruby_llm.rb', line 63

def install!(only: nil, except: nil, placeholder: DataRedactor::PLACEHOLDER_DEFAULT)
  ensure_compatible!

  @options = { only: only, except: except, placeholder: placeholder }
  return if installed?

  ::RubyLLM::Protocol.prepend(PayloadPatch)
end

.installed?Boolean

Returns whether the redaction patch is currently on RubyLLM::Protocol.

Returns:

  • (Boolean)

    whether the redaction patch is currently on RubyLLM::Protocol.



74
75
76
77
# File 'lib/data_redactor/integrations/ruby_llm.rb', line 74

def installed?
  defined?(::RubyLLM::Protocol) &&
    ::RubyLLM::Protocol.ancestors.include?(PayloadPatch)
end