Skip to content

Commit ccd7915

Browse files
authored
Introduce RuboCop Performance (#316)
## What this does Hello, thanks for your work on this library This is my first contribution here. I think that adding RuboCop Performance plugin this can benefit on the long term Each offense has been fixed in an individual commit, with explanation and benchmarks, where possible. Some cops have been temporarily disabled because they can break API ## Type of change - [ ] Bug fix - [ ] New feature - [ ] Breaking change ⚠️ ⚠️ ⚠️ **not at the best of my knowledge** - [ ] Documentation - [x] Performance improvement ## Scope check - [x] I read the [Contributing Guide](https://github.com/crmne/ruby_llm/blob/main/CONTRIBUTING.md) - [x] This aligns with RubyLLM's focus on **LLM communication** - [x] This isn't application-specific logic that belongs in user code - [x] This benefits most users, not just my specific use case ## Quality check - [x] I ran `overcommit --install` and all hooks pass - [x] I tested my changes thoroughly - [x] ~I updated documentation if needed~ - [x] I didn't modify auto-generated files manually (`models.json`, `aliases.json`) ## API changes - [ ] Breaking change - [ ] New public methods/classes - [ ] Changed method signatures - [x] No API changes ## Related issues <!-- Link issues: "Fixes #123" or "Related to #123" -->
1 parent ee0d8c2 commit ccd7915

File tree

13 files changed

+31
-18
lines changed

13 files changed

+31
-18
lines changed

.rubocop.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
plugins:
2+
- rubocop-performance
23
- rubocop-rake
34
- rubocop-rspec
45

@@ -21,7 +22,18 @@ Metrics/MethodLength:
2122
Enabled: false
2223
Metrics/ModuleLength:
2324
Enabled: false
25+
Performance/CollectionLiteralInLoop:
26+
Exclude:
27+
- spec/**/*
28+
Performance/RedundantBlockCall:
29+
Enabled: false # TODO: temporarily disabled to avoid potential breaking change
30+
Performance/StringInclude:
31+
Exclude:
32+
- lib/ruby_llm/providers/**/capabilities.rb
33+
Performance/UnfreezeString:
34+
Exclude:
35+
- spec/**/*
2436
RSpec/ExampleLength:
2537
Enabled: false
2638
RSpec/MultipleExpectations:
27-
Enabled: false
39+
Enabled: false

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ group :development do # rubocop:disable Metrics/BlockLength
2222
gem 'reline'
2323
gem 'rspec', '~> 3.12'
2424
gem 'rubocop', '>= 1.0'
25+
gem 'rubocop-performance'
2526
gem 'rubocop-rake', '>= 0.6'
2627
gem 'rubocop-rspec'
2728
gem 'ruby_llm-schema', '~> 0.1.0'

lib/ruby_llm/active_record/acts_as.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ def setup_persistence_callbacks
234234
end
235235

236236
def persist_new_message
237-
@message = messages.create!(role: :assistant, content: String.new)
237+
@message = messages.create!(role: :assistant, content: '')
238238
end
239239

240240
def persist_message_completion(message)

lib/ruby_llm/content.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def process_attachments_array_or_string(attachments)
4343
def process_attachments(attachments)
4444
if attachments.is_a?(Hash)
4545
# Ignores types (like :image, :audio, :text, :pdf) since we have robust MIME type detection
46-
attachments.each_value(&method(:process_attachments_array_or_string))
46+
attachments.each_value { |attachment| process_attachments_array_or_string(attachment) }
4747
else
4848
process_attachments_array_or_string attachments
4949
end

lib/ruby_llm/models.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def resolve(model_id, provider: nil, assume_exists: false, config: nil) # ruboco
6464

6565
model = Model::Info.new(
6666
id: model_id,
67-
name: model_id.gsub('-', ' ').capitalize,
67+
name: model_id.tr('-', ' ').capitalize,
6868
provider: provider_instance.slug,
6969
capabilities: %w[function_calling streaming],
7070
modalities: { input: %w[text image], output: %w[text] },

lib/ruby_llm/providers/bedrock/streaming/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def stream_response(connection, payload, additional_headers = {}, &block)
4747
end
4848

4949
def handle_stream(&block)
50-
buffer = String.new
50+
buffer = +''
5151
proc do |chunk, _bytes, env|
5252
if env && env.status != 200
5353
handle_failed_response(chunk, buffer, env)

lib/ruby_llm/providers/openai/capabilities.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,11 @@ def apply_special_formatting(name)
198198
.gsub(/(\d{4}) (\d{2}) (\d{2})/, '\1\2\3')
199199
.gsub(/^(?:Gpt|Chatgpt|Tts|Dall E) /) { |m| special_prefix_format(m.strip) }
200200
.gsub(/^O([13]) /, 'O\1-')
201-
.gsub(/^O[13] Mini/, '\0'.gsub(' ', '-'))
201+
.gsub(/^O[13] Mini/, '\0'.tr(' ', '-'))
202202
.gsub(/\d\.\d /, '\0'.sub(' ', '-'))
203203
.gsub(/4o (?=Mini|Preview|Turbo|Audio|Realtime|Transcribe|Tts)/, '4o-')
204204
.gsub(/\bHd\b/, 'HD')
205-
.gsub(/(?:Omni|Text) Moderation/, '\0'.gsub(' ', '-'))
205+
.gsub(/(?:Omni|Text) Moderation/, '\0'.tr(' ', '-'))
206206
.gsub('Text Embedding', 'text-embedding-')
207207
end
208208

lib/ruby_llm/stream_accumulator.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class StreamAccumulator
88
attr_reader :content, :model_id, :tool_calls
99

1010
def initialize
11-
@content = String.new
11+
@content = +''
1212
@tool_calls = {}
1313
@input_tokens = 0
1414
@output_tokens = 0
@@ -66,7 +66,7 @@ def accumulate_tool_calls(new_tool_calls)
6666
new_tool_calls.each_value do |tool_call|
6767
if tool_call.id
6868
tool_call_id = tool_call.id.empty? ? SecureRandom.uuid : tool_call.id
69-
tool_call_arguments = tool_call.arguments.empty? ? String.new : tool_call.arguments
69+
tool_call_arguments = tool_call.arguments.empty? ? +'' : tool_call.arguments
7070
@tool_calls[tool_call.id] = ToolCall.new(
7171
id: tool_call_id,
7272
name: tool_call.name,

lib/ruby_llm/streaming.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def handle_stream(&block)
4343
private
4444

4545
def to_json_stream(&)
46-
buffer = String.new
46+
buffer = +''
4747
parser = EventStreamParser::Parser.new
4848

4949
create_stream_processor(parser, buffer, &)

lib/tasks/aliases.rake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ namespace :aliases do # rubocop:disable Metrics/BlockLength
6565

6666
base_name = Regexp.last_match(1)
6767
# Normalize to Anthropic naming convention
68-
anthropic_name = base_name.gsub('.', '-')
68+
anthropic_name = base_name.tr('.', '-')
6969

7070
# Skip if we already have an alias for this
7171
next if aliases[anthropic_name]
@@ -91,7 +91,7 @@ namespace :aliases do # rubocop:disable Metrics/BlockLength
9191
# OpenRouter uses "google/" prefix and sometimes different naming
9292
openrouter_variants = [
9393
"google/#{model}",
94-
"google/#{model.gsub('gemini-', 'gemini-').gsub('.', '-')}",
94+
"google/#{model.gsub('gemini-', 'gemini-').tr('.', '-')}",
9595
"google/#{model.gsub('gemini-', 'gemini-')}"
9696
]
9797

0 commit comments

Comments
 (0)