ruby_llm-agents 0.5.0 → 1.0.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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +189 -31
  3. data/app/controllers/ruby_llm/agents/agents_controller.rb +136 -16
  4. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +29 -9
  5. data/app/controllers/ruby_llm/agents/workflows_controller.rb +355 -0
  6. data/app/helpers/ruby_llm/agents/application_helper.rb +25 -0
  7. data/app/models/ruby_llm/agents/execution.rb +3 -0
  8. data/app/models/ruby_llm/agents/tenant_budget.rb +58 -15
  9. data/app/services/ruby_llm/agents/agent_registry.rb +51 -12
  10. data/app/views/layouts/ruby_llm/agents/application.html.erb +2 -29
  11. data/app/views/ruby_llm/agents/agents/_agent.html.erb +13 -1
  12. data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +235 -0
  13. data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +70 -0
  14. data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +152 -0
  15. data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +63 -0
  16. data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +108 -0
  17. data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +91 -0
  18. data/app/views/ruby_llm/agents/agents/_workflow.html.erb +1 -1
  19. data/app/views/ruby_llm/agents/agents/index.html.erb +74 -9
  20. data/app/views/ruby_llm/agents/agents/show.html.erb +18 -378
  21. data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +269 -15
  22. data/app/views/ruby_llm/agents/executions/show.html.erb +16 -0
  23. data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +93 -0
  24. data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +236 -0
  25. data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +76 -0
  26. data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +74 -0
  27. data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +108 -0
  28. data/app/views/ruby_llm/agents/workflows/show.html.erb +442 -0
  29. data/config/routes.rb +1 -0
  30. data/lib/generators/ruby_llm_agents/agent_generator.rb +56 -7
  31. data/lib/generators/ruby_llm_agents/background_remover_generator.rb +110 -0
  32. data/lib/generators/ruby_llm_agents/embedder_generator.rb +107 -0
  33. data/lib/generators/ruby_llm_agents/image_analyzer_generator.rb +115 -0
  34. data/lib/generators/ruby_llm_agents/image_editor_generator.rb +108 -0
  35. data/lib/generators/ruby_llm_agents/image_generator_generator.rb +116 -0
  36. data/lib/generators/ruby_llm_agents/image_pipeline_generator.rb +178 -0
  37. data/lib/generators/ruby_llm_agents/image_transformer_generator.rb +109 -0
  38. data/lib/generators/ruby_llm_agents/image_upscaler_generator.rb +103 -0
  39. data/lib/generators/ruby_llm_agents/image_variator_generator.rb +102 -0
  40. data/lib/generators/ruby_llm_agents/install_generator.rb +76 -4
  41. data/lib/generators/ruby_llm_agents/restructure_generator.rb +292 -0
  42. data/lib/generators/ruby_llm_agents/speaker_generator.rb +121 -0
  43. data/lib/generators/ruby_llm_agents/templates/add_execution_type_migration.rb.tt +8 -0
  44. data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +99 -84
  45. data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +42 -40
  46. data/lib/generators/ruby_llm_agents/templates/application_background_remover.rb.tt +26 -0
  47. data/lib/generators/ruby_llm_agents/templates/application_embedder.rb.tt +50 -0
  48. data/lib/generators/ruby_llm_agents/templates/application_image_analyzer.rb.tt +26 -0
  49. data/lib/generators/ruby_llm_agents/templates/application_image_editor.rb.tt +20 -0
  50. data/lib/generators/ruby_llm_agents/templates/application_image_generator.rb.tt +38 -0
  51. data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +139 -0
  52. data/lib/generators/ruby_llm_agents/templates/application_image_transformer.rb.tt +21 -0
  53. data/lib/generators/ruby_llm_agents/templates/application_image_upscaler.rb.tt +20 -0
  54. data/lib/generators/ruby_llm_agents/templates/application_image_variator.rb.tt +20 -0
  55. data/lib/generators/ruby_llm_agents/templates/application_speaker.rb.tt +49 -0
  56. data/lib/generators/ruby_llm_agents/templates/application_transcriber.rb.tt +53 -0
  57. data/lib/generators/ruby_llm_agents/templates/background_remover.rb.tt +44 -0
  58. data/lib/generators/ruby_llm_agents/templates/embedder.rb.tt +41 -0
  59. data/lib/generators/ruby_llm_agents/templates/image_analyzer.rb.tt +45 -0
  60. data/lib/generators/ruby_llm_agents/templates/image_editor.rb.tt +35 -0
  61. data/lib/generators/ruby_llm_agents/templates/image_generator.rb.tt +47 -0
  62. data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +50 -0
  63. data/lib/generators/ruby_llm_agents/templates/image_transformer.rb.tt +44 -0
  64. data/lib/generators/ruby_llm_agents/templates/image_upscaler.rb.tt +38 -0
  65. data/lib/generators/ruby_llm_agents/templates/image_variator.rb.tt +33 -0
  66. data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +228 -0
  67. data/lib/generators/ruby_llm_agents/templates/skills/BACKGROUND_REMOVERS.md.tt +131 -0
  68. data/lib/generators/ruby_llm_agents/templates/skills/EMBEDDERS.md.tt +255 -0
  69. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_ANALYZERS.md.tt +120 -0
  70. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_EDITORS.md.tt +102 -0
  71. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_GENERATORS.md.tt +282 -0
  72. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +228 -0
  73. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_TRANSFORMERS.md.tt +120 -0
  74. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_UPSCALERS.md.tt +110 -0
  75. data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_VARIATORS.md.tt +120 -0
  76. data/lib/generators/ruby_llm_agents/templates/skills/SPEAKERS.md.tt +212 -0
  77. data/lib/generators/ruby_llm_agents/templates/skills/TOOLS.md.tt +227 -0
  78. data/lib/generators/ruby_llm_agents/templates/skills/TRANSCRIBERS.md.tt +251 -0
  79. data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +300 -0
  80. data/lib/generators/ruby_llm_agents/templates/speaker.rb.tt +56 -0
  81. data/lib/generators/ruby_llm_agents/templates/transcriber.rb.tt +51 -0
  82. data/lib/generators/ruby_llm_agents/transcriber_generator.rb +107 -0
  83. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +152 -1
  84. data/lib/ruby_llm/agents/audio/speaker.rb +553 -0
  85. data/lib/ruby_llm/agents/audio/transcriber.rb +669 -0
  86. data/lib/ruby_llm/agents/base_agent.rb +675 -0
  87. data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +181 -0
  88. data/lib/ruby_llm/agents/core/base/moderation_execution.rb +274 -0
  89. data/lib/ruby_llm/agents/core/base.rb +135 -0
  90. data/lib/ruby_llm/agents/core/configuration.rb +981 -0
  91. data/lib/ruby_llm/agents/core/errors.rb +150 -0
  92. data/lib/ruby_llm/agents/{instrumentation.rb → core/instrumentation.rb} +22 -1
  93. data/lib/ruby_llm/agents/core/llm_tenant.rb +358 -0
  94. data/lib/ruby_llm/agents/{version.rb → core/version.rb} +1 -1
  95. data/lib/ruby_llm/agents/dsl/base.rb +110 -0
  96. data/lib/ruby_llm/agents/dsl/caching.rb +142 -0
  97. data/lib/ruby_llm/agents/dsl/reliability.rb +307 -0
  98. data/lib/ruby_llm/agents/dsl.rb +41 -0
  99. data/lib/ruby_llm/agents/image/analyzer/dsl.rb +130 -0
  100. data/lib/ruby_llm/agents/image/analyzer/execution.rb +402 -0
  101. data/lib/ruby_llm/agents/image/analyzer.rb +90 -0
  102. data/lib/ruby_llm/agents/image/background_remover/dsl.rb +154 -0
  103. data/lib/ruby_llm/agents/image/background_remover/execution.rb +240 -0
  104. data/lib/ruby_llm/agents/image/background_remover.rb +89 -0
  105. data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +91 -0
  106. data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +165 -0
  107. data/lib/ruby_llm/agents/image/editor/dsl.rb +56 -0
  108. data/lib/ruby_llm/agents/image/editor/execution.rb +207 -0
  109. data/lib/ruby_llm/agents/image/editor.rb +92 -0
  110. data/lib/ruby_llm/agents/image/generator/active_storage_support.rb +127 -0
  111. data/lib/ruby_llm/agents/image/generator/content_policy.rb +95 -0
  112. data/lib/ruby_llm/agents/image/generator/pricing.rb +353 -0
  113. data/lib/ruby_llm/agents/image/generator/templates.rb +124 -0
  114. data/lib/ruby_llm/agents/image/generator.rb +455 -0
  115. data/lib/ruby_llm/agents/image/pipeline/dsl.rb +213 -0
  116. data/lib/ruby_llm/agents/image/pipeline/execution.rb +382 -0
  117. data/lib/ruby_llm/agents/image/pipeline.rb +97 -0
  118. data/lib/ruby_llm/agents/image/transformer/dsl.rb +148 -0
  119. data/lib/ruby_llm/agents/image/transformer/execution.rb +223 -0
  120. data/lib/ruby_llm/agents/image/transformer.rb +95 -0
  121. data/lib/ruby_llm/agents/image/upscaler/dsl.rb +83 -0
  122. data/lib/ruby_llm/agents/image/upscaler/execution.rb +219 -0
  123. data/lib/ruby_llm/agents/image/upscaler.rb +81 -0
  124. data/lib/ruby_llm/agents/image/variator/dsl.rb +62 -0
  125. data/lib/ruby_llm/agents/image/variator/execution.rb +189 -0
  126. data/lib/ruby_llm/agents/image/variator.rb +80 -0
  127. data/lib/ruby_llm/agents/{alert_manager.rb → infrastructure/alert_manager.rb} +17 -22
  128. data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +145 -0
  129. data/lib/ruby_llm/agents/infrastructure/budget/config_resolver.rb +149 -0
  130. data/lib/ruby_llm/agents/infrastructure/budget/forecaster.rb +68 -0
  131. data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +279 -0
  132. data/lib/ruby_llm/agents/infrastructure/budget_tracker.rb +275 -0
  133. data/lib/ruby_llm/agents/{execution_logger_job.rb → infrastructure/execution_logger_job.rb} +17 -1
  134. data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/executor.rb +2 -1
  135. data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/retry_strategy.rb +9 -3
  136. data/lib/ruby_llm/agents/{reliability.rb → infrastructure/reliability.rb} +11 -21
  137. data/lib/ruby_llm/agents/pipeline/builder.rb +215 -0
  138. data/lib/ruby_llm/agents/pipeline/context.rb +255 -0
  139. data/lib/ruby_llm/agents/pipeline/executor.rb +86 -0
  140. data/lib/ruby_llm/agents/pipeline/middleware/base.rb +124 -0
  141. data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +95 -0
  142. data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +171 -0
  143. data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +415 -0
  144. data/lib/ruby_llm/agents/pipeline/middleware/reliability.rb +276 -0
  145. data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +196 -0
  146. data/lib/ruby_llm/agents/pipeline.rb +68 -0
  147. data/lib/ruby_llm/agents/{engine.rb → rails/engine.rb} +79 -11
  148. data/lib/ruby_llm/agents/results/background_removal_result.rb +286 -0
  149. data/lib/ruby_llm/agents/{result.rb → results/base.rb} +73 -1
  150. data/lib/ruby_llm/agents/results/embedding_result.rb +243 -0
  151. data/lib/ruby_llm/agents/results/image_analysis_result.rb +314 -0
  152. data/lib/ruby_llm/agents/results/image_edit_result.rb +250 -0
  153. data/lib/ruby_llm/agents/results/image_generation_result.rb +346 -0
  154. data/lib/ruby_llm/agents/results/image_pipeline_result.rb +399 -0
  155. data/lib/ruby_llm/agents/results/image_transform_result.rb +251 -0
  156. data/lib/ruby_llm/agents/results/image_upscale_result.rb +255 -0
  157. data/lib/ruby_llm/agents/results/image_variation_result.rb +237 -0
  158. data/lib/ruby_llm/agents/results/moderation_result.rb +158 -0
  159. data/lib/ruby_llm/agents/results/speech_result.rb +338 -0
  160. data/lib/ruby_llm/agents/results/transcription_result.rb +408 -0
  161. data/lib/ruby_llm/agents/text/embedder.rb +444 -0
  162. data/lib/ruby_llm/agents/text/moderator.rb +237 -0
  163. data/lib/ruby_llm/agents/workflow/async.rb +220 -0
  164. data/lib/ruby_llm/agents/workflow/async_executor.rb +156 -0
  165. data/lib/ruby_llm/agents/{workflow.rb → workflow/orchestrator.rb} +6 -5
  166. data/lib/ruby_llm/agents/workflow/parallel.rb +34 -17
  167. data/lib/ruby_llm/agents/workflow/thread_pool.rb +185 -0
  168. data/lib/ruby_llm/agents.rb +86 -20
  169. metadata +172 -34
  170. data/lib/ruby_llm/agents/base/caching.rb +0 -40
  171. data/lib/ruby_llm/agents/base/cost_calculation.rb +0 -105
  172. data/lib/ruby_llm/agents/base/dsl.rb +0 -324
  173. data/lib/ruby_llm/agents/base/execution.rb +0 -366
  174. data/lib/ruby_llm/agents/base/reliability_dsl.rb +0 -82
  175. data/lib/ruby_llm/agents/base/reliability_execution.rb +0 -136
  176. data/lib/ruby_llm/agents/base/response_building.rb +0 -86
  177. data/lib/ruby_llm/agents/base/tool_tracking.rb +0 -57
  178. data/lib/ruby_llm/agents/base.rb +0 -210
  179. data/lib/ruby_llm/agents/budget_tracker.rb +0 -733
  180. data/lib/ruby_llm/agents/configuration.rb +0 -394
  181. /data/lib/ruby_llm/agents/{deprecations.rb → core/deprecations.rb} +0 -0
  182. /data/lib/ruby_llm/agents/{inflections.rb → core/inflections.rb} +0 -0
  183. /data/lib/ruby_llm/agents/{resolved_config.rb → core/resolved_config.rb} +0 -0
  184. /data/lib/ruby_llm/agents/{attempt_tracker.rb → infrastructure/attempt_tracker.rb} +0 -0
  185. /data/lib/ruby_llm/agents/{cache_helper.rb → infrastructure/cache_helper.rb} +0 -0
  186. /data/lib/ruby_llm/agents/{circuit_breaker.rb → infrastructure/circuit_breaker.rb} +0 -0
  187. /data/lib/ruby_llm/agents/{redactor.rb → infrastructure/redactor.rb} +0 -0
  188. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/breaker_manager.rb +0 -0
  189. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/execution_constraints.rb +0 -0
  190. /data/lib/ruby_llm/agents/{reliability → infrastructure/reliability}/fallback_routing.rb +0 -0
