Skip to content

Commit 3dbf637

Browse files
authored
Merge pull request #3 from scientist-labs/wrap-errors
Wrap generation errors with helpful messages
2 parents 06ee87f + 614af51 commit 3dbf637

File tree

2 files changed

+103
-5
lines changed

2 files changed

+103
-5
lines changed

lib/ruby_llm/red_candle/chat.rb

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def perform_completion!(payload)
6767
prompt = build_prompt(model, messages)
6868
validate_context_length!(prompt, payload[:model])
6969
config = build_generation_config(payload)
70-
model.generate(prompt, config: config)
70+
generate_with_error_handling(model, prompt, config, payload[:model])
7171
end
7272

7373
format_response(response, payload[:schema])
@@ -84,8 +84,8 @@ def perform_streaming_completion!(payload, &block)
8484
# Collect all streamed content
8585
full_content = ""
8686

87-
# Stream tokens
88-
model.generate_stream(prompt, config: config) do |token|
87+
# Stream tokens with error handling
88+
stream_with_error_handling(model, prompt, config, payload[:model]) do |token|
8989
full_content += token
9090
chunk = format_stream_chunk(token)
9191
block.call(chunk)
@@ -189,6 +189,44 @@ def model_error_message(exception, model_id)
189189
ERROR_MESSAGE
190190
end
191191

192+
def generate_with_error_handling(model, prompt, config, model_id)
193+
model.generate(prompt, config: config)
194+
rescue StandardError => e
195+
raise RubyLLM::Error.new(nil, generation_error_message(e, model_id))
196+
end
197+
198+
def stream_with_error_handling(model, prompt, config, model_id, &block)
199+
model.generate_stream(prompt, config: config, &block)
200+
rescue StandardError => e
201+
raise RubyLLM::Error.new(nil, generation_error_message(e, model_id))
202+
end
203+
204+
def generation_error_message(exception, model_id)
205+
message = exception.message.to_s
206+
207+
if message.include?("out of memory") || message.include?("OOM")
208+
<<~ERROR_MESSAGE.strip
209+
Out of memory while generating with #{model_id}.
210+
Try using a smaller model or reducing the context length.
211+
Original error: #{message}
212+
ERROR_MESSAGE
213+
elsif message.include?("context") || message.include?("sequence")
214+
<<~ERROR_MESSAGE.strip
215+
Context length exceeded for #{model_id}.
216+
The input is too long for this model's context window.
217+
Original error: #{message}
218+
ERROR_MESSAGE
219+
elsif message.include?("tensor") || message.include?("shape")
220+
<<~ERROR_MESSAGE.strip
221+
Model execution error for #{model_id}.
222+
This may indicate an incompatible model format or corrupted weights.
223+
Original error: #{message}
224+
ERROR_MESSAGE
225+
else
226+
"Generation failed for #{model_id}: #{message}"
227+
end
228+
end
229+
192230
def format_messages(messages)
193231
messages.map do |msg|
194232
# Handle both hash and Message objects

spec/ruby_llm/red_candle/chat_spec.rb

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,66 @@
8787
role: "assistant"
8888
)
8989
end
90+
91+
it "wraps generation errors with helpful messages" do
92+
allow(mock_model).to receive(:generate).and_raise(StandardError, "raw error from candle")
93+
94+
payload = {
95+
messages: messages,
96+
model: "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF",
97+
temperature: 0.7
98+
}
99+
100+
expect { provider.perform_completion!(payload) }.to raise_error(
101+
RubyLLM::Error,
102+
/Generation failed for TheBloke\/TinyLlama.*raw error from candle/
103+
)
104+
end
105+
106+
it "provides helpful message for out of memory errors" do
107+
allow(mock_model).to receive(:generate).and_raise(StandardError, "out of memory")
108+
109+
payload = {
110+
messages: messages,
111+
model: "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF",
112+
temperature: 0.7
113+
}
114+
115+
expect { provider.perform_completion!(payload) }.to raise_error(
116+
RubyLLM::Error,
117+
/Out of memory.*Try using a smaller model/m
118+
)
119+
end
120+
121+
it "provides helpful message for context length errors" do
122+
allow(mock_model).to receive(:generate).and_raise(StandardError, "context length exceeded")
123+
124+
payload = {
125+
messages: messages,
126+
model: "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF",
127+
temperature: 0.7
128+
}
129+
130+
expect { provider.perform_completion!(payload) }.to raise_error(
131+
RubyLLM::Error,
132+
/Context length exceeded.*input is too long/m
133+
)
134+
end
135+
136+
it "provides helpful message for tensor/shape errors" do
137+
allow(mock_model).to receive(:generate).and_raise(StandardError, "tensor shape mismatch")
138+
139+
payload = {
140+
messages: messages,
141+
model: "TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF",
142+
temperature: 0.7
143+
}
144+
145+
expect { provider.perform_completion!(payload) }.to raise_error(
146+
RubyLLM::Error,
147+
/Model execution error.*incompatible model format/m
148+
)
149+
end
90150
end
91151

92152
context "with structured generation" do
@@ -113,7 +173,7 @@
113173
schema = { type: "object", properties: { name: { type: "string" } } }
114174

115175
allow(mock_model).to receive(:generate_structured).and_raise(StandardError, "Structured gen failed")
116-
allow(RubyLLM.logger).to receive(:error)
176+
allow(RubyLLM.logger).to receive(:debug)
117177

118178
payload = {
119179
messages: messages,
@@ -126,7 +186,7 @@
126186
RubyLLM::Error,
127187
/Structured generation failed/
128188
)
129-
expect(RubyLLM.logger).to have_received(:error).at_least(:once)
189+
expect(RubyLLM.logger).to have_received(:debug).at_least(:once)
130190
end
131191

132192
it "normalizes schema keys to symbols" do

0 commit comments

Comments
 (0)