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