ruby_llm 0.1.0.pre37 → 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/.rspec_status +38 -7
- data/README.md +2 -0
- data/lib/ruby_llm/models.json +11 -125
- data/lib/ruby_llm/providers/deepseek/capabilities.rb +2 -2
- data/lib/ruby_llm/providers/openai/capabilities.rb +1 -2
- 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 -53
- data/docs/installation.md +0 -98
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
|
@@ -1,301 +0,0 @@
|
|
1
|
-
---
|
2
|
-
layout: default
|
3
|
-
title: Error Handling
|
4
|
-
parent: Guides
|
5
|
-
nav_order: 8
|
6
|
-
permalink: /guides/error-handling
|
7
|
-
---
|
8
|
-
|
9
|
-
# Error Handling
|
10
|
-
|
11
|
-
Proper error handling is crucial when working with AI services. RubyLLM provides a comprehensive error handling system that helps you build robust applications.
|
12
|
-
|
13
|
-
## Error Hierarchy
|
14
|
-
|
15
|
-
RubyLLM uses a structured error hierarchy:
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
RubyLLM::Error # Base error class
|
19
|
-
RubyLLM::BadRequestError # Invalid request parameters (400)
|
20
|
-
RubyLLM::UnauthorizedError # API key issues (401)
|
21
|
-
RubyLLM::PaymentRequiredError # Billing issues (402)
|
22
|
-
RubyLLM::RateLimitError # Rate limit exceeded (429)
|
23
|
-
RubyLLM::ServerError # Provider server error (500)
|
24
|
-
RubyLLM::ServiceUnavailableError # Service unavailable (503)
|
25
|
-
RubyLLM::ModelNotFoundError # Invalid model ID
|
26
|
-
RubyLLM::InvalidRoleError # Invalid message role
|
27
|
-
```
|
28
|
-
|
29
|
-
## Basic Error Handling
|
30
|
-
|
31
|
-
Wrap your AI interactions in `begin/rescue` blocks:
|
32
|
-
|
33
|
-
```ruby
|
34
|
-
begin
|
35
|
-
chat = RubyLLM.chat
|
36
|
-
response = chat.ask "What's the capital of France?"
|
37
|
-
puts response.content
|
38
|
-
rescue RubyLLM::Error => e
|
39
|
-
puts "AI interaction failed: #{e.message}"
|
40
|
-
end
|
41
|
-
```
|
42
|
-
|
43
|
-
## Handling Specific Errors
|
44
|
-
|
45
|
-
Target specific error types for more precise handling:
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
begin
|
49
|
-
chat = RubyLLM.chat
|
50
|
-
response = chat.ask "Generate a detailed analysis"
|
51
|
-
rescue RubyLLM::UnauthorizedError
|
52
|
-
puts "Please check your API credentials"
|
53
|
-
rescue RubyLLM::PaymentRequiredError
|
54
|
-
puts "Payment required - please check your account balance"
|
55
|
-
rescue RubyLLM::RateLimitError
|
56
|
-
puts "Rate limit exceeded - please try again later"
|
57
|
-
rescue RubyLLM::ServiceUnavailableError
|
58
|
-
puts "Service temporarily unavailable - please try again later"
|
59
|
-
rescue RubyLLM::BadRequestError => e
|
60
|
-
puts "Bad request: #{e.message}"
|
61
|
-
rescue RubyLLM::Error => e
|
62
|
-
puts "Other error: #{e.message}"
|
63
|
-
end
|
64
|
-
```
|
65
|
-
|
66
|
-
## API Response Details
|
67
|
-
|
68
|
-
The `Error` class contains the original response, allowing for detailed error inspection:
|
69
|
-
|
70
|
-
```ruby
|
71
|
-
begin
|
72
|
-
chat = RubyLLM.chat
|
73
|
-
chat.ask "Some question"
|
74
|
-
rescue RubyLLM::Error => e
|
75
|
-
puts "Error: #{e.message}"
|
76
|
-
puts "Status: #{e.response.status}"
|
77
|
-
puts "Body: #{e.response.body}"
|
78
|
-
end
|
79
|
-
```
|
80
|
-
|
81
|
-
## Error Handling with Streaming
|
82
|
-
|
83
|
-
When using streaming, errors can occur during the stream:
|
84
|
-
|
85
|
-
```ruby
|
86
|
-
begin
|
87
|
-
chat = RubyLLM.chat
|
88
|
-
chat.ask "Generate a long response" do |chunk|
|
89
|
-
print chunk.content
|
90
|
-
end
|
91
|
-
rescue RubyLLM::Error => e
|
92
|
-
puts "\nStreaming error: #{e.message}"
|
93
|
-
end
|
94
|
-
```
|
95
|
-
|
96
|
-
## Handling Tool Errors
|
97
|
-
|
98
|
-
When using tools, errors can be handled within the tool or in the calling code:
|
99
|
-
|
100
|
-
```ruby
|
101
|
-
# Error handling within tools
|
102
|
-
class Calculator < RubyLLM::Tool
|
103
|
-
description "Performs calculations"
|
104
|
-
|
105
|
-
param :expression,
|
106
|
-
type: :string,
|
107
|
-
desc: "Math expression to evaluate"
|
108
|
-
|
109
|
-
def execute(expression:)
|
110
|
-
eval(expression).to_s
|
111
|
-
rescue StandardError => e
|
112
|
-
# Return error as structured data
|
113
|
-
{ error: "Calculation error: #{e.message}" }
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# Error handling when using tools
|
118
|
-
begin
|
119
|
-
chat = RubyLLM.chat.with_tool(Calculator)
|
120
|
-
chat.ask "What's 1/0?"
|
121
|
-
rescue RubyLLM::Error => e
|
122
|
-
puts "Error using tools: #{e.message}"
|
123
|
-
end
|
124
|
-
```
|
125
|
-
|
126
|
-
## Automatic Retries
|
127
|
-
|
128
|
-
RubyLLM automatically retries on certain transient errors:
|
129
|
-
|
130
|
-
```ruby
|
131
|
-
# Configure retry behavior
|
132
|
-
RubyLLM.configure do |config|
|
133
|
-
config.max_retries = 5 # Maximum number of retries
|
134
|
-
end
|
135
|
-
```
|
136
|
-
|
137
|
-
The following errors trigger automatic retries:
|
138
|
-
- Network timeouts
|
139
|
-
- Connection failures
|
140
|
-
- Rate limit errors (429)
|
141
|
-
- Server errors (500, 502, 503, 504)
|
142
|
-
|
143
|
-
## Provider-Specific Errors
|
144
|
-
|
145
|
-
Each provider may return slightly different error messages. RubyLLM normalizes these into standard error types, but the original error details are preserved:
|
146
|
-
|
147
|
-
```ruby
|
148
|
-
begin
|
149
|
-
chat = RubyLLM.chat
|
150
|
-
chat.ask "Some question"
|
151
|
-
rescue RubyLLM::Error => e
|
152
|
-
if e.response.body.include?("organization_quota_exceeded")
|
153
|
-
puts "Your organization's quota has been exceeded"
|
154
|
-
else
|
155
|
-
puts "Error: #{e.message}"
|
156
|
-
end
|
157
|
-
end
|
158
|
-
```
|
159
|
-
|
160
|
-
## Error Handling in Rails
|
161
|
-
|
162
|
-
When using RubyLLM in a Rails application, you can handle errors at different levels:
|
163
|
-
|
164
|
-
### Controller Level
|
165
|
-
|
166
|
-
```ruby
|
167
|
-
class ChatController < ApplicationController
|
168
|
-
rescue_from RubyLLM::Error, with: :handle_ai_error
|
169
|
-
|
170
|
-
def create
|
171
|
-
@chat = Chat.create!(chat_params)
|
172
|
-
@chat.ask(params[:message])
|
173
|
-
redirect_to @chat
|
174
|
-
end
|
175
|
-
|
176
|
-
private
|
177
|
-
|
178
|
-
def handle_ai_error(exception)
|
179
|
-
flash[:error] = "AI service error: #{exception.message}"
|
180
|
-
redirect_to chats_path
|
181
|
-
end
|
182
|
-
end
|
183
|
-
```
|
184
|
-
|
185
|
-
### Background Job Level
|
186
|
-
|
187
|
-
```ruby
|
188
|
-
class AiChatJob < ApplicationJob
|
189
|
-
retry_on RubyLLM::RateLimitError, RubyLLM::ServiceUnavailableError,
|
190
|
-
wait: :exponentially_longer, attempts: 5
|
191
|
-
|
192
|
-
discard_on RubyLLM::UnauthorizedError, RubyLLM::BadRequestError
|
193
|
-
|
194
|
-
def perform(chat_id, message)
|
195
|
-
chat = Chat.find(chat_id)
|
196
|
-
chat.ask(message)
|
197
|
-
rescue RubyLLM::Error => e
|
198
|
-
# Log error and notify user
|
199
|
-
ErrorNotifier.notify(chat.user, "AI chat error: #{e.message}")
|
200
|
-
end
|
201
|
-
end
|
202
|
-
```
|
203
|
-
|
204
|
-
## Monitoring Errors
|
205
|
-
|
206
|
-
For production applications, monitor AI service errors:
|
207
|
-
|
208
|
-
```ruby
|
209
|
-
# Custom error handler
|
210
|
-
module AiErrorMonitoring
|
211
|
-
def self.track_error(error, context = {})
|
212
|
-
# Record error in your monitoring system
|
213
|
-
Sentry.capture_exception(error, extra: context)
|
214
|
-
|
215
|
-
# Log details
|
216
|
-
Rails.logger.error "[AI Error] #{error.class}: #{error.message}"
|
217
|
-
Rails.logger.error "Context: #{context.inspect}"
|
218
|
-
|
219
|
-
# Return or re-raise as needed
|
220
|
-
error
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# Usage
|
225
|
-
begin
|
226
|
-
chat.ask "Some question"
|
227
|
-
rescue RubyLLM::Error => e
|
228
|
-
AiErrorMonitoring.track_error(e, {
|
229
|
-
model: chat.model.id,
|
230
|
-
tokens: chat.messages.sum(&:input_tokens)
|
231
|
-
})
|
232
|
-
|
233
|
-
# Show appropriate message to user
|
234
|
-
flash[:error] = "Sorry, we encountered an issue with our AI service"
|
235
|
-
end
|
236
|
-
```
|
237
|
-
|
238
|
-
## Graceful Degradation
|
239
|
-
|
240
|
-
For critical applications, implement fallback strategies:
|
241
|
-
|
242
|
-
```ruby
|
243
|
-
def get_ai_response(question, fallback_message = nil)
|
244
|
-
begin
|
245
|
-
chat = RubyLLM.chat
|
246
|
-
response = chat.ask(question)
|
247
|
-
response.content
|
248
|
-
rescue RubyLLM::Error => e
|
249
|
-
Rails.logger.error "AI error: #{e.message}"
|
250
|
-
|
251
|
-
# Fallback to alternative model
|
252
|
-
begin
|
253
|
-
fallback_chat = RubyLLM.chat(model: 'gpt-3.5-turbo')
|
254
|
-
fallback_response = fallback_chat.ask(question)
|
255
|
-
fallback_response.content
|
256
|
-
rescue RubyLLM::Error => e2
|
257
|
-
Rails.logger.error "Fallback AI error: #{e2.message}"
|
258
|
-
fallback_message || "Sorry, our AI service is currently unavailable"
|
259
|
-
end
|
260
|
-
end
|
261
|
-
end
|
262
|
-
```
|
263
|
-
|
264
|
-
## Best Practices
|
265
|
-
|
266
|
-
1. **Always wrap AI calls in error handling** - Don't assume AI services will always be available
|
267
|
-
2. **Implement timeouts** - Configure appropriate request timeouts
|
268
|
-
3. **Use background jobs** - Process AI requests asynchronously when possible
|
269
|
-
4. **Set up monitoring** - Track error rates and response times
|
270
|
-
5. **Have fallback content** - Prepare fallback responses when AI services fail
|
271
|
-
6. **Gracefully degrade** - Implement multiple fallback strategies
|
272
|
-
7. **Communicate to users** - Provide clear error messages when AI services are unavailable
|
273
|
-
|
274
|
-
## Error Recovery
|
275
|
-
|
276
|
-
When dealing with errors, consider recovery strategies:
|
277
|
-
|
278
|
-
```ruby
|
279
|
-
MAX_RETRIES = 3
|
280
|
-
|
281
|
-
def ask_with_recovery(chat, question, retries = 0)
|
282
|
-
chat.ask(question)
|
283
|
-
rescue RubyLLM::RateLimitError, RubyLLM::ServiceUnavailableError => e
|
284
|
-
if retries < MAX_RETRIES
|
285
|
-
# Exponential backoff
|
286
|
-
sleep_time = 2 ** retries
|
287
|
-
puts "Error: #{e.message}. Retrying in #{sleep_time} seconds..."
|
288
|
-
sleep sleep_time
|
289
|
-
ask_with_recovery(chat, question, retries + 1)
|
290
|
-
else
|
291
|
-
raise e
|
292
|
-
end
|
293
|
-
end
|
294
|
-
```
|
295
|
-
|
296
|
-
## Next Steps
|
297
|
-
|
298
|
-
Now that you understand error handling in RubyLLM, you might want to explore:
|
299
|
-
|
300
|
-
- [Rails Integration]({% link guides/rails.md %}) for using RubyLLM in Rails applications
|
301
|
-
- [Tools]({% link guides/tools.md %}) for using tools with error handling
|