ruby_llm-agents 3.0.0 → 3.2.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/controllers/ruby_llm/agents/agents_controller.rb +16 -14
  4. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +20 -20
  5. data/app/controllers/ruby_llm/agents/executions_controller.rb +5 -7
  6. data/app/helpers/ruby_llm/agents/application_helper.rb +57 -58
  7. data/app/models/ruby_llm/agents/execution/analytics.rb +27 -27
  8. data/app/models/ruby_llm/agents/execution/scopes.rb +4 -6
  9. data/app/models/ruby_llm/agents/execution.rb +26 -26
  10. data/app/models/ruby_llm/agents/tenant/budgetable.rb +16 -10
  11. data/app/models/ruby_llm/agents/tenant/resettable.rb +12 -12
  12. data/app/models/ruby_llm/agents/tenant/trackable.rb +7 -7
  13. data/app/services/ruby_llm/agents/agent_registry.rb +6 -6
  14. data/app/views/layouts/ruby_llm/agents/application.html.erb +142 -11
  15. data/app/views/ruby_llm/agents/agents/show.html.erb +10 -10
  16. data/app/views/ruby_llm/agents/dashboard/index.html.erb +10 -10
  17. data/app/views/ruby_llm/agents/executions/show.html.erb +13 -0
  18. data/lib/generators/ruby_llm_agents/agent_generator.rb +4 -4
  19. data/lib/generators/ruby_llm_agents/background_remover_generator.rb +6 -6
  20. data/lib/generators/ruby_llm_agents/embedder_generator.rb +4 -4
  21. data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +7 -7
  22. data/lib/generators/ruby_llm_agents/image_editor_generator.rb +4 -4
  23. data/lib/generators/ruby_llm_agents/image_generator_generator.rb +6 -6
  24. data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +9 -9
  25. data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +6 -6
  26. data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +4 -4
  27. data/lib/generators/ruby_llm_agents/image_variator_generator.rb +4 -4
  28. data/lib/generators/ruby_llm_agents/install_generator.rb +3 -3
  29. data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +4 -4
  30. data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +2 -2
  31. data/lib/generators/ruby_llm_agents/restructure_generator.rb +13 -13
  32. data/lib/generators/ruby_llm_agents/speaker_generator.rb +6 -6
  33. data/lib/generators/ruby_llm_agents/templates/add_assistant_prompt_migration.rb.tt +9 -0
  34. data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +2 -1
  35. data/lib/generators/ruby_llm_agents/transcriber_generator.rb +4 -4
  36. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +22 -3
  37. data/lib/ruby_llm/agents/audio/speaker.rb +40 -31
  38. data/lib/ruby_llm/agents/audio/speech_client.rb +328 -0
  39. data/lib/ruby_llm/agents/audio/speech_pricing.rb +273 -0
  40. data/lib/ruby_llm/agents/audio/transcriber.rb +33 -33
  41. data/lib/ruby_llm/agents/base_agent.rb +16 -15
  42. data/lib/ruby_llm/agents/core/base/callbacks.rb +3 -3
  43. data/lib/ruby_llm/agents/core/configuration.rb +86 -73
  44. data/lib/ruby_llm/agents/core/errors.rb +27 -2
  45. data/lib/ruby_llm/agents/core/instrumentation.rb +101 -65
  46. data/lib/ruby_llm/agents/core/llm_tenant.rb +7 -7
  47. data/lib/ruby_llm/agents/core/version.rb +1 -1
  48. data/lib/ruby_llm/agents/dsl/base.rb +3 -3
  49. data/lib/ruby_llm/agents/dsl/reliability.rb +9 -9
  50. data/lib/ruby_llm/agents/image/analyzer/dsl.rb +1 -1
  51. data/lib/ruby_llm/agents/image/analyzer/execution.rb +4 -4
  52. data/lib/ruby_llm/agents/image/background_remover/dsl.rb +1 -1
  53. data/lib/ruby_llm/agents/image/background_remover/execution.rb +3 -3
  54. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +8 -8
  55. data/lib/ruby_llm/agents/image/editor/execution.rb +1 -1
  56. data/lib/ruby_llm/agents/image/generator/pricing.rb +9 -10
  57. data/lib/ruby_llm/agents/image/generator.rb +6 -6
  58. data/lib/ruby_llm/agents/image/pipeline/dsl.rb +6 -6
  59. data/lib/ruby_llm/agents/image/pipeline/execution.rb +9 -9
  60. data/lib/ruby_llm/agents/image/pipeline.rb +1 -1
  61. data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -1
  62. data/lib/ruby_llm/agents/image/upscaler/dsl.rb +1 -1
  63. data/lib/ruby_llm/agents/image/upscaler/execution.rb +3 -5
  64. data/lib/ruby_llm/agents/image/variator/execution.rb +1 -1
  65. data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +4 -4
  66. data/lib/ruby_llm/agents/infrastructure/attempt_tracker.rb +4 -4
  67. data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +9 -9
  68. data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +3 -3
  69. data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +1 -1
  70. data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +17 -17
  71. data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +1 -0
  72. data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +1 -1
  73. data/lib/ruby_llm/agents/infrastructure/reliability.rb +6 -6
  74. data/lib/ruby_llm/agents/pipeline/builder.rb +11 -11
  75. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +3 -3
  76. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +4 -4
  77. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +62 -21
  78. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +2 -3
  79. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +82 -4
  80. data/lib/ruby_llm/agents/results/background_removal_result.rb +6 -6
  81. data/lib/ruby_llm/agents/results/embedding_result.rb +15 -15
  82. data/lib/ruby_llm/agents/results/image_analysis_result.rb +7 -7
  83. data/lib/ruby_llm/agents/results/image_edit_result.rb +4 -4
  84. data/lib/ruby_llm/agents/results/image_generation_result.rb +5 -5
  85. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +4 -4
  86. data/lib/ruby_llm/agents/results/image_transform_result.rb +4 -4
  87. data/lib/ruby_llm/agents/results/image_upscale_result.rb +5 -5
  88. data/lib/ruby_llm/agents/results/image_variation_result.rb +4 -4
  89. data/lib/ruby_llm/agents/results/transcription_result.rb +1 -1
  90. data/lib/ruby_llm/agents/text/embedder.rb +13 -13
  91. metadata +4 -1
@@ -52,10 +52,10 @@ module RubyLLM
52
52
  begin
53
53
  complete_execution(execution, context, status: "success")
54
54
  status_update_completed = true
55
- rescue StandardError
55
+ rescue
56
56
  # Let ensure block handle via mark_execution_failed!
57
57
  end
58
- rescue StandardError => e
58
+ rescue => e
59
59
  context.completed_at = Time.current
60
60
  context.error = e
61
61
  raised_exception = e
@@ -63,7 +63,7 @@ module RubyLLM
63
63
  begin
64
64
  complete_execution(execution, context, status: determine_error_status(e))
65
65
  status_update_completed = true
66
- rescue StandardError
66
+ rescue
67
67
  # Let ensure block handle via mark_execution_failed!
68
68
  end
69
69
 
@@ -100,7 +100,7 @@ module RubyLLM
100
100
  end
101
101
 
102
102
  execution
103
- rescue StandardError => e
103
+ rescue => e
104
104
  error("Failed to create running execution record: #{e.message}")
105
105
  nil
106
106
  end
@@ -131,7 +131,7 @@ module RubyLLM
131
131
 
132
132
  # Save detail data (prompts, responses, tool calls, etc.)
133
133
  save_execution_details(execution, context, status)
134
- rescue StandardError => e
134
+ rescue => e
135
135
  error("Failed to complete execution record: #{e.message}")
136
136
  raise # Re-raise for ensure block to handle via mark_execution_failed!
137
137
  end
@@ -160,16 +160,16 @@ module RubyLLM
160
160
 
161
161
  # Store error_message in detail table (best-effort)
162
162
  begin
163
- detail_attrs = { error_message: error_message }
163
+ detail_attrs = {error_message: error_message}
164
164
  if execution.detail
165
165
  execution.detail.update_columns(detail_attrs)
166
166
  else
167
167
  RubyLLM::Agents::ExecutionDetail.create!(detail_attrs.merge(execution_id: execution.id))
168
168
  end
169
- rescue StandardError
169
+ rescue
170
170
  # Non-critical
171
171
  end
172
- rescue StandardError => e
172
+ rescue => e
173
173
  error("CRITICAL: Failed emergency status update for execution #{execution&.id}: #{e.message}")
174
174
  end
175
175
 
