ruby_llm-agents 3.8.0 → 3.9.0

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -10
  3. data/app/controllers/ruby_llm/agents/requests_controller.rb +117 -0
  4. data/app/views/layouts/ruby_llm/agents/application.html.erb +4 -2
  5. data/app/views/ruby_llm/agents/requests/index.html.erb +153 -0
  6. data/app/views/ruby_llm/agents/requests/show.html.erb +136 -0
  7. data/config/routes.rb +2 -0
  8. data/lib/generators/ruby_llm_agents/agent_generator.rb +2 -2
  9. data/lib/generators/ruby_llm_agents/demo_generator.rb +102 -0
  10. data/lib/generators/ruby_llm_agents/doctor_generator.rb +196 -0
  11. data/lib/generators/ruby_llm_agents/install_generator.rb +7 -19
  12. data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +27 -80
  13. data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +18 -51
  14. data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +19 -17
  15. data/lib/ruby_llm/agents/base_agent.rb +68 -7
  16. data/lib/ruby_llm/agents/core/base.rb +4 -0
  17. data/lib/ruby_llm/agents/core/configuration.rb +10 -0
  18. data/lib/ruby_llm/agents/core/version.rb +1 -1
  19. data/lib/ruby_llm/agents/pipeline/context.rb +26 -0
  20. data/lib/ruby_llm/agents/pipeline/middleware/base.rb +58 -4
  21. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +17 -15
  22. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +34 -22
  23. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +105 -50
  24. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +7 -5
  25. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +6 -4
  26. data/lib/ruby_llm/agents/rails/engine.rb +11 -0
  27. data/lib/ruby_llm/agents/results/background_removal_result.rb +7 -1
  28. data/lib/ruby_llm/agents/results/base.rb +24 -2
  29. data/lib/ruby_llm/agents/results/embedding_result.rb +4 -0
  30. data/lib/ruby_llm/agents/results/image_analysis_result.rb +7 -1
  31. data/lib/ruby_llm/agents/results/image_edit_result.rb +7 -1
  32. data/lib/ruby_llm/agents/results/image_generation_result.rb +7 -1
  33. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +7 -1
  34. data/lib/ruby_llm/agents/results/image_transform_result.rb +7 -1
  35. data/lib/ruby_llm/agents/results/image_upscale_result.rb +7 -1
  36. data/lib/ruby_llm/agents/results/image_variation_result.rb +7 -1
  37. data/lib/ruby_llm/agents/results/speech_result.rb +6 -0
  38. data/lib/ruby_llm/agents/results/trackable.rb +25 -0
  39. data/lib/ruby_llm/agents/results/transcription_result.rb +6 -0
  40. data/lib/ruby_llm/agents/text/embedder.rb +7 -4
  41. data/lib/ruby_llm/agents/track_report.rb +127 -0
  42. data/lib/ruby_llm/agents/tracker.rb +32 -0
  43. data/lib/ruby_llm/agents.rb +208 -0
  44. data/lib/tasks/ruby_llm_agents.rake +6 -0
  45. metadata +10 -2
@@ -37,7 +37,12 @@ require_relative "agents/infrastructure/budget/config_resolver"
37
37
  require_relative "agents/infrastructure/budget/forecaster"
38
38
  require_relative "agents/infrastructure/budget/spend_recorder"
39
39
 
40
+ # Tracking
41
+ require_relative "agents/tracker"
42
+ require_relative "agents/track_report"
43
+
40
44
  # Results
45
+ require_relative "agents/results/trackable"
41
46
  require_relative "agents/results/base"
42
47
  require_relative "agents/results/embedding_result"
43
48
  require_relative "agents/results/transcription_result"
@@ -119,6 +124,69 @@ module RubyLLM
119
124
  # @see RubyLLM::Agents::Configuration
120
125
  module Agents
121
126
  class << self
