ruby_llm-agents 0.4.0 → 1.0.0.beta.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 +4 -4
- data/README.md +225 -34
- data/app/controllers/ruby_llm/agents/agents_controller.rb +136 -16
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +214 -0
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +29 -9
- data/app/controllers/ruby_llm/agents/{settings_controller.rb → system_config_controller.rb} +3 -3
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +109 -0
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +355 -0
- data/app/helpers/ruby_llm/agents/application_helper.rb +25 -0
- data/app/models/ruby_llm/agents/api_configuration.rb +386 -0
- data/app/models/ruby_llm/agents/execution.rb +3 -0
- data/app/models/ruby_llm/agents/tenant_budget.rb +112 -14
- data/app/services/ruby_llm/agents/agent_registry.rb +51 -12
- data/app/views/layouts/ruby_llm/agents/application.html.erb +5 -30
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +13 -1
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +235 -0
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +70 -0
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +152 -0
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +63 -0
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +108 -0
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +91 -0
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +1 -1
- data/app/views/ruby_llm/agents/agents/index.html.erb +74 -9
- data/app/views/ruby_llm/agents/agents/show.html.erb +18 -378
- data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +34 -0
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +288 -0
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +95 -0
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +97 -0
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +211 -0
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +179 -0
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +1 -1
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +269 -15
- data/app/views/ruby_llm/agents/executions/show.html.erb +98 -0
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +93 -0
- data/app/views/ruby_llm/agents/{settings → system_config}/show.html.erb +1 -1
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +150 -0
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +13 -0
- data/app/views/ruby_llm/agents/tenants/index.html.erb +129 -0
- data/app/views/ruby_llm/agents/tenants/show.html.erb +374 -0
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +236 -0
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +76 -0
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +74 -0
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +108 -0
- data/app/views/ruby_llm/agents/workflows/show.html.erb +442 -0
- data/config/routes.rb +13 -1
- data/lib/generators/ruby_llm_agents/agent_generator.rb +56 -7
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +100 -0
- data/lib/generators/ruby_llm_agents/background_remover_generator.rb +110 -0
- data/lib/generators/ruby_llm_agents/embedder_generator.rb +107 -0
- data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +115 -0
- data/lib/generators/ruby_llm_agents/image_editor_generator.rb +108 -0
- data/lib/generators/ruby_llm_agents/image_generator_generator.rb +116 -0
- data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +178 -0
- data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +109 -0
- data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +103 -0
- data/lib/generators/ruby_llm_agents/image_variator_generator.rb +102 -0
- data/lib/generators/ruby_llm_agents/install_generator.rb +76 -4
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +292 -0
- data/lib/generators/ruby_llm_agents/speaker_generator.rb +121 -0
- data/lib/generators/ruby_llm_agents/templates/add_execution_type_migration.rb.tt +8 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +99 -84
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +42 -40
- data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +26 -0
- data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +50 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +26 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +38 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +139 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +21 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +20 -0
- data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +49 -0
- data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +53 -0
- data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +44 -0
- data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +90 -0
- data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +41 -0
- data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +45 -0
- data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +35 -0
- data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +47 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +50 -0
- data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +44 -0
- data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +38 -0
- data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +33 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +228 -0
- data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +131 -0
- data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +255 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +102 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +282 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +228 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +110 -0
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +120 -0
- data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +212 -0
- data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +227 -0
- data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +251 -0
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +300 -0
- data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +56 -0
- data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +51 -0
- data/lib/generators/ruby_llm_agents/transcriber_generator.rb +107 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +152 -1
- data/lib/ruby_llm/agents/audio/speaker.rb +553 -0
- data/lib/ruby_llm/agents/audio/transcriber.rb +669 -0
- data/lib/ruby_llm/agents/base_agent.rb +675 -0
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +181 -0
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +274 -0
- data/lib/ruby_llm/agents/core/base.rb +135 -0
- data/lib/ruby_llm/agents/core/configuration.rb +981 -0
- data/lib/ruby_llm/agents/core/errors.rb +150 -0
- data/lib/ruby_llm/agents/{instrumentation.rb → core/instrumentation.rb} +93 -4
- data/lib/ruby_llm/agents/core/llm_tenant.rb +358 -0
- data/lib/ruby_llm/agents/core/resolved_config.rb +348 -0
- data/lib/ruby_llm/agents/{version.rb → core/version.rb} +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +110 -0
- data/lib/ruby_llm/agents/dsl/caching.rb +142 -0
- data/lib/ruby_llm/agents/dsl/reliability.rb +307 -0
- data/lib/ruby_llm/agents/dsl.rb +41 -0
- data/lib/ruby_llm/agents/image/analyzer/dsl.rb +130 -0
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +402 -0
- data/lib/ruby_llm/agents/image/analyzer.rb +90 -0
- data/lib/ruby_llm/agents/image/background_remover/dsl.rb +154 -0
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +240 -0
- data/lib/ruby_llm/agents/image/background_remover.rb +89 -0
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +91 -0
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +165 -0
- data/lib/ruby_llm/agents/image/editor/dsl.rb +56 -0
- data/lib/ruby_llm/agents/image/editor/execution.rb +207 -0
- data/lib/ruby_llm/agents/image/editor.rb +92 -0
- data/lib/ruby_llm/agents/image/generator/active_storage_support.rb +127 -0
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +95 -0
- data/lib/ruby_llm/agents/image/generator/pricing.rb +353 -0
- data/lib/ruby_llm/agents/image/generator/templates.rb +124 -0
- data/lib/ruby_llm/agents/image/generator.rb +455 -0
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +213 -0
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +382 -0
- data/lib/ruby_llm/agents/image/pipeline.rb +97 -0
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +148 -0
- data/lib/ruby_llm/agents/image/transformer/execution.rb +223 -0
- data/lib/ruby_llm/agents/image/transformer.rb +95 -0
- data/lib/ruby_llm/agents/image/upscaler/dsl.rb +83 -0
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +219 -0
- data/lib/ruby_llm/agents/image/upscaler.rb +81 -0
- data/lib/ruby_llm/agents/image/variator/dsl.rb +62 -0
- data/lib/ruby_llm/agents/image/variator/execution.rb +189 -0
- data/lib/ruby_llm/agents/image/variator.rb +80 -0
- data/lib/ruby_llm/agents/{alert_manager.rb → infrastructure/alert_manager.rb} +17 -22
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +145 -0
- data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +149 -0
- data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +68 -0
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +279 -0
- data/lib/ruby_llm/agents/infrastructure/budget_tracker.rb +275 -0
- data/lib/ruby_llm/agents/{execution_logger_job.rb → infrastructure/execution_logger_job.rb} +17 -1
- data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/executor.rb +2 -1
- data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/retry_strategy.rb +9 -3
- data/lib/ruby_llm/agents/{reliability.rb → infrastructure/reliability.rb} +11 -21
- data/lib/ruby_llm/agents/pipeline/builder.rb +215 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +255 -0
- data/lib/ruby_llm/agents/pipeline/executor.rb +86 -0
- data/lib/ruby_llm/agents/pipeline/middleware/base.rb +124 -0
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +95 -0
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +171 -0
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +415 -0
- data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +276 -0
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +196 -0
- data/lib/ruby_llm/agents/pipeline.rb +68 -0
- data/lib/ruby_llm/agents/{engine.rb → rails/engine.rb} +79 -10
- data/lib/ruby_llm/agents/results/background_removal_result.rb +286 -0
- data/lib/ruby_llm/agents/{result.rb → results/base.rb} +73 -1
- data/lib/ruby_llm/agents/results/embedding_result.rb +243 -0
- data/lib/ruby_llm/agents/results/image_analysis_result.rb +314 -0
- data/lib/ruby_llm/agents/results/image_edit_result.rb +250 -0
- data/lib/ruby_llm/agents/results/image_generation_result.rb +346 -0
- data/lib/ruby_llm/agents/results/image_pipeline_result.rb +399 -0
- data/lib/ruby_llm/agents/results/image_transform_result.rb +251 -0
- data/lib/ruby_llm/agents/results/image_upscale_result.rb +255 -0
- data/lib/ruby_llm/agents/results/image_variation_result.rb +237 -0
- data/lib/ruby_llm/agents/results/moderation_result.rb +158 -0
- data/lib/ruby_llm/agents/results/speech_result.rb +338 -0
- data/lib/ruby_llm/agents/results/transcription_result.rb +408 -0
- data/lib/ruby_llm/agents/text/embedder.rb +444 -0
- data/lib/ruby_llm/agents/text/moderator.rb +237 -0
- data/lib/ruby_llm/agents/workflow/async.rb +220 -0
- data/lib/ruby_llm/agents/workflow/async_executor.rb +156 -0
- data/lib/ruby_llm/agents/{workflow.rb → workflow/orchestrator.rb} +6 -5
- data/lib/ruby_llm/agents/workflow/parallel.rb +34 -17
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +185 -0
- data/lib/ruby_llm/agents.rb +86 -20
- metadata +189 -35
- data/lib/ruby_llm/agents/base/caching.rb +0 -40
- data/lib/ruby_llm/agents/base/cost_calculation.rb +0 -105
- data/lib/ruby_llm/agents/base/dsl.rb +0 -324
- data/lib/ruby_llm/agents/base/execution.rb +0 -283
- data/lib/ruby_llm/agents/base/reliability_dsl.rb +0 -82
- data/lib/ruby_llm/agents/base/reliability_execution.rb +0 -136
- data/lib/ruby_llm/agents/base/response_building.rb +0 -86
- data/lib/ruby_llm/agents/base/tool_tracking.rb +0 -57
- data/lib/ruby_llm/agents/base.rb +0 -209
- data/lib/ruby_llm/agents/budget_tracker.rb +0 -471
- data/lib/ruby_llm/agents/configuration.rb +0 -357
- /data/lib/ruby_llm/agents/{deprecations.rb → core/deprecations.rb} +0 -0
- /data/lib/ruby_llm/agents/{inflections.rb → core/inflections.rb} +0 -0
- /data/lib/ruby_llm/agents/{attempt_tracker.rb → infrastructure/attempt_tracker.rb} +0 -0
- /data/lib/ruby_llm/agents/{cache_helper.rb → infrastructure/cache_helper.rb} +0 -0
- /data/lib/ruby_llm/agents/{circuit_breaker.rb → infrastructure/circuit_breaker.rb} +0 -0
- /data/lib/ruby_llm/agents/{redactor.rb → infrastructure/redactor.rb} +0 -0
- /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/breaker_manager.rb +0 -0
- /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/execution_constraints.rb +0 -0
- /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/fallback_routing.rb +0 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
# Resolves API configuration with priority chain
|
|
6
|
+
#
|
|
7
|
+
# Resolution order:
|
|
8
|
+
# 1. Tenant-specific database config (if tenant_id provided)
|
|
9
|
+
# 2. Global database config
|
|
10
|
+
# 3. RubyLLM.configuration (set via initializer or environment)
|
|
11
|
+
#
|
|
12
|
+
# This class provides a unified interface for accessing configuration
|
|
13
|
+
# values regardless of their source, and can apply the resolved
|
|
14
|
+
# configuration to RubyLLM.
|
|
15
|
+
#
|
|
16
|
+
# @example Basic resolution
|
|
17
|
+
# resolved = ResolvedConfig.new(
|
|
18
|
+
# tenant_config: ApiConfiguration.for_tenant("acme"),
|
|
19
|
+
# global_config: ApiConfiguration.global,
|
|
20
|
+
# ruby_llm_config: RubyLLM.configuration
|
|
21
|
+
# )
|
|
22
|
+
#
|
|
23
|
+
# resolved.openai_api_key # => Returns from highest priority source
|
|
24
|
+
# resolved.source_for(:openai_api_key) # => "tenant:acme"
|
|
25
|
+
#
|
|
26
|
+
# @example Applying to RubyLLM
|
|
27
|
+
# resolved.apply_to_ruby_llm! # Applies all resolved values
|
|
28
|
+
#
|
|
29
|
+
# @see ApiConfiguration
|
|
30
|
+
# @api public
|
|
31
|
+
class ResolvedConfig
|
|
32
|
+
# Returns all resolvable attributes (API keys + settings)
|
|
33
|
+
# Lazy-loaded to avoid circular dependency with ApiConfiguration
|
|
34
|
+
#
|
|
35
|
+
# @return [Array<Symbol>]
|
|
36
|
+
def self.resolvable_attributes
|
|
37
|
+
@resolvable_attributes ||= (
|
|
38
|
+
ApiConfiguration::API_KEY_ATTRIBUTES +
|
|
39
|
+
ApiConfiguration::NON_KEY_ATTRIBUTES
|
|
40
|
+
).freeze
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [ApiConfiguration, nil] Tenant-specific configuration
|
|
44
|
+
attr_reader :tenant_config
|
|
45
|
+
|
|
46
|
+
# @return [ApiConfiguration, nil] Global database configuration
|
|
47
|
+
attr_reader :global_config
|
|
48
|
+
|
|
49
|
+
# @return [Object] RubyLLM configuration object
|
|
50
|
+
attr_reader :ruby_llm_config
|
|
51
|
+
|
|
52
|
+
# Creates a new resolved configuration
|
|
53
|
+
#
|
|
54
|
+
# @param tenant_config [ApiConfiguration, nil] Tenant-specific config
|
|
55
|
+
# @param global_config [ApiConfiguration, nil] Global database config
|
|
56
|
+
# @param ruby_llm_config [Object] RubyLLM.configuration
|
|
57
|
+
def initialize(tenant_config:, global_config:, ruby_llm_config:)
|
|
58
|
+
@tenant_config = tenant_config
|
|
59
|
+
@global_config = global_config
|
|
60
|
+
@ruby_llm_config = ruby_llm_config
|
|
61
|
+
@resolved_cache = {}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Resolves a specific attribute value using the priority chain
|
|
65
|
+
#
|
|
66
|
+
# @param attr_name [Symbol, String] The attribute name
|
|
67
|
+
# @return [Object, nil] The resolved value
|
|
68
|
+
def resolve(attr_name)
|
|
69
|
+
attr_sym = attr_name.to_sym
|
|
70
|
+
return @resolved_cache[attr_sym] if @resolved_cache.key?(attr_sym)
|
|
71
|
+
|
|
72
|
+
@resolved_cache[attr_sym] = resolve_attribute(attr_sym)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns the source of a resolved attribute value
|
|
76
|
+
#
|
|
77
|
+
# @param attr_name [Symbol, String] The attribute name
|
|
78
|
+
# @return [String] Source label: "tenant:ID", "global_db", "ruby_llm_config", or "not_set"
|
|
79
|
+
def source_for(attr_name)
|
|
80
|
+
attr_sym = attr_name.to_sym
|
|
81
|
+
|
|
82
|
+
# Check tenant config first (if present and inherits or has value)
|
|
83
|
+
if tenant_config&.has_value?(attr_sym)
|
|
84
|
+
return "tenant:#{tenant_config.scope_id}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Check global DB config (only if tenant inherits or no tenant)
|
|
88
|
+
if should_check_global?(attr_sym) && global_config&.has_value?(attr_sym)
|
|
89
|
+
return "global_db"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Check RubyLLM config
|
|
93
|
+
if ruby_llm_value_present?(attr_sym)
|
|
94
|
+
return "ruby_llm_config"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
"not_set"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns all resolved values as a hash
|
|
101
|
+
#
|
|
102
|
+
# @return [Hash] All resolved configuration values
|
|
103
|
+
def to_hash
|
|
104
|
+
self.class.resolvable_attributes.each_with_object({}) do |attr, hash|
|
|
105
|
+
value = resolve(attr)
|
|
106
|
+
hash[attr] = value if value.present?
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Returns a hash suitable for RubyLLM configuration
|
|
111
|
+
#
|
|
112
|
+
# Only includes values that differ from or override the current
|
|
113
|
+
# RubyLLM configuration.
|
|
114
|
+
#
|
|
115
|
+
# @return [Hash] Configuration hash for RubyLLM
|
|
116
|
+
def to_ruby_llm_options
|
|
117
|
+
to_hash.slice(*ruby_llm_configurable_attributes)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Applies the resolved configuration to RubyLLM
|
|
121
|
+
#
|
|
122
|
+
# This temporarily overrides RubyLLM.configuration with the
|
|
123
|
+
# resolved values. Useful for per-request configuration.
|
|
124
|
+
#
|
|
125
|
+
# @return [void]
|
|
126
|
+
def apply_to_ruby_llm!
|
|
127
|
+
options = to_ruby_llm_options
|
|
128
|
+
return if options.empty?
|
|
129
|
+
|
|
130
|
+
RubyLLM.configure do |config|
|
|
131
|
+
options.each do |key, value|
|
|
132
|
+
setter = "#{key}="
|
|
133
|
+
config.public_send(setter, value) if config.respond_to?(setter)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Dynamic accessor for resolvable attributes
|
|
139
|
+
# Uses method_missing to provide accessors without eager loading constants
|
|
140
|
+
#
|
|
141
|
+
# @param method_name [Symbol] The method being called
|
|
142
|
+
# @param args [Array] Method arguments
|
|
143
|
+
# @return [Object] The resolved value for the attribute
|
|
144
|
+
def method_missing(method_name, *args)
|
|
145
|
+
if self.class.resolvable_attributes.include?(method_name)
|
|
146
|
+
resolve(method_name)
|
|
147
|
+
else
|
|
148
|
+
super
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Indicates which dynamic methods are supported
|
|
153
|
+
#
|
|
154
|
+
# @param method_name [Symbol] The method name to check
|
|
155
|
+
# @param include_private [Boolean] Whether to include private methods
|
|
156
|
+
# @return [Boolean] True if the method is a resolvable attribute
|
|
157
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
158
|
+
self.class.resolvable_attributes.include?(method_name) || super
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Returns provider status with source information
|
|
162
|
+
#
|
|
163
|
+
# @return [Array<Hash>] Provider status with source info
|
|
164
|
+
def provider_statuses_with_source
|
|
165
|
+
ApiConfiguration::PROVIDERS.map do |key, info|
|
|
166
|
+
key_attr = info[:key_attr]
|
|
167
|
+
value = resolve(key_attr)
|
|
168
|
+
|
|
169
|
+
{
|
|
170
|
+
key: key,
|
|
171
|
+
name: info[:name],
|
|
172
|
+
configured: value.present?,
|
|
173
|
+
masked_key: value.present? ? mask_string(value) : nil,
|
|
174
|
+
source: source_for(key_attr),
|
|
175
|
+
capabilities: info[:capabilities]
|
|
176
|
+
}
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Checks if any database configuration exists
|
|
181
|
+
#
|
|
182
|
+
# @return [Boolean]
|
|
183
|
+
def has_db_config?
|
|
184
|
+
tenant_config.present? || global_config.present?
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Returns summary of configuration sources
|
|
188
|
+
#
|
|
189
|
+
# @return [Hash] Summary with counts per source
|
|
190
|
+
def source_summary
|
|
191
|
+
summary = Hash.new(0)
|
|
192
|
+
|
|
193
|
+
self.class.resolvable_attributes.each do |attr|
|
|
194
|
+
source = source_for(attr)
|
|
195
|
+
summary[source] += 1 if resolve(attr).present?
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
summary
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Public method to get raw RubyLLM config value for an attribute
|
|
202
|
+
# This returns the value from RubyLLM.configuration (initializer/ENV)
|
|
203
|
+
# regardless of any database overrides.
|
|
204
|
+
#
|
|
205
|
+
# @param attr_name [Symbol, String] The attribute name
|
|
206
|
+
# @return [Object, nil] The RubyLLM config value
|
|
207
|
+
def ruby_llm_value_for(attr_name)
|
|
208
|
+
ruby_llm_value(attr_name.to_sym)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Masks a string for display (public wrapper)
|
|
212
|
+
#
|
|
213
|
+
# @param value [String] The string to mask
|
|
214
|
+
# @return [String] Masked string
|
|
215
|
+
def mask_string(value)
|
|
216
|
+
return nil if value.blank?
|
|
217
|
+
return "****" if value.length <= 8
|
|
218
|
+
|
|
219
|
+
"#{value[0..1]}****#{value[-4..]}"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
private
|
|
223
|
+
|
|
224
|
+
# Resolves a single attribute using the priority chain
|
|
225
|
+
#
|
|
226
|
+
# @param attr_sym [Symbol] The attribute name
|
|
227
|
+
# @return [Object, nil] The resolved value
|
|
228
|
+
def resolve_attribute(attr_sym)
|
|
229
|
+
# 1. Check tenant config
|
|
230
|
+
if tenant_config&.has_value?(attr_sym)
|
|
231
|
+
return tenant_config.send(attr_sym)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# 2. Check global DB config (if tenant allows inheritance or no tenant)
|
|
235
|
+
if should_check_global?(attr_sym)
|
|
236
|
+
if global_config&.has_value?(attr_sym)
|
|
237
|
+
return global_config.send(attr_sym)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# 3. Fall back to RubyLLM config
|
|
242
|
+
ruby_llm_value(attr_sym)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Determines if we should check global config
|
|
246
|
+
#
|
|
247
|
+
# @param attr_sym [Symbol] The attribute name
|
|
248
|
+
# @return [Boolean]
|
|
249
|
+
def should_check_global?(attr_sym)
|
|
250
|
+
return true unless tenant_config
|
|
251
|
+
|
|
252
|
+
# If tenant has inherit_global_defaults enabled, check global
|
|
253
|
+
tenant_config.inherit_global_defaults != false
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Gets a value from RubyLLM configuration
|
|
257
|
+
#
|
|
258
|
+
# @param attr_sym [Symbol] The attribute name
|
|
259
|
+
# @return [Object, nil]
|
|
260
|
+
def ruby_llm_value(attr_sym)
|
|
261
|
+
return nil unless ruby_llm_config
|
|
262
|
+
|
|
263
|
+
# Map our attribute names to RubyLLM config method names
|
|
264
|
+
method_name = ruby_llm_config_mapping(attr_sym)
|
|
265
|
+
return nil unless method_name
|
|
266
|
+
|
|
267
|
+
if ruby_llm_config.respond_to?(method_name)
|
|
268
|
+
ruby_llm_config.send(method_name)
|
|
269
|
+
end
|
|
270
|
+
rescue StandardError
|
|
271
|
+
nil
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Checks if a RubyLLM config value is present
|
|
275
|
+
#
|
|
276
|
+
# @param attr_sym [Symbol] The attribute name
|
|
277
|
+
# @return [Boolean]
|
|
278
|
+
def ruby_llm_value_present?(attr_sym)
|
|
279
|
+
value = ruby_llm_value(attr_sym)
|
|
280
|
+
value.present?
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# Maps our attribute names to RubyLLM configuration method names
|
|
284
|
+
#
|
|
285
|
+
# @param attr_sym [Symbol] Our attribute name
|
|
286
|
+
# @return [Symbol, nil] RubyLLM config method name
|
|
287
|
+
def ruby_llm_config_mapping(attr_sym)
|
|
288
|
+
# Most attributes map directly
|
|
289
|
+
mapping = {
|
|
290
|
+
openai_api_key: :openai_api_key,
|
|
291
|
+
anthropic_api_key: :anthropic_api_key,
|
|
292
|
+
gemini_api_key: :gemini_api_key,
|
|
293
|
+
deepseek_api_key: :deepseek_api_key,
|
|
294
|
+
mistral_api_key: :mistral_api_key,
|
|
295
|
+
perplexity_api_key: :perplexity_api_key,
|
|
296
|
+
openrouter_api_key: :openrouter_api_key,
|
|
297
|
+
gpustack_api_key: :gpustack_api_key,
|
|
298
|
+
xai_api_key: :xai_api_key,
|
|
299
|
+
ollama_api_key: :ollama_api_key,
|
|
300
|
+
bedrock_api_key: :bedrock_api_key,
|
|
301
|
+
bedrock_secret_key: :bedrock_secret_key,
|
|
302
|
+
bedrock_session_token: :bedrock_session_token,
|
|
303
|
+
bedrock_region: :bedrock_region,
|
|
304
|
+
vertexai_credentials: :vertexai_credentials,
|
|
305
|
+
vertexai_project_id: :vertexai_project_id,
|
|
306
|
+
vertexai_location: :vertexai_location,
|
|
307
|
+
openai_api_base: :openai_api_base,
|
|
308
|
+
gemini_api_base: :gemini_api_base,
|
|
309
|
+
ollama_api_base: :ollama_api_base,
|
|
310
|
+
gpustack_api_base: :gpustack_api_base,
|
|
311
|
+
xai_api_base: :xai_api_base,
|
|
312
|
+
openai_organization_id: :openai_organization_id,
|
|
313
|
+
openai_project_id: :openai_project_id,
|
|
314
|
+
default_model: :default_model,
|
|
315
|
+
default_embedding_model: :default_embedding_model,
|
|
316
|
+
default_image_model: :default_image_model,
|
|
317
|
+
default_moderation_model: :default_moderation_model,
|
|
318
|
+
request_timeout: :request_timeout,
|
|
319
|
+
max_retries: :max_retries,
|
|
320
|
+
retry_interval: :retry_interval,
|
|
321
|
+
retry_backoff_factor: :retry_backoff_factor,
|
|
322
|
+
retry_interval_randomness: :retry_interval_randomness,
|
|
323
|
+
http_proxy: :http_proxy
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
mapping[attr_sym]
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Returns attributes that can be set on RubyLLM configuration
|
|
330
|
+
#
|
|
331
|
+
# @return [Array<Symbol>]
|
|
332
|
+
def ruby_llm_configurable_attributes
|
|
333
|
+
ApiConfiguration::API_KEY_ATTRIBUTES +
|
|
334
|
+
ApiConfiguration::ENDPOINT_ATTRIBUTES +
|
|
335
|
+
ApiConfiguration::MODEL_ATTRIBUTES +
|
|
336
|
+
ApiConfiguration::CONNECTION_ATTRIBUTES +
|
|
337
|
+
%i[
|
|
338
|
+
openai_organization_id
|
|
339
|
+
openai_project_id
|
|
340
|
+
bedrock_region
|
|
341
|
+
vertexai_project_id
|
|
342
|
+
vertexai_location
|
|
343
|
+
]
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
module DSL
|
|
6
|
+
# Base DSL available to all agents.
|
|
7
|
+
#
|
|
8
|
+
# Provides common configuration methods that every agent type needs:
|
|
9
|
+
# - model: The LLM model to use
|
|
10
|
+
# - version: Cache invalidation version
|
|
11
|
+
# - description: Human-readable description
|
|
12
|
+
# - timeout: Request timeout
|
|
13
|
+
#
|
|
14
|
+
# @example Basic usage
|
|
15
|
+
# class MyAgent < RubyLLM::Agents::BaseAgent
|
|
16
|
+
# extend DSL::Base
|
|
17
|
+
#
|
|
18
|
+
# model "gpt-4o"
|
|
19
|
+
# version "2.0"
|
|
20
|
+
# description "A helpful agent"
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
module Base
|
|
24
|
+
# @!group Configuration DSL
|
|
25
|
+
|
|
26
|
+
# Sets or returns the LLM model for this agent class
|
|
27
|
+
#
|
|
28
|
+
# @param value [String, nil] The model identifier to set
|
|
29
|
+
# @return [String] The current model setting
|
|
30
|
+
# @example
|
|
31
|
+
# model "gpt-4o"
|
|
32
|
+
def model(value = nil)
|
|
33
|
+
@model = value if value
|
|
34
|
+
@model || inherited_or_default(:model, default_model)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Sets or returns the version string for cache invalidation
|
|
38
|
+
#
|
|
39
|
+
# Change this when you want to invalidate cached results
|
|
40
|
+
# (e.g., after changing prompts or behavior).
|
|
41
|
+
#
|
|
42
|
+
# @param value [String, nil] Version string
|
|
43
|
+
# @return [String] The current version
|
|
44
|
+
# @example
|
|
45
|
+
# version "2.0"
|
|
46
|
+
def version(value = nil)
|
|
47
|
+
@version = value if value
|
|
48
|
+
@version || inherited_or_default(:version, "1.0")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Sets or returns the description for this agent class
|
|
52
|
+
#
|
|
53
|
+
# Useful for documentation and tool registration.
|
|
54
|
+
#
|
|
55
|
+
# @param value [String, nil] The description text
|
|
56
|
+
# @return [String, nil] The current description
|
|
57
|
+
# @example
|
|
58
|
+
# description "Searches the knowledge base for relevant documents"
|
|
59
|
+
def description(value = nil)
|
|
60
|
+
@description = value if value
|
|
61
|
+
@description || inherited_or_default(:description, nil)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Sets or returns the timeout in seconds for LLM requests
|
|
65
|
+
#
|
|
66
|
+
# @param value [Integer, nil] Timeout in seconds
|
|
67
|
+
# @return [Integer] The current timeout setting
|
|
68
|
+
# @example
|
|
69
|
+
# timeout 30
|
|
70
|
+
def timeout(value = nil)
|
|
71
|
+
@timeout = value if value
|
|
72
|
+
@timeout || inherited_or_default(:timeout, default_timeout)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @!endgroup
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# Looks up setting from superclass or uses default
|
|
80
|
+
#
|
|
81
|
+
# @param method [Symbol] The method to call on superclass
|
|
82
|
+
# @param default [Object] Default value if not found
|
|
83
|
+
# @return [Object] The resolved value
|
|
84
|
+
def inherited_or_default(method, default)
|
|
85
|
+
return default unless superclass.respond_to?(method)
|
|
86
|
+
|
|
87
|
+
superclass.send(method)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Returns the default model from configuration
|
|
91
|
+
#
|
|
92
|
+
# @return [String] The default model
|
|
93
|
+
def default_model
|
|
94
|
+
RubyLLM::Agents.configuration.default_model
|
|
95
|
+
rescue StandardError
|
|
96
|
+
"gpt-4o"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns the default timeout from configuration
|
|
100
|
+
#
|
|
101
|
+
# @return [Integer] The default timeout
|
|
102
|
+
def default_timeout
|
|
103
|
+
RubyLLM::Agents.configuration.default_timeout
|
|
104
|
+
rescue StandardError
|
|
105
|
+
120
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
module DSL
|
|
6
|
+
# Caching DSL for agent response caching.
|
|
7
|
+
#
|
|
8
|
+
# This module provides configuration methods for caching features
|
|
9
|
+
# that can be mixed into any agent class.
|
|
10
|
+
#
|
|
11
|
+
# @example Basic usage
|
|
12
|
+
# class MyAgent < RubyLLM::Agents::BaseAgent
|
|
13
|
+
# extend DSL::Caching
|
|
14
|
+
#
|
|
15
|
+
# cache_for 1.hour
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# @example With conditional caching
|
|
19
|
+
# class MyAgent < RubyLLM::Agents::BaseAgent
|
|
20
|
+
# extend DSL::Caching
|
|
21
|
+
#
|
|
22
|
+
# cache_for 30.minutes
|
|
23
|
+
# cache_key_includes :user_id, :query
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
module Caching
|
|
27
|
+
# Default cache TTL when none specified
|
|
28
|
+
DEFAULT_CACHE_TTL = 1.hour
|
|
29
|
+
|
|
30
|
+
# @!group Caching DSL
|
|
31
|
+
|
|
32
|
+
# Enables caching for this agent with explicit TTL
|
|
33
|
+
#
|
|
34
|
+
# This is the preferred method for enabling caching.
|
|
35
|
+
#
|
|
36
|
+
# @param ttl [ActiveSupport::Duration] Time-to-live for cached responses
|
|
37
|
+
# @return [void]
|
|
38
|
+
# @example
|
|
39
|
+
# cache_for 1.hour
|
|
40
|
+
# cache_for 30.minutes
|
|
41
|
+
def cache_for(ttl)
|
|
42
|
+
@cache_enabled = true
|
|
43
|
+
@cache_ttl = ttl
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Alias for cache_for (for backward compatibility)
|
|
47
|
+
alias cache cache_for
|
|
48
|
+
|
|
49
|
+
# Returns whether caching is enabled for this agent
|
|
50
|
+
#
|
|
51
|
+
# Caching IS inherited from parent classes following Ruby patterns.
|
|
52
|
+
#
|
|
53
|
+
# @return [Boolean] true if caching is enabled
|
|
54
|
+
def cache_enabled?
|
|
55
|
+
return @cache_enabled if defined?(@cache_enabled)
|
|
56
|
+
|
|
57
|
+
inherited_cache_enabled
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the cache TTL for this agent
|
|
61
|
+
#
|
|
62
|
+
# @return [ActiveSupport::Duration] The cache TTL
|
|
63
|
+
def cache_ttl
|
|
64
|
+
@cache_ttl || inherited_cache_ttl || DEFAULT_CACHE_TTL
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Specifies which parameters should be included in the cache key
|
|
68
|
+
#
|
|
69
|
+
# By default, all options except :skip_cache and :dry_run are included.
|
|
70
|
+
# Use this to explicitly define which parameters affect caching.
|
|
71
|
+
#
|
|
72
|
+
# @param keys [Array<Symbol>] Parameter keys to include in cache key
|
|
73
|
+
# @return [Array<Symbol>, nil] The current cache key includes
|
|
74
|
+
# @example
|
|
75
|
+
# cache_key_includes :user_id, :query, :context
|
|
76
|
+
def cache_key_includes(*keys)
|
|
77
|
+
@cache_key_includes = keys.flatten if keys.any?
|
|
78
|
+
@cache_key_includes || inherited_cache_key_includes
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Specifies which parameters should be excluded from the cache key
|
|
82
|
+
#
|
|
83
|
+
# @param keys [Array<Symbol>] Parameter keys to exclude from cache key
|
|
84
|
+
# @return [Array<Symbol>] The current cache key excludes
|
|
85
|
+
# @example
|
|
86
|
+
# cache_key_excludes :timestamp, :request_id
|
|
87
|
+
def cache_key_excludes(*keys)
|
|
88
|
+
@cache_key_excludes = keys.flatten if keys.any?
|
|
89
|
+
@cache_key_excludes || inherited_cache_key_excludes || default_cache_key_excludes
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Returns the complete caching configuration hash
|
|
93
|
+
#
|
|
94
|
+
# Used by the Cache middleware to get all settings.
|
|
95
|
+
#
|
|
96
|
+
# @return [Hash, nil] The caching configuration
|
|
97
|
+
def caching_config
|
|
98
|
+
return nil unless cache_enabled?
|
|
99
|
+
|
|
100
|
+
{
|
|
101
|
+
enabled: true,
|
|
102
|
+
ttl: cache_ttl,
|
|
103
|
+
key_includes: cache_key_includes,
|
|
104
|
+
key_excludes: cache_key_excludes
|
|
105
|
+
}.compact
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# @!endgroup
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def inherited_cache_enabled
|
|
113
|
+
return false unless superclass.respond_to?(:cache_enabled?)
|
|
114
|
+
|
|
115
|
+
superclass.cache_enabled?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def inherited_cache_ttl
|
|
119
|
+
return nil unless superclass.respond_to?(:cache_ttl)
|
|
120
|
+
|
|
121
|
+
superclass.cache_ttl
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def inherited_cache_key_includes
|
|
125
|
+
return nil unless superclass.respond_to?(:cache_key_includes)
|
|
126
|
+
|
|
127
|
+
superclass.cache_key_includes
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def inherited_cache_key_excludes
|
|
131
|
+
return nil unless superclass.respond_to?(:cache_key_excludes)
|
|
132
|
+
|
|
133
|
+
superclass.cache_key_excludes
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def default_cache_key_excludes
|
|
137
|
+
%i[skip_cache dry_run with]
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|