@@ -223,7 +223,11 @@ module RubyLLM
223
223
  }
224
224
 
225
225
  # Store niche cache key in metadata
226
- merged_metadata = context.metadata.dup rescue {}
226
+ merged_metadata = begin
227
+ context.metadata.dup
228
+ rescue
229
+ {}
230
+ end
227
231
  if context.cached? && context[:cache_key]
228
232
  merged_metadata["response_cache_key"] = context[:cache_key]
229
233
  end
@@ -253,6 +257,13 @@ module RubyLLM
253
257
 
254
258
  detail_data = {}
255
259
 
260
+ if global_config.persist_prompts
261
+ exec_opts = context.options[:options] || {}
262
+ detail_data[:system_prompt] = exec_opts[:system_prompt]
263
+ detail_data[:user_prompt] = context.input.to_s.presence
264
+ detail_data[:assistant_prompt] = exec_opts[:assistant_prefill] if assistant_prompt_column_exists?
265
+ end
266
+
256
267
  if context.error
257
268
  detail_data[:error_message] = truncate_error_message(context.error.message)
258
269
  end
@@ -277,7 +288,7 @@ module RubyLLM
277
288
  else
278
289
  execution.create_detail!(detail_data)
279
290
  end
280
- rescue StandardError => e
291
+ rescue => e
281
292
  error("Failed to save execution details: #{e.message}")
282
293
  end
283
294
 
@@ -297,7 +308,7 @@ module RubyLLM
297
308
  else
298
309
  create_execution_record(data)
299
310
  end
300
- rescue StandardError => e
311
+ rescue => e
301
312
  error("Failed to record execution: #{e.message}")
302
313
  end
303
314
 
@@ -307,7 +318,11 @@ module RubyLLM
307
318
  # @param status [String] "success" or "error"
308
319
  # @return [Hash] Execution data
309
320
  def build_execution_data(context, status)
310
- merged_metadata = context.metadata.dup rescue {}
321
+ merged_metadata = begin
322
+ context.metadata.dup
323
+ rescue
324
+ {}
325
+ end
311
326
  if context.cached? && context[:cache_key]
312
327
  merged_metadata["response_cache_key"] = context[:cache_key]
313
328
  end
@@ -348,7 +363,13 @@ module RubyLLM
348
363
  end
349
364
 
350
365
  # Store detail data for separate creation
351
- detail_data = { parameters: sanitize_parameters(context) }
366
+ detail_data = {parameters: sanitize_parameters(context)}
367
+ if global_config.persist_prompts
368
+ exec_opts = context.options[:options] || {}
369
+ detail_data[:system_prompt] = exec_opts[:system_prompt]
370
+ detail_data[:user_prompt] = context.input.to_s.presence
371
+ detail_data[:assistant_prompt] = exec_opts[:assistant_prefill] if assistant_prompt_column_exists?
372
+ end
352
373
  detail_data[:error_message] = truncate_error_message(context.error.message) if context.error
353
374
  detail_data[:tool_calls] = context[:tool_calls] if context[:tool_calls].present?
354
375
  detail_data[:attempts] = context[:reliability_attempts] if context[:reliability_attempts].present?
@@ -383,7 +404,11 @@ module RubyLLM
383
404
  def sanitize_parameters(context)
384
405
  return {} unless context.agent_instance.respond_to?(:options, true)
385
406
 
386
- params = context.agent_instance.send(:options) rescue {}
407
+ params = begin
408
+ context.agent_instance.send(:options)
409
+ rescue
410
+ {}
411
+ end
387
412
  params = params.dup
388
413
  params.transform_keys!(&:to_s)
389
414
 
@@ -408,7 +433,7 @@ module RubyLLM
408
433
  return "" if message.nil?
409
434
 
410
435
  message.to_s.truncate(1000)
411
- rescue StandardError
436
+ rescue
412
437
  message.to_s[0, 1000]
413
438
  end
414
439
 
@@ -423,7 +448,7 @@ module RubyLLM
423
448
  return nil if content.nil?
424
449
 
425
450
  # Build response hash similar to core instrumentation
426
- response_data = { content: content }
451
+ response_data = {content: content}
427
452
 
428
453
  # Add model_id if available
429
454
  response_data[:model_id] = context.model_used if context.model_used
