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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +273 -0
  3. data/app/channels/ruby_llm/agents/executions_channel.rb +24 -1
  4. data/app/controllers/concerns/ruby_llm/agents/filterable.rb +81 -0
  5. data/app/controllers/concerns/ruby_llm/agents/paginatable.rb +51 -0
  6. data/app/controllers/ruby_llm/agents/agents_controller.rb +228 -59
  7. data/app/controllers/ruby_llm/agents/dashboard_controller.rb +167 -12
  8. data/app/controllers/ruby_llm/agents/executions_controller.rb +189 -31
  9. data/app/controllers/ruby_llm/agents/settings_controller.rb +20 -0
  10. data/app/helpers/ruby_llm/agents/application_helper.rb +307 -7
  11. data/app/models/ruby_llm/agents/execution/analytics.rb +224 -20
  12. data/app/models/ruby_llm/agents/execution/metrics.rb +41 -25
  13. data/app/models/ruby_llm/agents/execution/scopes.rb +234 -14
  14. data/app/models/ruby_llm/agents/execution.rb +259 -16
  15. data/app/services/ruby_llm/agents/agent_registry.rb +49 -12
  16. data/app/views/layouts/rubyllm/agents/application.html.erb +351 -85
  17. data/app/views/rubyllm/agents/agents/_version_comparison.html.erb +186 -0
  18. data/app/views/rubyllm/agents/agents/show.html.erb +233 -10
  19. data/app/views/rubyllm/agents/dashboard/_action_center.html.erb +62 -0
  20. data/app/views/rubyllm/agents/dashboard/_alerts_feed.html.erb +62 -0
  21. data/app/views/rubyllm/agents/dashboard/_breaker_strip.html.erb +47 -0
  22. data/app/views/rubyllm/agents/dashboard/_budgets_bar.html.erb +165 -0
  23. data/app/views/rubyllm/agents/dashboard/_now_strip.html.erb +10 -0
  24. data/app/views/rubyllm/agents/dashboard/_now_strip_values.html.erb +71 -0
  25. data/app/views/rubyllm/agents/dashboard/index.html.erb +215 -109
  26. data/app/views/rubyllm/agents/executions/_filters.html.erb +152 -155
  27. data/app/views/rubyllm/agents/executions/_list.html.erb +103 -12
  28. data/app/views/rubyllm/agents/executions/dry_run.html.erb +149 -0
  29. data/app/views/rubyllm/agents/executions/index.html.erb +17 -72
  30. data/app/views/rubyllm/agents/executions/index.turbo_stream.erb +16 -2
  31. data/app/views/rubyllm/agents/executions/show.html.erb +693 -14
  32. data/app/views/rubyllm/agents/settings/show.html.erb +369 -0
  33. data/app/views/rubyllm/agents/shared/_filter_dropdown.html.erb +121 -0
  34. data/app/views/rubyllm/agents/shared/_select_dropdown.html.erb +85 -0
  35. data/config/routes.rb +7 -0
  36. data/lib/generators/ruby_llm_agents/templates/add_attempts_migration.rb.tt +27 -0
  37. data/lib/generators/ruby_llm_agents/templates/add_caching_migration.rb.tt +23 -0
  38. data/lib/generators/ruby_llm_agents/templates/add_finish_reason_migration.rb.tt +19 -0
  39. data/lib/generators/ruby_llm_agents/templates/add_routing_migration.rb.tt +19 -0
  40. data/lib/generators/ruby_llm_agents/templates/add_streaming_migration.rb.tt +8 -0
  41. data/lib/generators/ruby_llm_agents/templates/add_tracing_migration.rb.tt +34 -0
  42. data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +66 -4
  43. data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +53 -6
  44. data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +139 -8
  45. data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +38 -1
  46. data/lib/generators/ruby_llm_agents/upgrade_generator.rb +78 -0
  47. data/lib/ruby_llm/agents/alert_manager.rb +207 -0
  48. data/lib/ruby_llm/agents/attempt_tracker.rb +295 -0
  49. data/lib/ruby_llm/agents/base.rb +580 -112
  50. data/lib/ruby_llm/agents/budget_tracker.rb +360 -0
  51. data/lib/ruby_llm/agents/circuit_breaker.rb +197 -0
  52. data/lib/ruby_llm/agents/configuration.rb +279 -1
  53. data/lib/ruby_llm/agents/engine.rb +58 -6
  54. data/lib/ruby_llm/agents/execution_logger_job.rb +17 -6
  55. data/lib/ruby_llm/agents/inflections.rb +13 -2
  56. data/lib/ruby_llm/agents/instrumentation.rb +538 -87
  57. data/lib/ruby_llm/agents/redactor.rb +130 -0
  58. data/lib/ruby_llm/agents/reliability.rb +185 -0
  59. data/lib/ruby_llm/agents/version.rb +3 -1
  60. data/lib/ruby_llm/agents.rb +52 -0
  61. metadata +41 -2
  62. 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 # Uncomment to enable caching
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
- # Uncomment to use structured output with RubyLLM::Schema
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
- # Uncomment to add custom metadata to execution logs
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 should inherit from this class to share common configuration.
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
- # Shared configuration for all agents
18
- # Uncomment and modify as needed:
19
- #
20
- # model "gemini-2.0-flash"
21
- # temperature 0.0
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
- # Default model for all agents (can be overridden per agent)
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, 1.0 = creative)
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 (set to false to log synchronously, useful for debugging)
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
- # Anomaly detection thresholds (executions exceeding these are logged as warnings)
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
- # HTTP Basic Auth (simple username/password protection)
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
- # Dashboard authentication (advanced - custom logic)
60
+ # Option 2: Custom authentication (advanced)
35
61
  # Return true to allow access, false to deny
36
- # Note: If basic_auth_username/password are set, they take precedence
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 CreateRubyLlmAgentsExecutions < ActiveRecord::Migration<%= migration_version %>
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