127
+ # Wraps a block of agent calls, collecting all Results and
128
+ # returning an aggregated TrackReport.
129
+ #
130
+ # Shared options (tenant, tags, request_id) are injected into
131
+ # every agent instantiated inside the block unless overridden.
132
+ #
133
+ # @param tenant [Hash, Object, nil] Shared tenant for all calls
134
+ # @param request_id [String, nil] Shared request ID (auto-generated if nil)
135
+ # @param tags [Hash] Tags merged into each execution's metadata
136
+ # @param defaults [Hash] Additional shared options for agents
137
+ # @yield Block containing agent calls to track
138
+ # @return [TrackReport] Aggregated report of all calls
139
+ #
140
+ # @example Basic usage
141
+ # report = RubyLLM::Agents.track do
142
+ # ChatAgent.call(query: "hello")
143
+ # SummaryAgent.call(text: "...")
144
+ # end
145
+ # report.total_cost # => 0.015
146
+ #
147
+ # @example With shared tenant
148
+ # report = RubyLLM::Agents.track(tenant: current_user) do
149
+ # AgentA.call(query: "test")
150
+ # end
151
+ def track(tenant: nil, request_id: nil, tags: {}, **defaults)
152
+ defaults[:tenant] = tenant if tenant
153
+ tracker = Tracker.new(defaults: defaults, request_id: request_id, tags: tags)
154
+
155
+ # Stack trackers for nesting support
156
+ previous_tracker = Thread.current[:ruby_llm_agents_tracker]
157
+ Thread.current[:ruby_llm_agents_tracker] = tracker
158
+
159
+ started_at = Time.current
160
+ value = nil
161
+ error = nil
162
+
163
+ begin
164
+ value = yield
165
+ rescue => e
166
+ error = e
167
+ end
168
+
169
+ completed_at = Time.current
170
+
171
+ report = TrackReport.new(
172
+ value: value,
173
+ error: error,
174
+ results: tracker.results,
175
+ request_id: tracker.request_id,
176
+ started_at: started_at,
177
+ completed_at: completed_at
178
+ )
179
+
180
+ # Bubble results up to parent tracker if nested
181
+ if previous_tracker
182
+ tracker.results.each { |r| previous_tracker << r }
183
+ end
184
+
185
+ report
186
+ ensure
187
+ Thread.current[:ruby_llm_agents_tracker] = previous_tracker
188
+ end
189
+
122
190
  # Returns the global configuration instance
123
191
  #
124
192
  # @return [Configuration] The configuration object
@@ -148,6 +216,146 @@ module RubyLLM
148
216
  @configuration = Configuration.new
149
217
  end
150
218
 