@@ -433,7 +458,7 @@ module RubyLLM
433
458
  response_data[:output_tokens] = context.output_tokens if context.output_tokens
434
459
 
435
460
  response_data
436
- rescue StandardError => e
461
+ rescue => e
437
462
  error("Failed to serialize response: #{e.message}")
438
463
  nil
439
464
  end
@@ -451,7 +476,7 @@ module RubyLLM
451
476
  def create_execution_record(data)
452
477
  detail_data = data.delete(:_detail_data)
453
478
  execution = Execution.create!(data)
454
- if detail_data && detail_data.values.any? { |v| v.present? && v != {} && v != [] }
479
+ if detail_data&.values&.any? { |v| v.present? && v != {} && v != [] }
455
480
  execution.create_detail!(detail_data)
456
481
  end
457
482
  execution
@@ -474,7 +499,7 @@ module RubyLLM
474
499
  else
475
500
  cfg.track_executions
476
501
  end
477
- rescue StandardError
502
+ rescue
478
503
  false
479
504
  end
480
505
 
@@ -483,7 +508,7 @@ module RubyLLM
483
508
  # @return [Boolean]
484
509
  def track_cache_hits?
485
510
  global_config.respond_to?(:track_cache_hits) && global_config.track_cache_hits
486
- rescue StandardError
511
+ rescue
487
512
  false
488
513
  end
489
514
 
@@ -492,10 +517,26 @@ module RubyLLM
492
517
  # @return [Boolean]
493
518
  def async_logging?
494
519
  global_config.async_logging && defined?(Infrastructure::ExecutionLoggerJob)
495
- rescue StandardError
520
+ rescue
496
521
  false
497
522
  end
498
523
 
524
+ # Checks if the assistant_prompt column exists on execution_details
525
+ #
526
+ # Memoized to avoid repeated schema queries.
527
+ #
528
+ # @return [Boolean]
529
+ def assistant_prompt_column_exists?
530
+ return @_assistant_prompt_column_exists if defined?(@_assistant_prompt_column_exists)
531
+
532
+ @_assistant_prompt_column_exists = begin
533
+ defined?(RubyLLM::Agents::ExecutionDetail) &&
534
+ RubyLLM::Agents::ExecutionDetail.column_names.include?("assistant_prompt")
535
+ rescue
536
+ false
537
+ end
538
+ end
539
+
499
540
  # Returns whether the Execution model is available
500
541
  #
501
542
  # @return [Boolean]
@@ -172,8 +172,7 @@ module RubyLLM
172
172
  tracker.complete_attempt(attempt, success: true, response: context.output)
173
173
 
174
174
  return context
175
-
176
- rescue StandardError => e
175
+ rescue => e
177
176
  context.error = e
178
177
  breaker&.record_failure!
179
178
  tracker.complete_attempt(attempt, success: false, error: e)
@@ -305,7 +304,7 @@ module RubyLLM
305
304
  else
306
305
  sleep(seconds)
307
306
  end
308
- rescue StandardError
307
+ rescue
309
308
  # Fall back to regular sleep if async detection fails
310
309
  sleep(seconds)
311
310
  end
@@ -42,6 +42,7 @@ module RubyLLM
42
42
  # @return [Context] The context with tenant fields populated
43
43
  def call(context)
44
44
  resolve_tenant!(context)
45
+ ensure_tenant_record!(context)
45
46
  apply_api_configuration!(context)
46
47
  @app.call(context)
47
48
  end
@@ -78,12 +79,89 @@ module RubyLLM
78
79
  context.tenant_config = extract_tenant_config(tenant_value)
79
80
  else
80
81
  raise ArgumentError,
81
- "tenant must respond to :llm_tenant_id (use llm_tenant DSL), " \
82
- "or be a Hash with :id key, got #{tenant_value.class}"
82
+ "tenant must respond to :llm_tenant_id (use llm_tenant DSL), " \
83
+ "or be a Hash with :id key, got #{tenant_value.class}"
83
84
  end
84
85
  end
85
86
  end
86
87
 
