ruby_llm-agents 1.3.4 → 2.1.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 +112 -336
- data/app/controllers/concerns/ruby_llm/agents/sortable.rb +0 -1
- data/app/controllers/ruby_llm/agents/agents_controller.rb +5 -56
- data/app/controllers/ruby_llm/agents/dashboard_controller.rb +22 -106
- data/app/controllers/ruby_llm/agents/executions_controller.rb +4 -114
- data/app/controllers/ruby_llm/agents/tenants_controller.rb +30 -2
- data/app/helpers/ruby_llm/agents/application_helper.rb +19 -53
- data/app/models/ruby_llm/agents/execution/analytics.rb +13 -54
- data/app/models/ruby_llm/agents/execution/scopes.rb +61 -14
- data/app/models/ruby_llm/agents/execution.rb +52 -12
- data/app/models/ruby_llm/agents/execution_detail.rb +18 -0
- data/app/models/ruby_llm/agents/tenant/budgetable.rb +132 -24
- data/app/models/ruby_llm/agents/tenant/incrementable.rb +117 -0
- data/app/models/ruby_llm/agents/tenant/resettable.rb +128 -0
- data/app/models/ruby_llm/agents/tenant/trackable.rb +46 -12
- data/app/models/ruby_llm/agents/tenant.rb +2 -3
- data/app/models/ruby_llm/agents/tenant_budget.rb +6 -3
- data/app/services/ruby_llm/agents/agent_registry.rb +6 -112
- data/app/views/layouts/ruby_llm/agents/application.html.erb +89 -252
- data/app/views/ruby_llm/agents/agents/_config_agent.html.erb +71 -218
- data/app/views/ruby_llm/agents/agents/_config_embedder.html.erb +20 -63
- data/app/views/ruby_llm/agents/agents/_config_image_generator.html.erb +44 -131
- data/app/views/ruby_llm/agents/agents/_config_moderator.html.erb +16 -57
- data/app/views/ruby_llm/agents/agents/_config_speaker.html.erb +39 -104
- data/app/views/ruby_llm/agents/agents/_config_transcriber.html.erb +29 -82
- data/app/views/ruby_llm/agents/agents/_empty_state.html.erb +4 -14
- data/app/views/ruby_llm/agents/agents/index.html.erb +105 -274
- data/app/views/ruby_llm/agents/agents/show.html.erb +248 -378
- data/app/views/ruby_llm/agents/dashboard/_action_center.html.erb +29 -52
- data/app/views/ruby_llm/agents/dashboard/_tenant_budget.html.erb +73 -99
- data/app/views/ruby_llm/agents/dashboard/index.html.erb +228 -433
- data/app/views/ruby_llm/agents/executions/_execution.html.erb +1 -1
- data/app/views/ruby_llm/agents/executions/_filters.html.erb +4 -25
- data/app/views/ruby_llm/agents/executions/_list.html.erb +111 -152
- data/app/views/ruby_llm/agents/executions/index.html.erb +5 -7
- data/app/views/ruby_llm/agents/executions/show.html.erb +526 -1037
- data/app/views/ruby_llm/agents/shared/_agent_type_badge.html.erb +5 -21
- data/app/views/ruby_llm/agents/shared/_executions_table.html.erb +70 -191
- data/app/views/ruby_llm/agents/shared/_filter_dropdown.html.erb +16 -44
- data/app/views/ruby_llm/agents/shared/_select_dropdown.html.erb +12 -41
- data/app/views/ruby_llm/agents/shared/_status_badge.html.erb +11 -65
- data/app/views/ruby_llm/agents/shared/_tenant_filter.html.erb +6 -5
- data/app/views/ruby_llm/agents/system_config/show.html.erb +240 -351
- data/app/views/ruby_llm/agents/tenants/_form.html.erb +67 -77
- data/app/views/ruby_llm/agents/tenants/edit.html.erb +7 -9
- data/app/views/ruby_llm/agents/tenants/index.html.erb +100 -122
- data/app/views/ruby_llm/agents/tenants/show.html.erb +146 -336
- data/config/routes.rb +0 -13
- data/lib/generators/ruby_llm_agents/install_generator.rb +13 -17
- data/lib/generators/ruby_llm_agents/migrate_structure_generator.rb +2 -12
- data/lib/generators/ruby_llm_agents/restructure_generator.rb +0 -2
- data/lib/generators/ruby_llm_agents/templates/add_usage_counters_to_tenants_migration.rb.tt +37 -0
- data/lib/generators/ruby_llm_agents/templates/agent.rb.tt +1 -2
- data/lib/generators/ruby_llm_agents/templates/application_agent.rb.tt +1 -1
- data/lib/generators/ruby_llm_agents/templates/application_image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/create_execution_details_migration.rb.tt +27 -0
- data/lib/generators/ruby_llm_agents/templates/create_tenants_migration.rb.tt +25 -0
- data/lib/generators/ruby_llm_agents/templates/image_pipeline.rb.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/initializer.rb.tt +33 -12
- data/lib/generators/ruby_llm_agents/templates/migration.rb.tt +40 -71
- data/lib/generators/ruby_llm_agents/templates/remove_agent_version_migration.rb.tt +13 -0
- data/lib/generators/ruby_llm_agents/templates/remove_workflow_columns_migration.rb.tt +19 -0
- data/lib/generators/ruby_llm_agents/templates/skills/AGENTS.md.tt +2 -4
- data/lib/generators/ruby_llm_agents/templates/skills/IMAGE_PIPELINES.md.tt +0 -1
- data/lib/generators/ruby_llm_agents/templates/split_execution_details_migration.rb.tt +232 -0
- data/lib/generators/ruby_llm_agents/upgrade_generator.rb +77 -259
- data/lib/ruby_llm/agents/audio/speaker.rb +0 -1
- data/lib/ruby_llm/agents/audio/transcriber.rb +0 -1
- data/lib/ruby_llm/agents/base_agent.rb +54 -23
- data/lib/ruby_llm/agents/core/base/callbacks.rb +142 -0
- data/lib/ruby_llm/agents/core/base.rb +23 -55
- data/lib/ruby_llm/agents/core/configuration.rb +97 -117
- data/lib/ruby_llm/agents/core/errors.rb +0 -58
- data/lib/ruby_llm/agents/core/instrumentation.rb +157 -110
- data/lib/ruby_llm/agents/core/llm_tenant.rb +8 -7
- data/lib/ruby_llm/agents/core/version.rb +1 -1
- data/lib/ruby_llm/agents/dsl/base.rb +157 -17
- data/lib/ruby_llm/agents/dsl/caching.rb +33 -2
- data/lib/ruby_llm/agents/dsl/reliability.rb +148 -0
- data/lib/ruby_llm/agents/dsl.rb +1 -2
- data/lib/ruby_llm/agents/image/analyzer/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/background_remover/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/concerns/image_operation_dsl.rb +1 -13
- data/lib/ruby_llm/agents/image/concerns/image_operation_execution.rb +2 -2
- data/lib/ruby_llm/agents/image/editor/dsl.rb +0 -14
- data/lib/ruby_llm/agents/image/editor/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/editor.rb +0 -1
- data/lib/ruby_llm/agents/image/generator.rb +0 -21
- data/lib/ruby_llm/agents/image/pipeline/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/pipeline/execution.rb +0 -1
- data/lib/ruby_llm/agents/image/transformer/dsl.rb +0 -13
- data/lib/ruby_llm/agents/image/transformer/execution.rb +1 -10
- data/lib/ruby_llm/agents/image/transformer.rb +0 -1
- data/lib/ruby_llm/agents/image/upscaler/execution.rb +1 -2
- data/lib/ruby_llm/agents/image/variator/execution.rb +1 -2
- data/lib/ruby_llm/agents/infrastructure/alert_manager.rb +78 -173
- data/lib/ruby_llm/agents/infrastructure/budget/budget_query.rb +66 -2
- data/lib/ruby_llm/agents/infrastructure/budget/spend_recorder.rb +0 -12
- data/lib/ruby_llm/agents/infrastructure/circuit_breaker.rb +10 -13
- data/lib/ruby_llm/agents/infrastructure/execution_logger_job.rb +8 -0
- data/lib/ruby_llm/agents/pipeline/context.rb +0 -1
- data/lib/ruby_llm/agents/pipeline/middleware/budget.rb +28 -4
- data/lib/ruby_llm/agents/pipeline/middleware/cache.rb +3 -10
- data/lib/ruby_llm/agents/pipeline/middleware/instrumentation.rb +88 -55
- data/lib/ruby_llm/agents/pipeline/middleware/tenant.rb +5 -41
- data/lib/ruby_llm/agents/rails/engine.rb +6 -6
- data/lib/ruby_llm/agents/results/base.rb +1 -49
- data/lib/ruby_llm/agents/text/embedder.rb +0 -1
- data/lib/ruby_llm/agents.rb +1 -9
- data/lib/tasks/ruby_llm_agents.rake +34 -0
- metadata +14 -83
- data/app/controllers/ruby_llm/agents/api_configurations_controller.rb +0 -214
- data/app/controllers/ruby_llm/agents/workflows_controller.rb +0 -544
- data/app/mailers/ruby_llm/agents/alert_mailer.rb +0 -84
- data/app/mailers/ruby_llm/agents/application_mailer.rb +0 -28
- data/app/models/ruby_llm/agents/api_configuration.rb +0 -386
- data/app/models/ruby_llm/agents/execution/workflow.rb +0 -170
- data/app/models/ruby_llm/agents/tenant/configurable.rb +0 -135
- data/app/views/ruby_llm/agents/agents/_agent.html.erb +0 -98
- data/app/views/ruby_llm/agents/agents/_version_comparison.html.erb +0 -186
- data/app/views/ruby_llm/agents/agents/_workflow.html.erb +0 -126
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.html.erb +0 -107
- data/app/views/ruby_llm/agents/alert_mailer/alert_notification.text.erb +0 -18
- data/app/views/ruby_llm/agents/api_configurations/_api_key_field.html.erb +0 -34
- data/app/views/ruby_llm/agents/api_configurations/_form.html.erb +0 -288
- data/app/views/ruby_llm/agents/api_configurations/edit.html.erb +0 -95
- data/app/views/ruby_llm/agents/api_configurations/edit_tenant.html.erb +0 -97
- data/app/views/ruby_llm/agents/api_configurations/show.html.erb +0 -214
- data/app/views/ruby_llm/agents/api_configurations/tenant.html.erb +0 -179
- data/app/views/ruby_llm/agents/dashboard/_agent_comparison.html.erb +0 -73
- data/app/views/ruby_llm/agents/dashboard/_alerts_feed.html.erb +0 -62
- data/app/views/ruby_llm/agents/dashboard/_breaker_strip.html.erb +0 -47
- data/app/views/ruby_llm/agents/dashboard/_budgets_bar.html.erb +0 -75
- data/app/views/ruby_llm/agents/dashboard/_model_comparison.html.erb +0 -56
- data/app/views/ruby_llm/agents/dashboard/_model_cost_breakdown.html.erb +0 -115
- data/app/views/ruby_llm/agents/dashboard/_now_strip.html.erb +0 -59
- data/app/views/ruby_llm/agents/dashboard/_top_errors.html.erb +0 -60
- data/app/views/ruby_llm/agents/executions/_workflow_summary.html.erb +0 -86
- data/app/views/ruby_llm/agents/executions/dry_run.html.erb +0 -149
- data/app/views/ruby_llm/agents/shared/_breadcrumbs.html.erb +0 -48
- data/app/views/ruby_llm/agents/shared/_nav_link.html.erb +0 -27
- data/app/views/ruby_llm/agents/shared/_stat_card.html.erb +0 -14
- data/app/views/ruby_llm/agents/shared/_workflow_type_badge.html.erb +0 -35
- data/app/views/ruby_llm/agents/workflows/_empty_state.html.erb +0 -22
- data/app/views/ruby_llm/agents/workflows/_step_performance.html.erb +0 -228
- data/app/views/ruby_llm/agents/workflows/_structure_dsl.html.erb +0 -539
- data/app/views/ruby_llm/agents/workflows/_structure_parallel.html.erb +0 -76
- data/app/views/ruby_llm/agents/workflows/_structure_pipeline.html.erb +0 -74
- data/app/views/ruby_llm/agents/workflows/_structure_router.html.erb +0 -108
- data/app/views/ruby_llm/agents/workflows/_workflow_diagram.html.erb +0 -920
- data/app/views/ruby_llm/agents/workflows/index.html.erb +0 -179
- data/app/views/ruby_llm/agents/workflows/show.html.erb +0 -467
- data/lib/generators/ruby_llm_agents/api_configuration_generator.rb +0 -100
- data/lib/generators/ruby_llm_agents/templates/add_workflow_migration.rb.tt +0 -38
- data/lib/generators/ruby_llm_agents/templates/application_workflow.rb.tt +0 -48
- data/lib/generators/ruby_llm_agents/templates/create_api_configurations_migration.rb.tt +0 -90
- data/lib/generators/ruby_llm_agents/templates/skills/WORKFLOWS.md.tt +0 -551
- data/lib/ruby_llm/agents/core/base/moderation_dsl.rb +0 -181
- data/lib/ruby_llm/agents/core/base/moderation_execution.rb +0 -274
- data/lib/ruby_llm/agents/core/resolved_config.rb +0 -348
- data/lib/ruby_llm/agents/image/generator/content_policy.rb +0 -95
- data/lib/ruby_llm/agents/infrastructure/redactor.rb +0 -130
- data/lib/ruby_llm/agents/results/moderation_result.rb +0 -158
- data/lib/ruby_llm/agents/text/moderator.rb +0 -237
- data/lib/ruby_llm/agents/workflow/approval.rb +0 -205
- data/lib/ruby_llm/agents/workflow/approval_store.rb +0 -179
- data/lib/ruby_llm/agents/workflow/async.rb +0 -220
- data/lib/ruby_llm/agents/workflow/async_executor.rb +0 -156
- data/lib/ruby_llm/agents/workflow/dsl/executor.rb +0 -467
- data/lib/ruby_llm/agents/workflow/dsl/input_schema.rb +0 -244
- data/lib/ruby_llm/agents/workflow/dsl/iteration_executor.rb +0 -289
- data/lib/ruby_llm/agents/workflow/dsl/parallel_group.rb +0 -107
- data/lib/ruby_llm/agents/workflow/dsl/route_builder.rb +0 -150
- data/lib/ruby_llm/agents/workflow/dsl/schedule_helpers.rb +0 -187
- data/lib/ruby_llm/agents/workflow/dsl/step_config.rb +0 -352
- data/lib/ruby_llm/agents/workflow/dsl/step_executor.rb +0 -415
- data/lib/ruby_llm/agents/workflow/dsl/wait_config.rb +0 -257
- data/lib/ruby_llm/agents/workflow/dsl/wait_executor.rb +0 -317
- data/lib/ruby_llm/agents/workflow/dsl.rb +0 -576
- data/lib/ruby_llm/agents/workflow/instrumentation.rb +0 -249
- data/lib/ruby_llm/agents/workflow/notifiers/base.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/email.rb +0 -117
- data/lib/ruby_llm/agents/workflow/notifiers/slack.rb +0 -180
- data/lib/ruby_llm/agents/workflow/notifiers/webhook.rb +0 -121
- data/lib/ruby_llm/agents/workflow/notifiers.rb +0 -70
- data/lib/ruby_llm/agents/workflow/orchestrator.rb +0 -416
- data/lib/ruby_llm/agents/workflow/result.rb +0 -592
- data/lib/ruby_llm/agents/workflow/thread_pool.rb +0 -185
- data/lib/ruby_llm/agents/workflow/throttle_manager.rb +0 -206
- data/lib/ruby_llm/agents/workflow/wait_result.rb +0 -213
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Idempotent migration to split large payload columns from executions to execution_details.
|
|
4
|
+
#
|
|
5
|
+
# Handles all upgrade scenarios:
|
|
6
|
+
# - execution_details table doesn't exist: creates it and migrates data
|
|
7
|
+
# - execution_details table exists but old columns remain on executions: migrates data and removes columns
|
|
8
|
+
# - already clean: no-ops safely
|
|
9
|
+
class SplitExecutionDetailsFromExecutions < ActiveRecord::Migration<%= migration_version %>
|
|
10
|
+
# Columns that belong on execution_details, not executions
|
|
11
|
+
DETAIL_COLUMNS = %i[
|
|
12
|
+
error_message system_prompt user_prompt response messages_summary
|
|
13
|
+
tool_calls attempts fallback_chain parameters routed_to
|
|
14
|
+
classification_result cached_at cache_creation_tokens
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
# Niche columns moved to metadata JSON
|
|
18
|
+
NICHE_COLUMNS = %i[
|
|
19
|
+
span_id response_cache_key time_to_first_token_ms
|
|
20
|
+
retryable rate_limited fallback_reason
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
# Polymorphic tenant columns removed from executions (access via Tenant model)
|
|
24
|
+
TENANT_RECORD_COLUMNS = %i[tenant_record_type tenant_record_id].freeze
|
|
25
|
+
|
|
26
|
+
def up
|
|
27
|
+
create_details_table_if_needed
|
|
28
|
+
backfill_and_remove_old_columns
|
|
29
|
+
remove_niche_columns
|
|
30
|
+
remove_tenant_record_columns
|
|
31
|
+
ensure_required_columns
|
|
32
|
+
cleanup_indexes
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def down
|
|
36
|
+
raise ActiveRecord::IrreversibleMigration,
|
|
37
|
+
"This migration cannot be reversed. Use rails db:schema:load to restore."
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def create_details_table_if_needed
|
|
43
|
+
return if table_exists?(:ruby_llm_agents_execution_details)
|
|
44
|
+
|
|
45
|
+
create_table :ruby_llm_agents_execution_details do |t|
|
|
46
|
+
t.references :execution, null: false,
|
|
47
|
+
foreign_key: { to_table: :ruby_llm_agents_executions, on_delete: :cascade },
|
|
48
|
+
index: { unique: true }
|
|
49
|
+
|
|
50
|
+
t.text :error_message
|
|
51
|
+
t.text :system_prompt
|
|
52
|
+
t.text :user_prompt
|
|
53
|
+
t.json :response, default: {}
|
|
54
|
+
t.json :messages_summary, default: {}, null: false
|
|
55
|
+
t.json :tool_calls, default: [], null: false
|
|
56
|
+
t.json :attempts, default: [], null: false
|
|
57
|
+
t.json :fallback_chain
|
|
58
|
+
t.json :parameters, default: {}, null: false
|
|
59
|
+
t.string :routed_to
|
|
60
|
+
t.json :classification_result
|
|
61
|
+
t.datetime :cached_at
|
|
62
|
+
t.integer :cache_creation_tokens, default: 0
|
|
63
|
+
|
|
64
|
+
t.timestamps
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def backfill_and_remove_old_columns
|
|
69
|
+
# Only proceed if detail columns still exist on executions
|
|
70
|
+
columns_present = DETAIL_COLUMNS.select { |col| column_exists?(:ruby_llm_agents_executions, col) }
|
|
71
|
+
return if columns_present.empty?
|
|
72
|
+
|
|
73
|
+
# Backfill data from executions to execution_details
|
|
74
|
+
say_with_time "Backfilling execution_details from executions" do
|
|
75
|
+
backfill_execution_details(columns_present)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Remove old columns from executions
|
|
79
|
+
columns_present.each do |col|
|
|
80
|
+
remove_column :ruby_llm_agents_executions, col
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def backfill_execution_details(columns_present)
|
|
85
|
+
batch_size = 1000
|
|
86
|
+
count = 0
|
|
87
|
+
|
|
88
|
+
# Build WHERE clause to only copy rows that have data
|
|
89
|
+
has_data_conditions = columns_present.map { |col| "e.#{col} IS NOT NULL" }.join(" OR ")
|
|
90
|
+
|
|
91
|
+
loop do
|
|
92
|
+
ids = exec_query(<<~SQL).rows.flatten
|
|
93
|
+
SELECT e.id FROM ruby_llm_agents_executions e
|
|
94
|
+
LEFT JOIN ruby_llm_agents_execution_details d ON d.execution_id = e.id
|
|
95
|
+
WHERE d.id IS NULL AND (#{has_data_conditions})
|
|
96
|
+
ORDER BY e.id
|
|
97
|
+
LIMIT #{batch_size}
|
|
98
|
+
SQL
|
|
99
|
+
|
|
100
|
+
break if ids.empty?
|
|
101
|
+
|
|
102
|
+
# Build dynamic column lists based on what actually exists
|
|
103
|
+
detail_cols = %w[execution_id created_at updated_at] + columns_present.map(&:to_s)
|
|
104
|
+
select_exprs = %w[id created_at updated_at] + columns_present.map { |col|
|
|
105
|
+
case col
|
|
106
|
+
when :messages_summary then "COALESCE(messages_summary, '{}')"
|
|
107
|
+
when :tool_calls then "COALESCE(tool_calls, '[]')"
|
|
108
|
+
when :attempts then "COALESCE(attempts, '[]')"
|
|
109
|
+
when :parameters then "COALESCE(parameters, '{}')"
|
|
110
|
+
else col.to_s
|
|
111
|
+
end
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
execute <<~SQL
|
|
115
|
+
INSERT INTO ruby_llm_agents_execution_details (#{detail_cols.join(', ')})
|
|
116
|
+
SELECT #{select_exprs.join(', ')}
|
|
117
|
+
FROM ruby_llm_agents_executions
|
|
118
|
+
WHERE id IN (#{ids.join(',')})
|
|
119
|
+
SQL
|
|
120
|
+
|
|
121
|
+
count += ids.size
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
count
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def remove_niche_columns
|
|
128
|
+
NICHE_COLUMNS.each do |col|
|
|
129
|
+
if column_exists?(:ruby_llm_agents_executions, col)
|
|
130
|
+
remove_column :ruby_llm_agents_executions, col
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def remove_tenant_record_columns
|
|
136
|
+
remove_index :ruby_llm_agents_executions, column: TENANT_RECORD_COLUMNS,
|
|
137
|
+
name: "index_executions_on_tenant_record", if_exists: true
|
|
138
|
+
|
|
139
|
+
TENANT_RECORD_COLUMNS.each do |col|
|
|
140
|
+
if column_exists?(:ruby_llm_agents_executions, col)
|
|
141
|
+
remove_column :ruby_llm_agents_executions, col
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def ensure_required_columns
|
|
147
|
+
unless column_exists?(:ruby_llm_agents_executions, :execution_type)
|
|
148
|
+
add_column :ruby_llm_agents_executions, :execution_type, :string, null: false, default: "chat"
|
|
149
|
+
end
|
|
150
|
+
unless column_exists?(:ruby_llm_agents_executions, :chosen_model_id)
|
|
151
|
+
add_column :ruby_llm_agents_executions, :chosen_model_id, :string
|
|
152
|
+
end
|
|
153
|
+
unless column_exists?(:ruby_llm_agents_executions, :messages_count)
|
|
154
|
+
add_column :ruby_llm_agents_executions, :messages_count, :integer, default: 0, null: false
|
|
155
|
+
end
|
|
156
|
+
unless column_exists?(:ruby_llm_agents_executions, :attempts_count)
|
|
157
|
+
add_column :ruby_llm_agents_executions, :attempts_count, :integer, default: 1, null: false
|
|
158
|
+
end
|
|
159
|
+
unless column_exists?(:ruby_llm_agents_executions, :tool_calls_count)
|
|
160
|
+
add_column :ruby_llm_agents_executions, :tool_calls_count, :integer, default: 0, null: false
|
|
161
|
+
end
|
|
162
|
+
unless column_exists?(:ruby_llm_agents_executions, :streaming)
|
|
163
|
+
add_column :ruby_llm_agents_executions, :streaming, :boolean, default: false
|
|
164
|
+
end
|
|
165
|
+
unless column_exists?(:ruby_llm_agents_executions, :finish_reason)
|
|
166
|
+
add_column :ruby_llm_agents_executions, :finish_reason, :string
|
|
167
|
+
end
|
|
168
|
+
unless column_exists?(:ruby_llm_agents_executions, :cache_hit)
|
|
169
|
+
add_column :ruby_llm_agents_executions, :cache_hit, :boolean, default: false
|
|
170
|
+
end
|
|
171
|
+
unless column_exists?(:ruby_llm_agents_executions, :trace_id)
|
|
172
|
+
add_column :ruby_llm_agents_executions, :trace_id, :string
|
|
173
|
+
add_index :ruby_llm_agents_executions, :trace_id
|
|
174
|
+
end
|
|
175
|
+
unless column_exists?(:ruby_llm_agents_executions, :request_id)
|
|
176
|
+
add_column :ruby_llm_agents_executions, :request_id, :string
|
|
177
|
+
add_index :ruby_llm_agents_executions, :request_id
|
|
178
|
+
end
|
|
179
|
+
unless column_exists?(:ruby_llm_agents_executions, :parent_execution_id)
|
|
180
|
+
add_column :ruby_llm_agents_executions, :parent_execution_id, :bigint
|
|
181
|
+
add_index :ruby_llm_agents_executions, :parent_execution_id
|
|
182
|
+
add_foreign_key :ruby_llm_agents_executions, :ruby_llm_agents_executions,
|
|
183
|
+
column: :parent_execution_id, on_delete: :nullify
|
|
184
|
+
end
|
|
185
|
+
unless column_exists?(:ruby_llm_agents_executions, :root_execution_id)
|
|
186
|
+
add_column :ruby_llm_agents_executions, :root_execution_id, :bigint
|
|
187
|
+
add_index :ruby_llm_agents_executions, :root_execution_id
|
|
188
|
+
add_foreign_key :ruby_llm_agents_executions, :ruby_llm_agents_executions,
|
|
189
|
+
column: :root_execution_id, on_delete: :nullify
|
|
190
|
+
end
|
|
191
|
+
unless column_exists?(:ruby_llm_agents_executions, :tenant_id)
|
|
192
|
+
add_column :ruby_llm_agents_executions, :tenant_id, :string
|
|
193
|
+
end
|
|
194
|
+
unless column_exists?(:ruby_llm_agents_executions, :cached_tokens)
|
|
195
|
+
add_column :ruby_llm_agents_executions, :cached_tokens, :integer, default: 0
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def cleanup_indexes
|
|
200
|
+
# Remove single-column indexes that are redundant with composite ones
|
|
201
|
+
%i[duration_ms total_cost messages_count attempts_count tool_calls_count
|
|
202
|
+
chosen_model_id execution_type response_cache_key agent_type tenant_id].each do |col|
|
|
203
|
+
remove_index :ruby_llm_agents_executions, col, if_exists: true
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Ensure composite tenant indexes exist
|
|
207
|
+
unless index_exists?(:ruby_llm_agents_executions, [:tenant_id, :created_at])
|
|
208
|
+
add_index :ruby_llm_agents_executions, [:tenant_id, :created_at]
|
|
209
|
+
end
|
|
210
|
+
unless index_exists?(:ruby_llm_agents_executions, [:tenant_id, :status])
|
|
211
|
+
add_index :ruby_llm_agents_executions, [:tenant_id, :status]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Remove workflow indexes (feature removed)
|
|
215
|
+
remove_index :ruby_llm_agents_executions, [:workflow_id, :workflow_step], if_exists: true
|
|
216
|
+
remove_index :ruby_llm_agents_executions, :workflow_id, if_exists: true
|
|
217
|
+
remove_index :ruby_llm_agents_executions, :workflow_type, if_exists: true
|
|
218
|
+
|
|
219
|
+
# Remove workflow columns if present
|
|
220
|
+
%i[workflow_id workflow_type workflow_step].each do |col|
|
|
221
|
+
if column_exists?(:ruby_llm_agents_executions, col)
|
|
222
|
+
remove_column :ruby_llm_agents_executions, col
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Remove agent_version if present
|
|
227
|
+
if column_exists?(:ruby_llm_agents_executions, :agent_version)
|
|
228
|
+
remove_index :ruby_llm_agents_executions, [:agent_type, :agent_version], if_exists: true
|
|
229
|
+
remove_column :ruby_llm_agents_executions, :agent_version
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "rails/generators"
|
|
4
4
|
require "rails/generators/active_record"
|
|
5
|
-
require "fileutils"
|
|
6
|
-
|
|
7
5
|
module RubyLlmAgents
|
|
8
6
|
# Upgrade generator for ruby_llm-agents
|
|
9
7
|
#
|
|
@@ -11,142 +9,37 @@ module RubyLlmAgents
|
|
|
11
9
|
# rails generate ruby_llm_agents:upgrade
|
|
12
10
|
#
|
|
13
11
|
# This will create any missing migrations for upgrading from older versions.
|
|
12
|
+
# It handles all upgrade scenarios:
|
|
13
|
+
#
|
|
14
|
+
# - v0.x/v1.x -> v2.0: Splits detail columns from executions to execution_details,
|
|
15
|
+
# removes deprecated columns, renames tenant_budgets to tenants
|
|
16
|
+
# - v2.0 -> latest: No-ops safely if already up to date
|
|
14
17
|
#
|
|
15
18
|
class UpgradeGenerator < ::Rails::Generators::Base
|
|
16
19
|
include ::ActiveRecord::Generators::Migration
|
|
17
20
|
|
|
18
21
|
source_root File.expand_path("templates", __dir__)
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def create_add_attempts_migration
|
|
34
|
-
# Check if columns already exist
|
|
35
|
-
if column_exists?(:ruby_llm_agents_executions, :attempts)
|
|
36
|
-
say_status :skip, "attempts column already exists", :yellow
|
|
37
|
-
return
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
migration_template(
|
|
41
|
-
"add_attempts_migration.rb.tt",
|
|
42
|
-
File.join(db_migrate_path, "add_attempts_to_ruby_llm_agents_executions.rb")
|
|
43
|
-
)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def create_add_streaming_migration
|
|
47
|
-
# Check if columns already exist
|
|
48
|
-
if column_exists?(:ruby_llm_agents_executions, :streaming)
|
|
49
|
-
say_status :skip, "streaming column already exists", :yellow
|
|
50
|
-
return
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
migration_template(
|
|
54
|
-
"add_streaming_migration.rb.tt",
|
|
55
|
-
File.join(db_migrate_path, "add_streaming_to_ruby_llm_agents_executions.rb")
|
|
56
|
-
)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def create_add_tracing_migration
|
|
60
|
-
# Check if columns already exist
|
|
61
|
-
if column_exists?(:ruby_llm_agents_executions, :trace_id)
|
|
62
|
-
say_status :skip, "trace_id column already exists", :yellow
|
|
63
|
-
return
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
migration_template(
|
|
67
|
-
"add_tracing_migration.rb.tt",
|
|
68
|
-
File.join(db_migrate_path, "add_tracing_to_ruby_llm_agents_executions.rb")
|
|
69
|
-
)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def create_add_routing_migration
|
|
73
|
-
# Check if columns already exist
|
|
74
|
-
if column_exists?(:ruby_llm_agents_executions, :fallback_reason)
|
|
75
|
-
say_status :skip, "fallback_reason column already exists", :yellow
|
|
76
|
-
return
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
migration_template(
|
|
80
|
-
"add_routing_migration.rb.tt",
|
|
81
|
-
File.join(db_migrate_path, "add_routing_to_ruby_llm_agents_executions.rb")
|
|
82
|
-
)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def create_add_finish_reason_migration
|
|
86
|
-
# Check if columns already exist
|
|
87
|
-
if column_exists?(:ruby_llm_agents_executions, :finish_reason)
|
|
88
|
-
say_status :skip, "finish_reason column already exists", :yellow
|
|
89
|
-
return
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
migration_template(
|
|
93
|
-
"add_finish_reason_migration.rb.tt",
|
|
94
|
-
File.join(db_migrate_path, "add_finish_reason_to_ruby_llm_agents_executions.rb")
|
|
95
|
-
)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def create_add_caching_migration
|
|
99
|
-
# Check if columns already exist
|
|
100
|
-
if column_exists?(:ruby_llm_agents_executions, :cache_hit)
|
|
101
|
-
say_status :skip, "cache_hit column already exists", :yellow
|
|
102
|
-
return
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
migration_template(
|
|
106
|
-
"add_caching_migration.rb.tt",
|
|
107
|
-
File.join(db_migrate_path, "add_caching_to_ruby_llm_agents_executions.rb")
|
|
108
|
-
)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def create_add_tool_calls_migration
|
|
112
|
-
# Check if columns already exist
|
|
113
|
-
if column_exists?(:ruby_llm_agents_executions, :tool_calls)
|
|
114
|
-
say_status :skip, "tool_calls column already exists", :yellow
|
|
115
|
-
return
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
migration_template(
|
|
119
|
-
"add_tool_calls_migration.rb.tt",
|
|
120
|
-
File.join(db_migrate_path, "add_tool_calls_to_ruby_llm_agents_executions.rb")
|
|
121
|
-
)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def create_add_workflow_migration
|
|
125
|
-
# Check if columns already exist
|
|
126
|
-
if column_exists?(:ruby_llm_agents_executions, :workflow_id)
|
|
127
|
-
say_status :skip, "workflow_id column already exists", :yellow
|
|
128
|
-
return
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
migration_template(
|
|
132
|
-
"add_workflow_migration.rb.tt",
|
|
133
|
-
File.join(db_migrate_path, "add_workflow_to_ruby_llm_agents_executions.rb")
|
|
134
|
-
)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def create_add_execution_type_migration
|
|
138
|
-
# Check if columns already exist
|
|
139
|
-
if column_exists?(:ruby_llm_agents_executions, :execution_type)
|
|
140
|
-
say_status :skip, "execution_type column already exists", :yellow
|
|
23
|
+
# Main upgrade: split execution_details from executions table
|
|
24
|
+
#
|
|
25
|
+
# This single migration handles ALL schema transitions:
|
|
26
|
+
# - Creates execution_details table if missing
|
|
27
|
+
# - Migrates data from old columns on executions to execution_details
|
|
28
|
+
# - Removes deprecated columns (detail, niche, workflow, agent_version)
|
|
29
|
+
# - Adds any missing columns that should stay on executions
|
|
30
|
+
def create_split_execution_details_migration
|
|
31
|
+
if already_split?
|
|
32
|
+
say_status :skip, "execution_details already split from executions", :yellow
|
|
141
33
|
return
|
|
142
34
|
end
|
|
143
35
|
|
|
144
36
|
migration_template(
|
|
145
|
-
"
|
|
146
|
-
File.join(db_migrate_path, "
|
|
37
|
+
"split_execution_details_migration.rb.tt",
|
|
38
|
+
File.join(db_migrate_path, "split_execution_details_from_executions.rb")
|
|
147
39
|
)
|
|
148
40
|
end
|
|
149
41
|
|
|
42
|
+
# Rename tenant_budgets to tenants (v1.x -> v2.0 upgrade)
|
|
150
43
|
def create_rename_tenant_budgets_migration
|
|
151
44
|
# Skip if already using new table name
|
|
152
45
|
if table_exists?(:ruby_llm_agents_tenants)
|
|
@@ -167,49 +60,35 @@ module RubyLlmAgents
|
|
|
167
60
|
)
|
|
168
61
|
end
|
|
169
62
|
|
|
170
|
-
def
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
migrate_directory("agents", "#{root_dir}/agents", namespace)
|
|
174
|
-
end
|
|
63
|
+
def suggest_config_consolidation
|
|
64
|
+
ruby_llm_initializer = File.join(destination_root, "config/initializers/ruby_llm.rb")
|
|
65
|
+
agents_initializer = File.join(destination_root, "config/initializers/ruby_llm_agents.rb")
|
|
175
66
|
|
|
176
|
-
|
|
177
|
-
root_dir = RubyLLM::Agents.configuration.root_directory
|
|
178
|
-
namespace = RubyLLM::Agents.configuration.root_namespace
|
|
179
|
-
migrate_directory("tools", "#{root_dir}/tools", namespace)
|
|
180
|
-
end
|
|
67
|
+
return unless File.exist?(ruby_llm_initializer) && File.exist?(agents_initializer)
|
|
181
68
|
|
|
182
|
-
def show_post_upgrade_message
|
|
183
69
|
say ""
|
|
184
|
-
say "
|
|
185
|
-
say ""
|
|
186
|
-
say "Next steps:"
|
|
187
|
-
say " 1. Run migrations: rails db:migrate"
|
|
188
|
-
say " 2. Update class references in your controllers, views, and tests"
|
|
189
|
-
say " 3. Run your test suite to find any broken references"
|
|
70
|
+
say "Optional: You can now consolidate your API key configuration.", :yellow
|
|
190
71
|
say ""
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def show_migration_summary
|
|
194
|
-
namespace = RubyLLM::Agents.configuration.root_namespace
|
|
195
|
-
root_dir = RubyLLM::Agents.configuration.root_directory
|
|
196
|
-
|
|
197
|
-
return unless @agents_migrated || @tools_migrated
|
|
198
|
-
|
|
72
|
+
say "Move your API keys from config/initializers/ruby_llm.rb"
|
|
73
|
+
say "into config/initializers/ruby_llm_agents.rb:"
|
|
199
74
|
say ""
|
|
200
|
-
say "
|
|
201
|
-
say "
|
|
202
|
-
say "="
|
|
75
|
+
say " RubyLLM::Agents.configure do |config|"
|
|
76
|
+
say " config.openai_api_key = ENV['OPENAI_API_KEY']"
|
|
77
|
+
say " config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']"
|
|
78
|
+
say " # ... rest of your agent config"
|
|
79
|
+
say " end"
|
|
203
80
|
say ""
|
|
204
|
-
say "
|
|
81
|
+
say "Then delete config/initializers/ruby_llm.rb if it only contained API keys."
|
|
205
82
|
say ""
|
|
206
|
-
|
|
207
|
-
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def show_post_upgrade_message
|
|
208
86
|
say ""
|
|
209
|
-
say "
|
|
87
|
+
say "RubyLLM::Agents upgrade complete!", :green
|
|
210
88
|
say ""
|
|
211
|
-
say "
|
|
212
|
-
say "
|
|
89
|
+
say "Next steps:"
|
|
90
|
+
say " 1. Run migrations: rails db:migrate"
|
|
91
|
+
say " 2. Run your test suite to verify everything works"
|
|
213
92
|
say ""
|
|
214
93
|
end
|
|
215
94
|
|
|
@@ -223,118 +102,57 @@ module RubyLlmAgents
|
|
|
223
102
|
"db/migrate"
|
|
224
103
|
end
|
|
225
104
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
false
|
|
232
|
-
|
|
105
|
+
# Check if the split has already been completed:
|
|
106
|
+
# - execution_details table exists
|
|
107
|
+
# - No detail columns remain on executions
|
|
108
|
+
# - No deprecated columns remain on executions
|
|
109
|
+
def already_split?
|
|
110
|
+
return false unless table_exists?(:ruby_llm_agents_execution_details)
|
|
111
|
+
return false if has_detail_columns_on_executions?
|
|
112
|
+
return false if has_deprecated_columns_on_executions?
|
|
233
113
|
|
|
234
|
-
|
|
235
|
-
ActiveRecord::Base.connection.table_exists?(table)
|
|
236
|
-
rescue StandardError
|
|
237
|
-
false
|
|
114
|
+
true
|
|
238
115
|
end
|
|
239
116
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
say_status :skip, "app/#{old_dir}/ does not exist", :yellow
|
|
247
|
-
return
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
# Skip if source and destination are the same
|
|
251
|
-
if source.to_s == destination.to_s
|
|
252
|
-
say_status :skip, "app/#{old_dir}/ is already at destination", :yellow
|
|
253
|
-
return
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
files_to_migrate = Dir.glob("#{source}/**/*.rb")
|
|
257
|
-
if files_to_migrate.empty?
|
|
258
|
-
say_status :skip, "app/#{old_dir}/ has no Ruby files to migrate", :yellow
|
|
259
|
-
return
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
if options[:pretend]
|
|
263
|
-
say_status :preview, "Would move #{files_to_migrate.size} files from app/#{old_dir}/ → app/#{new_dir}/", :yellow
|
|
264
|
-
files_to_migrate.each do |file|
|
|
265
|
-
relative = file.sub("#{source}/", "")
|
|
266
|
-
say_status :would_move, relative, :cyan
|
|
267
|
-
end
|
|
268
|
-
return
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
# Create destination directory
|
|
272
|
-
FileUtils.mkdir_p(destination)
|
|
273
|
-
|
|
274
|
-
# Track conflicts and migrated files
|
|
275
|
-
conflicts = []
|
|
276
|
-
migrated = []
|
|
277
|
-
|
|
278
|
-
files_to_migrate.each do |file|
|
|
279
|
-
relative_path = file.sub("#{source}/", "")
|
|
280
|
-
dest_file = File.join(destination, relative_path)
|
|
281
|
-
|
|
282
|
-
# Skip if destination file already exists (conflict)
|
|
283
|
-
if File.exist?(dest_file)
|
|
284
|
-
conflicts << relative_path
|
|
285
|
-
say_status :conflict, "app/#{new_dir}/#{relative_path} already exists, skipping", :red
|
|
286
|
-
next
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
FileUtils.mkdir_p(File.dirname(dest_file))
|
|
290
|
-
FileUtils.mv(file, dest_file)
|
|
291
|
-
|
|
292
|
-
wrap_in_namespace(dest_file, namespace)
|
|
293
|
-
say_status :migrated, "app/#{new_dir}/#{relative_path}", :green
|
|
294
|
-
migrated << relative_path
|
|
295
|
-
end
|
|
117
|
+
# Detail columns that should only exist on execution_details, not executions
|
|
118
|
+
DETAIL_COLUMNS = %i[
|
|
119
|
+
error_message system_prompt user_prompt response messages_summary
|
|
120
|
+
tool_calls attempts fallback_chain parameters routed_to
|
|
121
|
+
classification_result cached_at cache_creation_tokens
|
|
122
|
+
].freeze
|
|
296
123
|
|
|
297
|
-
|
|
298
|
-
|
|
124
|
+
# Niche columns that should be in metadata JSON, not separate columns
|
|
125
|
+
NICHE_COLUMNS = %i[
|
|
126
|
+
span_id response_cache_key time_to_first_token_ms
|
|
127
|
+
retryable rate_limited fallback_reason
|
|
128
|
+
].freeze
|
|
299
129
|
|
|
300
|
-
|
|
301
|
-
|
|
130
|
+
# Deprecated columns
|
|
131
|
+
DEPRECATED_COLUMNS = %i[
|
|
132
|
+
agent_version workflow_id workflow_type workflow_step
|
|
133
|
+
tenant_record_type tenant_record_id
|
|
134
|
+
].freeze
|
|
302
135
|
|
|
303
|
-
|
|
136
|
+
def has_detail_columns_on_executions?
|
|
137
|
+
DETAIL_COLUMNS.any? { |col| column_exists?(:ruby_llm_agents_executions, col) }
|
|
304
138
|
end
|
|
305
139
|
|
|
306
|
-
def
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
# Skip if already namespaced
|
|
310
|
-
return if content.include?("module #{namespace}")
|
|
311
|
-
|
|
312
|
-
# Wrap content in namespace with proper indentation
|
|
313
|
-
indented = content.lines.map { |line| line.empty? || line.strip.empty? ? line : " #{line}" }.join
|
|
314
|
-
wrapped = "module #{namespace}\n#{indented}end\n"
|
|
315
|
-
|
|
316
|
-
File.write(file, wrapped)
|
|
140
|
+
def has_deprecated_columns_on_executions?
|
|
141
|
+
(NICHE_COLUMNS + DEPRECATED_COLUMNS).any? { |col| column_exists?(:ruby_llm_agents_executions, col) }
|
|
317
142
|
end
|
|
318
143
|
|
|
319
|
-
def
|
|
320
|
-
return unless
|
|
321
|
-
|
|
322
|
-
# Remove empty subdirectories first (deepest first)
|
|
323
|
-
Dir.glob("#{dir}/**/", File::FNM_DOTMATCH).sort_by(&:length).reverse.each do |subdir|
|
|
324
|
-
next if subdir.end_with?(".", "..")
|
|
144
|
+
def column_exists?(table, column)
|
|
145
|
+
return false unless ActiveRecord::Base.connection.table_exists?(table)
|
|
325
146
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
147
|
+
ActiveRecord::Base.connection.column_exists?(table, column)
|
|
148
|
+
rescue StandardError
|
|
149
|
+
false
|
|
150
|
+
end
|
|
330
151
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
end
|
|
336
|
-
rescue Errno::ENOTEMPTY
|
|
337
|
-
say_status :kept, "app/#{dir_name}/ (not empty)", :yellow
|
|
152
|
+
def table_exists?(table)
|
|
153
|
+
ActiveRecord::Base.connection.table_exists?(table)
|
|
154
|
+
rescue StandardError
|
|
155
|
+
false
|
|
338
156
|
end
|
|
339
157
|
end
|
|
340
158
|
end
|