Skip to content

Conversation

@delner
Copy link
Collaborator

@delner delner commented Jan 9, 2026

Purpose

This PR migrates the RubyLLM instrumentation from the legacy monolithic implementation to the new modular Integration API framework. The refactoring improves maintainability, enables consistent auto-instrumentation patterns, adds support for instance-level tracing, and enables it for auto-instrumentation.

Key changes:

  • Introduced instance-level instrumentation via target: option
  • Migrated tool execution tracing from callbacks to method wrapping (this was broken previously)

Architectural Overview

The new implementation follows the three-tier Integration API structure:

Integration Layer (integration.rb)

Handles gem detection, version validation, and the high-level instrument! entry point. Defines metadata like GEM_NAMES, REQUIRE_PATHS, and MINIMUM_VERSION (1.8.0).

Patcher Layer (patcher.rb)

ChatPatcher manages thread-safe patching of RubyLLM::Chat. Supports both class-level patching (all instances) and instance-level patching (specific chat objects via singleton class).

Instrumentation Modules (instrumentation/)

  • chat.rb - Wraps complete() and execute_tool() methods using Module#prepend to create OpenTelemetry spans
  • common.rb - Token usage normalization utilities shared across streaming/non-streaming flows

Backward Compatibility (deprecated.rb)

Legacy Braintrust::Trace::Contrib::Github::Crmne::RubyLLM.wrap() API is preserved with deprecation warnings, delegating to the new Braintrust.instrument!() API.

Usage Examples

Class-Level Instrumentation

Instrument all RubyLLM chat instances automatically:

require "braintrust"
require "ruby_llm"

Braintrust.init
Braintrust.instrument!(:ruby_llm)

# All chat instances are now traced
chat = RubyLLM.chat(model: "gpt-4o-mini")
chat.ask("What is the capital of France?")

Instance-Level Instrumentation

Instrument only specific chat instances:

require "braintrust"
require "ruby_llm"

Braintrust.init

# Create two chat instances
chat_traced = RubyLLM.chat(model: "gpt-4o-mini")
chat_untraced = RubyLLM.chat(model: "gpt-4o-mini")

# Only instrument one of them
Braintrust.instrument!(:ruby_llm, target: chat_traced)

chat_traced.ask("Hello")   # Creates a ruby_llm.chat span
chat_untraced.ask("Hello") # No span created

Streaming Support

Streaming responses are automatically traced with chunk aggregation:

Braintrust.init
Braintrust.instrument!(:ruby_llm)

chat = RubyLLM.chat(model: "gpt-4o-mini")
chat.ask("Write a haiku") do |chunk|
  print chunk.content
end
# Span captures aggregated content and time_to_first_token metric

Tool Usage

Tool calls create nested spans automatically:

Braintrust.init
Braintrust.instrument!(:ruby_llm)

chat = RubyLLM.chat(model: "gpt-4o")
chat.with_tool(WeatherTool)
chat.ask("What's the weather in Paris?")
# Creates ruby_llm.chat span with nested ruby_llm.tool.get_weather span

What Changed

Before After
Braintrust::Trace::Contrib::Github::Crmne::RubyLLM.wrap(chat) Braintrust.instrument!(:ruby_llm, target: chat)
Braintrust::Trace::Contrib::Github::Crmne::RubyLLM.wrap() Braintrust.instrument!(:ruby_llm)
Tool tracing via on_tool_call/on_tool_result callbacks Tool tracing via execute_tool() method wrapping
631 lines in single file Modular structure across 5 files

Migration Path

The legacy API continues to work but emits deprecation warnings:

# Old (deprecated, still works)
Braintrust::Trace::Contrib::Github::Crmne::RubyLLM.wrap(chat)

# New (recommended)
Braintrust.instrument!(:ruby_llm, target: chat)

@delner delner requested review from clutchski and realark January 9, 2026 15:59
@delner delner self-assigned this Jan 9, 2026
@delner delner added the enhancement New feature or request label Jan 9, 2026
@delner delner marked this pull request as ready for review January 9, 2026 17:06
@delner delner force-pushed the auto_instrument/ruby_llm branch from 0a8b77b to 4dffbb1 Compare January 9, 2026 22:29
@delner delner merged commit eedb136 into feature/auto_instrument Jan 9, 2026
@delner delner deleted the auto_instrument/ruby_llm branch January 9, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants