ruby_llm-agents 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +273 -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 +139 -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 +580 -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,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration to add finish_reason column to executions
|
|
4
|
+
#
|
|
5
|
+
# This column tracks why the LLM stopped generating:
|
|
6
|
+
# - stop: Natural end of response
|
|
7
|
+
# - length: Hit max_tokens limit
|
|
8
|
+
# - content_filter: Blocked by safety filter
|
|
9
|
+
# - tool_calls: Model wants to call a tool
|
|
10
|
+
# - other: Unknown reason
|
|
11
|
+
#
|
|
12
|
+
# Note: streaming and time_to_first_token_ms are added by add_streaming migration
|
|
13
|
+
#
|
|
14
|
+
# Run with: rails db:migrate
|
|
15
|
+
class AddFinishReasonToRubyLLMAgentsExecutions < ActiveRecord::Migration<%= migration_version %>
|
|
16
|
+
def change
|
|
17
|
+
add_column :ruby_llm_agents_executions, :finish_reason, :string
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration to add routing and retry tracking columns to executions
|
|
4
|
+
#
|
|
5
|
+
# These columns help understand why fallbacks happen and which errors
|
|
6
|
+
# are retryable for reliability analysis.
|
|
7
|
+
#
|
|
8
|
+
# Run with: rails db:migrate
|
|
9
|
+
class AddRoutingToRubyLLMAgentsExecutions < ActiveRecord::Migration<%= migration_version %>
|
|
10
|
+
def change
|
|
11
|
+
# Fallback tracking - why did we switch models?
|
|
12
|
+
# Values: price_limit, quality_fail, rate_limit, timeout, safety, other
|
|
13
|
+
add_column :ruby_llm_agents_executions, :fallback_reason, :string
|
|
14
|
+
|
|
15
|
+
# Error classification
|
|
16
|
+
add_column :ruby_llm_agents_executions, :retryable, :boolean
|
|
17
|
+
add_column :ruby_llm_agents_executions, :rate_limited, :boolean
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddStreamingToRubyLLMAgentsExecutions < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
def change
|
|
5
|
+
add_column :ruby_llm_agents_executions, :streaming, :boolean, default: false
|
|
6
|
+
add_column :ruby_llm_agents_executions, :time_to_first_token_ms, :integer
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Migration to add distributed tracing columns to executions
|
|
4
|
+
#
|
|
5
|
+
# These columns enable linking executions across services and debugging
|
|
6
|
+
# production issues by correlating agent calls with web requests.
|
|
7
|
+
#
|
|
8
|
+
# Run with: rails db:migrate
|
|
9
|
+
class AddTracingToRubyLLMAgentsExecutions < ActiveRecord::Migration<%= migration_version %>
|
|
10
|
+
def change
|
|
11
|
+
# Request correlation - links to the originating HTTP request
|
|
12
|
+
add_column :ruby_llm_agents_executions, :request_id, :string
|
|
13
|
+
|
|
14
|
+
# OpenTelemetry tracing - for distributed tracing
|
|
15
|
+
add_column :ruby_llm_agents_executions, :trace_id, :string
|
|
16
|
+
add_column :ruby_llm_agents_executions, :span_id, :string
|
|
17
|
+
|
|
18
|
+
# Execution hierarchy - for nested agent calls
|
|
19
|
+
add_column :ruby_llm_agents_executions, :parent_execution_id, :bigint
|
|
20
|
+
add_column :ruby_llm_agents_executions, :root_execution_id, :bigint
|
|
21
|
+
|
|
22
|
+
# Indexes for common lookups
|
|
23
|
+
add_index :ruby_llm_agents_executions, :request_id
|
|
24
|
+
add_index :ruby_llm_agents_executions, :trace_id
|
|
25
|
+
add_index :ruby_llm_agents_executions, :parent_execution_id
|
|
26
|
+
add_index :ruby_llm_agents_executions, :root_execution_id
|
|
27
|
+
|
|
28
|
+
# Foreign keys for execution hierarchy
|
|
29
|
+
add_foreign_key :ruby_llm_agents_executions, :ruby_llm_agents_executions,
|
|
30
|
+
column: :parent_execution_id, on_delete: :nullify
|
|
31
|
+
add_foreign_key :ruby_llm_agents_executions, :ruby_llm_agents_executions,
|
|
32
|
+
column: :root_execution_id, on_delete: :nullify
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -1,21 +1,63 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class <%= class_name %>Agent < ApplicationAgent
|
|
4
|
+
# ============================================
|
|
5
|
+
# Model Configuration
|
|
6
|
+
# ============================================
|
|
7
|
+
|
|
4
8
|
model "<%= options[:model] %>"
|
|
5
9
|
temperature <%= options[:temperature] %>
|
|
6
10
|
version "1.0"
|
|
11
|
+
# timeout 30 # Per-request timeout in seconds (default: 60)
|
|
12
|
+
|
|
13
|
+
# ============================================
|
|
14
|
+
# Caching
|
|
15
|
+
# ============================================
|
|
16
|
+
|
|
7
17
|
<% if options[:cache] -%>
|
|
8
18
|
cache <%= options[:cache] %>
|
|
9
19
|
<% else -%>
|
|
10
|
-
# cache 1.hour #
|
|
20
|
+
# cache 1.hour # Enable response caching with TTL
|
|
11
21
|
<% end -%>
|
|
12
22
|
|
|
23
|
+
# ============================================
|
|
24
|
+
# Reliability (Retries & Fallbacks)
|
|
25
|
+
# ============================================
|
|
26
|
+
|
|
27
|
+
# Automatic retries with exponential backoff
|
|
28
|
+
# - max: Number of retry attempts
|
|
29
|
+
# - backoff: :constant or :exponential
|
|
30
|
+
# - base: Base delay in seconds
|
|
31
|
+
# - max_delay: Maximum delay between retries
|
|
32
|
+
# - on: Additional error classes to retry on
|
|
33
|
+
# retries max: 2, backoff: :exponential, base: 0.4, max_delay: 3.0
|
|
34
|
+
|
|
35
|
+
# Fallback models (tried in order when primary model fails)
|
|
36
|
+
# fallback_models ["gpt-4o-mini", "claude-3-haiku"]
|
|
37
|
+
|
|
38
|
+
# Total timeout across all retry/fallback attempts
|
|
39
|
+
# total_timeout 30
|
|
40
|
+
|
|
41
|
+
# Circuit breaker (prevents repeated calls to failing models)
|
|
42
|
+
# - errors: Number of errors to trigger open state
|
|
43
|
+
# - within: Rolling window in seconds
|
|
44
|
+
# - cooldown: Time to wait before allowing requests again
|
|
45
|
+
# circuit_breaker errors: 5, within: 60, cooldown: 300
|
|
46
|
+
|
|
47
|
+
# ============================================
|
|
48
|
+
# Parameters
|
|
49
|
+
# ============================================
|
|
50
|
+
|
|
13
51
|
<% parsed_params.each do |param| -%>
|
|
14
52
|
param :<%= param.name %><%= ", required: true" if param.required? %><%= ", default: #{param.default.inspect}" if param.default && !param.required? %>
|
|
15
53
|
<% end -%>
|
|
16
54
|
|
|
17
55
|
private
|
|
18
56
|
|
|
57
|
+
# ============================================
|
|
58
|
+
# Prompts (required)
|
|
59
|
+
# ============================================
|
|
60
|
+
|
|
19
61
|
def system_prompt
|
|
20
62
|
<<~PROMPT
|
|
21
63
|
You are a helpful assistant.
|
|
@@ -32,15 +74,35 @@ class <%= class_name %>Agent < ApplicationAgent
|
|
|
32
74
|
<% end -%>
|
|
33
75
|
end
|
|
34
76
|
|
|
35
|
-
#
|
|
77
|
+
# ============================================
|
|
78
|
+
# Optional Overrides
|
|
79
|
+
# ============================================
|
|
80
|
+
|
|
81
|
+
# Structured output schema (returns parsed hash instead of raw text)
|
|
36
82
|
# def schema
|
|
37
83
|
# @schema ||= RubyLLM::Schema.create do
|
|
38
84
|
# string :result, description: "The result"
|
|
85
|
+
# integer :confidence, description: "Confidence score 1-100"
|
|
86
|
+
# array :tags, description: "Relevant tags" do
|
|
87
|
+
# string
|
|
88
|
+
# end
|
|
39
89
|
# end
|
|
40
90
|
# end
|
|
41
91
|
|
|
42
|
-
#
|
|
92
|
+
# Custom response processing (default: symbolize hash keys)
|
|
93
|
+
# def process_response(response)
|
|
94
|
+
# content = response.content
|
|
95
|
+
# # Transform or validate the response
|
|
96
|
+
# content
|
|
97
|
+
# end
|
|
98
|
+
|
|
99
|
+
# Custom metadata to include in execution logs
|
|
43
100
|
# def execution_metadata
|
|
44
|
-
# { custom_field: value }
|
|
101
|
+
# { custom_field: "value", request_id: params[:request_id] }
|
|
102
|
+
# end
|
|
103
|
+
|
|
104
|
+
# Custom cache key data (default: all params except skip_cache, dry_run)
|
|
105
|
+
# def cache_key_data
|
|
106
|
+
# { query: params[:query], locale: I18n.locale }
|
|
45
107
|
# end
|
|
46
108
|
end
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# ApplicationAgent - Base class for all agents in this application
|
|
4
4
|
#
|
|
5
|
-
# All agents
|
|
5
|
+
# All agents inherit from this class. Configure shared settings here
|
|
6
|
+
# that apply to all agents, or override them per-agent as needed.
|
|
6
7
|
#
|
|
7
8
|
# Example:
|
|
8
9
|
# class MyAgent < ApplicationAgent
|
|
@@ -13,10 +14,56 @@
|
|
|
13
14
|
# end
|
|
14
15
|
# end
|
|
15
16
|
#
|
|
17
|
+
# Usage:
|
|
18
|
+
# MyAgent.call(query: "hello")
|
|
19
|
+
# MyAgent.call(query: "hello", dry_run: true) # Debug mode
|
|
20
|
+
# MyAgent.call(query: "hello", skip_cache: true) # Bypass cache
|
|
21
|
+
#
|
|
16
22
|
class ApplicationAgent < RubyLLM::Agents::Base
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
|
|
23
|
+
# ============================================
|
|
24
|
+
# Shared Model Configuration
|
|
25
|
+
# ============================================
|
|
26
|
+
# These settings are inherited by all agents
|
|
27
|
+
|
|
28
|
+
# model "gemini-2.0-flash" # Default model for all agents
|
|
29
|
+
# temperature 0.0 # Default temperature (0.0 = deterministic)
|
|
30
|
+
# timeout 60 # Default timeout in seconds
|
|
31
|
+
|
|
32
|
+
# ============================================
|
|
33
|
+
# Shared Caching
|
|
34
|
+
# ============================================
|
|
35
|
+
|
|
36
|
+
# cache 1.hour # Enable caching for all agents (override per-agent if needed)
|
|
37
|
+
|
|
38
|
+
# ============================================
|
|
39
|
+
# Shared Reliability Settings
|
|
40
|
+
# ============================================
|
|
41
|
+
# Configure once here, all agents inherit these settings
|
|
42
|
+
|
|
43
|
+
# Automatic retries for all agents
|
|
44
|
+
# retries max: 2, backoff: :exponential, base: 0.4, max_delay: 3.0
|
|
45
|
+
|
|
46
|
+
# Shared fallback models
|
|
47
|
+
# fallback_models ["gpt-4o-mini", "claude-3-haiku"]
|
|
48
|
+
|
|
49
|
+
# Total timeout across retries/fallbacks
|
|
50
|
+
# total_timeout 30
|
|
51
|
+
|
|
52
|
+
# Circuit breaker (per agent-model pair)
|
|
53
|
+
# circuit_breaker errors: 5, within: 60, cooldown: 300
|
|
54
|
+
|
|
55
|
+
# ============================================
|
|
56
|
+
# Shared Helper Methods
|
|
57
|
+
# ============================================
|
|
58
|
+
# Define methods here that can be used by all agents
|
|
59
|
+
|
|
60
|
+
# Example: Common system prompt prefix
|
|
61
|
+
# def system_prompt_prefix
|
|
62
|
+
# "You are an AI assistant for #{Rails.application.class.module_parent_name}."
|
|
63
|
+
# end
|
|
64
|
+
|
|
65
|
+
# Example: Common metadata
|
|
66
|
+
# def execution_metadata
|
|
67
|
+
# { app_version: Rails.application.config.version }
|
|
68
|
+
# end
|
|
22
69
|
end
|
|
@@ -5,37 +5,168 @@
|
|
|
5
5
|
# For more information, see: https://github.com/adham90/ruby_llm-agents
|
|
6
6
|
|
|
7
7
|
RubyLLM::Agents.configure do |config|
|
|
8
|
-
#
|
|
8
|
+
# ============================================
|
|
9
|
+
# Model Defaults
|
|
10
|
+
# ============================================
|
|
11
|
+
|
|
12
|
+
# Default LLM model for all agents (can be overridden per agent with `model "model-name"`)
|
|
9
13
|
# config.default_model = "gemini-2.0-flash"
|
|
10
14
|
|
|
11
|
-
# Default temperature (0.0 = deterministic,
|
|
15
|
+
# Default temperature (0.0 = deterministic, 2.0 = creative)
|
|
12
16
|
# config.default_temperature = 0.0
|
|
13
17
|
|
|
14
|
-
# Default timeout in seconds
|
|
18
|
+
# Default timeout in seconds for each LLM request
|
|
15
19
|
# config.default_timeout = 60
|
|
16
20
|
|
|
21
|
+
# ============================================
|
|
22
|
+
# Caching
|
|
23
|
+
# ============================================
|
|
24
|
+
|
|
17
25
|
# Cache store for agent response caching (defaults to Rails.cache)
|
|
18
26
|
# config.cache_store = Rails.cache
|
|
27
|
+
# config.cache_store = ActiveSupport::Cache::MemoryStore.new
|
|
28
|
+
|
|
29
|
+
# ============================================
|
|
30
|
+
# Execution Logging
|
|
31
|
+
# ============================================
|
|
19
32
|
|
|
20
|
-
# Async logging
|
|
33
|
+
# Async logging via background job (recommended for production)
|
|
34
|
+
# Set to false to log synchronously (useful for debugging)
|
|
21
35
|
# config.async_logging = true
|
|
22
36
|
|
|
37
|
+
# Number of retry attempts for the async logging job on failure
|
|
38
|
+
# config.job_retry_attempts = 3
|
|
39
|
+
|
|
23
40
|
# Retention period for execution records (used by cleanup tasks)
|
|
24
41
|
# config.retention_period = 30.days
|
|
25
42
|
|
|
26
|
-
#
|
|
43
|
+
# ============================================
|
|
44
|
+
# Anomaly Detection
|
|
45
|
+
# ============================================
|
|
46
|
+
|
|
47
|
+
# Executions exceeding these thresholds are logged as warnings
|
|
27
48
|
# config.anomaly_cost_threshold = 5.00 # dollars
|
|
28
49
|
# config.anomaly_duration_threshold = 10_000 # milliseconds
|
|
29
50
|
|
|
30
|
-
#
|
|
51
|
+
# ============================================
|
|
52
|
+
# Dashboard Authentication
|
|
53
|
+
# ============================================
|
|
54
|
+
|
|
55
|
+
# Option 1: HTTP Basic Auth (simple username/password protection)
|
|
56
|
+
# Both username and password must be set to enable Basic Auth
|
|
31
57
|
# config.basic_auth_username = "admin"
|
|
32
58
|
# config.basic_auth_password = Rails.application.credentials.agents_password
|
|
33
59
|
|
|
34
|
-
#
|
|
60
|
+
# Option 2: Custom authentication (advanced)
|
|
35
61
|
# Return true to allow access, false to deny
|
|
36
|
-
# Note: If
|
|
62
|
+
# Note: If basic_auth is set, it takes precedence over dashboard_auth
|
|
37
63
|
# config.dashboard_auth = ->(controller) { controller.current_user&.admin? }
|
|
38
64
|
|
|
39
65
|
# Parent controller for dashboard (for authentication/layout inheritance)
|
|
40
66
|
# config.dashboard_parent_controller = "ApplicationController"
|
|
67
|
+
# config.dashboard_parent_controller = "AdminController"
|
|
68
|
+
|
|
69
|
+
# ============================================
|
|
70
|
+
# Dashboard Display
|
|
71
|
+
# ============================================
|
|
72
|
+
|
|
73
|
+
# Number of records per page in dashboard listings
|
|
74
|
+
# config.per_page = 25
|
|
75
|
+
|
|
76
|
+
# Number of recent executions shown on the dashboard home
|
|
77
|
+
# config.recent_executions_limit = 10
|
|
78
|
+
|
|
79
|
+
# ============================================
|
|
80
|
+
# Reliability Defaults
|
|
81
|
+
# ============================================
|
|
82
|
+
# These defaults apply to all agents unless overridden per-agent
|
|
83
|
+
|
|
84
|
+
# Default retry configuration
|
|
85
|
+
# - max: Maximum retry attempts (0 = disabled)
|
|
86
|
+
# - backoff: Strategy (:constant or :exponential)
|
|
87
|
+
# - base: Base delay in seconds
|
|
88
|
+
# - max_delay: Maximum delay between retries
|
|
89
|
+
# - on: Additional error classes to retry on (extends defaults)
|
|
90
|
+
# config.default_retries = {
|
|
91
|
+
# max: 2,
|
|
92
|
+
# backoff: :exponential,
|
|
93
|
+
# base: 0.4,
|
|
94
|
+
# max_delay: 3.0,
|
|
95
|
+
# on: []
|
|
96
|
+
# }
|
|
97
|
+
|
|
98
|
+
# Default fallback models (tried in order when primary model fails)
|
|
99
|
+
# config.default_fallback_models = ["gpt-4o-mini", "claude-3-haiku"]
|
|
100
|
+
|
|
101
|
+
# Default total timeout across all retry/fallback attempts (nil = no limit)
|
|
102
|
+
# config.default_total_timeout = 30
|
|
103
|
+
|
|
104
|
+
# ============================================
|
|
105
|
+
# Governance - Budget Tracking
|
|
106
|
+
# ============================================
|
|
107
|
+
|
|
108
|
+
# Budget limits for cost governance
|
|
109
|
+
# - global_daily/global_monthly: Limits across all agents
|
|
110
|
+
# - per_agent_daily/per_agent_monthly: Per-agent limits (Hash of agent name => limit)
|
|
111
|
+
# - enforcement: :none (disabled), :soft (warn only), :hard (block requests)
|
|
112
|
+
# config.budgets = {
|
|
113
|
+
# global_daily: 25.0,
|
|
114
|
+
# global_monthly: 500.0,
|
|
115
|
+
# per_agent_daily: {
|
|
116
|
+
# "ContentGeneratorAgent" => 10.0,
|
|
117
|
+
# "SummaryAgent" => 5.0
|
|
118
|
+
# },
|
|
119
|
+
# per_agent_monthly: {
|
|
120
|
+
# "ContentGeneratorAgent" => 200.0
|
|
121
|
+
# },
|
|
122
|
+
# enforcement: :soft
|
|
123
|
+
# }
|
|
124
|
+
|
|
125
|
+
# ============================================
|
|
126
|
+
# Governance - Alerts
|
|
127
|
+
# ============================================
|
|
128
|
+
|
|
129
|
+
# Alert notifications for important events
|
|
130
|
+
# - slack_webhook_url: Slack incoming webhook URL
|
|
131
|
+
# - webhook_url: Generic webhook URL (receives JSON POST)
|
|
132
|
+
# - on_events: Events to trigger alerts
|
|
133
|
+
# - :budget_soft_cap - Soft budget limit reached
|
|
134
|
+
# - :budget_hard_cap - Hard budget limit exceeded
|
|
135
|
+
# - :breaker_open - Circuit breaker opened
|
|
136
|
+
# - :agent_anomaly - Cost/duration anomaly detected
|
|
137
|
+
# - custom: Lambda for custom handling
|
|
138
|
+
# config.alerts = {
|
|
139
|
+
# slack_webhook_url: ENV["SLACK_AGENTS_WEBHOOK"],
|
|
140
|
+
# webhook_url: ENV["AGENTS_ALERT_WEBHOOK"],
|
|
141
|
+
# on_events: [:budget_soft_cap, :budget_hard_cap, :breaker_open],
|
|
142
|
+
# custom: ->(event, payload) {
|
|
143
|
+
# Rails.logger.info("[AgentAlert] #{event}: #{payload}")
|
|
144
|
+
# }
|
|
145
|
+
# }
|
|
146
|
+
|
|
147
|
+
# ============================================
|
|
148
|
+
# Governance - Data Handling
|
|
149
|
+
# ============================================
|
|
150
|
+
|
|
151
|
+
# Whether to persist prompts in execution records
|
|
152
|
+
# Set to false to reduce storage or for privacy compliance
|
|
153
|
+
# config.persist_prompts = true
|
|
154
|
+
|
|
155
|
+
# Whether to persist LLM responses in execution records
|
|
156
|
+
# config.persist_responses = true
|
|
157
|
+
|
|
158
|
+
# Redaction configuration for PII and sensitive data
|
|
159
|
+
# - fields: Parameter names to redact (extends defaults: password, token, api_key, secret, etc.)
|
|
160
|
+
# - patterns: Regex patterns to match and redact in string values
|
|
161
|
+
# - placeholder: String to replace redacted values with
|
|
162
|
+
# - max_value_length: Truncate values longer than this (nil = no limit)
|
|
163
|
+
# config.redaction = {
|
|
164
|
+
# fields: %w[ssn credit_card phone_number email],
|
|
165
|
+
# patterns: [
|
|
166
|
+
# /\b\d{3}-\d{2}-\d{4}\b/, # SSN
|
|
167
|
+
# /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/ # Credit card
|
|
168
|
+
# ],
|
|
169
|
+
# placeholder: "[REDACTED]",
|
|
170
|
+
# max_value_length: 5000
|
|
171
|
+
# }
|
|
41
172
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
class
|
|
3
|
+
class CreateRubyLLMAgentsExecutions < ActiveRecord::Migration<%= migration_version %>
|
|
4
4
|
def change
|
|
5
5
|
create_table :ruby_llm_agents_executions do |t|
|
|
6
6
|
# Agent identification
|
|
@@ -17,6 +17,28 @@ class CreateRubyLlmAgentsExecutions < ActiveRecord::Migration<%= migration_versi
|
|
|
17
17
|
t.datetime :completed_at
|
|
18
18
|
t.integer :duration_ms
|
|
19
19
|
|
|
20
|
+
# Streaming and finish
|
|
21
|
+
t.boolean :streaming, default: false
|
|
22
|
+
t.integer :time_to_first_token_ms
|
|
23
|
+
t.string :finish_reason
|
|
24
|
+
|
|
25
|
+
# Distributed tracing
|
|
26
|
+
t.string :request_id
|
|
27
|
+
t.string :trace_id
|
|
28
|
+
t.string :span_id
|
|
29
|
+
t.bigint :parent_execution_id
|
|
30
|
+
t.bigint :root_execution_id
|
|
31
|
+
|
|
32
|
+
# Routing and retries
|
|
33
|
+
t.string :fallback_reason
|
|
34
|
+
t.boolean :retryable
|
|
35
|
+
t.boolean :rate_limited
|
|
36
|
+
|
|
37
|
+
# Caching
|
|
38
|
+
t.boolean :cache_hit, default: false
|
|
39
|
+
t.string :response_cache_key
|
|
40
|
+
t.datetime :cached_at
|
|
41
|
+
|
|
20
42
|
# Status
|
|
21
43
|
t.string :status, default: "success", null: false
|
|
22
44
|
|
|
@@ -58,6 +80,21 @@ class CreateRubyLlmAgentsExecutions < ActiveRecord::Migration<%= migration_versi
|
|
|
58
80
|
add_index :ruby_llm_agents_executions, :duration_ms
|
|
59
81
|
add_index :ruby_llm_agents_executions, :total_cost
|
|
60
82
|
|
|
83
|
+
# Tracing indexes
|
|
84
|
+
add_index :ruby_llm_agents_executions, :request_id
|
|
85
|
+
add_index :ruby_llm_agents_executions, :trace_id
|
|
86
|
+
add_index :ruby_llm_agents_executions, :parent_execution_id
|
|
87
|
+
add_index :ruby_llm_agents_executions, :root_execution_id
|
|
88
|
+
|
|
89
|
+
# Caching index
|
|
90
|
+
add_index :ruby_llm_agents_executions, :response_cache_key
|
|
91
|
+
|
|
92
|
+
# Foreign keys for execution hierarchy
|
|
93
|
+
add_foreign_key :ruby_llm_agents_executions, :ruby_llm_agents_executions,
|
|
94
|
+
column: :parent_execution_id, on_delete: :nullify
|
|
95
|
+
add_foreign_key :ruby_llm_agents_executions, :ruby_llm_agents_executions,
|
|
96
|
+
column: :root_execution_id, on_delete: :nullify
|
|
97
|
+
|
|
61
98
|
# GIN indexes for JSONB columns (PostgreSQL only)
|
|
62
99
|
# Uncomment if using PostgreSQL:
|
|
63
100
|
# add_index :ruby_llm_agents_executions, :parameters, using: :gin
|
|
@@ -29,6 +29,84 @@ module RubyLlmAgents
|
|
|
29
29
|
)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
def create_add_attempts_migration
|
|
33
|
+
# Check if columns already exist
|
|
34
|
+
if column_exists?(:ruby_llm_agents_executions, :attempts)
|
|
35
|
+
say_status :skip, "attempts column already exists", :yellow
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
migration_template(
|
|
40
|
+
"add_attempts_migration.rb.tt",
|
|
41
|
+
File.join(db_migrate_path, "add_attempts_to_ruby_llm_agents_executions.rb")
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def create_add_streaming_migration
|
|
46
|
+
# Check if columns already exist
|
|
47
|
+
if column_exists?(:ruby_llm_agents_executions, :streaming)
|
|
48
|
+
say_status :skip, "streaming column already exists", :yellow
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
migration_template(
|
|
53
|
+
"add_streaming_migration.rb.tt",
|
|
54
|
+
File.join(db_migrate_path, "add_streaming_to_ruby_llm_agents_executions.rb")
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def create_add_tracing_migration
|
|
59
|
+
# Check if columns already exist
|
|
60
|
+
if column_exists?(:ruby_llm_agents_executions, :trace_id)
|
|
61
|
+
say_status :skip, "trace_id column already exists", :yellow
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
migration_template(
|
|
66
|
+
"add_tracing_migration.rb.tt",
|
|
67
|
+
File.join(db_migrate_path, "add_tracing_to_ruby_llm_agents_executions.rb")
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def create_add_routing_migration
|
|
72
|
+
# Check if columns already exist
|
|
73
|
+
if column_exists?(:ruby_llm_agents_executions, :fallback_reason)
|
|
74
|
+
say_status :skip, "fallback_reason column already exists", :yellow
|
|
75
|
+
return
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
migration_template(
|
|
79
|
+
"add_routing_migration.rb.tt",
|
|
80
|
+
File.join(db_migrate_path, "add_routing_to_ruby_llm_agents_executions.rb")
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def create_add_finish_reason_migration
|
|
85
|
+
# Check if columns already exist
|
|
86
|
+
if column_exists?(:ruby_llm_agents_executions, :finish_reason)
|
|
87
|
+
say_status :skip, "finish_reason column already exists", :yellow
|
|
88
|
+
return
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
migration_template(
|
|
92
|
+
"add_finish_reason_migration.rb.tt",
|
|
93
|
+
File.join(db_migrate_path, "add_finish_reason_to_ruby_llm_agents_executions.rb")
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def create_add_caching_migration
|
|
98
|
+
# Check if columns already exist
|
|
99
|
+
if column_exists?(:ruby_llm_agents_executions, :cache_hit)
|
|
100
|
+
say_status :skip, "cache_hit column already exists", :yellow
|
|
101
|
+
return
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
migration_template(
|
|
105
|
+
"add_caching_migration.rb.tt",
|
|
106
|
+
File.join(db_migrate_path, "add_caching_to_ruby_llm_agents_executions.rb")
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
|
|
32
110
|
def show_post_upgrade_message
|
|
33
111
|
say ""
|
|
34
112
|
say "RubyLLM::Agents upgrade migration created!", :green
|