Module: Braintrust::Contrib::Setup
- Defined in:
- lib/braintrust/contrib/setup.rb
Overview
Automatic instrumentation setup for LLM libraries.
Intercepts require calls to detect when LLM libraries are loaded, then patches
them automatically. The main challenge is doing this safely with zeitwerk (Rails'
autoloader) which also hooks into require.
The Zeitwerk Problem
Zeitwerk uses alias_method :zeitwerk_original_require, :require. If we prepend
to Kernel before zeitwerk loads, zeitwerk captures our method as its "original",
creating an infinite loop.
Solution: Two-Phase Hook
┌─────────────────────────────────────────────────────────────────────────┐
│ Setup.run! called │
└─────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ Rails loaded? │ │ Zeitwerk loaded? │ │ Neither loaded yet │
│ (Rails::Railtie)│ │ │ │ │
└────────┬────────┘ └──────────┬──────────┘ └──────────┬──────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ Install Railtie │ │ Install prepend │ │ Install watcher │
│ (after_init) │ │ hook directly │ │ hook (alias_method) │
└─────────────────┘ └─────────────────────┘ └──────────┬──────────┘
│ │ │
│ │ ┌──────────┴──────────┐
│ │ ▼ ▼
│ │ ┌──────────────┐ ┌──────────────────┐
│ │ │ Rails loads? │ │ Zeitwerk loads? │
│ │ │ → Railtie │ │ → Prepend hook │
│ │ └──────────────┘ └──────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────────────┐
│ LLM library loads → patch! │
└─────────────────────────────────────────────────────────────────────────┘
The watcher hook uses alias_method which zeitwerk captures harmlessly (alias
chains work correctly). Once zeitwerk/Rails loads, we upgrade to the better
approach: prepend (takes precedence, super chains through zeitwerk) or
Railtie (patches after all gems loaded via after_initialize).
Constant Summary collapse
- REENTRANCY_KEY =
:braintrust_in_require_hook
Class Method Summary collapse
- .install_railtie! ⇒ Object
- .install_require_hook! ⇒ Object
-
.on_require(path) ⇒ Object
Called after each require to check if we should patch anything.
- .railtie_installed? ⇒ Boolean
- .require_hook_installed? ⇒ Boolean
-
.run! ⇒ Object
Main entry point.
-
.with_reentrancy_guard ⇒ Object
Execute block with reentrancy protection (prevents infinite loops).
Class Method Details
.install_railtie! ⇒ Object
112 113 114 115 116 |
# File 'lib/braintrust/contrib/setup.rb', line 112 def install_railtie! return if @railtie_installed @railtie_installed = true require_relative "rails/railtie" end |
.install_require_hook! ⇒ Object
118 119 120 121 122 |
# File 'lib/braintrust/contrib/setup.rb', line 118 def install_require_hook! return if @require_hook_installed @require_hook_installed = true Kernel.prepend(RequireHook) end |
.on_require(path) ⇒ Object
Called after each require to check if we should patch anything.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/braintrust/contrib/setup.rb', line 83 def on_require(path) return unless @registry @registry.integrations_for_require_path(path).each do |integration| next unless integration.available? && integration.compatible? next if @only && !@only.include?(integration.integration_name) next if @except&.include?(integration.integration_name) Braintrust::Log.debug("Contrib::Setup: patching #{integration.integration_name}") integration.patch! end rescue => e Braintrust::Log.error("Auto-instrument failed: #{e.}") end |
.railtie_installed? ⇒ Boolean
109 |
# File 'lib/braintrust/contrib/setup.rb', line 109 def railtie_installed? = @railtie_installed |
.require_hook_installed? ⇒ Boolean
110 |
# File 'lib/braintrust/contrib/setup.rb', line 110 def require_hook_installed? = @require_hook_installed |
.run! ⇒ Object
Main entry point. Call once per process.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/braintrust/contrib/setup.rb', line 60 def run! unless Internal::Env.auto_instrument Braintrust::Log.debug("Contrib::Setup: auto-instrumentation disabled via environment") return end @registry = Contrib::Registry.instance @only = Internal::Env.instrument_only @except = Internal::Env.instrument_except if defined?(::Rails::Railtie) Braintrust::Log.debug("Contrib::Setup: using Rails railtie hook") install_railtie! elsif defined?(::Zeitwerk) Braintrust::Log.debug("Contrib::Setup: using require hook (zeitwerk detected)") install_require_hook! else Braintrust::Log.debug("Contrib::Setup: using watcher hook") install_watcher_hook! end end |
.with_reentrancy_guard ⇒ Object
Execute block with reentrancy protection (prevents infinite loops).
99 100 101 102 103 104 105 106 107 |
# File 'lib/braintrust/contrib/setup.rb', line 99 def with_reentrancy_guard return if Thread.current[REENTRANCY_KEY] Thread.current[REENTRANCY_KEY] = true yield rescue => e Braintrust::Log.error("Auto-instrument failed: #{e.}") ensure Thread.current[REENTRANCY_KEY] = false end |