aidp 0.3.0 → 0.5.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 +59 -4
- data/lib/aidp/analyze/agent_personas.rb +1 -1
- data/lib/aidp/analyze/database.rb +99 -82
- data/lib/aidp/analyze/error_handler.rb +12 -76
- data/lib/aidp/analyze/focus_guidance.rb +2 -2
- data/lib/aidp/analyze/metrics_storage.rb +336 -0
- data/lib/aidp/analyze/prioritizer.rb +2 -2
- data/lib/aidp/analyze/ruby_maat_integration.rb +6 -102
- data/lib/aidp/analyze/runner.rb +107 -191
- data/lib/aidp/analyze/steps.rb +29 -30
- data/lib/aidp/analyze/storage.rb +233 -171
- data/lib/aidp/cli/jobs_command.rb +489 -0
- data/lib/aidp/cli/terminal_io.rb +52 -0
- data/lib/aidp/cli.rb +104 -45
- data/lib/aidp/core_ext/class_attribute.rb +36 -0
- data/lib/aidp/database/pg_adapter.rb +148 -0
- data/lib/aidp/database_config.rb +69 -0
- data/lib/aidp/database_connection.rb +72 -0
- data/lib/aidp/database_migration.rb +158 -0
- data/lib/aidp/execute/runner.rb +65 -92
- data/lib/aidp/execute/steps.rb +81 -82
- data/lib/aidp/job_manager.rb +41 -0
- data/lib/aidp/jobs/base_job.rb +47 -0
- data/lib/aidp/jobs/provider_execution_job.rb +96 -0
- data/lib/aidp/provider_manager.rb +25 -0
- data/lib/aidp/providers/agent_supervisor.rb +348 -0
- data/lib/aidp/providers/anthropic.rb +166 -3
- data/lib/aidp/providers/base.rb +153 -6
- data/lib/aidp/providers/cursor.rb +247 -43
- data/lib/aidp/providers/gemini.rb +166 -3
- data/lib/aidp/providers/supervised_base.rb +317 -0
- data/lib/aidp/providers/supervised_cursor.rb +22 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp.rb +25 -34
- data/templates/ANALYZE/01_REPOSITORY_ANALYSIS.md +4 -4
- metadata +72 -35
data/lib/aidp/execute/runner.rb
CHANGED
@@ -1,135 +1,108 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "erb"
|
4
|
-
require "yaml"
|
5
|
-
require "json"
|
6
|
-
|
7
3
|
module Aidp
|
8
4
|
module Execute
|
9
|
-
# Handles execution logic for execute mode steps
|
10
5
|
class Runner
|
11
|
-
attr_reader :project_dir, :progress
|
12
|
-
|
13
6
|
def initialize(project_dir)
|
14
7
|
@project_dir = project_dir
|
15
|
-
@progress = Aidp::Execute::Progress.new(project_dir)
|
16
8
|
end
|
17
9
|
|
18
|
-
def
|
19
|
-
|
10
|
+
def progress
|
11
|
+
@progress ||= Aidp::Execute::Progress.new(@project_dir)
|
12
|
+
end
|
20
13
|
|
14
|
+
def run_step(step_name, options = {})
|
15
|
+
# Always validate step exists first, even in mock mode
|
21
16
|
step_spec = Aidp::Execute::Steps::SPEC[step_name]
|
22
|
-
|
23
|
-
|
24
|
-
# Load template
|
25
|
-
template = find_template(template_name)
|
26
|
-
raise "Template '#{template_name}' not found" unless template
|
17
|
+
raise "Step '#{step_name}' not found" unless step_spec
|
27
18
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
if options[:simulate_error]
|
33
|
-
return {
|
34
|
-
status: "error",
|
35
|
-
error: options[:simulate_error],
|
36
|
-
step: step_name
|
37
|
-
}
|
19
|
+
if should_use_mock_mode?(options)
|
20
|
+
return options[:simulate_error] ?
|
21
|
+
{status: "error", error: options[:simulate_error]} :
|
22
|
+
mock_execution_result
|
38
23
|
end
|
39
24
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
25
|
+
job = Aidp::Jobs::ProviderExecutionJob.enqueue(
|
26
|
+
provider_type: "cursor",
|
27
|
+
prompt: composed_prompt(step_name, options),
|
28
|
+
metadata: {
|
29
|
+
step_name: step_name,
|
30
|
+
project_dir: @project_dir
|
31
|
+
}
|
32
|
+
)
|
47
33
|
|
48
|
-
|
49
|
-
|
34
|
+
wait_for_job_completion(job)
|
35
|
+
end
|
50
36
|
|
51
|
-
|
52
|
-
generate_output_files(step_name, step_spec["outs"], result)
|
37
|
+
private
|
53
38
|
|
54
|
-
|
55
|
-
|
39
|
+
def should_use_mock_mode?(options)
|
40
|
+
options[:mock_mode] || ENV["AIDP_MOCK_MODE"] == "1" || ENV["RAILS_ENV"] == "test"
|
41
|
+
end
|
56
42
|
|
57
|
-
|
43
|
+
def mock_execution_result
|
44
|
+
{
|
45
|
+
status: "completed",
|
46
|
+
provider: "mock",
|
47
|
+
message: "Mock execution",
|
48
|
+
output: "Mock execution result"
|
49
|
+
}
|
58
50
|
end
|
59
51
|
|
60
|
-
|
52
|
+
def wait_for_job_completion(job_id)
|
53
|
+
loop do
|
54
|
+
job = Que.execute("SELECT * FROM que_jobs WHERE id = $1", [job_id]).first
|
55
|
+
return {status: "completed"} if job && job["finished_at"] && job["error_count"] == 0
|
56
|
+
return {status: "failed", error: job["last_error_message"]} if job && job["error_count"] && job["error_count"] > 0
|
57
|
+
|
58
|
+
if job && job["finished_at"].nil?
|
59
|
+
duration = Time.now - job["run_at"]
|
60
|
+
minutes = (duration / 60).to_i
|
61
|
+
seconds = (duration % 60).to_i
|
62
|
+
duration_str = (minutes > 0) ? "#{minutes}m #{seconds}s" : "#{seconds}s"
|
63
|
+
print "\r🔄 Job #{job_id} is running (#{duration_str})...".ljust(80)
|
64
|
+
else
|
65
|
+
print "\r⏳ Job #{job_id} is pending...".ljust(80)
|
66
|
+
end
|
67
|
+
$stdout.flush
|
68
|
+
sleep 1
|
69
|
+
end
|
70
|
+
ensure
|
71
|
+
print "\r" + " " * 80 + "\r"
|
72
|
+
end
|
61
73
|
|
62
74
|
def find_template(template_name)
|
63
75
|
template_search_paths.each do |path|
|
64
|
-
|
65
|
-
return
|
76
|
+
template_path = File.join(path, template_name)
|
77
|
+
return template_path if File.exist?(template_path)
|
66
78
|
end
|
67
79
|
nil
|
68
80
|
end
|
69
81
|
|
70
82
|
def template_search_paths
|
71
83
|
[
|
72
|
-
File.join(@project_dir, "templates"),
|
73
|
-
File.join(@project_dir, "templates", "COMMON")
|
74
|
-
File.join(File.dirname(__FILE__), "..", "..", "..", "templates", "EXECUTE"),
|
75
|
-
File.join(File.dirname(__FILE__), "..", "..", "..", "templates", "COMMON")
|
84
|
+
File.join(@project_dir, "templates", "EXECUTE"),
|
85
|
+
File.join(@project_dir, "templates", "COMMON")
|
76
86
|
]
|
77
87
|
end
|
78
88
|
|
79
|
-
def composed_prompt(
|
80
|
-
|
81
|
-
|
89
|
+
def composed_prompt(step_name, options = {})
|
90
|
+
step_spec = Aidp::Execute::Steps::SPEC[step_name]
|
91
|
+
raise "Step '#{step_name}' not found" unless step_spec
|
92
|
+
|
93
|
+
template_name = step_spec["templates"].first
|
94
|
+
template_path = find_template(template_name)
|
95
|
+
raise "Template not found for step #{step_name}" unless template_path
|
82
96
|
|
83
|
-
|
84
|
-
agent_base = find_template("AGENT_BASE.md")
|
85
|
-
template = "#{agent_base}\n\n#{template}" if agent_base
|
97
|
+
template = File.read(template_path)
|
86
98
|
|
87
|
-
# Replace
|
99
|
+
# Replace template variables in the format {{key}} with option values
|
88
100
|
options.each do |key, value|
|
89
101
|
template = template.gsub("{{#{key}}}", value.to_s)
|
90
102
|
end
|
91
103
|
|
92
104
|
template
|
93
105
|
end
|
94
|
-
|
95
|
-
def generate_output_files(step_name, output_files, result)
|
96
|
-
output_files.each do |output_file|
|
97
|
-
file_path = File.join(@project_dir, output_file)
|
98
|
-
content = generate_output_content(step_name, output_file, result)
|
99
|
-
File.write(file_path, content)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def generate_output_content(step_name, output_file, result)
|
104
|
-
case output_file
|
105
|
-
when /\.md$/
|
106
|
-
"# #{step_name} Output\n\nGenerated on #{Time.now}\n\n## Result\n\n#{result[:status]}"
|
107
|
-
when /\.json$/
|
108
|
-
result.to_json
|
109
|
-
else
|
110
|
-
"Output for #{step_name}: #{result[:status]}"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def generate_database_export
|
115
|
-
database_file = File.join(@project_dir, ".aidp.db")
|
116
|
-
require "sqlite3"
|
117
|
-
|
118
|
-
begin
|
119
|
-
db = SQLite3::Database.new(database_file)
|
120
|
-
db.execute("CREATE TABLE IF NOT EXISTS execute_results (step TEXT, status TEXT, completed_at TEXT)")
|
121
|
-
|
122
|
-
Aidp::Execute::Steps::SPEC.keys.each do |step|
|
123
|
-
if @progress.step_completed?(step)
|
124
|
-
db.execute("INSERT INTO execute_results (step, status, completed_at) VALUES (?, ?, ?)",
|
125
|
-
[step, "success", Time.now.iso8601])
|
126
|
-
end
|
127
|
-
end
|
128
|
-
rescue => e
|
129
|
-
# Log the error but don't fail the execution
|
130
|
-
puts "Warning: Database export failed: #{e.message}"
|
131
|
-
end
|
132
|
-
end
|
133
106
|
end
|
134
107
|
end
|
135
108
|
end
|
data/lib/aidp/execute/steps.rb
CHANGED
@@ -2,110 +2,109 @@
|
|
2
2
|
|
3
3
|
module Aidp
|
4
4
|
module Execute
|
5
|
-
|
6
|
-
class Steps
|
5
|
+
module Steps
|
7
6
|
SPEC = {
|
8
7
|
"00_PRD" => {
|
9
|
-
"templates" => ["
|
10
|
-
"
|
11
|
-
"
|
12
|
-
"
|
8
|
+
"templates" => ["prd.md"],
|
9
|
+
"description" => "Generate Product Requirements Document",
|
10
|
+
"outs" => ["docs/prd.md"],
|
11
|
+
"gate" => true
|
13
12
|
},
|
14
13
|
"01_NFRS" => {
|
15
|
-
"templates" => ["
|
16
|
-
"
|
17
|
-
"
|
18
|
-
"
|
14
|
+
"templates" => ["nfrs.md"],
|
15
|
+
"description" => "Define Non-Functional Requirements",
|
16
|
+
"outs" => ["docs/nfrs.md"],
|
17
|
+
"gate" => true
|
19
18
|
},
|
20
19
|
"02_ARCHITECTURE" => {
|
21
|
-
"templates" => ["
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
20
|
+
"templates" => ["architecture.md"],
|
21
|
+
"description" => "Design System Architecture",
|
22
|
+
"outs" => ["docs/architecture.md"],
|
23
|
+
"gate" => true
|
25
24
|
},
|
26
25
|
"02A_ARCH_GATE_QUESTIONS" => {
|
27
|
-
"templates" => ["
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"
|
26
|
+
"templates" => ["arch_gate_questions.md"],
|
27
|
+
"description" => "Architecture Gate Questions",
|
28
|
+
"outs" => ["docs/arch_gate_questions.md"],
|
29
|
+
"gate" => true
|
31
30
|
},
|
32
31
|
"03_ADR_FACTORY" => {
|
33
|
-
"templates" => ["
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
32
|
+
"templates" => ["adr_factory.md"],
|
33
|
+
"description" => "Generate Architecture Decision Records",
|
34
|
+
"outs" => ["docs/adr/*.md"],
|
35
|
+
"gate" => false
|
37
36
|
},
|
38
37
|
"04_DOMAIN_DECOMPOSITION" => {
|
39
|
-
"templates" => ["
|
40
|
-
"
|
41
|
-
"
|
42
|
-
"
|
43
|
-
},
|
44
|
-
"
|
45
|
-
"templates" => ["
|
46
|
-
"
|
47
|
-
"
|
48
|
-
"
|
49
|
-
},
|
50
|
-
"
|
51
|
-
"templates" => ["
|
52
|
-
"
|
53
|
-
"
|
54
|
-
"
|
55
|
-
},
|
56
|
-
"
|
57
|
-
"templates" => ["
|
58
|
-
"
|
59
|
-
"
|
60
|
-
"
|
61
|
-
},
|
62
|
-
"
|
63
|
-
"templates" => ["
|
64
|
-
"
|
65
|
-
"
|
66
|
-
"
|
67
|
-
},
|
68
|
-
"
|
69
|
-
"templates" => ["
|
70
|
-
"
|
71
|
-
"
|
72
|
-
"
|
73
|
-
},
|
74
|
-
"
|
75
|
-
"templates" => ["
|
76
|
-
"
|
77
|
-
"
|
78
|
-
"
|
38
|
+
"templates" => ["domain_decomposition.md"],
|
39
|
+
"description" => "Decompose Domain into Components",
|
40
|
+
"outs" => ["docs/domain_decomposition.md"],
|
41
|
+
"gate" => true
|
42
|
+
},
|
43
|
+
"05_API_DESIGN" => {
|
44
|
+
"templates" => ["api_design.md"],
|
45
|
+
"description" => "Design APIs and Interfaces",
|
46
|
+
"outs" => ["docs/api_design.md"],
|
47
|
+
"gate" => true
|
48
|
+
},
|
49
|
+
"06_DATA_MODEL" => {
|
50
|
+
"templates" => ["data_model.md"],
|
51
|
+
"description" => "Design Data Model",
|
52
|
+
"outs" => ["docs/data_model.md"],
|
53
|
+
"gate" => true
|
54
|
+
},
|
55
|
+
"07_SECURITY_REVIEW" => {
|
56
|
+
"templates" => ["security_review.md"],
|
57
|
+
"description" => "Security Review and Threat Model",
|
58
|
+
"outs" => ["docs/security_review.md"],
|
59
|
+
"gate" => true
|
60
|
+
},
|
61
|
+
"08_PERFORMANCE_REVIEW" => {
|
62
|
+
"templates" => ["performance_review.md"],
|
63
|
+
"description" => "Performance Review and Optimization",
|
64
|
+
"outs" => ["docs/performance_review.md"],
|
65
|
+
"gate" => true
|
66
|
+
},
|
67
|
+
"09_RELIABILITY_REVIEW" => {
|
68
|
+
"templates" => ["reliability_review.md"],
|
69
|
+
"description" => "Reliability Review and SLOs",
|
70
|
+
"outs" => ["docs/reliability_review.md"],
|
71
|
+
"gate" => true
|
72
|
+
},
|
73
|
+
"10_TESTING_STRATEGY" => {
|
74
|
+
"templates" => ["testing_strategy.md"],
|
75
|
+
"description" => "Define Testing Strategy",
|
76
|
+
"outs" => ["docs/testing_strategy.md"],
|
77
|
+
"gate" => true
|
79
78
|
},
|
80
79
|
"11_STATIC_ANALYSIS" => {
|
81
|
-
"templates" => ["
|
82
|
-
"
|
83
|
-
"
|
84
|
-
"
|
80
|
+
"templates" => ["static_analysis.md"],
|
81
|
+
"description" => "Static Code Analysis",
|
82
|
+
"outs" => ["docs/static_analysis.md"],
|
83
|
+
"gate" => false
|
85
84
|
},
|
86
85
|
"12_OBSERVABILITY_SLOS" => {
|
87
|
-
"templates" => ["
|
88
|
-
"
|
89
|
-
"
|
90
|
-
"
|
86
|
+
"templates" => ["observability_slos.md"],
|
87
|
+
"description" => "Define Observability and SLOs",
|
88
|
+
"outs" => ["docs/observability_slos.md"],
|
89
|
+
"gate" => true
|
91
90
|
},
|
92
91
|
"13_DELIVERY_ROLLOUT" => {
|
93
|
-
"templates" => ["
|
94
|
-
"
|
95
|
-
"
|
96
|
-
"
|
92
|
+
"templates" => ["delivery_rollout.md"],
|
93
|
+
"description" => "Plan Delivery and Rollout",
|
94
|
+
"outs" => ["docs/delivery_rollout.md"],
|
95
|
+
"gate" => true
|
97
96
|
},
|
98
97
|
"14_DOCS_PORTAL" => {
|
99
|
-
"templates" => ["
|
100
|
-
"
|
101
|
-
"
|
102
|
-
"
|
98
|
+
"templates" => ["docs_portal.md"],
|
99
|
+
"description" => "Documentation Portal",
|
100
|
+
"outs" => ["docs/docs_portal.md"],
|
101
|
+
"gate" => false
|
103
102
|
},
|
104
103
|
"15_POST_RELEASE" => {
|
105
|
-
"templates" => ["
|
106
|
-
"
|
107
|
-
"
|
108
|
-
"
|
104
|
+
"templates" => ["post_release.md"],
|
105
|
+
"description" => "Post-Release Review",
|
106
|
+
"outs" => ["docs/post_release.md"],
|
107
|
+
"gate" => true
|
109
108
|
}
|
110
109
|
}.freeze
|
111
110
|
end
|
@@ -0,0 +1,41 @@
|
|
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
|
@@ -0,0 +1,47 @@
|
|
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
|
@@ -0,0 +1,96 @@
|
|
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
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aidp
|
4
|
+
class ProviderManager
|
5
|
+
class << self
|
6
|
+
def get_provider(provider_type)
|
7
|
+
case provider_type
|
8
|
+
when "cursor"
|
9
|
+
Aidp::Providers::Cursor.new
|
10
|
+
when "anthropic"
|
11
|
+
Aidp::Providers::Anthropic.new
|
12
|
+
when "gemini"
|
13
|
+
Aidp::Providers::Gemini.new
|
14
|
+
when "macos_ui"
|
15
|
+
Aidp::Providers::MacosUI.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_from_config(config = {})
|
20
|
+
provider_type = config["provider"] || "cursor"
|
21
|
+
get_provider(provider_type)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|