ruby_llm 0.1.0.pre38 → 0.1.0.pre39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/cicd.yml +6 -0
- data/README.md +2 -0
- data/lib/ruby_llm/version.rb +1 -1
- data/ruby_llm.gemspec +1 -1
- metadata +1 -16
- data/docs/.gitignore +0 -7
- data/docs/Gemfile +0 -11
- data/docs/_config.yml +0 -43
- data/docs/_data/navigation.yml +0 -25
- data/docs/guides/chat.md +0 -206
- data/docs/guides/embeddings.md +0 -325
- data/docs/guides/error-handling.md +0 -301
- data/docs/guides/getting-started.md +0 -164
- data/docs/guides/image-generation.md +0 -274
- data/docs/guides/index.md +0 -45
- data/docs/guides/rails.md +0 -401
- data/docs/guides/streaming.md +0 -242
- data/docs/guides/tools.md +0 -247
- data/docs/index.md +0 -73
- data/docs/installation.md +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c887462ad2c35c5e831991b8b6cd78095c3f286861fd4913afddd5887833f3b
|
4
|
+
data.tar.gz: 382a2dd05532510ed5ac8ae5deb7a81febb831e3bbd8a11376eba0ad266451c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8c372a54f0bb94ecf6b22ead70f34a324974f4f61ad4610ba15ffee64ed8ec226fac24a74b73667bc07ea73a14fb5ee5d813759ab60f86c54c7d00cfc3905aa
|
7
|
+
data.tar.gz: 868fadbdaf583aa911a98cce36b6b61208a2065d92f32c31f1a2ea0908a37aa91f2047d2ea7aa77911093a7fcbb4ae10ace87f7ddafa2d39856e67a54d5bf846
|
data/.github/workflows/cicd.yml
CHANGED
data/README.md
CHANGED
@@ -20,6 +20,8 @@ A delightful Ruby way to work with AI. Chat in text, analyze and generate images
|
|
20
20
|
<a href="https://codecov.io/gh/crmne/ruby_llm"><img src="https://codecov.io/gh/crmne/ruby_llm/branch/main/graph/badge.svg" alt="codecov" /></a>
|
21
21
|
</p>
|
22
22
|
|
23
|
+
🤺 Battle tested at [💬 Chat with Work](https://chatwithwork.com)
|
24
|
+
|
23
25
|
## Features
|
24
26
|
|
25
27
|
- 💬 **Beautiful Chat Interface** - Converse with AI models as easily as `RubyLLM.chat.ask "teach me Ruby"`
|
data/lib/ruby_llm/version.rb
CHANGED
data/ruby_llm.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
# Specify which files should be added to the gem when it is released.
|
29
29
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
30
30
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
31
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
31
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|docs)/}) }
|
32
32
|
end
|
33
33
|
spec.bindir = 'exe'
|
34
34
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_llm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre39
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carmine Paolino
|
@@ -111,21 +111,6 @@ files:
|
|
111
111
|
- Rakefile
|
112
112
|
- bin/console
|
113
113
|
- bin/setup
|
114
|
-
- docs/.gitignore
|
115
|
-
- docs/Gemfile
|
116
|
-
- docs/_config.yml
|
117
|
-
- docs/_data/navigation.yml
|
118
|
-
- docs/guides/chat.md
|
119
|
-
- docs/guides/embeddings.md
|
120
|
-
- docs/guides/error-handling.md
|
121
|
-
- docs/guides/getting-started.md
|
122
|
-
- docs/guides/image-generation.md
|
123
|
-
- docs/guides/index.md
|
124
|
-
- docs/guides/rails.md
|
125
|
-
- docs/guides/streaming.md
|
126
|
-
- docs/guides/tools.md
|
127
|
-
- docs/index.md
|
128
|
-
- docs/installation.md
|
129
114
|
- lib/ruby_llm.rb
|
130
115
|
- lib/ruby_llm/active_record/acts_as.rb
|
131
116
|
- lib/ruby_llm/chat.rb
|
data/docs/.gitignore
DELETED
data/docs/Gemfile
DELETED
data/docs/_config.yml
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
title: RubyLLM
|
2
|
-
description: A delightful Ruby way to work with AI
|
3
|
-
url: https://rubyllm.com
|
4
|
-
baseurl: /
|
5
|
-
remote_theme: just-the-docs/just-the-docs
|
6
|
-
|
7
|
-
# Enable search
|
8
|
-
search_enabled: true
|
9
|
-
search:
|
10
|
-
heading_level: 2
|
11
|
-
previews: 3
|
12
|
-
preview_words_before: 5
|
13
|
-
preview_words_after: 10
|
14
|
-
tokenizer_separator: /[\s/]+/
|
15
|
-
rel_url: true
|
16
|
-
button: false
|
17
|
-
|
18
|
-
# Navigation structure
|
19
|
-
nav_external_links:
|
20
|
-
- title: RubyLLM on GitHub
|
21
|
-
url: https://github.com/crmne/ruby_llm
|
22
|
-
hide_icon: false
|
23
|
-
|
24
|
-
# Footer content
|
25
|
-
footer_content: "Copyright © 2025 <a href='https://paolino.me'>Carmine Paolino</a>. Distributed under an <a href=\"https://github.com/crmne/ruby_llm/tree/main/LICENSE\">MIT license.</a>"
|
26
|
-
|
27
|
-
# Enable copy button on code blocks
|
28
|
-
enable_copy_code_button: true
|
29
|
-
|
30
|
-
# Make Anchor links show on hover
|
31
|
-
heading_anchors: true
|
32
|
-
|
33
|
-
# Color scheme
|
34
|
-
color_scheme: light
|
35
|
-
|
36
|
-
# Google Analytics
|
37
|
-
ga_tracking:
|
38
|
-
ga_tracking_anonymize_ip: true
|
39
|
-
|
40
|
-
# Custom plugins (GitHub Pages allows these)
|
41
|
-
plugins:
|
42
|
-
- jekyll-remote-theme
|
43
|
-
- jekyll-seo-tag
|
data/docs/_data/navigation.yml
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
- title: Home
|
2
|
-
url: /
|
3
|
-
- title: Installation
|
4
|
-
url: /installation
|
5
|
-
- title: Guides
|
6
|
-
url: /guides/
|
7
|
-
subfolderitems:
|
8
|
-
- title: Getting Started
|
9
|
-
url: /guides/getting-started
|
10
|
-
- title: Chat
|
11
|
-
url: /guides/chat
|
12
|
-
- title: Tools
|
13
|
-
url: /guides/tools
|
14
|
-
- title: Streaming
|
15
|
-
url: /guides/streaming
|
16
|
-
- title: Rails Integration
|
17
|
-
url: /guides/rails
|
18
|
-
- title: Image Generation
|
19
|
-
url: /guides/image-generation
|
20
|
-
- title: Embeddings
|
21
|
-
url: /guides/embeddings
|
22
|
-
- title: Error Handling
|
23
|
-
url: /guides/error-handling
|
24
|
-
- title: GitHub
|
25
|
-
url: https://github.com/crmne/ruby_llm
|
data/docs/guides/chat.md
DELETED
@@ -1,206 +0,0 @@
|
|
1
|
-
---
|
2
|
-
layout: default
|
3
|
-
title: Chat
|
4
|
-
parent: Guides
|
5
|
-
nav_order: 2
|
6
|
-
permalink: /guides/chat
|
7
|
-
---
|
8
|
-
|
9
|
-
# Chatting with AI Models
|
10
|
-
|
11
|
-
RubyLLM's chat interface provides a natural way to interact with various AI models. This guide covers everything from basic chatting to advanced features like multimodal inputs and streaming responses.
|
12
|
-
|
13
|
-
## Basic Chat
|
14
|
-
|
15
|
-
Creating a chat and asking questions is straightforward:
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
# Create a chat with the default model
|
19
|
-
chat = RubyLLM.chat
|
20
|
-
|
21
|
-
# Ask a question
|
22
|
-
response = chat.ask "What's the best way to learn Ruby?"
|
23
|
-
|
24
|
-
# The response is a Message object
|
25
|
-
puts response.content
|
26
|
-
puts "Role: #{response.role}"
|
27
|
-
puts "Model: #{response.model_id}"
|
28
|
-
puts "Tokens: #{response.input_tokens} input, #{response.output_tokens} output"
|
29
|
-
```
|
30
|
-
|
31
|
-
## Choosing Models
|
32
|
-
|
33
|
-
You can specify which model to use when creating a chat:
|
34
|
-
|
35
|
-
```ruby
|
36
|
-
# Create a chat with a specific model
|
37
|
-
chat = RubyLLM.chat(model: 'gpt-4o-mini')
|
38
|
-
|
39
|
-
# Use Claude instead
|
40
|
-
claude_chat = RubyLLM.chat(model: 'claude-3-5-sonnet-20241022')
|
41
|
-
|
42
|
-
# Or change the model for an existing chat
|
43
|
-
chat.with_model('gemini-2.0-flash')
|
44
|
-
```
|
45
|
-
|
46
|
-
## Multi-turn Conversations
|
47
|
-
|
48
|
-
Chats maintain conversation history automatically:
|
49
|
-
|
50
|
-
```ruby
|
51
|
-
chat = RubyLLM.chat
|
52
|
-
|
53
|
-
# Start a conversation
|
54
|
-
chat.ask "What's your favorite programming language?"
|
55
|
-
|
56
|
-
# Follow up
|
57
|
-
chat.ask "Why do you like that language?"
|
58
|
-
|
59
|
-
# Continue the conversation
|
60
|
-
chat.ask "What are its weaknesses?"
|
61
|
-
|
62
|
-
# Access the conversation history
|
63
|
-
chat.messages.each do |message|
|
64
|
-
puts "#{message.role}: #{message.content[0..50]}..."
|
65
|
-
end
|
66
|
-
```
|
67
|
-
|
68
|
-
## Working with Images
|
69
|
-
|
70
|
-
Vision-capable models can understand images:
|
71
|
-
|
72
|
-
```ruby
|
73
|
-
chat = RubyLLM.chat
|
74
|
-
|
75
|
-
# Ask about an image (local file)
|
76
|
-
chat.ask "What's in this image?", with: { image: "path/to/image.jpg" }
|
77
|
-
|
78
|
-
# Or use an image URL
|
79
|
-
chat.ask "Describe this picture", with: { image: "https://example.com/image.jpg" }
|
80
|
-
|
81
|
-
# Include multiple images
|
82
|
-
chat.ask "Compare these two charts", with: {
|
83
|
-
image: ["chart1.png", "chart2.png"]
|
84
|
-
}
|
85
|
-
|
86
|
-
# Combine text and image
|
87
|
-
chat.ask "Is this the Ruby logo?", with: { image: "logo.png" }
|
88
|
-
```
|
89
|
-
|
90
|
-
## Working with Audio
|
91
|
-
|
92
|
-
Models with audio capabilities can process spoken content:
|
93
|
-
|
94
|
-
```ruby
|
95
|
-
chat = RubyLLM.chat(model: 'gpt-4o-audio-preview')
|
96
|
-
|
97
|
-
# Analyze audio content
|
98
|
-
chat.ask "What's being said in this recording?", with: {
|
99
|
-
audio: "meeting.wav"
|
100
|
-
}
|
101
|
-
|
102
|
-
# Ask follow-up questions about the audio
|
103
|
-
chat.ask "Summarize the key points mentioned"
|
104
|
-
```
|
105
|
-
|
106
|
-
## Streaming Responses
|
107
|
-
|
108
|
-
For a more interactive experience, you can stream responses as they're generated:
|
109
|
-
|
110
|
-
```ruby
|
111
|
-
chat = RubyLLM.chat
|
112
|
-
|
113
|
-
# Stream the response with a block
|
114
|
-
chat.ask "Tell me a story about a Ruby programmer" do |chunk|
|
115
|
-
# Each chunk is a partial response
|
116
|
-
print chunk.content
|
117
|
-
$stdout.flush # Ensure output is displayed immediately
|
118
|
-
end
|
119
|
-
|
120
|
-
# Useful for long responses or real-time displays
|
121
|
-
chat.ask "Write a detailed essay about programming paradigms" do |chunk|
|
122
|
-
add_to_ui(chunk.content) # Your method to update UI
|
123
|
-
end
|
124
|
-
```
|
125
|
-
|
126
|
-
## Temperature Control
|
127
|
-
|
128
|
-
Control the creativity and randomness of AI responses:
|
129
|
-
|
130
|
-
```ruby
|
131
|
-
# Higher temperature (more creative)
|
132
|
-
creative_chat = RubyLLM.chat.with_temperature(0.9)
|
133
|
-
creative_chat.ask "Write a poem about Ruby programming"
|
134
|
-
|
135
|
-
# Lower temperature (more deterministic)
|
136
|
-
precise_chat = RubyLLM.chat.with_temperature(0.1)
|
137
|
-
precise_chat.ask "Explain how Ruby's garbage collector works"
|
138
|
-
```
|
139
|
-
|
140
|
-
## Access Token Usage
|
141
|
-
|
142
|
-
RubyLLM automatically tracks token usage for billing and quota management:
|
143
|
-
|
144
|
-
```ruby
|
145
|
-
chat = RubyLLM.chat
|
146
|
-
response = chat.ask "Explain quantum computing"
|
147
|
-
|
148
|
-
# Check token usage
|
149
|
-
puts "Input tokens: #{response.input_tokens}"
|
150
|
-
puts "Output tokens: #{response.output_tokens}"
|
151
|
-
puts "Total tokens: #{response.input_tokens + response.output_tokens}"
|
152
|
-
|
153
|
-
# Estimate cost (varies by model)
|
154
|
-
model = RubyLLM.models.find(response.model_id)
|
155
|
-
input_cost = response.input_tokens * model.input_price_per_million / 1_000_000
|
156
|
-
output_cost = response.output_tokens * model.output_price_per_million / 1_000_000
|
157
|
-
puts "Estimated cost: $#{(input_cost + output_cost).round(6)}"
|
158
|
-
```
|
159
|
-
|
160
|
-
## Registering Event Handlers
|
161
|
-
|
162
|
-
You can register callbacks for chat events:
|
163
|
-
|
164
|
-
```ruby
|
165
|
-
chat = RubyLLM.chat
|
166
|
-
|
167
|
-
# Called when a new assistant message starts
|
168
|
-
chat.on_new_message do
|
169
|
-
puts "Assistant is typing..."
|
170
|
-
end
|
171
|
-
|
172
|
-
# Called when a message is complete
|
173
|
-
chat.on_end_message do |message|
|
174
|
-
puts "Response complete!"
|
175
|
-
puts "Used #{message.input_tokens + message.output_tokens} tokens"
|
176
|
-
end
|
177
|
-
|
178
|
-
# These callbacks work with both streaming and non-streaming responses
|
179
|
-
chat.ask "Tell me about Ruby's history"
|
180
|
-
```
|
181
|
-
|
182
|
-
## Multiple Parallel Chats
|
183
|
-
|
184
|
-
You can maintain multiple separate chat instances:
|
185
|
-
|
186
|
-
```ruby
|
187
|
-
# Create multiple chat instances
|
188
|
-
ruby_chat = RubyLLM.chat
|
189
|
-
python_chat = RubyLLM.chat
|
190
|
-
|
191
|
-
# Each has its own conversation history
|
192
|
-
ruby_chat.ask "What's great about Ruby?"
|
193
|
-
python_chat.ask "What's great about Python?"
|
194
|
-
|
195
|
-
# Continue separate conversations
|
196
|
-
ruby_chat.ask "How does Ruby handle metaprogramming?"
|
197
|
-
python_chat.ask "How does Python handle decorators?"
|
198
|
-
```
|
199
|
-
|
200
|
-
## Next Steps
|
201
|
-
|
202
|
-
Now that you understand chat basics, you might want to explore:
|
203
|
-
|
204
|
-
- [Using Tools]({% link guides/tools.md %}) to let AI use your Ruby code
|
205
|
-
- [Streaming Responses]({% link guides/streaming.md %}) for real-time interactions
|
206
|
-
- [Rails Integration]({% link guides/rails.md %}) to persist conversations in your apps
|
data/docs/guides/embeddings.md
DELETED
@@ -1,325 +0,0 @@
|
|
1
|
-
---
|
2
|
-
layout: default
|
3
|
-
title: Embeddings
|
4
|
-
parent: Guides
|
5
|
-
nav_order: 7
|
6
|
-
permalink: /guides/embeddings
|
7
|
-
---
|
8
|
-
|
9
|
-
# Embeddings
|
10
|
-
|
11
|
-
Text embeddings are numerical representations of text that capture semantic meaning. RubyLLM makes it easy to generate embeddings for a variety of applications, including semantic search, clustering, and recommendation systems.
|
12
|
-
|
13
|
-
## Basic Embedding Generation
|
14
|
-
|
15
|
-
The simplest way to create an embedding is with the global `embed` method:
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
# Create an embedding for a single text
|
19
|
-
embedding = RubyLLM.embed("Ruby is a programmer's best friend")
|
20
|
-
|
21
|
-
# The vector representation
|
22
|
-
vector = embedding.vectors
|
23
|
-
puts "Vector dimension: #{vector.length}" # => 1536 for text-embedding-3-small
|
24
|
-
```
|
25
|
-
|
26
|
-
## Embedding Multiple Texts
|
27
|
-
|
28
|
-
You can efficiently embed multiple texts at once:
|
29
|
-
|
30
|
-
```ruby
|
31
|
-
# Create embeddings for multiple texts
|
32
|
-
texts = ["Ruby", "Python", "JavaScript"]
|
33
|
-
embeddings = RubyLLM.embed(texts)
|
34
|
-
|
35
|
-
# Each text gets its own vector
|
36
|
-
puts "Number of vectors: #{embeddings.vectors.length}" # => 3
|
37
|
-
puts "First vector dimensions: #{embeddings.vectors.first.length}"
|
38
|
-
```
|
39
|
-
|
40
|
-
## Choosing Models
|
41
|
-
|
42
|
-
By default, RubyLLM uses OpenAI's `text-embedding-3-small`, but you can specify a different model:
|
43
|
-
|
44
|
-
```ruby
|
45
|
-
# Use a specific model
|
46
|
-
embedding = RubyLLM.embed(
|
47
|
-
"This is a test sentence",
|
48
|
-
model: "text-embedding-3-large"
|
49
|
-
)
|
50
|
-
|
51
|
-
# Or use a Google model
|
52
|
-
google_embedding = RubyLLM.embed(
|
53
|
-
"This is a test sentence",
|
54
|
-
model: "text-embedding-004"
|
55
|
-
)
|
56
|
-
```
|
57
|
-
|
58
|
-
You can configure the default embedding model globally:
|
59
|
-
|
60
|
-
```ruby
|
61
|
-
RubyLLM.configure do |config|
|
62
|
-
config.default_embedding_model = "text-embedding-3-large"
|
63
|
-
end
|
64
|
-
```
|
65
|
-
|
66
|
-
## Using Embedding Results
|
67
|
-
|
68
|
-
### Vector Properties
|
69
|
-
|
70
|
-
The embedding result contains useful information:
|
71
|
-
|
72
|
-
```ruby
|
73
|
-
embedding = RubyLLM.embed("Example text")
|
74
|
-
|
75
|
-
# The vector representation
|
76
|
-
puts embedding.vectors.class # => Array
|
77
|
-
puts embedding.vectors.first.class # => Float
|
78
|
-
|
79
|
-
# The model used
|
80
|
-
puts embedding.model # => "text-embedding-3-small"
|
81
|
-
|
82
|
-
# Token usage
|
83
|
-
puts embedding.input_tokens # => 3
|
84
|
-
```
|
85
|
-
|
86
|
-
### Calculating Similarity
|
87
|
-
|
88
|
-
Embeddings are commonly used to calculate similarity between texts:
|
89
|
-
|
90
|
-
```ruby
|
91
|
-
require 'matrix'
|
92
|
-
|
93
|
-
# Create embeddings for two texts
|
94
|
-
embedding1 = RubyLLM.embed("I love Ruby programming")
|
95
|
-
embedding2 = RubyLLM.embed("Ruby is my favorite language")
|
96
|
-
|
97
|
-
# Convert to Vector objects
|
98
|
-
vector1 = Vector.elements(embedding1.vectors)
|
99
|
-
vector2 = Vector.elements(embedding2.vectors)
|
100
|
-
|
101
|
-
# Calculate cosine similarity
|
102
|
-
similarity = vector1.inner_product(vector2) / (vector1.norm * vector2.norm)
|
103
|
-
puts "Similarity: #{similarity}" # Higher values (closer to 1) mean more similar
|
104
|
-
```
|
105
|
-
|
106
|
-
### Simple Semantic Search
|
107
|
-
|
108
|
-
```ruby
|
109
|
-
# Create a simple search index
|
110
|
-
class SearchIndex
|
111
|
-
def initialize(texts, model: nil)
|
112
|
-
@texts = texts
|
113
|
-
@embeddings = RubyLLM.embed(texts, model: model).vectors
|
114
|
-
end
|
115
|
-
|
116
|
-
def search(query, top_k: 3)
|
117
|
-
query_embedding = RubyLLM.embed(query).vectors
|
118
|
-
query_vector = Vector.elements(query_embedding)
|
119
|
-
|
120
|
-
# Calculate similarities
|
121
|
-
similarities = @embeddings.map.with_index do |embedding, idx|
|
122
|
-
vector = Vector.elements(embedding)
|
123
|
-
similarity = query_vector.inner_product(vector) / (query_vector.norm * vector.norm)
|
124
|
-
[idx, similarity]
|
125
|
-
end
|
126
|
-
|
127
|
-
# Return top results
|
128
|
-
similarities.sort_by { |_, similarity| -similarity }
|
129
|
-
.take(top_k)
|
130
|
-
.map { |idx, similarity| { text: @texts[idx], similarity: similarity } }
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# Create an index
|
135
|
-
documents = [
|
136
|
-
"Ruby is a dynamic, interpreted language",
|
137
|
-
"Python is known for its readability",
|
138
|
-
"JavaScript runs in the browser",
|
139
|
-
"Ruby on Rails is a web framework",
|
140
|
-
"Django is a Python web framework"
|
141
|
-
]
|
142
|
-
|
143
|
-
index = SearchIndex.new(documents)
|
144
|
-
|
145
|
-
# Search for similar documents
|
146
|
-
results = index.search("web development frameworks")
|
147
|
-
results.each do |result|
|
148
|
-
puts "#{result[:text]} (Similarity: #{result[:similarity].round(4)})"
|
149
|
-
end
|
150
|
-
```
|
151
|
-
|
152
|
-
## Error Handling
|
153
|
-
|
154
|
-
Handle errors that may occur during embedding generation:
|
155
|
-
|
156
|
-
```ruby
|
157
|
-
begin
|
158
|
-
embedding = RubyLLM.embed("Example text")
|
159
|
-
rescue RubyLLM::UnauthorizedError
|
160
|
-
puts "Please check your API key"
|
161
|
-
rescue RubyLLM::BadRequestError => e
|
162
|
-
puts "Invalid request: #{e.message}"
|
163
|
-
rescue RubyLLM::Error => e
|
164
|
-
puts "Error generating embedding: #{e.message}"
|
165
|
-
end
|
166
|
-
```
|
167
|
-
|
168
|
-
## Performance Considerations
|
169
|
-
|
170
|
-
When working with embeddings, keep these best practices in mind:
|
171
|
-
|
172
|
-
1. **Batch processing** - Embedding multiple texts at once is more efficient than making separate calls
|
173
|
-
2. **Caching** - Store embeddings in your database rather than regenerating them
|
174
|
-
3. **Dimensionality** - Different models produce embeddings with different dimensions
|
175
|
-
4. **Normalization** - Consider normalizing vectors to improve similarity calculations
|
176
|
-
|
177
|
-
## Working with Large Datasets
|
178
|
-
|
179
|
-
For larger datasets, process embeddings in batches:
|
180
|
-
|
181
|
-
```ruby
|
182
|
-
def embed_in_batches(texts, batch_size: 100, model: nil)
|
183
|
-
all_embeddings = []
|
184
|
-
|
185
|
-
texts.each_slice(batch_size) do |batch|
|
186
|
-
batch_embeddings = RubyLLM.embed(batch, model: model).vectors
|
187
|
-
all_embeddings.concat(batch_embeddings)
|
188
|
-
|
189
|
-
# Optional: add a small delay to avoid rate limiting
|
190
|
-
sleep(0.1)
|
191
|
-
end
|
192
|
-
|
193
|
-
all_embeddings
|
194
|
-
end
|
195
|
-
|
196
|
-
# Usage
|
197
|
-
documents = File.readlines("documents.txt", chomp: true)
|
198
|
-
embeddings = embed_in_batches(documents)
|
199
|
-
```
|
200
|
-
|
201
|
-
## Rails Integration
|
202
|
-
|
203
|
-
In a Rails application, you might integrate embeddings like this:
|
204
|
-
|
205
|
-
```ruby
|
206
|
-
class Document < ApplicationRecord
|
207
|
-
serialize :embedding, Array
|
208
|
-
|
209
|
-
before_save :generate_embedding, if: -> { content_changed? }
|
210
|
-
|
211
|
-
def self.search(query, limit: 10)
|
212
|
-
# Generate query embedding
|
213
|
-
query_embedding = RubyLLM.embed(query).vectors
|
214
|
-
|
215
|
-
# Convert to SQL for similarity search
|
216
|
-
where.not(embedding: nil)
|
217
|
-
.select("*, (embedding <=> ?) AS similarity", query_embedding)
|
218
|
-
.order("similarity DESC")
|
219
|
-
.limit(limit)
|
220
|
-
end
|
221
|
-
|
222
|
-
private
|
223
|
-
|
224
|
-
def generate_embedding
|
225
|
-
return if content.blank?
|
226
|
-
|
227
|
-
self.embedding = RubyLLM.embed(content).vectors
|
228
|
-
rescue RubyLLM::Error => e
|
229
|
-
errors.add(:base, "Failed to generate embedding: #{e.message}")
|
230
|
-
throw :abort
|
231
|
-
end
|
232
|
-
end
|
233
|
-
```
|
234
|
-
|
235
|
-
Note: The above example assumes you're using PostgreSQL with the `pgvector` extension for vector similarity search.
|
236
|
-
|
237
|
-
## Example Use Cases
|
238
|
-
|
239
|
-
### Document Classification
|
240
|
-
|
241
|
-
```ruby
|
242
|
-
# Train a simple classifier
|
243
|
-
class SimpleClassifier
|
244
|
-
def initialize
|
245
|
-
@categories = {}
|
246
|
-
end
|
247
|
-
|
248
|
-
def train(text, category)
|
249
|
-
@categories[category] ||= []
|
250
|
-
@categories[category] << RubyLLM.embed(text).vectors
|
251
|
-
end
|
252
|
-
|
253
|
-
def classify(text)
|
254
|
-
# Get embedding for the query text
|
255
|
-
query_embedding = RubyLLM.embed(text).vectors
|
256
|
-
query_vector = Vector.elements(query_embedding)
|
257
|
-
|
258
|
-
# Find the closest category
|
259
|
-
best_similarity = -1
|
260
|
-
best_category = nil
|
261
|
-
|
262
|
-
@categories.each do |category, embeddings|
|
263
|
-
# Calculate average similarity to this category
|
264
|
-
similarity = embeddings.map do |embedding|
|
265
|
-
vector = Vector.elements(embedding)
|
266
|
-
query_vector.inner_product(vector) / (query_vector.norm * vector.norm)
|
267
|
-
end.sum / embeddings.size
|
268
|
-
|
269
|
-
if similarity > best_similarity
|
270
|
-
best_similarity = similarity
|
271
|
-
best_category = category
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
{ category: best_category, confidence: best_similarity }
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
# Usage
|
280
|
-
classifier = SimpleClassifier.new
|
281
|
-
|
282
|
-
# Train with examples
|
283
|
-
classifier.train("How do I install Ruby?", :installation)
|
284
|
-
classifier.train("Setting up Ruby environment", :installation)
|
285
|
-
classifier.train("What are blocks in Ruby?", :language_features)
|
286
|
-
classifier.train("Understanding Ruby modules", :language_features)
|
287
|
-
|
288
|
-
# Classify new queries
|
289
|
-
puts classifier.classify("How to install Ruby on Ubuntu?")
|
290
|
-
# => {:category=>:installation, :confidence=>0.92}
|
291
|
-
```
|
292
|
-
|
293
|
-
### Content Recommendation
|
294
|
-
|
295
|
-
```ruby
|
296
|
-
def recommend_similar_content(content_id, library, count: 3)
|
297
|
-
# Get the target content
|
298
|
-
target = library.find(content_id)
|
299
|
-
target_embedding = RubyLLM.embed(target.description).vectors
|
300
|
-
target_vector = Vector.elements(target_embedding)
|
301
|
-
|
302
|
-
# Compare with all other content
|
303
|
-
similarities = library.reject { |item| item.id == content_id }.map do |item|
|
304
|
-
next if item.embedding.nil?
|
305
|
-
|
306
|
-
item_vector = Vector.elements(item.embedding)
|
307
|
-
similarity = target_vector.inner_product(item_vector) / (target_vector.norm * item_vector.norm)
|
308
|
-
|
309
|
-
[item, similarity]
|
310
|
-
end.compact
|
311
|
-
|
312
|
-
# Return top matches
|
313
|
-
similarities.sort_by { |_, similarity| -similarity }
|
314
|
-
.take(count)
|
315
|
-
.map { |item, similarity| { item: item, similarity: similarity } }
|
316
|
-
end
|
317
|
-
```
|
318
|
-
|
319
|
-
## Next Steps
|
320
|
-
|
321
|
-
Now that you understand embeddings, you might want to explore:
|
322
|
-
|
323
|
-
- [Chat]({% link guides/chat.md %}) for interactive AI conversations
|
324
|
-
- [Tools]({% link guides/tools.md %}) to extend AI capabilities
|
325
|
-
- [Error Handling]({% link guides/error-handling.md %}) for robust applications
|