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.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/app/controllers/ruby_llm/agents/agents_controller.rb +16 -14
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +20 -20
- data/app/controllers/ruby_llm/agents/executions_controller.rb +5 -7
- data/app/helpers/ruby_llm/agents/application_helper.rb +57 -58
- data/app/models/ruby_llm/agents/execution/analytics.rb +27 -27
- data/app/models/ruby_llm/agents/execution/scopes.rb +4 -6
- data/app/models/ruby_llm/agents/execution.rb +26 -26
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +16 -10
- data/app/models/ruby_llm/agents/tenant/resettable.rb +12 -12
- data/app/models/ruby_llm/agents/tenant/trackable.rb +7 -7
- data/app/services/ruby_llm/agents/agent_registry.rb +6 -6
- data/app/views/layouts/ruby_llm/agents/application.html.erb +142 -11
- data/app/views/ruby_llm/agents/agents/show.html.erb +10 -10
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +10 -10
- data/app/views/ruby_llm/agents/executions/show.html.erb +13 -0
- data/lib/generators/ruby_llm_agents/agent_generator.rb +4 -4
- data/lib/generators/ruby_llm_agents/background_remover_generator.rb +6 -6
- data/lib/generators/ruby_llm_agents/embedder_generator.rb +4 -4
- data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +7 -7
- data/lib/generators/ruby_llm_agents/image_editor_generator.rb +4 -4
- data/lib/generators/ruby_llm_agents/image_generator_generator.rb +6 -6
- data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +9 -9
- data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +6 -6
- data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +4 -4
- data/lib/generators/ruby_llm_agents/image_variator_generator.rb +4 -4
- data/lib/generators/ruby_llm_agents/install_generator.rb +3 -3
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +4 -4
- data/lib/generators/ruby_llm_agents/multi_tenancy_generator.rb +2 -2
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +13 -13
- data/lib/generators/ruby_llm_agents/speaker_generator.rb +6 -6
- data/lib/generators/ruby_llm_agents/templates/add_assistant_prompt_migration.rb.tt +9 -0
- data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +2 -1
- data/lib/generators/ruby_llm_agents/transcriber_generator.rb +4 -4
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +22 -3
- data/lib/ruby_llm/agents/audio/speaker.rb +40 -31
- data/lib/ruby_llm/agents/audio/speech_client.rb +328 -0
- data/lib/ruby_llm/agents/audio/speech_pricing.rb +273 -0
- data/lib/ruby_llm/agents/audio/transcriber.rb +33 -33
- data/lib/ruby_llm/agents/base_agent.rb +16 -15
- data/lib/ruby_llm/agents/core/base/callbacks.rb +3 -3
- data/lib/ruby_llm/agents/core/configuration.rb +86 -73
- data/lib/ruby_llm/agents/core/errors.rb +27 -2
- data/lib/ruby_llm/agents/core/instrumentation.rb +101 -65
- data/lib/ruby_llm/agents/core/llm_tenant.rb +7 -7
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +3 -3
- data/lib/ruby_llm/agents/dsl/reliability.rb +9 -9
- data/lib/ruby_llm/agents/image/analyzer/dsl.rb +1 -1
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +4 -4
- data/lib/ruby_llm/agents/image/background_remover/dsl.rb +1 -1
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +3 -3
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +8 -8
- data/lib/ruby_llm/agents/image/editor/execution.rb +1 -1
- data/lib/ruby_llm/agents/image/generator/pricing.rb +9 -10
- data/lib/ruby_llm/agents/image/generator.rb +6 -6
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +6 -6
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +9 -9
- data/lib/ruby_llm/agents/image/pipeline.rb +1 -1
- data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -1
- data/lib/ruby_llm/agents/image/upscaler/dsl.rb +1 -1
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +3 -5
- data/lib/ruby_llm/agents/image/variator/execution.rb +1 -1
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +4 -4
- data/lib/ruby_llm/agents/infrastructure/attempt_tracker.rb +4 -4
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +9 -9
- data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +3 -3
- data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +1 -1
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +17 -17
- data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +1 -0
- data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +1 -1
- data/lib/ruby_llm/agents/infrastructure/reliability.rb +6 -6
- data/lib/ruby_llm/agents/pipeline/builder.rb +11 -11
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +3 -3
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +4 -4
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +62 -21
- data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +2 -3
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +82 -4
- data/lib/ruby_llm/agents/results/background_removal_result.rb +6 -6
- data/lib/ruby_llm/agents/results/embedding_result.rb +15 -15
- data/lib/ruby_llm/agents/results/image_analysis_result.rb +7 -7
- data/lib/ruby_llm/agents/results/image_edit_result.rb +4 -4
- data/lib/ruby_llm/agents/results/image_generation_result.rb +5 -5
- data/lib/ruby_llm/agents/results/image_pipeline_result.rb +4 -4
- data/lib/ruby_llm/agents/results/image_transform_result.rb +4 -4
- data/lib/ruby_llm/agents/results/image_upscale_result.rb +5 -5
- data/lib/ruby_llm/agents/results/image_variation_result.rb +4 -4
- data/lib/ruby_llm/agents/results/transcription_result.rb +1 -1
- data/lib/ruby_llm/agents/text/embedder.rb +13 -13
- 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
|
|
55
|
+
rescue
|
|
56
56
|
# Let ensure block handle via mark_execution_failed!
|
|
57
57
|
end
|
|
58
|
-
rescue
|
|
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
|
|
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
|
|
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
|
|
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 = {
|
|
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
|
|
169
|
+
rescue
|
|
170
170
|
# Non-critical
|
|
171
171
|
end
|
|
172
|
-
rescue
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
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 = {
|
|
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 =
|
|
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
|
|
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 = {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
82
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
239
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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
|