88
+ # Ensures a Tenant record exists in the database for the resolved tenant.
89
+ #
90
+ # When a host model (e.g., Organization) with LLMTenant is passed as
91
+ # tenant: to an agent, the after_create callback only fires for new records.
92
+ # Pre-existing records won't have a Tenant row yet. This method auto-creates
93
+ # it on first use so budget tracking and the dashboard work correctly.
94
+ #
95
+ # @param context [Context] The execution context
96
+ def ensure_tenant_record!(context)
97
+ return unless context.tenant_id.present?
98
+ return unless tenant_table_exists?
99
+
100
+ tenant_object = context.tenant_object
101
+
102
+ # Only auto-create when the tenant object uses the LLMTenant concern
103
+ if tenant_object.respond_to?(:llm_tenant_id) && tenant_object.is_a?(::ActiveRecord::Base)
104
+ ensure_tenant_for_model!(tenant_object)
105
+ else
106
+ # For hash-based or string tenants, ensure a minimal record exists
107
+ RubyLLM::Agents::Tenant.find_or_create_by!(tenant_id: context.tenant_id)
108
+ end
109
+ rescue => e
110
+ # Don't fail the execution if tenant record creation fails
111
+ log_tenant_warning("ensure tenant record", e)
112
+ end
113
+
114
+ # Creates a Tenant record linked to the host model if one doesn't exist
115
+ #
116
+ # @param tenant_object [ActiveRecord::Base] The host model with LLMTenant
117
+ def ensure_tenant_for_model!(tenant_object)
118
+ # Check polymorphic link first, then tenant_id
119
+ existing = RubyLLM::Agents::Tenant.find_by(tenant_record: tenant_object) ||
120
+ RubyLLM::Agents::Tenant.find_by(tenant_id: tenant_object.llm_tenant_id)
121
+ return if existing
122
+
123
+ options = tenant_object.class.try(:llm_tenant_options) || {}
124
+ limits = options[:limits] || {}
125
+ name_method = options[:name] || :to_s
126
+
127
+ RubyLLM::Agents::Tenant.create!(
128
+ tenant_id: tenant_object.llm_tenant_id,
129
+ name: tenant_object.send(name_method).to_s,
130
+ tenant_record: tenant_object,
131
+ daily_limit: limits[:daily_cost],
132
+ monthly_limit: limits[:monthly_cost],
133
+ daily_token_limit: limits[:daily_tokens],
134
+ monthly_token_limit: limits[:monthly_tokens],
135
+ daily_execution_limit: limits[:daily_executions],
136
+ monthly_execution_limit: limits[:monthly_executions],
137
+ enforcement: options[:enforcement]&.to_s || "soft",
138
+ inherit_global_defaults: options.fetch(:inherit_global, true)
139
+ )
140
+ end
141
+
142
+ # Checks if the tenants table exists (memoized)
143
+ #
144
+ # @return [Boolean]
145
+ def tenant_table_exists?
146
+ return @tenant_table_exists if defined?(@tenant_table_exists)
147
+
148
+ @tenant_table_exists = ::ActiveRecord::Base.connection.table_exists?(:ruby_llm_agents_tenants)
149
+ rescue
150
+ @tenant_table_exists = false
151
+ end
152
+
153
+ # Logs a warning without failing the execution
154
+ #
155
+ # @param action [String] What was being attempted
156
+ # @param error [StandardError] The error
157
+ def log_tenant_warning(action, error)
158
+ return unless defined?(Rails) && Rails.respond_to?(:logger)
159
+
160
+ Rails.logger.warn(
161
+ "[RubyLLM::Agents] Failed to #{action}: #{error.message}"
162
+ )
163
+ end
164
+
87
165
  # Applies API configuration to RubyLLM based on resolved tenant
88
166
  #
89
167
  # @param context [Context] The execution context
@@ -103,7 +181,7 @@ module RubyLLM
103
181
  return if api_keys.blank?
104
182
 
105
183
  apply_api_keys_to_ruby_llm(api_keys)
106
- rescue StandardError => e
184
+ rescue => e
107
185
  # Log but don't fail if API key extraction fails
108
186
  warn_api_key_error("tenant object", e)
109
187
  end
@@ -150,7 +228,7 @@ module RubyLLM
150
228
  return nil unless tenant.respond_to?(:llm_config)
151
229
 
152
230
  tenant.llm_config
153
- rescue StandardError
231
+ rescue
154
232
  nil
155
233
  end
156
234
  end
@@ -16,9 +16,9 @@ module RubyLLM
16
16
  #
17
17
  class BackgroundRemovalResult
18
18
  attr_reader :foreground, :mask, :source_image, :model_id, :output_format,
