ruby_llm-agents 0.3.0 → 0.3.1
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/README.md +140 -0
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +4 -0
- data/lib/ruby_llm/agents/base.rb +20 -3
- data/lib/ruby_llm/agents/redactor.rb +1 -1
- data/lib/ruby_llm/agents/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2a8bd149077abc08185f8bc5c59d03323ba6adce25f1feed23dfc35d17376de
|
|
4
|
+
data.tar.gz: 4e9d466a76aa4565a6936a8d9cc7499b4b18aa6efb1e9c40baa0a28c35ca656d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3758ac407012134aab9fcf89f0ad7895b3cb38dd9b385772bb8102cea93a4d9247e9b3083e5eaa7e7f2d1969c4079d1b4286168c00353976af84effa6272ec2b
|
|
7
|
+
data.tar.gz: 3787b8665b33714ac6f4221e7a79753563d4b4553b8868a8dba2d9a1b2d3e310c43c3f27064fb7e0aa1bf9addad865b26626127d7862ed52221637c6b81eaa91
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# RubyLLM::Agents
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/rb/ruby_llm-agents)
|
|
4
|
+
|
|
3
5
|
A powerful Rails engine for building, managing, and monitoring LLM-powered agents using [RubyLLM](https://github.com/crmne/ruby_llm).
|
|
4
6
|
|
|
5
7
|
## Features
|
|
@@ -12,6 +14,8 @@ A powerful Rails engine for building, managing, and monitoring LLM-powered agent
|
|
|
12
14
|
- **🛠️ Generators** - Quickly scaffold new agents with customizable templates
|
|
13
15
|
- **🔍 Anomaly Detection** - Automatic warnings for unusual cost or duration patterns
|
|
14
16
|
- **🎯 Type Safety** - Structured output with RubyLLM::Schema integration
|
|
17
|
+
- **⚡ Real-time Streaming** - Stream LLM responses with time-to-first-token tracking
|
|
18
|
+
- **📎 Attachments** - Send images, PDFs, and files to vision-capable models
|
|
15
19
|
- **🔄 Reliability** - Automatic retries, model fallbacks, and circuit breakers for resilient agents
|
|
16
20
|
- **💵 Budget Controls** - Daily/monthly spending limits with hard and soft enforcement
|
|
17
21
|
- **🔔 Alerts** - Slack, webhook, and custom notifications for budget and circuit breaker events
|
|
@@ -150,6 +154,123 @@ SearchIntentAgent.call(query: "test", dry_run: true)
|
|
|
150
154
|
SearchIntentAgent.call(query: "test", skip_cache: true)
|
|
151
155
|
```
|
|
152
156
|
|
|
157
|
+
### Streaming Responses
|
|
158
|
+
|
|
159
|
+
Enable real-time streaming to receive LLM responses as they're generated:
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
class StreamingAgent < ApplicationAgent
|
|
163
|
+
model "gpt-4o"
|
|
164
|
+
streaming true # Enable streaming for this agent
|
|
165
|
+
|
|
166
|
+
param :prompt, required: true
|
|
167
|
+
|
|
168
|
+
def user_prompt
|
|
169
|
+
prompt
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Using Streaming with a Block
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
# Stream responses in real-time
|
|
178
|
+
StreamingAgent.call(prompt: "Write a story") do |chunk|
|
|
179
|
+
print chunk # Process each chunk as it arrives
|
|
180
|
+
end
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### HTTP Streaming with ActionController::Live
|
|
184
|
+
|
|
185
|
+
```ruby
|
|
186
|
+
class StreamingController < ApplicationController
|
|
187
|
+
include ActionController::Live
|
|
188
|
+
|
|
189
|
+
def stream_response
|
|
190
|
+
response.headers['Content-Type'] = 'text/event-stream'
|
|
191
|
+
response.headers['Cache-Control'] = 'no-cache'
|
|
192
|
+
|
|
193
|
+
StreamingAgent.call(prompt: params[:prompt]) do |chunk|
|
|
194
|
+
response.stream.write "data: #{chunk}\n\n"
|
|
195
|
+
end
|
|
196
|
+
ensure
|
|
197
|
+
response.stream.close
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Time-to-First-Token Tracking
|
|
203
|
+
|
|
204
|
+
Streaming executions automatically track latency metrics:
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
execution = RubyLLM::Agents::Execution.last
|
|
208
|
+
execution.streaming? # => true
|
|
209
|
+
execution.time_to_first_token_ms # => 245 (milliseconds to first chunk)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### Global Streaming Configuration
|
|
213
|
+
|
|
214
|
+
Enable streaming by default for all agents:
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
# config/initializers/ruby_llm_agents.rb
|
|
218
|
+
RubyLLM::Agents.configure do |config|
|
|
219
|
+
config.default_streaming = true
|
|
220
|
+
end
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Attachments (Vision & Multimodal)
|
|
224
|
+
|
|
225
|
+
Send images, PDFs, and other files to vision-capable models using the `with:` option:
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
class VisionAgent < ApplicationAgent
|
|
229
|
+
model "gpt-4o" # Use a vision-capable model
|
|
230
|
+
param :question, required: true
|
|
231
|
+
|
|
232
|
+
def user_prompt
|
|
233
|
+
question
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Single Attachment
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
# Local file
|
|
242
|
+
VisionAgent.call(question: "Describe this image", with: "photo.jpg")
|
|
243
|
+
|
|
244
|
+
# URL
|
|
245
|
+
VisionAgent.call(question: "What architecture is shown?", with: "https://example.com/building.jpg")
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### Multiple Attachments
|
|
249
|
+
|
|
250
|
+
```ruby
|
|
251
|
+
VisionAgent.call(
|
|
252
|
+
question: "Compare these two screenshots",
|
|
253
|
+
with: ["screenshot_v1.png", "screenshot_v2.png"]
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
#### Supported File Types
|
|
258
|
+
|
|
259
|
+
RubyLLM automatically detects file types:
|
|
260
|
+
|
|
261
|
+
- **Images:** `.jpg`, `.jpeg`, `.png`, `.gif`, `.webp`, `.bmp`
|
|
262
|
+
- **Videos:** `.mp4`, `.mov`, `.avi`, `.webm`
|
|
263
|
+
- **Audio:** `.mp3`, `.wav`, `.m4a`, `.ogg`, `.flac`
|
|
264
|
+
- **Documents:** `.pdf`, `.txt`, `.md`, `.csv`, `.json`, `.xml`
|
|
265
|
+
- **Code:** `.rb`, `.py`, `.js`, `.html`, `.css`, and many others
|
|
266
|
+
|
|
267
|
+
#### Debug Mode with Attachments
|
|
268
|
+
|
|
269
|
+
```ruby
|
|
270
|
+
VisionAgent.call(question: "test", with: "image.png", dry_run: true)
|
|
271
|
+
# => { ..., attachments: "image.png", ... }
|
|
272
|
+
```
|
|
273
|
+
|
|
153
274
|
## Usage Guide
|
|
154
275
|
|
|
155
276
|
### Agent DSL
|
|
@@ -680,6 +801,9 @@ RubyLLM::Agents.configure do |config|
|
|
|
680
801
|
# Default timeout for LLM requests (in seconds)
|
|
681
802
|
config.default_timeout = 60
|
|
682
803
|
|
|
804
|
+
# Enable streaming by default for all agents
|
|
805
|
+
config.default_streaming = false
|
|
806
|
+
|
|
683
807
|
# ============================================================================
|
|
684
808
|
# Caching Configuration
|
|
685
809
|
# ============================================================================
|
|
@@ -866,6 +990,18 @@ trend = RubyLLM::Agents::Execution.trend_analysis(
|
|
|
866
990
|
# ]
|
|
867
991
|
```
|
|
868
992
|
|
|
993
|
+
### Streaming Analytics
|
|
994
|
+
|
|
995
|
+
```ruby
|
|
996
|
+
# Percentage of executions using streaming
|
|
997
|
+
RubyLLM::Agents::Execution.streaming_rate
|
|
998
|
+
# => 45.5
|
|
999
|
+
|
|
1000
|
+
# Average time-to-first-token for streaming executions (milliseconds)
|
|
1001
|
+
RubyLLM::Agents::Execution.avg_time_to_first_token
|
|
1002
|
+
# => 245.3
|
|
1003
|
+
```
|
|
1004
|
+
|
|
869
1005
|
### Scopes
|
|
870
1006
|
|
|
871
1007
|
Chain scopes for complex queries:
|
|
@@ -919,6 +1055,10 @@ expensive_slow_failures = RubyLLM::Agents::Execution
|
|
|
919
1055
|
|
|
920
1056
|
# Token usage
|
|
921
1057
|
.high_token_usage(threshold)
|
|
1058
|
+
|
|
1059
|
+
# Streaming
|
|
1060
|
+
.streaming
|
|
1061
|
+
.non_streaming
|
|
922
1062
|
```
|
|
923
1063
|
|
|
924
1064
|
## Generators
|
|
@@ -18,6 +18,10 @@ RubyLLM::Agents.configure do |config|
|
|
|
18
18
|
# Default timeout in seconds for each LLM request
|
|
19
19
|
# config.default_timeout = 60
|
|
20
20
|
|
|
21
|
+
# Enable streaming by default for all agents
|
|
22
|
+
# When enabled, agents stream responses and track time-to-first-token
|
|
23
|
+
# config.default_streaming = false
|
|
24
|
+
|
|
21
25
|
# ============================================
|
|
22
26
|
# Caching
|
|
23
27
|
# ============================================
|
data/lib/ruby_llm/agents/base.rb
CHANGED
|
@@ -50,6 +50,7 @@ module RubyLLM
|
|
|
50
50
|
# @param kwargs [Hash] Named parameters for the agent
|
|
51
51
|
# @option kwargs [Boolean] :dry_run Return prompt info without API call
|
|
52
52
|
# @option kwargs [Boolean] :skip_cache Bypass caching even if enabled
|
|
53
|
+
# @option kwargs [String, Array<String>] :with Attachments (files, URLs) to send with the prompt
|
|
53
54
|
# @yield [chunk] Yields chunks when streaming is enabled
|
|
54
55
|
# @yieldparam chunk [RubyLLM::Chunk] A streaming chunk with content
|
|
55
56
|
# @return [Object] The processed response from the agent
|
|
@@ -64,6 +65,10 @@ module RubyLLM
|
|
|
64
65
|
# ChatAgent.call(message: "Hello") do |chunk|
|
|
65
66
|
# print chunk.content
|
|
66
67
|
# end
|
|
68
|
+
#
|
|
69
|
+
# @example With attachments
|
|
70
|
+
# VisionAgent.call(query: "Describe this image", with: "photo.jpg")
|
|
71
|
+
# VisionAgent.call(query: "Compare these", with: ["a.png", "b.png"])
|
|
67
72
|
def call(*args, **kwargs, &block)
|
|
68
73
|
new(*args, **kwargs).call(&block)
|
|
69
74
|
end
|
|
@@ -429,6 +434,7 @@ module RubyLLM
|
|
|
429
434
|
timeout: self.class.timeout,
|
|
430
435
|
system_prompt: system_prompt,
|
|
431
436
|
user_prompt: user_prompt,
|
|
437
|
+
attachments: @options[:with],
|
|
432
438
|
schema: schema&.class&.name,
|
|
433
439
|
streaming: self.class.streaming,
|
|
434
440
|
tools: self.class.tools.map { |t| t.respond_to?(:name) ? t.name : t.to_s }
|
|
@@ -465,7 +471,7 @@ module RubyLLM
|
|
|
465
471
|
if self.class.streaming && block_given?
|
|
466
472
|
execute_with_streaming(current_client, &block)
|
|
467
473
|
else
|
|
468
|
-
response = current_client.ask(user_prompt)
|
|
474
|
+
response = current_client.ask(user_prompt, **ask_options)
|
|
469
475
|
process_response(capture_response(response))
|
|
470
476
|
end
|
|
471
477
|
end
|
|
@@ -483,7 +489,7 @@ module RubyLLM
|
|
|
483
489
|
def execute_with_streaming(current_client, &block)
|
|
484
490
|
first_chunk_at = nil
|
|
485
491
|
|
|
486
|
-
response = current_client.ask(user_prompt) do |chunk|
|
|
492
|
+
response = current_client.ask(user_prompt, **ask_options) do |chunk|
|
|
487
493
|
first_chunk_at ||= Time.current
|
|
488
494
|
yield chunk if block_given?
|
|
489
495
|
end
|
|
@@ -692,7 +698,18 @@ module RubyLLM
|
|
|
692
698
|
#
|
|
693
699
|
# @return [Hash] Data to hash for cache key
|
|
694
700
|
def cache_key_data
|
|
695
|
-
@options.except(:skip_cache, :dry_run)
|
|
701
|
+
@options.except(:skip_cache, :dry_run, :with)
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
# Returns options to pass to the ask method
|
|
705
|
+
#
|
|
706
|
+
# Currently supports :with for attachments (images, PDFs, etc.)
|
|
707
|
+
#
|
|
708
|
+
# @return [Hash] Options for the ask call
|
|
709
|
+
def ask_options
|
|
710
|
+
opts = {}
|
|
711
|
+
opts[:with] = @options[:with] if @options[:with]
|
|
712
|
+
opts
|
|
696
713
|
end
|
|
697
714
|
|
|
698
715
|
# Validates that all required parameters are present
|
|
@@ -108,7 +108,7 @@ module RubyLLM
|
|
|
108
108
|
redact_string(value, config)
|
|
109
109
|
when defined?(ActiveRecord::Base) && ActiveRecord::Base
|
|
110
110
|
# Convert ActiveRecord objects to safe references
|
|
111
|
-
{ id: value
|
|
111
|
+
{ id: value&.id, type: value&.class&.name }
|
|
112
112
|
else
|
|
113
113
|
value
|
|
114
114
|
end
|