aidp 0.5.0 → 0.8.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +128 -151
  3. data/bin/aidp +1 -1
  4. data/lib/aidp/analysis/kb_inspector.rb +471 -0
  5. data/lib/aidp/analysis/seams.rb +159 -0
  6. data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +480 -0
  7. data/lib/aidp/analysis/tree_sitter_scan.rb +686 -0
  8. data/lib/aidp/analyze/error_handler.rb +2 -78
  9. data/lib/aidp/analyze/json_file_storage.rb +292 -0
  10. data/lib/aidp/analyze/progress.rb +12 -0
  11. data/lib/aidp/analyze/progress_visualizer.rb +12 -17
  12. data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
  13. data/lib/aidp/analyze/runner.rb +256 -87
  14. data/lib/aidp/analyze/steps.rb +6 -0
  15. data/lib/aidp/cli/jobs_command.rb +103 -435
  16. data/lib/aidp/cli.rb +317 -191
  17. data/lib/aidp/config.rb +298 -10
  18. data/lib/aidp/debug_logger.rb +195 -0
  19. data/lib/aidp/debug_mixin.rb +187 -0
  20. data/lib/aidp/execute/progress.rb +9 -0
  21. data/lib/aidp/execute/runner.rb +221 -40
  22. data/lib/aidp/execute/steps.rb +17 -7
  23. data/lib/aidp/execute/workflow_selector.rb +211 -0
  24. data/lib/aidp/harness/completion_checker.rb +268 -0
  25. data/lib/aidp/harness/condition_detector.rb +1526 -0
  26. data/lib/aidp/harness/config_loader.rb +373 -0
  27. data/lib/aidp/harness/config_manager.rb +382 -0
  28. data/lib/aidp/harness/config_schema.rb +1006 -0
  29. data/lib/aidp/harness/config_validator.rb +355 -0
  30. data/lib/aidp/harness/configuration.rb +477 -0
  31. data/lib/aidp/harness/enhanced_runner.rb +494 -0
  32. data/lib/aidp/harness/error_handler.rb +616 -0
  33. data/lib/aidp/harness/provider_config.rb +423 -0
  34. data/lib/aidp/harness/provider_factory.rb +306 -0
  35. data/lib/aidp/harness/provider_manager.rb +1269 -0
  36. data/lib/aidp/harness/provider_type_checker.rb +88 -0
  37. data/lib/aidp/harness/runner.rb +411 -0
  38. data/lib/aidp/harness/state/errors.rb +28 -0
  39. data/lib/aidp/harness/state/metrics.rb +219 -0
  40. data/lib/aidp/harness/state/persistence.rb +128 -0
  41. data/lib/aidp/harness/state/provider_state.rb +132 -0
  42. data/lib/aidp/harness/state/ui_state.rb +68 -0
  43. data/lib/aidp/harness/state/workflow_state.rb +123 -0
  44. data/lib/aidp/harness/state_manager.rb +586 -0
  45. data/lib/aidp/harness/status_display.rb +888 -0
  46. data/lib/aidp/harness/ui/base.rb +16 -0
  47. data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
  48. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
  49. data/lib/aidp/harness/ui/error_handler.rb +132 -0
  50. data/lib/aidp/harness/ui/frame_manager.rb +361 -0
  51. data/lib/aidp/harness/ui/job_monitor.rb +500 -0
  52. data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
  53. data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
  54. data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
  55. data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
  56. data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
  57. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
  58. data/lib/aidp/harness/ui/progress_display.rb +280 -0
  59. data/lib/aidp/harness/ui/question_collector.rb +141 -0
  60. data/lib/aidp/harness/ui/spinner_group.rb +184 -0
  61. data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
  62. data/lib/aidp/harness/ui/status_manager.rb +312 -0
  63. data/lib/aidp/harness/ui/status_widget.rb +280 -0
  64. data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
  65. data/lib/aidp/harness/user_interface.rb +2381 -0
  66. data/lib/aidp/provider_manager.rb +131 -7
  67. data/lib/aidp/providers/anthropic.rb +28 -109
  68. data/lib/aidp/providers/base.rb +170 -0
  69. data/lib/aidp/providers/cursor.rb +52 -183
  70. data/lib/aidp/providers/gemini.rb +24 -109
  71. data/lib/aidp/providers/macos_ui.rb +99 -5
  72. data/lib/aidp/providers/opencode.rb +194 -0
  73. data/lib/aidp/storage/csv_storage.rb +172 -0
  74. data/lib/aidp/storage/file_manager.rb +214 -0
  75. data/lib/aidp/storage/json_storage.rb +140 -0
  76. data/lib/aidp/version.rb +1 -1
  77. data/lib/aidp.rb +56 -35
  78. data/templates/ANALYZE/06a_tree_sitter_scan.md +217 -0
  79. data/templates/COMMON/AGENT_BASE.md +11 -0
  80. data/templates/EXECUTE/00_PRD.md +4 -4
  81. data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
  82. data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
  83. data/templates/EXECUTE/08_TASKS.md +4 -4
  84. data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
  85. data/templates/README.md +279 -0
  86. data/templates/aidp-development.yml.example +373 -0
  87. data/templates/aidp-minimal.yml.example +48 -0
  88. data/templates/aidp-production.yml.example +475 -0
  89. data/templates/aidp.yml.example +598 -0
  90. metadata +106 -64
  91. data/lib/aidp/analyze/agent_personas.rb +0 -71
  92. data/lib/aidp/analyze/agent_tool_executor.rb +0 -445
  93. data/lib/aidp/analyze/data_retention_manager.rb +0 -426
  94. data/lib/aidp/analyze/database.rb +0 -260
  95. data/lib/aidp/analyze/dependencies.rb +0 -335
  96. data/lib/aidp/analyze/export_manager.rb +0 -425
  97. data/lib/aidp/analyze/focus_guidance.rb +0 -517
  98. data/lib/aidp/analyze/incremental_analyzer.rb +0 -543
  99. data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
  100. data/lib/aidp/analyze/large_analysis_progress.rb +0 -504
  101. data/lib/aidp/analyze/memory_manager.rb +0 -365
  102. data/lib/aidp/analyze/metrics_storage.rb +0 -336
  103. data/lib/aidp/analyze/parallel_processor.rb +0 -460
  104. data/lib/aidp/analyze/performance_optimizer.rb +0 -694
  105. data/lib/aidp/analyze/repository_chunker.rb +0 -704
  106. data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
  107. data/lib/aidp/analyze/storage.rb +0 -662
  108. data/lib/aidp/analyze/tool_configuration.rb +0 -456
  109. data/lib/aidp/analyze/tool_modernization.rb +0 -750
  110. data/lib/aidp/database/pg_adapter.rb +0 -148
  111. data/lib/aidp/database_config.rb +0 -69
  112. data/lib/aidp/database_connection.rb +0 -72
  113. data/lib/aidp/database_migration.rb +0 -158
  114. data/lib/aidp/job_manager.rb +0 -41
  115. data/lib/aidp/jobs/base_job.rb +0 -47
  116. data/lib/aidp/jobs/provider_execution_job.rb +0 -96
  117. data/lib/aidp/project_detector.rb +0 -117
  118. data/lib/aidp/providers/agent_supervisor.rb +0 -348
  119. data/lib/aidp/providers/supervised_base.rb +0 -317
  120. data/lib/aidp/providers/supervised_cursor.rb +0 -22
  121. data/lib/aidp/sync.rb +0 -13
  122. data/lib/aidp/workspace.rb +0 -19