19
- :alpha_matting, :refine_edges,
20
- :started_at, :completed_at, :tenant_id, :remover_class,
21
- :error_class, :error_message
19
+ :alpha_matting, :refine_edges,
20
+ :started_at, :completed_at, :tenant_id, :remover_class,
21
+ :error_class, :error_message
22
22
 
23
23
  # Initialize a new result
24
24
  #
@@ -36,8 +36,8 @@ module RubyLLM
36
36
  # @param error_class [String, nil] Error class name if failed
37
37
  # @param error_message [String, nil] Error message if failed
38
38
  def initialize(foreground:, mask:, source_image:, model_id:, output_format:,
39
- alpha_matting:, refine_edges:, started_at:, completed_at:,
40
- tenant_id:, remover_class:, error_class: nil, error_message: nil)
39
+ alpha_matting:, refine_edges:, started_at:, completed_at:,
40
+ tenant_id:, remover_class:, error_class: nil, error_message: nil)
41
41
  @foreground = foreground
42
42
  @mask = mask
43
43
  @source_image = source_image
@@ -224,7 +224,7 @@ module RubyLLM
224
224
  # Lightweight result for cached removals
225
225
  class CachedBackgroundRemovalResult
226
226
  attr_reader :url, :data, :mask_url, :mask_data, :mime_type, :model_id,
227
- :output_format, :total_cost, :cached_at
227
+ :output_format, :total_cost, :cached_at
228
228
 
229
229
  def initialize(data)
230
230
  @url = data[:url]
@@ -156,13 +156,13 @@ module RubyLLM
156
156
  return nil if v1.nil?
157
157
 
158
158
  v2 = case other
159
- when EmbeddingResult
160
- other.vector
161
- when Array
162
- other
163
- else
164
- raise ArgumentError, "other must be EmbeddingResult or Array, got #{other.class}"
165
- end
159
+ when EmbeddingResult
160
+ other.vector
161
+ when Array
162
+ other
163
+ else
164
+ raise ArgumentError, "other must be EmbeddingResult or Array, got #{other.class}"
165
+ end
166
166
 
167
167
  return nil if v2.nil?
168
168
 
@@ -183,17 +183,17 @@ module RubyLLM
183
183
 
184
184
  similarities = others.each_with_index.map do |other, idx|
185
185
  v2 = case other
186
- when EmbeddingResult
187
- other.vector
188
- when Array
189
- other
190
- else
191
- next nil
192
- end
186
+ when EmbeddingResult
187
+ other.vector
188
+ when Array
189
+ other
190
+ else
191
+ next nil
192
+ end
193
193
 
194
194
  next nil if v2.nil?
195
195
 
196
- { index: idx, similarity: cosine_similarity(v1, v2) }
196
+ {index: idx, similarity: cosine_similarity(v1, v2)}
197
197
  end.compact
198
198
 
199
199
  similarities.sort_by { |s| -s[:similarity] }.first(limit)
@@ -18,9 +18,9 @@ module RubyLLM
18
18
  #
19
19
  class ImageAnalysisResult
20
20
  attr_reader :image, :model_id, :analysis_type,
21
- :caption, :description, :tags, :objects, :colors, :text,
22
- :raw_response, :started_at, :completed_at, :tenant_id, :analyzer_class,
23
- :error_class, :error_message
21
+ :caption, :description, :tags, :objects, :colors, :text,
22
+ :raw_response, :started_at, :completed_at, :tenant_id, :analyzer_class,
23
+ :error_class, :error_message
24
24
 
25
25
  # Initialize a new result
26
26
  #
@@ -41,8 +41,8 @@ module RubyLLM
41
41
  # @param error_class [String, nil] Error class name if failed
42
42
  # @param error_message [String, nil] Error message if failed
43
43
  def initialize(image:, model_id:, analysis_type:, caption:, description:, tags:,
44
- objects:, colors:, text:, raw_response:, started_at:, completed_at:,
45
- tenant_id:, analyzer_class:, error_class: nil, error_message: nil)
44
+ objects:, colors:, text:, raw_response:, started_at:, completed_at:,
45
+ tenant_id:, analyzer_class:, error_class: nil, error_message: nil)
46
46
  @image = image
47
47
  @model_id = model_id
48
48
  @analysis_type = analysis_type
