aidp 0.23.0 → 0.25.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 +27 -1
- data/lib/aidp/auto_update/bundler_adapter.rb +66 -0
- data/lib/aidp/auto_update/checkpoint.rb +178 -0
- data/lib/aidp/auto_update/checkpoint_store.rb +182 -0
- data/lib/aidp/auto_update/coordinator.rb +204 -0
- data/lib/aidp/auto_update/errors.rb +17 -0
- data/lib/aidp/auto_update/failure_tracker.rb +162 -0
- data/lib/aidp/auto_update/rubygems_api_adapter.rb +95 -0
- data/lib/aidp/auto_update/update_check.rb +106 -0
- data/lib/aidp/auto_update/update_logger.rb +143 -0
- data/lib/aidp/auto_update/update_policy.rb +109 -0
- data/lib/aidp/auto_update/version_detector.rb +144 -0
- data/lib/aidp/auto_update.rb +52 -0
- data/lib/aidp/cli.rb +168 -1
- data/lib/aidp/execute/work_loop_runner.rb +252 -45
- data/lib/aidp/execute/work_loop_unit_scheduler.rb +27 -2
- data/lib/aidp/harness/condition_detector.rb +42 -8
- data/lib/aidp/harness/config_manager.rb +7 -0
- data/lib/aidp/harness/config_schema.rb +75 -0
- data/lib/aidp/harness/configuration.rb +69 -6
- data/lib/aidp/harness/error_handler.rb +117 -44
- data/lib/aidp/harness/provider_factory.rb +2 -0
- data/lib/aidp/harness/provider_manager.rb +64 -0
- data/lib/aidp/harness/provider_metrics.rb +138 -0
- data/lib/aidp/harness/runner.rb +90 -29
- data/lib/aidp/harness/simple_user_interface.rb +4 -0
- data/lib/aidp/harness/state/ui_state.rb +0 -10
- data/lib/aidp/harness/state_manager.rb +1 -15
- data/lib/aidp/harness/test_runner.rb +39 -2
- data/lib/aidp/logger.rb +34 -4
- data/lib/aidp/message_display.rb +10 -2
- data/lib/aidp/prompt_optimization/style_guide_indexer.rb +3 -1
- data/lib/aidp/provider_manager.rb +2 -0
- data/lib/aidp/providers/adapter.rb +241 -0
- data/lib/aidp/providers/anthropic.rb +75 -7
- data/lib/aidp/providers/base.rb +29 -1
- data/lib/aidp/providers/capability_registry.rb +205 -0
- data/lib/aidp/providers/codex.rb +14 -0
- data/lib/aidp/providers/error_taxonomy.rb +195 -0
- data/lib/aidp/providers/gemini.rb +3 -2
- data/lib/aidp/providers/kilocode.rb +202 -0
- data/lib/aidp/setup/provider_registry.rb +122 -0
- data/lib/aidp/setup/wizard.rb +125 -33
- data/lib/aidp/skills/composer.rb +4 -0
- data/lib/aidp/skills/loader.rb +3 -1
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +323 -33
- data/lib/aidp/watch/ci_fix_processor.rb +448 -0
- data/lib/aidp/watch/plan_processor.rb +12 -2
- data/lib/aidp/watch/repository_client.rb +384 -4
- data/lib/aidp/watch/review_processor.rb +266 -0
- data/lib/aidp/watch/reviewers/base_reviewer.rb +164 -0
- data/lib/aidp/watch/reviewers/performance_reviewer.rb +65 -0
- data/lib/aidp/watch/reviewers/security_reviewer.rb +65 -0
- data/lib/aidp/watch/reviewers/senior_dev_reviewer.rb +33 -0
- data/lib/aidp/watch/runner.rb +222 -5
- data/lib/aidp/watch/state_store.rb +53 -0
- data/lib/aidp/workflows/guided_agent.rb +53 -0
- data/lib/aidp/worktree.rb +67 -10
- data/lib/aidp.rb +1 -0
- data/templates/work_loop/decide_whats_next.md +21 -0
- data/templates/work_loop/diagnose_failures.md +21 -0
- metadata +29 -3
- /data/{bin → exe}/aidp +0 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aidp
|
|
4
|
+
module Providers
|
|
5
|
+
# ErrorTaxonomy defines the five standardized error categories that all providers
|
|
6
|
+
# use for consistent error handling, retry logic, and escalation.
|
|
7
|
+
#
|
|
8
|
+
# Categories:
|
|
9
|
+
# - rate_limited: Provider is rate-limiting requests (switch provider immediately)
|
|
10
|
+
# - auth_expired: Authentication credentials are invalid or expired (escalate or switch)
|
|
11
|
+
# - quota_exceeded: Usage quota has been exceeded (switch provider)
|
|
12
|
+
# - transient: Temporary error that may resolve on retry (retry with backoff)
|
|
13
|
+
# - permanent: Permanent error that won't resolve with retry (escalate or abort)
|
|
14
|
+
#
|
|
15
|
+
# @see https://github.com/viamin/aidp/issues/243
|
|
16
|
+
module ErrorTaxonomy
|
|
17
|
+
# Error category constants
|
|
18
|
+
RATE_LIMITED = :rate_limited
|
|
19
|
+
AUTH_EXPIRED = :auth_expired
|
|
20
|
+
QUOTA_EXCEEDED = :quota_exceeded
|
|
21
|
+
TRANSIENT = :transient
|
|
22
|
+
PERMANENT = :permanent
|
|
23
|
+
|
|
24
|
+
# All valid error categories
|
|
25
|
+
CATEGORIES = [
|
|
26
|
+
RATE_LIMITED,
|
|
27
|
+
AUTH_EXPIRED,
|
|
28
|
+
QUOTA_EXCEEDED,
|
|
29
|
+
TRANSIENT,
|
|
30
|
+
PERMANENT
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
# Default error patterns for common error messages
|
|
34
|
+
# Providers can override these with provider-specific patterns
|
|
35
|
+
DEFAULT_PATTERNS = {
|
|
36
|
+
rate_limited: [
|
|
37
|
+
/rate.?limit/i,
|
|
38
|
+
/too.?many.?requests/i,
|
|
39
|
+
/429/,
|
|
40
|
+
/throttl(ed|ing)/i,
|
|
41
|
+
/request.?limit/i,
|
|
42
|
+
/requests.?per.?minute/i,
|
|
43
|
+
/rpm.?exceeded/i
|
|
44
|
+
],
|
|
45
|
+
auth_expired: [
|
|
46
|
+
/auth(entication|orization).?(fail(ed|ure)|error)/i,
|
|
47
|
+
/invalid.?(api.?key|token|credential)/i,
|
|
48
|
+
/expired.?(api.?key|token|credential)/i,
|
|
49
|
+
/unauthorized/i,
|
|
50
|
+
/401/,
|
|
51
|
+
/403/,
|
|
52
|
+
/permission.?denied/i,
|
|
53
|
+
/access.?denied/i
|
|
54
|
+
],
|
|
55
|
+
quota_exceeded: [
|
|
56
|
+
/quota.?(exceed(ed)?|limit|exhausted)/i,
|
|
57
|
+
/usage.?limit/i,
|
|
58
|
+
/billing.?limit/i,
|
|
59
|
+
/credit.?limit/i,
|
|
60
|
+
/insufficient.?quota/i,
|
|
61
|
+
/usage.?cap/i
|
|
62
|
+
],
|
|
63
|
+
transient: [
|
|
64
|
+
/timeout/i,
|
|
65
|
+
/timed?.?out/i,
|
|
66
|
+
/connection.?(reset|refused|lost|closed)/i,
|
|
67
|
+
/temporary.?error/i,
|
|
68
|
+
/try.?again/i,
|
|
69
|
+
/service.?unavailable/i,
|
|
70
|
+
/503/,
|
|
71
|
+
/502/,
|
|
72
|
+
/504/,
|
|
73
|
+
/gateway.?timeout/i,
|
|
74
|
+
/network.?error/i,
|
|
75
|
+
/socket.?error/i,
|
|
76
|
+
/connection.?error/i,
|
|
77
|
+
/broken.?pipe/i,
|
|
78
|
+
/host.?unreachable/i
|
|
79
|
+
],
|
|
80
|
+
permanent: [
|
|
81
|
+
/invalid.?(model|parameter|request|input)/i,
|
|
82
|
+
/unsupported.?(operation|feature|model)/i,
|
|
83
|
+
/not.?found/i,
|
|
84
|
+
/404/,
|
|
85
|
+
/bad.?request/i,
|
|
86
|
+
/400/,
|
|
87
|
+
/malformed/i,
|
|
88
|
+
/syntax.?error/i,
|
|
89
|
+
/validation.?error/i,
|
|
90
|
+
/model.?not.?available/i,
|
|
91
|
+
/model.?deprecated/i
|
|
92
|
+
]
|
|
93
|
+
}.freeze
|
|
94
|
+
|
|
95
|
+
# Retry policy for each category
|
|
96
|
+
RETRY_POLICIES = {
|
|
97
|
+
rate_limited: {
|
|
98
|
+
retry: false,
|
|
99
|
+
switch_provider: true,
|
|
100
|
+
escalate: false,
|
|
101
|
+
backoff_strategy: :none
|
|
102
|
+
},
|
|
103
|
+
auth_expired: {
|
|
104
|
+
retry: false,
|
|
105
|
+
switch_provider: true,
|
|
106
|
+
escalate: true,
|
|
107
|
+
backoff_strategy: :none
|
|
108
|
+
},
|
|
109
|
+
quota_exceeded: {
|
|
110
|
+
retry: false,
|
|
111
|
+
switch_provider: true,
|
|
112
|
+
escalate: false,
|
|
113
|
+
backoff_strategy: :none
|
|
114
|
+
},
|
|
115
|
+
transient: {
|
|
116
|
+
retry: true,
|
|
117
|
+
switch_provider: false,
|
|
118
|
+
escalate: false,
|
|
119
|
+
backoff_strategy: :exponential
|
|
120
|
+
},
|
|
121
|
+
permanent: {
|
|
122
|
+
retry: false,
|
|
123
|
+
switch_provider: false,
|
|
124
|
+
escalate: true,
|
|
125
|
+
backoff_strategy: :none
|
|
126
|
+
}
|
|
127
|
+
}.freeze
|
|
128
|
+
|
|
129
|
+
# Check if a category is valid
|
|
130
|
+
# @param category [Symbol] category to check
|
|
131
|
+
# @return [Boolean] true if valid
|
|
132
|
+
def self.valid_category?(category)
|
|
133
|
+
CATEGORIES.include?(category)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Get retry policy for a category
|
|
137
|
+
# @param category [Symbol] error category
|
|
138
|
+
# @return [Hash] retry policy configuration
|
|
139
|
+
def self.retry_policy(category)
|
|
140
|
+
RETRY_POLICIES[category] || RETRY_POLICIES[:transient]
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Classify an error message using default patterns
|
|
144
|
+
# @param message [String] error message
|
|
145
|
+
# @return [Symbol] error category
|
|
146
|
+
def self.classify_message(message)
|
|
147
|
+
return :transient if message.nil? || message.empty?
|
|
148
|
+
|
|
149
|
+
message_lower = message.downcase
|
|
150
|
+
|
|
151
|
+
# Check each category's patterns
|
|
152
|
+
DEFAULT_PATTERNS.each do |category, patterns|
|
|
153
|
+
patterns.each do |pattern|
|
|
154
|
+
return category if message_lower.match?(pattern)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Default to transient for unknown errors
|
|
159
|
+
:transient
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Check if an error category is retryable
|
|
163
|
+
# @param category [Symbol] error category
|
|
164
|
+
# @return [Boolean] true if should retry
|
|
165
|
+
def self.retryable?(category)
|
|
166
|
+
policy = retry_policy(category)
|
|
167
|
+
policy[:retry] == true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Check if an error category should trigger provider switch
|
|
171
|
+
# @param category [Symbol] error category
|
|
172
|
+
# @return [Boolean] true if should switch provider
|
|
173
|
+
def self.should_switch_provider?(category)
|
|
174
|
+
policy = retry_policy(category)
|
|
175
|
+
policy[:switch_provider] == true
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Check if an error category should be escalated
|
|
179
|
+
# @param category [Symbol] error category
|
|
180
|
+
# @return [Boolean] true if should escalate
|
|
181
|
+
def self.should_escalate?(category)
|
|
182
|
+
policy = retry_policy(category)
|
|
183
|
+
policy[:escalate] == true
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Get backoff strategy for a category
|
|
187
|
+
# @param category [Symbol] error category
|
|
188
|
+
# @return [Symbol] backoff strategy (:none, :linear, :exponential)
|
|
189
|
+
def self.backoff_strategy(category)
|
|
190
|
+
policy = retry_policy(category)
|
|
191
|
+
policy[:backoff_strategy] || :none
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -36,11 +36,12 @@ module Aidp
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
begin
|
|
39
|
+
command_args = ["--prompt", prompt]
|
|
39
40
|
# Use debug_execute_command with streaming support
|
|
40
|
-
result = debug_execute_command("gemini", args:
|
|
41
|
+
result = debug_execute_command("gemini", args: command_args, timeout: timeout_seconds, streaming: streaming_enabled)
|
|
41
42
|
|
|
42
43
|
# Log the results
|
|
43
|
-
debug_command("gemini", args:
|
|
44
|
+
debug_command("gemini", args: command_args, input: nil, output: result.out, error: result.err, exit_code: result.exit_status)
|
|
44
45
|
|
|
45
46
|
if result.exit_status == 0
|
|
46
47
|
result.out
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "timeout"
|
|
4
|
+
require_relative "base"
|
|
5
|
+
require_relative "../util"
|
|
6
|
+
require_relative "../debug_mixin"
|
|
7
|
+
|
|
8
|
+
module Aidp
|
|
9
|
+
module Providers
|
|
10
|
+
class Kilocode < Base
|
|
11
|
+
include Aidp::DebugMixin
|
|
12
|
+
|
|
13
|
+
def self.available?
|
|
14
|
+
!!Aidp::Util.which("kilocode")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def name
|
|
18
|
+
"kilocode"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def display_name
|
|
22
|
+
"Kilocode"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def send_message(prompt:, session: nil)
|
|
26
|
+
raise "kilocode not available" unless self.class.available?
|
|
27
|
+
|
|
28
|
+
# Smart timeout calculation
|
|
29
|
+
timeout_seconds = calculate_timeout
|
|
30
|
+
|
|
31
|
+
debug_provider("kilocode", "Starting execution", {timeout: timeout_seconds})
|
|
32
|
+
debug_log("📝 Sending prompt to kilocode (length: #{prompt.length})", level: :info)
|
|
33
|
+
|
|
34
|
+
# Check if streaming mode is enabled
|
|
35
|
+
streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
|
|
36
|
+
if streaming_enabled
|
|
37
|
+
display_message("📺 Display streaming enabled - output buffering reduced", type: :info)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Check if prompt is too large and warn
|
|
41
|
+
if prompt.length > 3000
|
|
42
|
+
debug_log("⚠️ Large prompt detected (#{prompt.length} chars) - this may cause rate limiting", level: :warn)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Set up activity monitoring
|
|
46
|
+
setup_activity_monitoring("kilocode", method(:activity_callback))
|
|
47
|
+
record_activity("Starting kilocode execution")
|
|
48
|
+
|
|
49
|
+
# Create a spinner for activity display
|
|
50
|
+
spinner = TTY::Spinner.new("[:spinner] :title", format: :dots, hide_cursor: true)
|
|
51
|
+
spinner.auto_spin
|
|
52
|
+
|
|
53
|
+
activity_display_thread = Thread.new do
|
|
54
|
+
start_time = Time.now
|
|
55
|
+
loop do
|
|
56
|
+
sleep 0.5 # Update every 500ms to reduce spam
|
|
57
|
+
elapsed = Time.now - start_time
|
|
58
|
+
|
|
59
|
+
# Break if we've been running too long or state changed
|
|
60
|
+
break if elapsed > timeout_seconds || @activity_state == :completed || @activity_state == :failed
|
|
61
|
+
|
|
62
|
+
update_spinner_status(spinner, elapsed, "🔄 kilocode")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
begin
|
|
67
|
+
# Build kilocode command arguments
|
|
68
|
+
args = ["--auto"]
|
|
69
|
+
|
|
70
|
+
# Add model if specified
|
|
71
|
+
model = ENV["KILOCODE_MODEL"]
|
|
72
|
+
if model
|
|
73
|
+
args.concat(["-m", model])
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Add workspace detection if needed
|
|
77
|
+
if Dir.exist?(".git") && ENV["KILOCODE_WORKSPACE"]
|
|
78
|
+
args.concat(["--workspace", ENV["KILOCODE_WORKSPACE"]])
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Set authentication via environment variable
|
|
82
|
+
env_vars = {}
|
|
83
|
+
if ENV["KILOCODE_TOKEN"]
|
|
84
|
+
env_vars["KILOCODE_TOKEN"] = ENV["KILOCODE_TOKEN"]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Use debug_execute_command for better debugging
|
|
88
|
+
result = debug_execute_command("kilocode", args: args, input: prompt, timeout: timeout_seconds, streaming: streaming_enabled, env: env_vars)
|
|
89
|
+
|
|
90
|
+
# Log the results
|
|
91
|
+
debug_command("kilocode", args: args, input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
|
|
92
|
+
|
|
93
|
+
if result.exit_status == 0
|
|
94
|
+
spinner.success("✓")
|
|
95
|
+
mark_completed
|
|
96
|
+
result.out
|
|
97
|
+
else
|
|
98
|
+
spinner.error("✗")
|
|
99
|
+
mark_failed("kilocode failed with exit code #{result.exit_status}")
|
|
100
|
+
debug_error(StandardError.new("kilocode failed"), {exit_code: result.exit_status, stderr: result.err})
|
|
101
|
+
raise "kilocode failed with exit code #{result.exit_status}: #{result.err}"
|
|
102
|
+
end
|
|
103
|
+
rescue => e
|
|
104
|
+
spinner&.error("✗")
|
|
105
|
+
mark_failed("kilocode execution failed: #{e.message}")
|
|
106
|
+
debug_error(e, {provider: "kilocode", prompt_length: prompt.length})
|
|
107
|
+
raise
|
|
108
|
+
ensure
|
|
109
|
+
cleanup_activity_display(activity_display_thread, spinner)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def calculate_timeout
|
|
116
|
+
# Priority order for timeout calculation:
|
|
117
|
+
# 1. Quick mode (for testing)
|
|
118
|
+
# 2. Environment variable override
|
|
119
|
+
# 3. Adaptive timeout based on step type
|
|
120
|
+
# 4. Default timeout
|
|
121
|
+
|
|
122
|
+
if ENV["AIDP_QUICK_MODE"]
|
|
123
|
+
display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
|
|
124
|
+
return TIMEOUT_QUICK_MODE
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
if ENV["AIDP_KILOCODE_TIMEOUT"]
|
|
128
|
+
return ENV["AIDP_KILOCODE_TIMEOUT"].to_i
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
if adaptive_timeout
|
|
132
|
+
display_message("🧠 Using adaptive timeout: #{adaptive_timeout} seconds", type: :info)
|
|
133
|
+
return adaptive_timeout
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Default timeout
|
|
137
|
+
display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
|
|
138
|
+
TIMEOUT_DEFAULT
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def adaptive_timeout
|
|
142
|
+
@adaptive_timeout ||= begin
|
|
143
|
+
# Timeout recommendations based on step type patterns
|
|
144
|
+
step_name = ENV["AIDP_CURRENT_STEP"] || ""
|
|
145
|
+
|
|
146
|
+
case step_name
|
|
147
|
+
when /REPOSITORY_ANALYSIS/
|
|
148
|
+
TIMEOUT_REPOSITORY_ANALYSIS
|
|
149
|
+
when /ARCHITECTURE_ANALYSIS/
|
|
150
|
+
TIMEOUT_ARCHITECTURE_ANALYSIS
|
|
151
|
+
when /TEST_ANALYSIS/
|
|
152
|
+
TIMEOUT_TEST_ANALYSIS
|
|
153
|
+
when /FUNCTIONALITY_ANALYSIS/
|
|
154
|
+
TIMEOUT_FUNCTIONALITY_ANALYSIS
|
|
155
|
+
when /DOCUMENTATION_ANALYSIS/
|
|
156
|
+
TIMEOUT_DOCUMENTATION_ANALYSIS
|
|
157
|
+
when /STATIC_ANALYSIS/
|
|
158
|
+
TIMEOUT_STATIC_ANALYSIS
|
|
159
|
+
when /REFACTORING_RECOMMENDATIONS/
|
|
160
|
+
TIMEOUT_REFACTORING_RECOMMENDATIONS
|
|
161
|
+
else
|
|
162
|
+
nil # Use default
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def activity_callback(state, message, provider)
|
|
168
|
+
# This is now handled by the animated display thread
|
|
169
|
+
# Only print static messages for state changes
|
|
170
|
+
case state
|
|
171
|
+
when :starting
|
|
172
|
+
display_message("🚀 Starting kilocode execution...", type: :info)
|
|
173
|
+
when :completed
|
|
174
|
+
display_message("✅ kilocode execution completed", type: :success)
|
|
175
|
+
when :failed
|
|
176
|
+
display_message("❌ kilocode execution failed: #{message}", type: :error)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def setup_activity_monitoring(provider_name, callback)
|
|
181
|
+
@activity_callback = callback
|
|
182
|
+
@activity_state = :starting
|
|
183
|
+
@activity_start_time = Time.now
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def record_activity(message)
|
|
187
|
+
@activity_state = :running
|
|
188
|
+
@activity_callback&.call(:running, message, "kilocode")
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def mark_completed
|
|
192
|
+
@activity_state = :completed
|
|
193
|
+
@activity_callback&.call(:completed, "Execution completed", "kilocode")
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def mark_failed(reason)
|
|
197
|
+
@activity_state = :failed
|
|
198
|
+
@activity_callback&.call(:failed, reason, "kilocode")
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aidp
|
|
4
|
+
module Setup
|
|
5
|
+
# Centralized registry for provider metadata including billing types and model families.
|
|
6
|
+
# This module provides a single source of truth for provider configuration options.
|
|
7
|
+
module ProviderRegistry
|
|
8
|
+
# Billing type options for providers
|
|
9
|
+
BILLING_TYPES = [
|
|
10
|
+
{
|
|
11
|
+
label: "Subscription / flat-rate",
|
|
12
|
+
value: "subscription",
|
|
13
|
+
description: "Monthly or annual subscription with unlimited usage"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: "Usage-based / metered (API)",
|
|
17
|
+
value: "usage_based",
|
|
18
|
+
description: "Pay per API call or token usage"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Passthrough / local (no billing)",
|
|
22
|
+
value: "passthrough",
|
|
23
|
+
description: "Local execution or proxy without direct billing"
|
|
24
|
+
}
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
# Model family options for providers
|
|
28
|
+
MODEL_FAMILIES = [
|
|
29
|
+
{
|
|
30
|
+
label: "Auto (let provider decide)",
|
|
31
|
+
value: "auto",
|
|
32
|
+
description: "Use provider's default model selection"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: "OpenAI o-series (reasoning models)",
|
|
36
|
+
value: "openai_o",
|
|
37
|
+
description: "Advanced reasoning capabilities, slower but more thorough"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: "Anthropic Claude (balanced)",
|
|
41
|
+
value: "claude",
|
|
42
|
+
description: "Balanced performance for general-purpose tasks"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
label: "Google Gemini (multimodal)",
|
|
46
|
+
value: "gemini",
|
|
47
|
+
description: "Google's multimodal AI with strong reasoning and vision capabilities"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
label: "Meta Llama (open-source)",
|
|
51
|
+
value: "llama",
|
|
52
|
+
description: "Meta's open-source model family, suitable for self-hosting"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: "DeepSeek (efficient reasoning)",
|
|
56
|
+
value: "deepseek",
|
|
57
|
+
description: "Cost-efficient reasoning models with strong performance"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
label: "Mistral (European/open)",
|
|
61
|
+
value: "mistral",
|
|
62
|
+
description: "European provider with open-source focus"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: "Local LLM (self-hosted)",
|
|
66
|
+
value: "local",
|
|
67
|
+
description: "Self-hosted or local model execution"
|
|
68
|
+
}
|
|
69
|
+
].freeze
|
|
70
|
+
|
|
71
|
+
# Returns array of [label, value] pairs for billing types
|
|
72
|
+
def self.billing_type_choices
|
|
73
|
+
BILLING_TYPES.map { |bt| [bt[:label], bt[:value]] }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Returns array of [label, value] pairs for model families
|
|
77
|
+
def self.model_family_choices
|
|
78
|
+
MODEL_FAMILIES.map { |mf| [mf[:label], mf[:value]] }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Finds label for a given billing type value
|
|
82
|
+
def self.billing_type_label(value)
|
|
83
|
+
BILLING_TYPES.find { |bt| bt[:value] == value }&.dig(:label) || value
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Finds label for a given model family value
|
|
87
|
+
def self.model_family_label(value)
|
|
88
|
+
MODEL_FAMILIES.find { |mf| mf[:value] == value }&.dig(:label) || value
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Finds description for a given billing type value
|
|
92
|
+
def self.billing_type_description(value)
|
|
93
|
+
BILLING_TYPES.find { |bt| bt[:value] == value }&.dig(:description)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Finds description for a given model family value
|
|
97
|
+
def self.model_family_description(value)
|
|
98
|
+
MODEL_FAMILIES.find { |mf| mf[:value] == value }&.dig(:description)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Validates if a billing type value is valid
|
|
102
|
+
def self.valid_billing_type?(value)
|
|
103
|
+
BILLING_TYPES.any? { |bt| bt[:value] == value }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Validates if a model family value is valid
|
|
107
|
+
def self.valid_model_family?(value)
|
|
108
|
+
MODEL_FAMILIES.any? { |mf| mf[:value] == value }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Returns all valid billing type values
|
|
112
|
+
def self.billing_type_values
|
|
113
|
+
BILLING_TYPES.map { |bt| bt[:value] }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Returns all valid model family values
|
|
117
|
+
def self.model_family_values
|
|
118
|
+
MODEL_FAMILIES.map { |mf| mf[:value] }
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|