ruby_llm-agents 0.2.4 → 0.3.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 +413 -0
- data/app/channels/ruby_llm/agents/executions_channel.rb +24 -1
- data/app/controllers/concerns/ruby_llm/agents/filterable.rb +81 -0
- data/app/controllers/concerns/ruby_llm/agents/paginatable.rb +51 -0
- data/app/controllers/ruby_llm/agents/agents_controller.rb +228 -59
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +167 -12
- data/app/controllers/ruby_llm/agents/executions_controller.rb +189 -31
- data/app/controllers/ruby_llm/agents/settings_controller.rb +20 -0
- data/app/helpers/ruby_llm/agents/application_helper.rb +307 -7
- data/app/models/ruby_llm/agents/execution/analytics.rb +224 -20
- data/app/models/ruby_llm/agents/execution/metrics.rb +41 -25
- data/app/models/ruby_llm/agents/execution/scopes.rb +234 -14
- data/app/models/ruby_llm/agents/execution.rb +259 -16
- data/app/services/ruby_llm/agents/agent_registry.rb +49 -12
- data/app/views/layouts/rubyllm/agents/application.html.erb +351 -85
- data/app/views/rubyllm/agents/agents/_version_comparison.html.erb +186 -0
- data/app/views/rubyllm/agents/agents/show.html.erb +233 -10
- data/app/views/rubyllm/agents/dashboard/_action_center.html.erb +62 -0
- data/app/views/rubyllm/agents/dashboard/_alerts_feed.html.erb +62 -0
- data/app/views/rubyllm/agents/dashboard/_breaker_strip.html.erb +47 -0
- data/app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb +165 -0
- data/app/views/rubyllm/agents/dashboard/_now_strip.html.erb +10 -0
- data/app/views/rubyllm/agents/dashboard/_now_strip_values.html.erb +71 -0
- data/app/views/rubyllm/agents/dashboard/index.html.erb +215 -109
- data/app/views/rubyllm/agents/executions/_filters.html.erb +152 -155
- data/app/views/rubyllm/agents/executions/_list.html.erb +103 -12
- data/app/views/rubyllm/agents/executions/dry_run.html.erb +149 -0
- data/app/views/rubyllm/agents/executions/index.html.erb +17 -72
- data/app/views/rubyllm/agents/executions/index.turbo_stream.erb +16 -2
- data/app/views/rubyllm/agents/executions/show.html.erb +693 -14
- data/app/views/rubyllm/agents/settings/show.html.erb +369 -0
- data/app/views/rubyllm/agents/shared/_filter_dropdown.html.erb +121 -0
- data/app/views/rubyllm/agents/shared/_select_dropdown.html.erb +85 -0
- data/config/routes.rb +7 -0
- data/lib/generators/ruby_llm_agents/templates/add_attempts_migration.rb.tt +27 -0
- data/lib/generators/ruby_llm_agents/templates/add_caching_migration.rb.tt +23 -0
- data/lib/generators/ruby_llm_agents/templates/add_finish_reason_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/add_routing_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/add_streaming_migration.rb.tt +8 -0
- data/lib/generators/ruby_llm_agents/templates/add_tracing_migration.rb.tt +34 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +66 -4
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +53 -6
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +143 -8
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +38 -1
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +78 -0
- data/lib/ruby_llm/agents/alert_manager.rb +207 -0
- data/lib/ruby_llm/agents/attempt_tracker.rb +295 -0
- data/lib/ruby_llm/agents/base.rb +597 -112
- data/lib/ruby_llm/agents/budget_tracker.rb +360 -0
- data/lib/ruby_llm/agents/circuit_breaker.rb +197 -0
- data/lib/ruby_llm/agents/configuration.rb +279 -1
- data/lib/ruby_llm/agents/engine.rb +58 -6
- data/lib/ruby_llm/agents/execution_logger_job.rb +17 -6
- data/lib/ruby_llm/agents/inflections.rb +13 -2
- data/lib/ruby_llm/agents/instrumentation.rb +538 -87
- data/lib/ruby_llm/agents/redactor.rb +130 -0
- data/lib/ruby_llm/agents/reliability.rb +185 -0
- data/lib/ruby_llm/agents/version.rb +3 -1
- data/lib/ruby_llm/agents.rb +52 -0
- metadata +41 -2
- data/app/controllers/ruby_llm/agents/application_controller.rb +0 -37
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
# Unified redaction utility for PII and sensitive data
|
|
6
|
+
#
|
|
7
|
+
# Provides methods to redact sensitive information from hashes, arrays, and strings
|
|
8
|
+
# based on configurable field names and regex patterns.
|
|
9
|
+
#
|
|
10
|
+
# @example Redacting a hash
|
|
11
|
+
# Redactor.redact({ password: "secret", name: "John" })
|
|
12
|
+
# # => { password: "[REDACTED]", name: "John" }
|
|
13
|
+
#
|
|
14
|
+
# @example Redacting a string with patterns
|
|
15
|
+
# Redactor.redact_string("SSN: 123-45-6789")
|
|
16
|
+
# # => "SSN: [REDACTED]"
|
|
17
|
+
#
|
|
18
|
+
# @see RubyLLM::Agents::Configuration
|
|
19
|
+
# @api public
|
|
20
|
+
module Redactor
|
|
21
|
+
class << self
|
|
22
|
+
# Redacts sensitive data from an object (hash, array, or primitive)
|
|
23
|
+
#
|
|
24
|
+
# @param obj [Object] The object to redact
|
|
25
|
+
# @param config [Configuration, nil] Optional configuration override
|
|
26
|
+
# @return [Object] The redacted object (new object, original not modified)
|
|
27
|
+
def redact(obj, config = nil)
|
|
28
|
+
config ||= RubyLLM::Agents.configuration
|
|
29
|
+
|
|
30
|
+
case obj
|
|
31
|
+
when Hash
|
|
32
|
+
redact_hash(obj, config)
|
|
33
|
+
when Array
|
|
34
|
+
redact_array(obj, config)
|
|
35
|
+
when String
|
|
36
|
+
redact_string(obj, config)
|
|
37
|
+
else
|
|
38
|
+
obj
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Redacts sensitive data from a string using configured patterns
|
|
43
|
+
#
|
|
44
|
+
# @param str [String, nil] The string to redact
|
|
45
|
+
# @param config [Configuration, nil] Optional configuration override
|
|
46
|
+
# @return [String, nil] The redacted string
|
|
47
|
+
def redact_string(str, config = nil)
|
|
48
|
+
return nil if str.nil?
|
|
49
|
+
return str unless str.is_a?(String)
|
|
50
|
+
|
|
51
|
+
config ||= RubyLLM::Agents.configuration
|
|
52
|
+
result = str.dup
|
|
53
|
+
|
|
54
|
+
# Apply pattern-based redaction
|
|
55
|
+
config.redaction_patterns.each do |pattern|
|
|
56
|
+
result = result.gsub(pattern, config.redaction_placeholder)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Truncate if max length is configured
|
|
60
|
+
max_length = config.redaction_max_value_length
|
|
61
|
+
if max_length && result.length > max_length
|
|
62
|
+
result = result[0, max_length] + "..."
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
result
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Redacts sensitive fields from a hash
|
|
71
|
+
#
|
|
72
|
+
# @param hash [Hash] The hash to redact
|
|
73
|
+
# @param config [Configuration] The configuration
|
|
74
|
+
# @return [Hash] The redacted hash
|
|
75
|
+
def redact_hash(hash, config)
|
|
76
|
+
hash.each_with_object({}) do |(key, value), result|
|
|
77
|
+
key_str = key.to_s.downcase
|
|
78
|
+
|
|
79
|
+
if sensitive_field?(key_str, config)
|
|
80
|
+
result[key] = config.redaction_placeholder
|
|
81
|
+
else
|
|
82
|
+
result[key] = redact_value(value, config)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Redacts sensitive values from an array
|
|
88
|
+
#
|
|
89
|
+
# @param array [Array] The array to redact
|
|
90
|
+
# @param config [Configuration] The configuration
|
|
91
|
+
# @return [Array] The redacted array
|
|
92
|
+
def redact_array(array, config)
|
|
93
|
+
array.map { |item| redact(item, config) }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Redacts a single value based on its type
|
|
97
|
+
#
|
|
98
|
+
# @param value [Object] The value to redact
|
|
99
|
+
# @param config [Configuration] The configuration
|
|
100
|
+
# @return [Object] The redacted value
|
|
101
|
+
def redact_value(value, config)
|
|
102
|
+
case value
|
|
103
|
+
when Hash
|
|
104
|
+
redact_hash(value, config)
|
|
105
|
+
when Array
|
|
106
|
+
redact_array(value, config)
|
|
107
|
+
when String
|
|
108
|
+
redact_string(value, config)
|
|
109
|
+
when defined?(ActiveRecord::Base) && ActiveRecord::Base
|
|
110
|
+
# Convert ActiveRecord objects to safe references
|
|
111
|
+
{ id: value&.id, type: value&.class&.name }
|
|
112
|
+
else
|
|
113
|
+
value
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Checks if a field name is sensitive
|
|
118
|
+
#
|
|
119
|
+
# @param field_name [String] The field name to check (lowercase)
|
|
120
|
+
# @param config [Configuration] The configuration
|
|
121
|
+
# @return [Boolean] true if the field should be redacted
|
|
122
|
+
def sensitive_field?(field_name, config)
|
|
123
|
+
config.redaction_fields.any? do |sensitive|
|
|
124
|
+
field_name.include?(sensitive.downcase)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RubyLLM
|
|
4
|
+
module Agents
|
|
5
|
+
# Reliability module providing error classes and utilities for retry/fallback logic
|
|
6
|
+
#
|
|
7
|
+
# This module defines custom error types for reliability features and provides
|
|
8
|
+
# utilities for determining which errors are retryable.
|
|
9
|
+
#
|
|
10
|
+
# @example Checking if an error is retryable
|
|
11
|
+
# Reliability.retryable_error?(Timeout::Error.new) # => true
|
|
12
|
+
# Reliability.retryable_error?(ArgumentError.new) # => false
|
|
13
|
+
#
|
|
14
|
+
# @see RubyLLM::Agents::Base
|
|
15
|
+
# @api public
|
|
16
|
+
module Reliability
|
|
17
|
+
# Base error class for all reliability-related errors
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
class Error < StandardError; end
|
|
21
|
+
|
|
22
|
+
# Raised when the circuit breaker is open and requests are being blocked
|
|
23
|
+
#
|
|
24
|
+
# @example
|
|
25
|
+
# raise CircuitBreakerOpenError.new("MyAgent", "gpt-4o")
|
|
26
|
+
#
|
|
27
|
+
# @api public
|
|
28
|
+
class CircuitBreakerOpenError < Error
|
|
29
|
+
attr_reader :agent_type, :model_id
|
|
30
|
+
|
|
31
|
+
# @param agent_type [String] The agent class name
|
|
32
|
+
# @param model_id [String] The model identifier
|
|
33
|
+
def initialize(agent_type, model_id)
|
|
34
|
+
@agent_type = agent_type
|
|
35
|
+
@model_id = model_id
|
|
36
|
+
super("Circuit breaker is open for #{agent_type} with model #{model_id}")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Raised when budget limits have been exceeded
|
|
41
|
+
#
|
|
42
|
+
# @example
|
|
43
|
+
# raise BudgetExceededError.new(:global_daily, 25.0, 27.5)
|
|
44
|
+
#
|
|
45
|
+
# @api public
|
|
46
|
+
class BudgetExceededError < Error
|
|
47
|
+
attr_reader :scope, :limit, :current
|
|
48
|
+
|
|
49
|
+
# @param scope [Symbol] The budget scope (:global_daily, :global_monthly, :per_agent_daily, etc.)
|
|
50
|
+
# @param limit [Float] The budget limit in USD
|
|
51
|
+
# @param current [Float] The current spend in USD
|
|
52
|
+
# @param agent_type [String, nil] The agent type for per-agent budgets
|
|
53
|
+
def initialize(scope, limit, current, agent_type: nil)
|
|
54
|
+
@scope = scope
|
|
55
|
+
@limit = limit
|
|
56
|
+
@current = current
|
|
57
|
+
@agent_type = agent_type
|
|
58
|
+
|
|
59
|
+
message = "Budget exceeded for #{scope}"
|
|
60
|
+
message += " (#{agent_type})" if agent_type
|
|
61
|
+
message += ": limit $#{limit}, current $#{current}"
|
|
62
|
+
super(message)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Raised when the total timeout for all retry/fallback attempts is exceeded
|
|
67
|
+
#
|
|
68
|
+
# @api public
|
|
69
|
+
class TotalTimeoutError < Error
|
|
70
|
+
attr_reader :timeout_seconds, :elapsed_seconds
|
|
71
|
+
|
|
72
|
+
# @param timeout_seconds [Integer] The configured total timeout
|
|
73
|
+
# @param elapsed_seconds [Float] The elapsed time when timeout occurred
|
|
74
|
+
def initialize(timeout_seconds, elapsed_seconds)
|
|
75
|
+
@timeout_seconds = timeout_seconds
|
|
76
|
+
@elapsed_seconds = elapsed_seconds
|
|
77
|
+
super("Total timeout of #{timeout_seconds}s exceeded (elapsed: #{elapsed_seconds.round(2)}s)")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Raised when all models in the fallback chain have been exhausted
|
|
82
|
+
#
|
|
83
|
+
# @api public
|
|
84
|
+
class AllModelsExhaustedError < Error
|
|
85
|
+
attr_reader :models_tried, :last_error
|
|
86
|
+
|
|
87
|
+
# @param models_tried [Array<String>] List of models that were attempted
|
|
88
|
+
# @param last_error [Exception] The last error that occurred
|
|
89
|
+
def initialize(models_tried, last_error)
|
|
90
|
+
@models_tried = models_tried
|
|
91
|
+
@last_error = last_error
|
|
92
|
+
super("All models exhausted: #{models_tried.join(', ')}. Last error: #{last_error.message}")
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class << self
|
|
97
|
+
# Default list of error classes that are considered retryable
|
|
98
|
+
#
|
|
99
|
+
# These errors typically indicate transient issues that may resolve on retry.
|
|
100
|
+
#
|
|
101
|
+
# @return [Array<Class>] Error classes that are retryable by default
|
|
102
|
+
def default_retryable_errors
|
|
103
|
+
@default_retryable_errors ||= [
|
|
104
|
+
Timeout::Error,
|
|
105
|
+
defined?(Net::OpenTimeout) ? Net::OpenTimeout : nil,
|
|
106
|
+
defined?(Net::ReadTimeout) ? Net::ReadTimeout : nil,
|
|
107
|
+
defined?(Faraday::TimeoutError) ? Faraday::TimeoutError : nil,
|
|
108
|
+
defined?(Faraday::ConnectionFailed) ? Faraday::ConnectionFailed : nil,
|
|
109
|
+
defined?(Errno::ECONNREFUSED) ? Errno::ECONNREFUSED : nil,
|
|
110
|
+
defined?(Errno::ECONNRESET) ? Errno::ECONNRESET : nil,
|
|
111
|
+
defined?(Errno::ETIMEDOUT) ? Errno::ETIMEDOUT : nil,
|
|
112
|
+
defined?(SocketError) ? SocketError : nil,
|
|
113
|
+
defined?(OpenSSL::SSL::SSLError) ? OpenSSL::SSL::SSLError : nil
|
|
114
|
+
].compact
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Determines if an error is retryable based on default and custom error classes
|
|
118
|
+
#
|
|
119
|
+
# @param error [Exception] The error to check
|
|
120
|
+
# @param custom_errors [Array<Class>] Additional error classes to consider retryable
|
|
121
|
+
# @return [Boolean] true if the error is retryable
|
|
122
|
+
def retryable_error?(error, custom_errors: [])
|
|
123
|
+
all_retryable = default_retryable_errors + Array(custom_errors)
|
|
124
|
+
all_retryable.any? { |klass| error.is_a?(klass) } || retryable_by_message?(error)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Determines if an error is retryable based on its message content
|
|
128
|
+
#
|
|
129
|
+
# Some errors (like HTTP 5xx responses) may not have specific error classes
|
|
130
|
+
# but can be identified by their message.
|
|
131
|
+
#
|
|
132
|
+
# @param error [Exception] The error to check
|
|
133
|
+
# @return [Boolean] true if the error message indicates a retryable condition
|
|
134
|
+
def retryable_by_message?(error)
|
|
135
|
+
message = error.message.to_s.downcase
|
|
136
|
+
retryable_patterns.any? { |pattern| message.include?(pattern) }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Patterns in error messages that indicate retryable errors
|
|
140
|
+
#
|
|
141
|
+
# @return [Array<String>] Patterns to match against error messages
|
|
142
|
+
def retryable_patterns
|
|
143
|
+
@retryable_patterns ||= [
|
|
144
|
+
"rate limit",
|
|
145
|
+
"rate_limit",
|
|
146
|
+
"too many requests",
|
|
147
|
+
"429",
|
|
148
|
+
"500",
|
|
149
|
+
"502",
|
|
150
|
+
"503",
|
|
151
|
+
"504",
|
|
152
|
+
"service unavailable",
|
|
153
|
+
"internal server error",
|
|
154
|
+
"bad gateway",
|
|
155
|
+
"gateway timeout",
|
|
156
|
+
"overloaded",
|
|
157
|
+
"capacity"
|
|
158
|
+
].freeze
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Calculates the backoff delay for a retry attempt
|
|
162
|
+
#
|
|
163
|
+
# @param strategy [Symbol] The backoff strategy (:constant or :exponential)
|
|
164
|
+
# @param base [Float] The base delay in seconds
|
|
165
|
+
# @param max_delay [Float] The maximum delay in seconds
|
|
166
|
+
# @param attempt [Integer] The current attempt number (0-indexed)
|
|
167
|
+
# @return [Float] The delay in seconds (including jitter)
|
|
168
|
+
def calculate_backoff(strategy:, base:, max_delay:, attempt:)
|
|
169
|
+
delay = case strategy
|
|
170
|
+
when :constant
|
|
171
|
+
base
|
|
172
|
+
when :exponential
|
|
173
|
+
[base * (2**attempt), max_delay].min
|
|
174
|
+
else
|
|
175
|
+
base
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Add jitter (0 to 50% of delay)
|
|
179
|
+
jitter = rand * delay * 0.5
|
|
180
|
+
delay + jitter
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
data/lib/ruby_llm/agents.rb
CHANGED
|
@@ -1,23 +1,75 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "csv"
|
|
4
|
+
|
|
3
5
|
require_relative "agents/version"
|
|
4
6
|
require_relative "agents/configuration"
|
|
7
|
+
require_relative "agents/reliability"
|
|
8
|
+
require_relative "agents/redactor"
|
|
9
|
+
require_relative "agents/circuit_breaker"
|
|
10
|
+
require_relative "agents/budget_tracker"
|
|
11
|
+
require_relative "agents/alert_manager"
|
|
12
|
+
require_relative "agents/attempt_tracker"
|
|
5
13
|
require_relative "agents/inflections" if defined?(Rails)
|
|
6
14
|
require_relative "agents/engine" if defined?(Rails::Engine)
|
|
7
15
|
|
|
8
16
|
module RubyLLM
|
|
17
|
+
# Agent framework for building LLM-powered agents with observability
|
|
18
|
+
#
|
|
19
|
+
# RubyLLM::Agents provides a DSL for creating agents that interact with
|
|
20
|
+
# large language models, with built-in execution tracking, cost monitoring,
|
|
21
|
+
# and a dashboard for observability.
|
|
22
|
+
#
|
|
23
|
+
# @example Basic configuration
|
|
24
|
+
# RubyLLM::Agents.configure do |config|
|
|
25
|
+
# config.default_model = "gpt-4o"
|
|
26
|
+
# config.async_logging = true
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# @example Creating an agent
|
|
30
|
+
# class SearchAgent < ApplicationAgent
|
|
31
|
+
# model "gpt-4o"
|
|
32
|
+
# param :query, required: true
|
|
33
|
+
#
|
|
34
|
+
# def user_prompt
|
|
35
|
+
# "Search for: #{query}"
|
|
36
|
+
# end
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# SearchAgent.call(query: "ruby gems")
|
|
40
|
+
#
|
|
41
|
+
# @see RubyLLM::Agents::Base
|
|
42
|
+
# @see RubyLLM::Agents::Configuration
|
|
9
43
|
module Agents
|
|
44
|
+
# Base error class for agent-related exceptions
|
|
10
45
|
class Error < StandardError; end
|
|
11
46
|
|
|
12
47
|
class << self
|
|
48
|
+
# Returns the global configuration instance
|
|
49
|
+
#
|
|
50
|
+
# @return [Configuration] The configuration object
|
|
13
51
|
def configuration
|
|
14
52
|
@configuration ||= Configuration.new
|
|
15
53
|
end
|
|
16
54
|
|
|
55
|
+
# Yields the configuration for modification
|
|
56
|
+
#
|
|
57
|
+
# @yield [Configuration] The configuration object
|
|
58
|
+
# @return [void]
|
|
59
|
+
# @example
|
|
60
|
+
# RubyLLM::Agents.configure do |config|
|
|
61
|
+
# config.default_model = "claude-3-sonnet"
|
|
62
|
+
# end
|
|
17
63
|
def configure
|
|
18
64
|
yield(configuration)
|
|
19
65
|
end
|
|
20
66
|
|
|
67
|
+
# Resets configuration to defaults
|
|
68
|
+
#
|
|
69
|
+
# Primarily used for testing to ensure clean state.
|
|
70
|
+
#
|
|
71
|
+
# @return [Configuration] A new configuration instance
|
|
72
|
+
# @api private
|
|
21
73
|
def reset_configuration!
|
|
22
74
|
@configuration = Configuration.new
|
|
23
75
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby_llm-agents
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- adham90
|
|
@@ -79,6 +79,20 @@ dependencies:
|
|
|
79
79
|
- - ">="
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '5.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: csv
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0'
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
82
96
|
description: A Rails engine for creating, managing, and monitoring LLM-powered agents.
|
|
83
97
|
Includes a DSL for agent configuration, execution tracking, cost analytics, and
|
|
84
98
|
a mountable dashboard UI.
|
|
@@ -91,10 +105,12 @@ files:
|
|
|
91
105
|
- LICENSE.txt
|
|
92
106
|
- README.md
|
|
93
107
|
- app/channels/ruby_llm/agents/executions_channel.rb
|
|
108
|
+
- app/controllers/concerns/ruby_llm/agents/filterable.rb
|
|
109
|
+
- app/controllers/concerns/ruby_llm/agents/paginatable.rb
|
|
94
110
|
- app/controllers/ruby_llm/agents/agents_controller.rb
|
|
95
|
-
- app/controllers/ruby_llm/agents/application_controller.rb
|
|
96
111
|
- app/controllers/ruby_llm/agents/dashboard_controller.rb
|
|
97
112
|
- app/controllers/ruby_llm/agents/executions_controller.rb
|
|
113
|
+
- app/controllers/ruby_llm/agents/settings_controller.rb
|
|
98
114
|
- app/helpers/ruby_llm/agents/application_helper.rb
|
|
99
115
|
- app/javascript/ruby_llm/agents/controllers/filter_controller.js
|
|
100
116
|
- app/javascript/ruby_llm/agents/controllers/index.js
|
|
@@ -105,24 +121,41 @@ files:
|
|
|
105
121
|
- app/models/ruby_llm/agents/execution/scopes.rb
|
|
106
122
|
- app/services/ruby_llm/agents/agent_registry.rb
|
|
107
123
|
- app/views/layouts/rubyllm/agents/application.html.erb
|
|
124
|
+
- app/views/rubyllm/agents/agents/_version_comparison.html.erb
|
|
108
125
|
- app/views/rubyllm/agents/agents/index.html.erb
|
|
109
126
|
- app/views/rubyllm/agents/agents/show.html.erb
|
|
127
|
+
- app/views/rubyllm/agents/dashboard/_action_center.html.erb
|
|
128
|
+
- app/views/rubyllm/agents/dashboard/_alerts_feed.html.erb
|
|
129
|
+
- app/views/rubyllm/agents/dashboard/_breaker_strip.html.erb
|
|
130
|
+
- app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb
|
|
110
131
|
- app/views/rubyllm/agents/dashboard/_execution_item.html.erb
|
|
132
|
+
- app/views/rubyllm/agents/dashboard/_now_strip.html.erb
|
|
133
|
+
- app/views/rubyllm/agents/dashboard/_now_strip_values.html.erb
|
|
111
134
|
- app/views/rubyllm/agents/dashboard/index.html.erb
|
|
112
135
|
- app/views/rubyllm/agents/executions/_execution.html.erb
|
|
113
136
|
- app/views/rubyllm/agents/executions/_filters.html.erb
|
|
114
137
|
- app/views/rubyllm/agents/executions/_list.html.erb
|
|
138
|
+
- app/views/rubyllm/agents/executions/dry_run.html.erb
|
|
115
139
|
- app/views/rubyllm/agents/executions/index.html.erb
|
|
116
140
|
- app/views/rubyllm/agents/executions/index.turbo_stream.erb
|
|
117
141
|
- app/views/rubyllm/agents/executions/show.html.erb
|
|
142
|
+
- app/views/rubyllm/agents/settings/show.html.erb
|
|
118
143
|
- app/views/rubyllm/agents/shared/_executions_table.html.erb
|
|
144
|
+
- app/views/rubyllm/agents/shared/_filter_dropdown.html.erb
|
|
145
|
+
- app/views/rubyllm/agents/shared/_select_dropdown.html.erb
|
|
119
146
|
- app/views/rubyllm/agents/shared/_stat_card.html.erb
|
|
120
147
|
- app/views/rubyllm/agents/shared/_status_badge.html.erb
|
|
121
148
|
- app/views/rubyllm/agents/shared/_status_dot.html.erb
|
|
122
149
|
- config/routes.rb
|
|
123
150
|
- lib/generators/ruby_llm_agents/agent_generator.rb
|
|
124
151
|
- lib/generators/ruby_llm_agents/install_generator.rb
|
|
152
|
+
- lib/generators/ruby_llm_agents/templates/add_attempts_migration.rb.tt
|
|
153
|
+
- lib/generators/ruby_llm_agents/templates/add_caching_migration.rb.tt
|
|
154
|
+
- lib/generators/ruby_llm_agents/templates/add_finish_reason_migration.rb.tt
|
|
125
155
|
- lib/generators/ruby_llm_agents/templates/add_prompts_migration.rb.tt
|
|
156
|
+
- lib/generators/ruby_llm_agents/templates/add_routing_migration.rb.tt
|
|
157
|
+
- lib/generators/ruby_llm_agents/templates/add_streaming_migration.rb.tt
|
|
158
|
+
- lib/generators/ruby_llm_agents/templates/add_tracing_migration.rb.tt
|
|
126
159
|
- lib/generators/ruby_llm_agents/templates/agent.rb.tt
|
|
127
160
|
- lib/generators/ruby_llm_agents/templates/application_agent.rb.tt
|
|
128
161
|
- lib/generators/ruby_llm_agents/templates/initializer.rb.tt
|
|
@@ -130,12 +163,18 @@ files:
|
|
|
130
163
|
- lib/generators/ruby_llm_agents/upgrade_generator.rb
|
|
131
164
|
- lib/ruby_llm-agents.rb
|
|
132
165
|
- lib/ruby_llm/agents.rb
|
|
166
|
+
- lib/ruby_llm/agents/alert_manager.rb
|
|
167
|
+
- lib/ruby_llm/agents/attempt_tracker.rb
|
|
133
168
|
- lib/ruby_llm/agents/base.rb
|
|
169
|
+
- lib/ruby_llm/agents/budget_tracker.rb
|
|
170
|
+
- lib/ruby_llm/agents/circuit_breaker.rb
|
|
134
171
|
- lib/ruby_llm/agents/configuration.rb
|
|
135
172
|
- lib/ruby_llm/agents/engine.rb
|
|
136
173
|
- lib/ruby_llm/agents/execution_logger_job.rb
|
|
137
174
|
- lib/ruby_llm/agents/inflections.rb
|
|
138
175
|
- lib/ruby_llm/agents/instrumentation.rb
|
|
176
|
+
- lib/ruby_llm/agents/redactor.rb
|
|
177
|
+
- lib/ruby_llm/agents/reliability.rb
|
|
139
178
|
- lib/ruby_llm/agents/version.rb
|
|
140
179
|
homepage: https://github.com/adham90/ruby_llm-agents
|
|
141
180
|
licenses:
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RubyLLM
|
|
4
|
-
module Agents
|
|
5
|
-
class ApplicationController < ActionController::Base
|
|
6
|
-
layout "rubyllm/agents/application"
|
|
7
|
-
|
|
8
|
-
before_action :authenticate_dashboard!
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
def authenticate_dashboard!
|
|
13
|
-
if basic_auth_configured?
|
|
14
|
-
authenticate_with_http_basic_auth
|
|
15
|
-
else
|
|
16
|
-
auth_proc = RubyLLM::Agents.configuration.dashboard_auth
|
|
17
|
-
return if auth_proc.call(self)
|
|
18
|
-
|
|
19
|
-
render plain: "Unauthorized", status: :unauthorized
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def basic_auth_configured?
|
|
24
|
-
config = RubyLLM::Agents.configuration
|
|
25
|
-
config.basic_auth_username.present? && config.basic_auth_password.present?
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def authenticate_with_http_basic_auth
|
|
29
|
-
config = RubyLLM::Agents.configuration
|
|
30
|
-
authenticate_or_request_with_http_basic("RubyLLM Agents") do |username, password|
|
|
31
|
-
ActiveSupport::SecurityUtils.secure_compare(username, config.basic_auth_username) &&
|
|
32
|
-
ActiveSupport::SecurityUtils.secure_compare(password, config.basic_auth_password)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|