219
+ # ============================================================
220
+ # Convenience Query API
221
+ # ============================================================
222
+ # These methods provide quick access to execution data without
223
+ # needing to reference model classes directly.
224
+
225
+ # Returns a chainable scope of all executions.
226
+ #
227
+ # @return [ActiveRecord::Relation] All executions
228
+ #
229
+ # @example Recent successful executions
230
+ # RubyLLM::Agents.executions.successful.recent(10)
231
+ #
232
+ # @example Filter by agent
233
+ # RubyLLM::Agents.executions.by_agent("ChatAgent").today
234
+ def executions
235
+ Execution.all
236
+ end
237
+
238
+ # Returns a usage summary for a given period.
239
+ #
240
+ # @param period [Symbol, Range] :today, :yesterday, :this_week, :this_month,
241
+ # :last_7_days, :last_30_days, or a custom Time range
242
+ # @param agent [String, Class, nil] Optional agent class or name to filter by
243
+ # @param tenant [String, Object, nil] Optional tenant ID or object to filter by
244
+ # @return [Hash] Usage summary with keys:
245
+ # :executions, :successful, :failed, :success_rate,
246
+ # :total_cost, :total_tokens, :avg_duration_ms, :avg_cost
247
+ #
248
+ # @example Global usage today
249
+ # RubyLLM::Agents.usage(period: :today)
250
+ #
251
+ # @example Per-agent usage
252
+ # RubyLLM::Agents.usage(period: :this_month, agent: "ChatAgent")
253
+ #
254
+ # @example Per-tenant usage
255
+ # RubyLLM::Agents.usage(period: :this_week, tenant: current_user)
256
+ def usage(period: :today, agent: nil, tenant: nil)
257
+ scope = scope_for_period(Execution, period)
258
+ scope = scope.by_agent(agent_name_for(agent)) if agent
259
+ scope = scope_for_tenant(scope, tenant) if tenant
260
+
261
+ total = scope.count
262
+ successful = scope.successful.count
263
+
264
+ {
265
+ executions: total,
266
+ successful: successful,
267
+ failed: scope.failed.count,
268
+ success_rate: total.zero? ? 0.0 : (successful.to_f / total * 100).round(1),
269
+ total_cost: scope.sum(:total_cost),
270
+ total_tokens: scope.sum(:total_tokens),
271
+ avg_duration_ms: scope.average(:duration_ms)&.round,
272
+ avg_cost: total.zero? ? 0 : (scope.sum(:total_cost).to_f / total).round(6)
273
+ }
274
+ end
275
+
276
+ # Returns cost breakdown by agent for a period.
277
+ #
278
+ # @param period [Symbol, Range] Time period (see #usage)
279
+ # @param tenant [String, Object, nil] Optional tenant filter
280
+ # @return [Hash{String => Hash}] Agent name => { cost:, count:, avg_cost: }
281
+ #
282
+ # @example
283
+ # RubyLLM::Agents.costs(period: :this_month)
284
+ # # => { "ChatAgent" => { cost: 12.50, count: 1000, avg_cost: 0.0125 } }
285
+ def costs(period: :today, tenant: nil)
286
+ scope = scope_for_period(Execution, period)
287
+ scope = scope_for_tenant(scope, tenant) if tenant
288
+
289
+ scope.group(:agent_type).pluck(
290
+ :agent_type,
291
+ Arel.sql("COUNT(*)"),
292
+ Arel.sql("SUM(total_cost)"),
293
+ Arel.sql("AVG(total_cost)")
294
+ ).each_with_object({}) do |(agent, count, total, avg), hash|
295
+ hash[agent] = {
296
+ cost: total&.to_f&.round(6) || 0,
297
+ count: count,
298
+ avg_cost: avg&.to_f&.round(6) || 0
299
+ }
300
+ end
301
+ end
302
+
303
+ # Returns all registered agents with their stats.
304
+ #
305
+ # @return [Array<Hash>] Agent info with name, model, stats, etc.
306
+ #
307
+ # @example
308
+ # RubyLLM::Agents.agents
309
+ # # => [{ name: "ChatAgent", active: true, model: "gpt-4o", ... }]
310
+ def agents
311
+ AgentRegistry.all_with_details
312
+ end
313
+
314
+ # Returns a tenant's usage data.
315
+ #
316
+ # @param tenant [String, Object] Tenant ID or object with llm_tenant_id
317
+ # @return [RubyLLM::Agents::Tenant, nil] The tenant record
318
+ #
319
+ # @example
320
+ # tenant = RubyLLM::Agents.tenant_for(current_user)
321
+ # tenant.cost_today # => 0.42
322
+ # tenant.budget_status # => { enabled: true, enforcement: :soft, ... }
323
+ def tenant_for(tenant)
324
+ Tenant.for(tenant)
325
+ rescue
326
+ nil
327
+ end
328
+
329
+ private
330
+
331
+ def scope_for_period(model, period)
332
+ case period
333
+ when :today then model.today
334
+ when :yesterday then model.yesterday
335
+ when :this_week then model.this_week
336
+ when :this_month then model.this_month
337
+ when :last_7_days then model.last_n_days(7)
338
+ when :last_30_days then model.last_n_days(30)
339
+ when Range then model.where(created_at: period)
340
+ else model.all
341
+ end
342
+ end
343
+
344
+ def scope_for_tenant(scope, tenant)
345
+ tenant_id = case tenant
346
+ when String then tenant
347
+ else
348
+ tenant.try(:llm_tenant_id) || tenant.try(:tenant_id) || tenant.try(:id)&.to_s
349
+ end
350
+ scope.by_tenant(tenant_id)
351
+ end
352
+
353
+ def agent_name_for(agent)
354
+ agent.is_a?(String) ? agent : agent.name
355
+ end
356
+
357
+ public
358
+
151
359
  # Renames an agent in the database, updating execution records and
