Skip to content
7 changes: 6 additions & 1 deletion docs/_getting_started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ RubyLLM.configure do |config|

# Or use Rails logger
config.logger = Rails.logger # Overrides log_file and log_level

# Set custom timeout for log filtering
# which is useful when working with large payloads
config.log_regexp_timeout = 4 # seconds
Comment on lines +205 to +208
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not categorise this as basic logging. Let's remove that.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth having a separate section called "Advanced logging options" where to put also the logger and log file options. Since that requires a rewrite of the section, I'm willing to take that on myself.

end
```

Expand Down Expand Up @@ -354,6 +358,7 @@ RubyLLM.configure do |config|
config.log_file = String
config.log_level = Symbol
config.log_stream_debug = Boolean
config.log_regexp_timeout = Integer
end
```

Expand All @@ -363,4 +368,4 @@ Now that you've configured RubyLLM, you're ready to:

- [Start chatting with AI models]({% link _core_features/chat.md %})
- [Work with different providers and models]({% link _advanced/models.md %})
- [Set up Rails integration]({% link _advanced/rails.md %})
- [Set up Rails integration]({% link _advanced/rails.md %})
4 changes: 3 additions & 1 deletion lib/ruby_llm/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class Configuration
:logger,
:log_file,
:log_level,
:log_stream_debug
:log_stream_debug,
:log_regexp_timeout

def initialize
@request_timeout = 120
Expand All @@ -64,6 +65,7 @@ def initialize
@log_file = $stdout
@log_level = ENV['RUBYLLM_DEBUG'] ? Logger::DEBUG : Logger::INFO
@log_stream_debug = ENV['RUBYLLM_STREAM_DEBUG'] == 'true'
@log_regexp_timeout = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.2.0') ? (Regexp.timeout || 1.0) : nil
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that in ruby_llm spec setup, Regexp.timeout ends up being nil by default (probably caused by some recent change as specs passed before), and confirmed that Ruby itself doesn’t set a default outside of Rails. I think it's a good idea to introduce a healthy default of 1.0 second if Regexp.timeout is nil, at the same time, I see the case for leaving nil untouched if that’s an intentional choice by the underlying system.

Do you have any thoughts or preferences on whether we should enforce a fallback, or keep respecting nil?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convention over configuration! 1 second is perfect.

end

def instance_variables
Expand Down
15 changes: 13 additions & 2 deletions lib/ruby_llm/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,19 @@ def setup_logging(faraday)
errors: true,
headers: false,
log_level: :debug do |logger|
logger.filter(%r{[A-Za-z0-9+/=]{100,}}, 'data":"[BASE64 DATA]"')
logger.filter(/[-\d.e,\s]{100,}/, '[EMBEDDINGS ARRAY]')
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.2.0')
logger.filter(
Regexp.new('[A-Za-z0-9+/=]{100,}', timeout: @config.log_regexp_timeout),
'data":"[BASE64 DATA]"'
)
logger.filter(Regexp.new('[-\\d.e,\\s]{100,}', timeout: @config.log_regexp_timeout), '[EMBEDDINGS ARRAY]')
else
if @config.log_regexp_timeout
RubyLLM.logger.warn("log_regexp_timeout is not supported on Ruby #{RUBY_VERSION}")
end
logger.filter(Regexp.new('[A-Za-z0-9+/=]{100,}'), 'data":"[BASE64 DATA]"')
logger.filter(Regexp.new('[-\\d.e,\\s]{100,}'), '[EMBEDDINGS ARRAY]')
end
Comment on lines +69 to +81
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should simplify all that and use the same approach as Rails: https://github.com/rails/rails/pull/53490/files

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I fully follow.
@crmne are you proposing that ruby_llm drops support for Ruby 3.1 in a newer version of ruby_llm? 😀
Rails 8 is compatible with Ruby 3.2+ (source) and installing rails 8 on Ruby 3.1.3 gives error: rails-8.0.0 requires Ruby version >= 3.2.0. The current ruby version is 3.1.3.
Because rails 8 requires Ruby 3.2+, it is possible to set Regexp.timeout for it.
In ruby_llm case, if Ruby 3.1 wasn't supported, we could drop else and revert this block to:

logger.filter(
            Regexp.new('[A-Za-z0-9+/=]{100,}', timeout: @config.log_regexp_timeout),
            'data":"[BASE64 DATA]"'
          )
logger.filter(Regexp.new('[-\\d.e,\\s]{100,}', timeout: @config.log_regexp_timeout), '[EMBEDDINGS ARRAY]')

If you are proposing to use global Regexp.timeout (e.g. from parent environment where gem is used) then ruby_llm is already doing it, but it doesn't solve my use-case where I need Regexp.timeout to be different for this logging.

end
end

Expand Down
9 changes: 9 additions & 0 deletions spec/ruby_llm/context_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@
# Get current config values
original_model = RubyLLM.config.default_model
original_api_key = RubyLLM.config.openai_api_key
original_log_regexp_timeout = RubyLLM.config.log_regexp_timeout

# Create context with modified config
context = RubyLLM.context do |config|
config.default_model = 'modified-model'
config.openai_api_key = 'modified-key'
config.log_regexp_timeout = 5.0
end

# Verify global config is unchanged
expect(RubyLLM.config.default_model).to eq(original_model)
expect(RubyLLM.config.openai_api_key).to eq(original_api_key)
expect(RubyLLM.config.log_regexp_timeout).to eq(original_log_regexp_timeout)

# Verify context has modified config
expect(context.config.default_model).to eq('modified-model')
expect(context.config.openai_api_key).to eq('modified-key')
expect(context.config.log_regexp_timeout).to eq(5.0)
end
end

Expand Down Expand Up @@ -95,6 +99,7 @@
it 'allows multiple contexts with different configurations' do
context1 = RubyLLM.context do |config|
config.default_model = 'gpt-4.1-nano'
config.log_regexp_timeout = 5.0
end

context2 = RubyLLM.context do |config|
Expand All @@ -105,7 +110,11 @@
chat2 = context2.chat

expect(chat1.model.id).to eq('gpt-4.1-nano')
expect(context1.config.log_regexp_timeout).not_to eq(Regexp.timeout)
expect(context1.config.log_regexp_timeout).to eq(5.0)

expect(chat2.model.id).to eq('claude-3-5-haiku-20241022')
expect(context2.config.log_regexp_timeout).to eq(Regexp.timeout)
end

it 'ensures changes in one context do not affect another' do
Expand Down