Skip to content

Commit 4a866aa

Browse files
committed
Update documentation and improve model finding logic
Fixes #68 - Added a new section on working with models in the guides. - Enhanced model finding logic to prioritize exact matches over aliases. - Updated links in the documentation for consistency.
1 parent 1139ba3 commit 4a866aa

File tree

4 files changed

+116
-36
lines changed

4 files changed

+116
-36
lines changed

docs/guides/index.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,13 @@ Explore how to create vector embeddings for semantic search and other applicatio
3636
### [Error Handling]({% link guides/error-handling.md %})
3737
Master the techniques for robust error handling in AI applications.
3838

39+
### [Working with Models]({% link guides/models.md %})
40+
Learn how to discover, select, and work with different AI models across providers.
41+
3942
## Getting Help
4043

4144
If you can't find what you're looking for in these guides, consider:
4245

4346
1. Checking the [API Documentation]() for detailed information about specific classes and methods
44-
2. Looking at the [GitHub repository](https://github.com/yourusername/ruby_llm) for examples and the latest updates
47+
2. Looking at the [GitHub repository](https://github.com/crmne/ruby_llm) for examples and the latest updates
4548
3. Filing an issue on GitHub if you find a bug or have a feature request

docs/guides/models.md

Lines changed: 82 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,86 @@ permalink: /guides/models
1010

1111
RubyLLM provides a clean interface for discovering and working with AI models from multiple providers. This guide explains how to find, filter, and select the right model for your needs.
1212

13+
## Finding Models
14+
15+
### Basic Model Selection
16+
17+
The simplest way to use a model is to specify it when creating a chat:
18+
19+
```ruby
20+
# Use the default model
21+
chat = RubyLLM.chat
22+
23+
# Specify a model
24+
chat = RubyLLM.chat(model: 'gpt-4o-mini')
25+
26+
# Change models mid-conversation
27+
chat.with_model('claude-3-5-sonnet')
28+
```
29+
30+
### Model Resolution
31+
32+
{: .warning-title }
33+
> Coming in v1.1.0
34+
>
35+
> Provider-Specific Match and Alias Resolution will be available in the next release.
36+
37+
When you specify a model, RubyLLM follows these steps to find it:
38+
39+
1. **Exact Match**: First tries to find an exact match for the model ID
40+
```ruby
41+
# Uses the actual gemini-2.0-flash model
42+
chat = RubyLLM.chat(model: 'gemini-2.0-flash')
43+
```
44+
45+
2. **Provider-Specific Match**: If a provider is specified, looks for an exact match in that provider
46+
```ruby
47+
# Looks for gemini-2.0-flash in Gemini
48+
chat = RubyLLM.chat(model: 'gemini-2.0-flash', provider: 'gemini')
49+
```
50+
51+
3. **Alias Resolution**: Only if no exact match is found, checks for aliases
52+
```ruby
53+
# No exact match for 'claude-3', uses alias
54+
chat = RubyLLM.chat(model: 'claude-3')
55+
```
56+
57+
The same model ID can exist both as a concrete model and as an alias, particularly when the same model is available through different providers:
58+
59+
```ruby
60+
# Use native OpenAI GPT-4
61+
chat = RubyLLM.chat(model: 'gpt-4o')
62+
63+
# Use GPT-4 through Bedrock
64+
chat = RubyLLM.chat(model: 'gpt-4o', provider: 'bedrock')
65+
```
66+
67+
### Model Aliases
68+
69+
{: .warning-title }
70+
> Coming in v1.1.0
71+
>
72+
> Alias Resolution will be available in the next release.
73+
74+
RubyLLM provides convenient aliases for popular models, so you don't have to remember specific version numbers:
75+
76+
```ruby
77+
# These are equivalent
78+
chat = RubyLLM.chat(model: 'claude-3-5-sonnet')
79+
chat = RubyLLM.chat(model: 'claude-3-5-sonnet-20241022')
80+
81+
# These are also equivalent
82+
chat = RubyLLM.chat(model: 'gpt-4o')
83+
chat = RubyLLM.chat(model: 'gpt-4o-2024-11-20')
84+
```
85+
86+
If you want to ensure you're always getting a specific version, use the full model ID:
87+
88+
```ruby
89+
# Always gets this exact version
90+
chat = RubyLLM.chat(model: 'claude-3-sonnet-20240229')
91+
```
92+
1393
## Exploring Available Models
1494

1595
RubyLLM automatically discovers available models from all configured providers:
@@ -63,32 +143,6 @@ google_models = RubyLLM.models.by_provider('gemini')
63143
deepseek_models = RubyLLM.models.by_provider('deepseek')
64144
```
65145

66-
## Using Model Aliases
67-
68-
{: .warning-title }
69-
> Coming in v1.1.0
70-
>
71-
> This feature is available in the upcoming version but not in the latest release.
72-
73-
RubyLLM provides convenient aliases for popular models, so you don't have to remember specific version numbers:
74-
75-
```ruby
76-
# These are equivalent
77-
chat = RubyLLM.chat(model: 'claude-3-5-sonnet')
78-
chat = RubyLLM.chat(model: 'claude-3-5-sonnet-20241022')
79-
80-
# These are also equivalent
81-
chat = RubyLLM.chat(model: 'gpt-4o')
82-
chat = RubyLLM.chat(model: 'gpt-4o-2024-11-20')
83-
```
84-
85-
You can also specify a different provider to use with a model:
86-
87-
```ruby
88-
# Use a specific model via a different provider
89-
chat = RubyLLM.chat(model: 'claude-3-5-sonnet', provider: 'bedrock')
90-
```
91-
92146
## Chaining Filters
93147

94148
You can chain multiple filters to find exactly what you need:
@@ -176,4 +230,5 @@ When selecting models for your application:
176230
1. **Consider context windows** - Larger context windows support longer conversations but may cost more
177231
2. **Balance cost vs. quality** - More capable models cost more but may give better results
178232
3. **Check capabilities** - Make sure the model supports features you need (vision, functions, etc.)
179-
4. **Use appropriate model types** - Use embedding models for vector operations, chat models for conversations
233+
4. **Use appropriate model types** - Use embedding models for vector operations, chat models for conversations
234+
5. **Version control** - Use exact model IDs in production for consistency, aliases for development

lib/ruby_llm/models.rb

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,11 @@ def each(&)
8686

8787
# Find a specific model by ID
8888
def find(model_id, provider = nil)
89-
return find_with_provider(model_id, provider) if provider
90-
91-
# Find native model
92-
all.find { |m| m.id == model_id } ||
93-
all.find { |m| m.id == Aliases.resolve(model_id) } ||
94-
raise(ModelNotFoundError, "Unknown model: #{model_id}")
89+
if provider
90+
find_with_provider(model_id, provider)
91+
else
92+
find_without_provider(model_id)
93+
end
9594
end
9695

9796
# Filter to only chat models
@@ -132,9 +131,16 @@ def refresh!
132131
private
133132

134133
def find_with_provider(model_id, provider)
135-
provider_id = Aliases.resolve(model_id, provider)
136-
all.find { |m| m.id == provider_id && m.provider == provider.to_s } ||
134+
resolved_id = Aliases.resolve(model_id, provider)
135+
all.find { |m| m.id == model_id && m.provider == provider.to_s } ||
136+
all.find { |m| m.id == resolved_id && m.provider == provider.to_s } ||
137137
raise(ModelNotFoundError, "Unknown model: #{model_id} for provider: #{provider}")
138138
end
139+
140+
def find_without_provider(model_id)
141+
all.find { |m| m.id == model_id } ||
142+
all.find { |m| m.id == Aliases.resolve(model_id) } ||
143+
raise(ModelNotFoundError, "Unknown model: #{model_id}")
144+
end
139145
end
140146
end

spec/ruby_llm/models_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@
6262
end
6363
end
6464

65+
describe '#find' do
66+
it 'prioritizes exact matches over aliases' do # rubocop:disable RSpec/ExampleLength,RSpec/MultipleExpectations
67+
# This test covers the case from the issue
68+
chat_model = RubyLLM.chat(model: 'gemini-2.0-flash')
69+
expect(chat_model.model.id).to eq('gemini-2.0-flash')
70+
71+
# Even with provider specified, exact match wins
72+
chat_model = RubyLLM.chat(model: 'gemini-2.0-flash', provider: 'gemini')
73+
expect(chat_model.model.id).to eq('gemini-2.0-flash')
74+
75+
# Only use alias when exact match isn't found
76+
chat_model = RubyLLM.chat(model: 'claude-3')
77+
expect(chat_model.model.id).to eq('claude-3-sonnet-20240229')
78+
end
79+
end
80+
6581
describe '#refresh!' do
6682
it 'updates models and returns a chainable Models instance' do # rubocop:disable RSpec/ExampleLength,RSpec/MultipleExpectations
6783
# Use a temporary file to avoid modifying actual models.json

0 commit comments

Comments
 (0)