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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0cf7b20ca960eab7d26da1c568c5aa5078e12e887a4d93c9b472d6897cf1e5e
4
- data.tar.gz: 59372d2c80bd8fdb23d49ffdd672911948bf1b78da8243aaac7df926a378bd59
3
+ metadata.gz: c2a8bd149077abc08185f8bc5c59d03323ba6adce25f1feed23dfc35d17376de
4
+ data.tar.gz: 4e9d466a76aa4565a6936a8d9cc7499b4b18aa6efb1e9c40baa0a28c35ca656d
5
5
  SHA512:
6
- metadata.gz: 1902ad245d20d405fe69633e122a83302bbd5b95e5b65fe0d24b1da14b0effaf27e9308e4a5a42bd9072545b2d128ed173214812667727fb2ea0de43948f1ee1
7
- data.tar.gz: d3937f03bc62481c6257c55c9a7f1a8c6eccc9fc08a759c8f40334b21000a12cc67e5277ca0d018b551c3abb6b077d4ffc1caa8bfaa3f2d8e784aac98c1e9bf8
6
+ metadata.gz: 3758ac407012134aab9fcf89f0ad7895b3cb38dd9b385772bb8102cea93a4d9247e9b3083e5eaa7e7f2d1969c4079d1b4286168c00353976af84effa6272ec2b
7
+ data.tar.gz: 3787b8665b33714ac6f4221e7a79753563d4b4553b8868a8dba2d9a1b2d3e310c43c3f27064fb7e0aa1bf9addad865b26626127d7862ed52221637c6b81eaa91
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # RubyLLM::Agents
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/ruby_llm-agents.svg)](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
  # ============================================
@@ -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.id, type: value.class.name }
111
+ { id: value&.id, type: value&.class&.name }
112
112
  else
113
113
  value
114
114
  end
@@ -4,6 +4,6 @@ module RubyLLM
4
4
  module Agents
5
5
  # Current version of the RubyLLM::Agents gem
6
6
  # @return [String] Semantic version string
7
- VERSION = "0.3.0"
7
+ VERSION = "0.3.1"
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_llm-agents
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - adham90