152
360
  # tenant budget configuration keys
153
361
  #
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  namespace :ruby_llm_agents do
4
+ desc "Validate your RubyLLM::Agents setup (API keys, migrations, routes, jobs)"
5
+ task doctor: :environment do
6
+ require "generators/ruby_llm_agents/doctor_generator"
7
+ RubyLlmAgents::DoctorGenerator.start([])
8
+ end
9
+
4
10
  desc "Rename an agent type in execution records. Usage: rake ruby_llm_agents:rename_agent FROM=OldName TO=NewName [DRY_RUN=1]"
5
11
  task rename_agent: :environment do
6
12
  from = ENV["FROM"]
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: 3.8.0
4
+ version: 3.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - adham90
@@ -82,6 +82,7 @@ files:
82
82
  - app/controllers/ruby_llm/agents/agents_controller.rb
83
83
  - app/controllers/ruby_llm/agents/dashboard_controller.rb
84
84
  - app/controllers/ruby_llm/agents/executions_controller.rb
85
+ - app/controllers/ruby_llm/agents/requests_controller.rb
85
86
  - app/controllers/ruby_llm/agents/system_config_controller.rb
86
87
  - app/controllers/ruby_llm/agents/tenants_controller.rb
87
88
  - app/helpers/ruby_llm/agents/application_helper.rb
@@ -120,6 +121,8 @@ files:
120
121
  - app/views/ruby_llm/agents/executions/_list.html.erb
121
122
  - app/views/ruby_llm/agents/executions/index.html.erb
122
123
  - app/views/ruby_llm/agents/executions/show.html.erb
124
+ - app/views/ruby_llm/agents/requests/index.html.erb
125
+ - app/views/ruby_llm/agents/requests/show.html.erb
123
126
  - app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb
124
127
  - app/views/ruby_llm/agents/shared/_doc_link.html.erb
125
128
  - app/views/ruby_llm/agents/shared/_executions_table.html.erb
@@ -137,6 +140,8 @@ files:
137
140
  - config/routes.rb
138
141
  - lib/generators/ruby_llm_agents/agent_generator.rb
139
142
  - lib/generators/ruby_llm_agents/background_remover_generator.rb
143
+ - lib/generators/ruby_llm_agents/demo_generator.rb
144
+ - lib/generators/ruby_llm_agents/doctor_generator.rb
140
145
  - lib/generators/ruby_llm_agents/embedder_generator.rb
141
146
  - lib/generators/ruby_llm_agents/image_analyzer_generator.rb
142
147
  - lib/generators/ruby_llm_agents/image_editor_generator.rb
@@ -315,11 +320,14 @@ files:
315
320
  - lib/ruby_llm/agents/results/image_upscale_result.rb
316
321
  - lib/ruby_llm/agents/results/image_variation_result.rb
317
322
  - lib/ruby_llm/agents/results/speech_result.rb
323
+ - lib/ruby_llm/agents/results/trackable.rb
318
324
  - lib/ruby_llm/agents/results/transcription_result.rb
319
325
  - lib/ruby_llm/agents/routing.rb
320
326
  - lib/ruby_llm/agents/routing/class_methods.rb
321
327
  - lib/ruby_llm/agents/routing/result.rb
322
328
  - lib/ruby_llm/agents/text/embedder.rb
329
+ - lib/ruby_llm/agents/track_report.rb
330
+ - lib/ruby_llm/agents/tracker.rb
323
331
  - lib/tasks/ruby_llm_agents.rake
324
332
  homepage: https://github.com/adham90/ruby_llm-agents
325
333
  licenses:
@@ -342,7 +350,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
342
350
  - !ruby/object:Gem::Version
343
351
  version: '0'
344
352
  requirements: []
345
- rubygems_version: 4.0.3
353
+ rubygems_version: 4.0.7
346
354
  specification_version: 4
347
355
  summary: Agent framework for building LLM-powered agents with RubyLLM
348
356
  test_files: []