@@ -235,8 +235,8 @@ module RubyLLM
235
235
  # Lightweight result for cached analyses
236
236
  class CachedImageAnalysisResult
237
237
  attr_reader :image, :model_id, :analysis_type,
238
- :caption, :description, :tags, :objects, :colors, :text,
239
- :total_cost, :cached_at
238
+ :caption, :description, :tags, :objects, :colors, :text,
239
+ :total_cost, :cached_at
240
240
 
241
241
  def initialize(data)
242
242
  @image = data[:image]
@@ -18,8 +18,8 @@ module RubyLLM
18
18
  #
19
19
  class ImageEditResult
20
20
  attr_reader :images, :source_image, :mask, :prompt, :model_id, :size,
21
- :started_at, :completed_at, :tenant_id, :editor_class,
22
- :error_class, :error_message
21
+ :started_at, :completed_at, :tenant_id, :editor_class,
22
+ :error_class, :error_message
23
23
 
24
24
  # Initialize a new result
25
25
  #
@@ -36,8 +36,8 @@ module RubyLLM
36
36
  # @param error_class [String, nil] Error class name if failed
37
37
  # @param error_message [String, nil] Error message if failed
38
38
  def initialize(images:, source_image:, mask:, prompt:, model_id:, size:,
39
- started_at:, completed_at:, tenant_id:, editor_class:,
40
- error_class: nil, error_message: nil)
39
+ started_at:, completed_at:, tenant_id:, editor_class:,
40
+ error_class: nil, error_message: nil)
41
41
  @images = images
42
42
  @source_image = source_image
43
43
  @mask = mask
@@ -21,8 +21,8 @@ module RubyLLM
21
21
  #
22
22
  class ImageGenerationResult
23
23
  attr_reader :images, :prompt, :model_id, :size, :quality, :style,
24
- :started_at, :completed_at, :tenant_id, :generator_class,
25
- :error_class, :error_message
24
+ :started_at, :completed_at, :tenant_id, :generator_class,
25
+ :error_class, :error_message
26
26
 
27
27
  # Initialize a new result
28
28
  #
@@ -39,8 +39,8 @@ module RubyLLM
39
39
  # @param error_class [String, nil] Error class name if failed
40
40
  # @param error_message [String, nil] Error message if failed
41
41
  def initialize(images:, prompt:, model_id:, size:, quality:, style:,
42
- started_at:, completed_at:, tenant_id:, generator_class:,
43
- error_class: nil, error_message: nil)
42
+ started_at:, completed_at:, tenant_id:, generator_class:,
43
+ error_class: nil, error_message: nil)
44
44
  @images = images
45
45
  @prompt = prompt
46
46
  @model_id = model_id
@@ -294,7 +294,7 @@ module RubyLLM
294
294
  #
295
295
  class CachedImageGenerationResult
296
296
  attr_reader :urls, :datas, :mime_type, :revised_prompts, :model_id,
297
- :total_cost, :cached_at
297
+ :total_cost, :cached_at
298
298
 
299
299
  def initialize(data)
300
300
  @urls = data[:urls] || []
@@ -22,7 +22,7 @@ module RubyLLM
22
22
  #
23
23
  class ImagePipelineResult
24
24
  attr_reader :step_results, :started_at, :completed_at, :tenant_id,
25
- :pipeline_class, :context, :error_class, :error_message
25
+ :pipeline_class, :context, :error_class, :error_message
26
26
 
27
27
  # Initialize a new pipeline result
28
28
  #
@@ -35,7 +35,7 @@ module RubyLLM
35
35
  # @param error_class [String, nil] Error class name if failed
36
36
  # @param error_message [String, nil] Error message if failed
37
37
  def initialize(step_results:, started_at:, completed_at:, tenant_id:,
38
- pipeline_class:, context:, error_class: nil, error_message: nil)
38
+ pipeline_class:, context:, error_class: nil, error_message: nil)
39
39
  @step_results = step_results
40
40
  @started_at = started_at
41
41
  @completed_at = completed_at
@@ -100,7 +100,7 @@ module RubyLLM
100
100
  step_data&.dig(:result)
101
101
  end
102
102
 
103
- alias [] step
103
+ alias_method :[], :step
104
104
 
105
105
  # Get step names
106
106
  #