@@ -1,394 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RubyLLM
4
- module Agents
5
- # Global configuration for RubyLLM::Agents
6
- #
7
- # Provides centralized settings for agent behavior, dashboard authentication,
8
- # caching, and observability thresholds.
9
- #
10
- # @example Basic configuration
11
- # RubyLLM::Agents.configure do |config|
12
- # config.default_model = "gpt-4o"
13
- # config.default_temperature = 0.7
14
- # config.async_logging = true
15
- # end
16
- #
17
- # @example Dashboard with HTTP Basic Auth
18
- # RubyLLM::Agents.configure do |config|
19
- # config.basic_auth_username = ENV["AGENTS_USER"]
20
- # config.basic_auth_password = ENV["AGENTS_PASS"]
21
- # end
22
- #
23
- # @example Dashboard with custom authentication
24
- # RubyLLM::Agents.configure do |config|
25
- # config.dashboard_parent_controller = "AdminController"
26
- # config.dashboard_auth = ->(controller) { controller.current_user&.admin? }
27
- # end
28
- #
29
- # @see RubyLLM::Agents.configure
30
- # @api public
31
- class Configuration
32
- # @!attribute [rw] default_model
33
- # The default LLM model identifier for all agents.
34
- # Can be overridden per-agent using the `model` DSL method.
35
- # @return [String] Model identifier (default: "gemini-2.0-flash")
36
- # @example
37
- # config.default_model = "gpt-4o"
38
-
39
- # @!attribute [rw] default_temperature
40
- # The default temperature for LLM responses (0.0 to 2.0).
41
- # Lower values produce more deterministic outputs.
42
- # @return [Float] Temperature value (default: 0.0)
43
-
44
- # @!attribute [rw] default_timeout
45
- # Maximum seconds to wait for an LLM response before timing out.
46
- # @return [Integer] Timeout in seconds (default: 60)
47
-
48
- # @!attribute [rw] async_logging
49
- # Whether to log executions via background job (recommended for production).
50
- # When false, executions are logged synchronously.
51
- # @return [Boolean] Enable async logging (default: true)
52
-
53
- # @!attribute [rw] retention_period
54
- # How long to retain execution records before cleanup.
55
- # @return [ActiveSupport::Duration] Retention period (default: 30.days)
56
-
57
- # @!attribute [rw] anomaly_cost_threshold
58
- # Cost threshold in dollars that triggers anomaly logging.
59
- # Executions exceeding this cost are logged as warnings.
60
- # @return [Float] Cost threshold in USD (default: 5.00)
61
-
62
- # @!attribute [rw] anomaly_duration_threshold
63
- # Duration threshold in milliseconds that triggers anomaly logging.
64
- # @return [Integer] Duration threshold in ms (default: 10_000)
65
-
66
- # @!attribute [rw] dashboard_auth
67
- # Lambda for custom dashboard authentication.
68
- # Receives the controller instance, should return truthy to allow access.
69
- # @return [Proc] Authentication lambda (default: allows all)
70
- # @example
71
- # config.dashboard_auth = ->(c) { c.current_user&.admin? }
72
-
73
- # @!attribute [rw] dashboard_parent_controller
74
- # Parent controller class name for the dashboard.
75
- # Use this to inherit authentication from your app's admin controller.
76
- # @return [String] Controller class name (default: "ActionController::Base")
77
-
78
- # @!attribute [rw] basic_auth_username
79
- # Username for HTTP Basic Auth on the dashboard.
80
- # Both username and password must be set to enable Basic Auth.
81
- # @return [String, nil] Username or nil to disable (default: nil)
82
-
83
- # @!attribute [rw] basic_auth_password
84
- # Password for HTTP Basic Auth on the dashboard.
85
- # @return [String, nil] Password or nil to disable (default: nil)
86
-
87
- # @!attribute [rw] per_page
88
- # Number of records per page in dashboard listings.
89
- # @return [Integer] Records per page (default: 25)
90
-
91
- # @!attribute [rw] recent_executions_limit
92
- # Number of recent executions shown on the dashboard home.
93
- # @return [Integer] Limit for recent executions (default: 10)
94
-
95
- # @!attribute [rw] job_retry_attempts
96
- # Number of retry attempts for the async logging job on failure.
97
- # @return [Integer] Retry attempts (default: 3)
98
-
99
- # @!attribute [w] cache_store
100
- # Custom cache store for agent response caching.
101
- # Falls back to Rails.cache if not set.
102
- # @return [ActiveSupport::Cache::Store, nil]
103
-
104
- # @!attribute [rw] default_retries
105
- # Default retry configuration for all agents.
106
- # Can be overridden per-agent using the `retries` DSL method.
107
- # @return [Hash] Retry config with :max, :backoff, :base, :max_delay, :on keys
108
- # @example
109
- # config.default_retries = { max: 2, backoff: :exponential, base: 0.4, max_delay: 3.0, on: [] }
110
-
111
- # @!attribute [rw] default_fallback_models
112
- # Default fallback models for all agents.
113
- # Can be overridden per-agent using the `fallback_models` DSL method.
114
- # @return [Array<String>] List of model identifiers to try on failure
115
-
116
- # @!attribute [rw] default_total_timeout
117
- # Default total timeout across all retry attempts.
118
- # Can be overridden per-agent using the `total_timeout` DSL method.
119
- # @return [Integer, nil] Total timeout in seconds, or nil for no limit
120
-
121
- # @!attribute [rw] default_streaming
122
- # Whether streaming mode is enabled by default for all agents.
123
- # When enabled and a block is passed to call, chunks are yielded as they arrive.
124
- # Can be overridden per-agent using the `streaming` DSL method.
125
- # @return [Boolean] Enable streaming (default: false)
126
- # @example
127
- # config.default_streaming = true
128
-
129
- # @!attribute [rw] default_tools
130
- # Default tools available to all agents.
131
- # Should be an array of RubyLLM::Tool classes.
132
- # Can be overridden or extended per-agent using the `tools` DSL method.
133
- # @return [Array<Class>] Tool classes (default: [])
134
- # @example
135
- # config.default_tools = [WeatherTool, SearchTool]
136
-
137
- # @!attribute [rw] budgets
138
- # Budget configuration for cost governance.
139
- # @return [Hash, nil] Budget config with :global_daily, :global_monthly, :per_agent_daily, :per_agent_monthly, :enforcement keys
140
- # @example
141
- # config.budgets = {
142
- # global_daily: 25.0,
143
- # global_monthly: 300.0,
144
- # per_agent_daily: { "ContentAgent" => 5.0 },
145
- # per_agent_monthly: { "ContentAgent" => 120.0 },
146
- # enforcement: :soft
147
- # }
148
-
149
- # @!attribute [rw] alerts
150
- # Alert configuration for notifications.
151
- # @return [Hash, nil] Alert config with :slack_webhook_url, :webhook_url, :on_events, :custom keys
152
- # @example
153
- # config.alerts = {
154
- # slack_webhook_url: ENV["SLACK_WEBHOOK"],
155
- # webhook_url: ENV["AGENTS_WEBHOOK"],
156
- # on_events: [:budget_soft_cap, :budget_hard_cap, :breaker_open],
157
- # custom: ->(event, payload) { Rails.logger.info("Alert: #{event}") }
158
- # }
159
-
160
- # @!attribute [rw] persist_prompts
161
- # Whether to persist system and user prompts in execution records.
162
- # Set to false to reduce storage or for privacy compliance.
163
- # @return [Boolean] Enable prompt persistence (default: true)
164
-
165
- # @!attribute [rw] persist_responses
166
- # Whether to persist LLM responses in execution records.
167
- # Set to false to reduce storage or for privacy compliance.
168
- # @return [Boolean] Enable response persistence (default: true)
169
-
170
- # @!attribute [rw] redaction
171
- # Redaction configuration for PII and sensitive data.
172
- # @return [Hash, nil] Redaction config with :fields, :patterns, :placeholder, :max_value_length keys
173
- # @example
174
- # config.redaction = {
175
- # fields: %w[password api_key email ssn],
176
- # patterns: [/\b\d{3}-\d{2}-\d{4}\b/],
177
- # placeholder: "[REDACTED]",
178
- # max_value_length: 5000
179
- # }
180
-
181
- # @!attribute [rw] multi_tenancy_enabled
182
- # Whether multi-tenancy features are enabled.
183
- # When false, the gem behaves exactly as before (backward compatible).
184
- # @return [Boolean] Enable multi-tenancy (default: false)
185
- # @example
186
- # config.multi_tenancy_enabled = true
187
-
188
- # @!attribute [rw] tenant_resolver
189
- # Lambda that returns the current tenant identifier.
190
- # Called whenever tenant context is needed for budget tracking,
191
- # circuit breakers, and execution recording.
192
- # @return [Proc] Tenant resolution lambda (default: -> { nil })
193
- # @example Using Rails CurrentAttributes
194
- # config.tenant_resolver = -> { Current.tenant&.id }
195
- # @example Using request store
196
- # config.tenant_resolver = -> { RequestStore[:tenant_id] }
197
-
198
- # @!attribute [rw] tenant_config_resolver
199
- # Lambda that returns tenant configuration without querying the database.
200
- # Called when resolving tenant budget config. If set, this takes priority
201
- # over the TenantBudget database lookup.
202
- # @return [Proc, nil] Tenant config resolver lambda (default: nil)
203
- # @example Using an external tenant service
204
- # config.tenant_config_resolver = ->(tenant_id) {
205
- # tenant = Tenant.find(tenant_id)
206
- # {
207
- # name: tenant.name,
208
- # daily_limit: tenant.subscription.daily_budget,
209
- # monthly_limit: tenant.subscription.monthly_budget,
210
- # daily_token_limit: tenant.subscription.daily_tokens,
211
- # monthly_token_limit: tenant.subscription.monthly_tokens,
212
- # enforcement: tenant.subscription.hard_limits? ? :hard : :soft
213
- # }
214
- # }
215
-
216
- # @!attribute [rw] persist_messages_summary
217
- # Whether to persist a summary of conversation messages in execution records.
218
- # When true, stores message count and first/last messages (truncated).
219
- # Set to false to disable message summary persistence.
220
- # @return [Boolean] Enable messages summary persistence (default: true)
221
-
222
- # @!attribute [rw] messages_summary_max_length
223
- # Maximum character length for message content in the summary.
224
- # Content exceeding this length will be truncated with "...".
225
- # @return [Integer] Max length for message content (default: 500)
226
-
227
- attr_accessor :default_model,
228
- :default_temperature,
229
- :default_timeout,
230
- :async_logging,
231
- :retention_period,
232
- :anomaly_cost_threshold,
233
- :anomaly_duration_threshold,
234
- :dashboard_auth,
235
- :dashboard_parent_controller,
236
- :basic_auth_username,
237
- :basic_auth_password,
238
- :per_page,
239
- :recent_executions_limit,
240
- :job_retry_attempts,
241
- :default_retries,
242
- :default_fallback_models,
243
- :default_total_timeout,
244
- :default_streaming,
245
- :default_tools,
246
- :budgets,
247
- :alerts,
248
- :persist_prompts,
249
- :persist_responses,
250
- :redaction,
251
- :multi_tenancy_enabled,
252
- :tenant_resolver,
253
- :tenant_config_resolver,
254
- :persist_messages_summary,
255
- :messages_summary_max_length
256
-
257
- attr_writer :cache_store
258
-
259
- # Initializes configuration with default values
260
- #
261
- # @return [Configuration] A new configuration instance with defaults
262
- # @api private
263
- def initialize
264
- @default_model = "gemini-2.0-flash"
265
- @default_temperature = 0.0
266
- @default_timeout = 60
267
- @cache_store = nil
268
- @async_logging = true
269
- @retention_period = 30.days
270
- @anomaly_cost_threshold = 5.00
271
- @anomaly_duration_threshold = 10_000
272
- @dashboard_auth = ->(_controller) { true }
273
- @dashboard_parent_controller = "ActionController::Base"
274
- @basic_auth_username = nil
275
- @basic_auth_password = nil
276
- @per_page = 25
277
- @recent_executions_limit = 10
278
- @job_retry_attempts = 3
279
-
280
- # Reliability defaults (all disabled by default for backward compatibility)
281
- @default_retries = { max: 0, backoff: :exponential, base: 0.4, max_delay: 3.0, on: [] }
282
- @default_fallback_models = []
283
- @default_total_timeout = nil
284
-
285
- # Streaming and tools defaults
286
- @default_streaming = false
287
- @default_tools = []
288
-
289
- # Governance defaults
290
- @budgets = nil
291
- @alerts = nil
292
- @persist_prompts = true
293
- @persist_responses = true
294
- @redaction = nil
295
-
296
- # Multi-tenancy defaults (disabled for backward compatibility)
297
- @multi_tenancy_enabled = false
298
- @tenant_resolver = -> { nil }
299
- @tenant_config_resolver = nil
300
-
301
- # Messages summary defaults
302
- @persist_messages_summary = true
303
- @messages_summary_max_length = 500
304
- end
305
-
306
- # Returns the configured cache store, falling back to Rails.cache
307
- #
308
- # @return [ActiveSupport::Cache::Store] The cache store instance
309
- # @example Using a custom cache store
310
- # config.cache_store = ActiveSupport::Cache::MemoryStore.new
311
- def cache_store
312
- @cache_store || Rails.cache
313
- end
314
-
315
- # Returns whether budgets are configured and enforcement is enabled
316
- #
317
- # @return [Boolean] true if budgets are configured with enforcement
318
- def budgets_enabled?
319
- budgets.is_a?(Hash) && budgets[:enforcement] && budgets[:enforcement] != :none
320
- end
321
-
322
- # Returns the budget enforcement mode
323
- #
324
- # @return [Symbol] :none, :soft, or :hard
325
- def budget_enforcement
326
- budgets&.dig(:enforcement) || :none
327
- end
328
-
329
- # Returns whether alerts are configured
330
- #
331
- # @return [Boolean] true if any alert destination is configured
332
- def alerts_enabled?
333
- return false unless alerts.is_a?(Hash)
334
-
335
- alerts[:slack_webhook_url].present? ||
336
- alerts[:webhook_url].present? ||
337
- alerts[:custom].present?
338
- end
339
-
340
- # Returns the list of events to alert on
341
- #
342
- # @return [Array<Symbol>] Event names to trigger alerts
343
- def alert_events
344
- alerts&.dig(:on_events) || []
345
- end
346
-
347
- # Returns merged redaction fields (default sensitive keys + configured)
348
- #
349
- # @return [Array<String>] Field names to redact
350
- def redaction_fields
351
- default_fields = %w[password token api_key secret credential auth key access_token]
352
- configured_fields = redaction&.dig(:fields) || []
353
- (default_fields + configured_fields).map(&:downcase).uniq
354
- end
355
-
356
- # Returns redaction patterns
357
- #
358
- # @return [Array<Regexp>] Patterns to match and redact
359
- def redaction_patterns
360
- redaction&.dig(:patterns) || []
361
- end
362
-
363
- # Returns the redaction placeholder string
364
- #
365
- # @return [String] Placeholder to replace redacted values
366
- def redaction_placeholder
367
- redaction&.dig(:placeholder) || "[REDACTED]"
368
- end
369
-
370
- # Returns the maximum value length before truncation
371
- #
372
- # @return [Integer, nil] Max length, or nil for no limit
373
- def redaction_max_value_length
374
- redaction&.dig(:max_value_length)
375
- end
376
-
377
- # Returns whether multi-tenancy is enabled
378
- #
379
- # @return [Boolean] true if multi-tenancy is enabled
380
- def multi_tenancy_enabled?
381
- @multi_tenancy_enabled == true
382
- end
383
-
384
- # Returns the current tenant ID from the resolver
385
- #
386
- # @return [String, nil] Current tenant identifier or nil
387
- def current_tenant_id
388
- return nil unless multi_tenancy_enabled?
389
-
390
- tenant_resolver&.call
391
- end
392
- end
393
- end
394
- end