@@ -1,148 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aidp
4
- module Database
5
- class PgAdapter
6
- def initialize(connection)
7
- @connection = connection
8
- end
9
-
10
- def execute(sql, params = [])
11
- result = @connection.exec_params(sql, params)
12
- result.to_a.map { |row| row.transform_keys(&:to_sym) }
13
- end
14
-
15
- def in_transaction?
16
- @connection.transaction_status != PG::PQTRANS_IDLE
17
- end
18
-
19
- def checkout
20
- yield self
21
- end
22
-
23
- def after_commit
24
- yield
25
- end
26
-
27
- def server_version
28
- @connection.server_version
29
- end
30
-
31
- def transaction_status
32
- @connection.transaction_status
33
- end
34
-
35
- def transaction
36
- @connection.transaction do
37
- yield
38
- end
39
- end
40
-
41
- def quote_table_name(name)
42
- "\"#{name}\""
43
- end
44
-
45
- def quote_identifier(name)
46
- "\"#{name}\""
47
- end
48
-
49
- def quote_string(string)
50
- "'#{string.gsub("'", "''")}'"
51
- end
52
-
53
- def quote_date(date)
54
- date.strftime("%Y-%m-%d")
55
- end
56
-
57
- def quote_time(time)
58
- time.strftime("%Y-%m-%d %H:%M:%S.%6N %z")
59
- end
60
-
61
- # Additional methods required by Que
62
- def async_connection
63
- self
64
- end
65
-
66
- def wait_for_notify(timeout = nil)
67
- @connection.wait_for_notify(timeout)
68
- end
69
-
70
- def listen(channel)
71
- @connection.exec("LISTEN #{quote_identifier(channel)}")
72
- end
73
-
74
- def unlisten(channel)
75
- @connection.exec("UNLISTEN #{quote_identifier(channel)}")
76
- end
77
-
78
- def unlisten_all
79
- @connection.exec("UNLISTEN *")
80
- end
81
-
82
- def notifications
83
- @connection.notifications
84
- end
85
-
86
- def reset
87
- @connection.reset
88
- end
89
-
90
- def type_map_for_queries
91
- @connection.type_map_for_queries
92
- end
93
-
94
- def type_map_for_results
95
- @connection.type_map_for_results
96
- end
97
-
98
- # Additional methods for Que compatibility
99
- def adapter_name
100
- "pg"
101
- end
102
-
103
- def active?
104
- @connection.status == PG::CONNECTION_OK
105
- end
106
-
107
- def disconnect!
108
- @connection.close
109
- end
110
-
111
- def reconnect!
112
- @connection.reset
113
- end
114
-
115
- def raw_connection
116
- @connection
117
- end
118
-
119
- def schema_search_path
120
- execute("SHOW search_path")[0][:search_path]
121
- end
122
-
123
- def schema_search_path=(path)
124
- execute("SET search_path TO #{path}")
125
- end
126
-
127
- def table_exists?(name)
128
- execute(<<~SQL, [name]).any?
129
- SELECT 1
130
- FROM pg_tables
131
- WHERE tablename = $1
132
- SQL
133
- end
134
-
135
- def advisory_lock(id)
136
- execute("SELECT pg_advisory_lock($1)", [id])
137
- end
138
-
139
- def advisory_unlock(id)
140
- execute("SELECT pg_advisory_unlock($1)", [id])
141
- end
142
-
143
- def advisory_unlock_all
144
- execute("SELECT pg_advisory_unlock_all()")
145
- end
146
- end
147
- end
148
- end
@@ -1,69 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "yaml"
4
- require "fileutils"
5
-
6
- module Aidp
7
- class DatabaseConfig
8
- DEFAULT_CONFIG = {
9
- "database" => {
10
- "adapter" => "postgresql",
11
- "host" => "localhost",
12
- "port" => 5432,
13
- "database" => "aidp",
14
- "username" => ENV["USER"],
15
- "password" => nil,
16
- "pool" => 5,
17
- "timeout" => 5000
18
- }
19
- }.freeze
20
-
21
- def self.load(project_dir = Dir.pwd)
22
- new(project_dir).load
23
- end
24
-
25
- def initialize(project_dir)
26
- @project_dir = project_dir
27
- @config_file = File.join(project_dir, ".aidp-config.yml")
28
- end
29
-
30
- def load
31
- ensure_config_exists
32
- config = YAML.load_file(@config_file)
33
- validate_config(config)
34
- config["database"]
35
- end
36
-
37
- private
38
-
39
- def ensure_config_exists
40
- return if File.exist?(@config_file)
41
-
42
- # Create config directory if it doesn't exist
43
- FileUtils.mkdir_p(File.dirname(@config_file))
44
-
45
- # Write default config
46
- File.write(@config_file, YAML.dump(DEFAULT_CONFIG))
47
-
48
- puts "Created default database configuration at #{@config_file}"
49
- puts "Please update the configuration with your database settings"
50
- end
51
-
52
- def validate_config(config)
53
- unless config.is_a?(Hash) && config["database"].is_a?(Hash)
54
- raise "Invalid configuration format in #{@config_file}"
55
- end
56
-
57
- required_keys = %w[adapter host port database username]
58
- missing_keys = required_keys - config["database"].keys
59
-
60
- unless missing_keys.empty?
61
- raise "Missing required configuration keys: #{missing_keys.join(", ")}"
62
- end
63
-
64
- unless config["database"]["adapter"] == "postgresql"
65
- raise "Only PostgreSQL is supported as a database adapter"
66
- end
67
- end
68
- end
69
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pg"
4
- require "que"
5
- require "sequel"
6
-
7
- module Aidp
8
- class DatabaseConnection
9
- class << self
10
- def initialize_mutex
11
- @mutex ||= Mutex.new
12
- end
13
-
14
- def establish_connection
15
- initialize_mutex
16
- @mutex.synchronize do
17
- # Return existing connection if already established
18
- return @connection if @connection && !@connection.finished?
19
- @connection = PG.connect(connection_params)
20
- @sequel_db = Sequel.connect(
21
- adapter: "postgres",
22
- host: ENV["AIDP_DB_HOST"] || "localhost",
23
- port: (ENV["AIDP_DB_PORT"] || 5432).to_i,
24
- database: ENV["AIDP_DB_NAME"] || "aidp",
25
- user: ENV["AIDP_DB_USER"] || ENV["USER"],
26
- password: ENV["AIDP_DB_PASSWORD"]
27
- )
28
- Que.connection = @sequel_db
29
- Que.migrate!(version: Que::Migrations::CURRENT_VERSION)
30
- @connection
31
- end
32
- end
33
-
34
- def connection
35
- return @connection if @connection && !@connection.finished?
36
- establish_connection
37
- end
38
-
39
- def disconnect
40
- initialize_mutex
41
- @mutex.synchronize do
42
- return unless @connection
43
-
44
- # Safely disconnect in reverse order
45
- begin
46
- Que.connection = nil
47
- rescue => e
48
- # Log but don't fail on Que disconnection issues
49
- puts "Warning: Error setting Que.connection to nil: #{e.message}" if ENV["AIDP_DEBUG"]
50
- end
51
-
52
- @sequel_db&.disconnect
53
- @connection&.close
54
- @connection = nil
55
- @sequel_db = nil
56
- end
57
- end
58
-
59
- private
60
-
61
- def connection_params
62
- {
63
- host: ENV["AIDP_DB_HOST"] || "localhost",
64
- port: (ENV["AIDP_DB_PORT"] || 5432).to_i,
65
- dbname: ENV["AIDP_DB_NAME"] || "aidp",
66
- user: ENV["AIDP_DB_USER"] || ENV["USER"],
67
- password: ENV["AIDP_DB_PASSWORD"]
68
- }
69
- end
70
- end
71
- end
72
- end
@@ -1,158 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sqlite3"
4
- require "fileutils"
5
-
6
- module Aidp
7
- # Handles database migrations for AIDP
8
- class DatabaseMigration
9
- def initialize(project_dir = Dir.pwd)
10
- @project_dir = project_dir
11
- @old_db_path = File.join(project_dir, ".aidp-analysis.db")
12
- @new_db_path = File.join(project_dir, ".aidp.db")
13
- end
14
-
15
- # Migrate database from old to new format
16
- def migrate
17
- # If neither database exists, create new one directly
18
- if !File.exist?(@old_db_path) && !File.exist?(@new_db_path)
19
- create_new_database
20
- return true
21
- end
22
-
23
- # If new database already exists, skip migration
24
- if File.exist?(@new_db_path)
25
- puts "Database .aidp.db already exists, skipping migration"
26
- return false
27
- end
28
-
29
- # Rename old database to new name
30
- FileUtils.mv(@old_db_path, @new_db_path)
31
-
32
- # Open database connection
33
- db = SQLite3::Database.new(@new_db_path)
34
-
35
- # Create new tables for job management
36
- create_job_tables(db)
37
-
38
- # Close connection
39
- db.close
40
-
41
- true
42
- rescue => e
43
- puts "Error during database migration: #{e.message}"
44
- # Try to restore old database if something went wrong
45
- if File.exist?(@new_db_path) && !File.exist?(@old_db_path)
46
- FileUtils.mv(@new_db_path, @old_db_path)
47
- end
48
- false
49
- end
50
-
51
- private
52
-
53
- def create_new_database
54
- db = SQLite3::Database.new(@new_db_path)
55
-
56
- # Create original tables
57
- create_original_tables(db)
58
-
59
- # Create new job tables
60
- create_job_tables(db)
61
-
62
- db.close
63
- end
64
-
65
- def create_original_tables(db)
66
- # Create analysis_results table
67
- db.execute(<<~SQL)
68
- CREATE TABLE analysis_results (
69
- step_name TEXT PRIMARY KEY,
70
- data TEXT NOT NULL,
71
- metadata TEXT,
72
- created_at TEXT NOT NULL,
73
- updated_at TEXT NOT NULL
74
- )
75
- SQL
76
-
77
- # Create analysis_metrics table
78
- db.execute(<<~SQL)
79
- CREATE TABLE analysis_metrics (
80
- id INTEGER PRIMARY KEY AUTOINCREMENT,
81
- step_name TEXT NOT NULL,
82
- metric_name TEXT NOT NULL,
83
- value TEXT NOT NULL,
84
- recorded_at TEXT NOT NULL,
85
- UNIQUE(step_name, metric_name, recorded_at)
86
- )
87
- SQL
88
-
89
- # Create embeddings table
90
- db.execute(<<~SQL)
91
- CREATE TABLE embeddings (
92
- step_name TEXT PRIMARY KEY,
93
- embeddings_data TEXT NOT NULL,
94
- created_at TEXT NOT NULL
95
- )
96
- SQL
97
-
98
- # Create indexes
99
- db.execute("CREATE INDEX idx_analysis_metrics_step_name ON analysis_metrics(step_name)")
100
- db.execute("CREATE INDEX idx_analysis_metrics_recorded_at ON analysis_metrics(recorded_at)")
101
- db.execute("CREATE INDEX idx_analysis_results_updated_at ON analysis_results(updated_at)")
102
- end
103
-
104
- def create_job_tables(db)
105
- # Create jobs table
106
- db.execute(<<~SQL)
107
- CREATE TABLE jobs (
108
- id INTEGER PRIMARY KEY,
109
- job_type TEXT NOT NULL,
110
- provider TEXT NOT NULL,
111
- status TEXT NOT NULL,
112
- created_at INTEGER NOT NULL,
113
- started_at INTEGER,
114
- completed_at INTEGER,
115
- error TEXT,
116
- metadata TEXT
117
- )
118
- SQL
119
-
120
- # Create job_executions table
121
- db.execute(<<~SQL)
122
- CREATE TABLE job_executions (
123
- id INTEGER PRIMARY KEY,
124
- job_id INTEGER NOT NULL,
125
- attempt INTEGER NOT NULL,
126
- status TEXT NOT NULL,
127
- started_at INTEGER NOT NULL,
128
- completed_at INTEGER,
129
- error TEXT,
130
- FOREIGN KEY (job_id) REFERENCES jobs(id)
131
- )
132
- SQL
133
-
134
- # Create job_logs table
135
- db.execute(<<~SQL)
136
- CREATE TABLE job_logs (
137
- id INTEGER PRIMARY KEY,
138
- job_id INTEGER NOT NULL,
139
- execution_id INTEGER NOT NULL,
140
- timestamp INTEGER NOT NULL,
141
- message TEXT NOT NULL,
142
- level TEXT NOT NULL,
143
- metadata TEXT,
144
- FOREIGN KEY (job_id) REFERENCES jobs(id),
145
- FOREIGN KEY (execution_id) REFERENCES job_executions(id)
146
- )
147
- SQL
148
-
149
- # Create indexes for job tables
150
- db.execute("CREATE INDEX idx_jobs_status ON jobs(status)")
151
- db.execute("CREATE INDEX idx_jobs_provider ON jobs(provider)")
152
- db.execute("CREATE INDEX idx_job_executions_job_id ON job_executions(job_id)")
153
- db.execute("CREATE INDEX idx_job_logs_job_id ON job_logs(job_id)")
154
- db.execute("CREATE INDEX idx_job_logs_execution_id ON job_logs(execution_id)")
155
- db.execute("CREATE INDEX idx_job_logs_timestamp ON job_logs(timestamp)")
156
- end
157
- end
158
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aidp
4
- class JobManager
5
- def initialize(project_dir)
6
- @project_dir = project_dir
7
- end
8
-
9
- def create_job(job_class, args = {})
10
- # Create a new job and return job ID
11
- # This is a placeholder implementation
12
- job_id = rand(1000..9999)
13
-
14
- # Store job metadata for testing
15
- @jobs ||= {}
16
- @jobs[job_id] = {
17
- id: job_id,
18
- job_class: job_class,
19
- args: args,
20
- status: "queued",
21
- created_at: Time.now
22
- }
23
-
24
- job_id
25
- end
26
-
27
- def get_job(job_id)
28
- @jobs ||= {}
29
- @jobs[job_id]
30
- end
31
-
32
- def update_job_status(job_id, status, error: nil)
33
- @jobs ||= {}
34
- return unless @jobs[job_id]
35
-
36
- @jobs[job_id][:status] = status
37
- @jobs[job_id][:error] = error if error
38
- @jobs[job_id][:updated_at] = Time.now
39
- end
40
- end
41
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "que"
4
-
5
- module Aidp
6
- module Jobs
7
- class BaseJob < Que::Job
8
- # Default settings
9
- self.retry_interval = 30.0 # 30 seconds between retries
10
- self.maximum_retry_count = 3
11
-
12
- # Error tracking
13
- class_attribute :error_handlers
14
- self.error_handlers = []
15
-
16
- def self.on_error(&block)
17
- error_handlers << block
18
- end
19
-
20
- protected
21
-
22
- def log_info(message)
23
- Que.logger.info "[#{self.class.name}] #{message}"
24
- end
25
-
26
- def log_error(message)
27
- Que.logger.error "[#{self.class.name}] #{message}"
28
- end
29
-
30
- def handle_error(error)
31
- self.class.error_handlers.each do |handler|
32
- handler.call(error, self)
33
- rescue => e
34
- log_error "Error handler failed: #{e.message}"
35
- end
36
- end
37
-
38
- # Override run to add error handling
39
- def run(*args)
40
- raise NotImplementedError, "#{self.class} must implement #run"
41
- rescue => error
42
- handle_error(error)
43
- raise # Re-raise to trigger Que's retry mechanism
44
- end
45
- end
46
- end
47
- end
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aidp
4
- module Jobs
5
- class ProviderExecutionJob < BaseJob
6
- def self.enqueue(provider_type:, prompt:, session: nil, metadata: {})
7
- job = super
8
- # Extract job ID explicitly for better readability and debugging
9
- job_id = job.que_attrs[:job_id]
10
- raise "Failed to enqueue job: no job ID returned" unless job_id
11
- job_id
12
- end
13
-
14
- def run(provider_type:, prompt:, session: nil, metadata: {})
15
- start_time = Time.now
16
-
17
- # Get provider instance
18
- provider = Aidp::ProviderManager.get_provider(provider_type)
19
- raise "Provider #{provider_type} not available" unless provider
20
-
21
- begin
22
- # Execute provider
23
- result = provider.send(prompt: prompt, session: session)
24
-
25
- # Store result
26
- store_result(result, metadata)
27
-
28
- # Record metrics
29
- record_metrics(
30
- provider_type: provider_type,
31
- duration: Time.now - start_time,
32
- success: true,
33
- error: nil
34
- )
35
- rescue => error
36
- # Record metrics
37
- record_metrics(
38
- provider_type: provider_type,
39
- duration: Time.now - start_time,
40
- success: false,
41
- error: error.message
42
- )
43
-
44
- # Re-raise error to trigger Que's retry mechanism
45
- raise
46
- end
47
- end
48
-
49
- private
50
-
51
- def store_result(result, metadata)
52
- return unless metadata[:step_name]
53
-
54
- Aidp::DatabaseConnection.connection.exec_params(
55
- <<~SQL,
56
- INSERT INTO analysis_results (step_name, data, metadata, created_at, updated_at)
57
- VALUES ($1, $2, $3, $4, $5)
58
- ON CONFLICT (step_name)
59
- DO UPDATE SET
60
- data = EXCLUDED.data,
61
- metadata = EXCLUDED.metadata,
62
- updated_at = EXCLUDED.updated_at
63
- SQL
64
- [
65
- metadata[:step_name],
66
- result.to_json,
67
- metadata.to_json,
68
- Time.now,
69
- Time.now
70
- ]
71
- )
72
- end
73
-
74
- def record_metrics(provider_type:, duration:, success:, error: nil)
75
- Aidp::DatabaseConnection.connection.exec_params(
76
- <<~SQL,
77
- INSERT INTO provider_metrics (
78
- provider_type, duration, success, error,
79
- job_id, attempt, created_at
80
- )
81
- VALUES ($1, $2, $3, $4, $5, $6, $7)
82
- SQL
83
- [
84
- provider_type,
85
- duration,
86
- success,
87
- error,
88
- que_attrs[:job_id],
89
- que_attrs[:error_count] + 1,
90
- Time.now
91
- ]
92
- )
93
- end
94
- end
95
- end
96
- end