@@ -378,7 +378,7 @@ module RubyLLM
378
378
  step_data&.dig(:cached_result)
379
379
  end
380
380
 
381
- alias [] step
381
+ alias_method :[], :step
382
382
 
383
383
  def final_image
384
384
  # Find last non-analyzer step
@@ -18,8 +18,8 @@ module RubyLLM
18
18
  #
19
19
  class ImageTransformResult
20
20
  attr_reader :images, :source_image, :prompt, :model_id, :size, :strength,
21
- :started_at, :completed_at, :tenant_id, :transformer_class,
22
- :error_class, :error_message
21
+ :started_at, :completed_at, :tenant_id, :transformer_class,
22
+ :error_class, :error_message
23
23
 
24
24
  # Initialize a new result
25
25
  #
@@ -36,8 +36,8 @@ module RubyLLM
36
36
  # @param error_class [String, nil] Error class name if failed
37
37
  # @param error_message [String, nil] Error message if failed
38
38
  def initialize(images:, source_image:, prompt:, model_id:, size:, strength:,
39
- started_at:, completed_at:, tenant_id:, transformer_class:,
40
- error_class: nil, error_message: nil)
39
+ started_at:, completed_at:, tenant_id:, transformer_class:,
40
+ error_class: nil, error_message: nil)
41
41
  @images = images
42
42
  @source_image = source_image
43
43
  @prompt = prompt
@@ -16,8 +16,8 @@ module RubyLLM
16
16
  #
17
17
  class ImageUpscaleResult
18
18
  attr_reader :image, :source_image, :model_id, :scale, :output_size, :face_enhance,
19
- :started_at, :completed_at, :tenant_id, :upscaler_class,
20
- :error_class, :error_message
19
+ :started_at, :completed_at, :tenant_id, :upscaler_class,
20
+ :error_class, :error_message
21
21
 
22
22
  # Initialize a new result
23
23
  #
@@ -34,8 +34,8 @@ module RubyLLM
34
34
  # @param error_class [String, nil] Error class name if failed
35
35
  # @param error_message [String, nil] Error message if failed
36
36
  def initialize(image:, source_image:, model_id:, scale:, output_size:, face_enhance:,
37
- started_at:, completed_at:, tenant_id:, upscaler_class:,
38
- error_class: nil, error_message: nil)
37
+ started_at:, completed_at:, tenant_id:, upscaler_class:,
38
+ error_class: nil, error_message: nil)
39
39
  @image = image
40
40
  @source_image = source_image
41
41
  @model_id = model_id
@@ -198,7 +198,7 @@ module RubyLLM
198
198
  # Lightweight result for cached upscales
199
199
  class CachedImageUpscaleResult
200
200
  attr_reader :url, :data, :mime_type, :model_id, :scale, :output_size,
201
- :total_cost, :cached_at
201
+ :total_cost, :cached_at
202
202
 
203
203
  def initialize(data)
204
204
  @url = data[:url]
@@ -15,8 +15,8 @@ module RubyLLM
15
15
  #
16
16
  class ImageVariationResult
17
17
  attr_reader :images, :source_image, :model_id, :size, :variation_strength,
18
- :started_at, :completed_at, :tenant_id, :variator_class,
19
- :error_class, :error_message
18
+ :started_at, :completed_at, :tenant_id, :variator_class,
19
+ :error_class, :error_message
20
20
 
21
21
  # Initialize a new result
22
22
  #
@@ -32,8 +32,8 @@ module RubyLLM
32
32
  # @param error_class [String, nil] Error class name if failed
33
33
  # @param error_message [String, nil] Error message if failed
34
34
  def initialize(images:, source_image:, model_id:, size:, variation_strength:,
35
- started_at:, completed_at:, tenant_id:, variator_class:,
36
- error_class: nil, error_message: nil)
35
+ started_at:, completed_at:, tenant_id:, variator_class:,
36
+ error_class: nil, error_message: nil)
37
37
  @images = images
38
38
  @source_image = source_image
39
39
  @model_id = model_id
@@ -322,7 +322,7 @@ module RubyLLM
322
322
  return nil unless segments.present?
323
323
 
324
324
  segments.find do |segment|
325
- timestamp >= segment[:start] && timestamp <= segment[:end]
325
+ timestamp.between?(segment[:start], segment[:end])
326
326
  end
327
327
  end
328
328