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 |