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.
- checksums.yaml +4 -4
- data/README.md +128 -151
- data/bin/aidp +1 -1
- data/lib/aidp/analysis/kb_inspector.rb +471 -0
- data/lib/aidp/analysis/seams.rb +159 -0
- data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +480 -0
- data/lib/aidp/analysis/tree_sitter_scan.rb +686 -0
- data/lib/aidp/analyze/error_handler.rb +2 -78
- data/lib/aidp/analyze/json_file_storage.rb +292 -0
- data/lib/aidp/analyze/progress.rb +12 -0
- data/lib/aidp/analyze/progress_visualizer.rb +12 -17
- data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
- data/lib/aidp/analyze/runner.rb +256 -87
- data/lib/aidp/analyze/steps.rb +6 -0
- data/lib/aidp/cli/jobs_command.rb +103 -435
- data/lib/aidp/cli.rb +317 -191
- data/lib/aidp/config.rb +298 -10
- data/lib/aidp/debug_logger.rb +195 -0
- data/lib/aidp/debug_mixin.rb +187 -0
- data/lib/aidp/execute/progress.rb +9 -0
- data/lib/aidp/execute/runner.rb +221 -40
- data/lib/aidp/execute/steps.rb +17 -7
- data/lib/aidp/execute/workflow_selector.rb +211 -0
- data/lib/aidp/harness/completion_checker.rb +268 -0
- data/lib/aidp/harness/condition_detector.rb +1526 -0
- data/lib/aidp/harness/config_loader.rb +373 -0
- data/lib/aidp/harness/config_manager.rb +382 -0
- data/lib/aidp/harness/config_schema.rb +1006 -0
- data/lib/aidp/harness/config_validator.rb +355 -0
- data/lib/aidp/harness/configuration.rb +477 -0
- data/lib/aidp/harness/enhanced_runner.rb +494 -0
- data/lib/aidp/harness/error_handler.rb +616 -0
- data/lib/aidp/harness/provider_config.rb +423 -0
- data/lib/aidp/harness/provider_factory.rb +306 -0
- data/lib/aidp/harness/provider_manager.rb +1269 -0
- data/lib/aidp/harness/provider_type_checker.rb +88 -0
- data/lib/aidp/harness/runner.rb +411 -0
- data/lib/aidp/harness/state/errors.rb +28 -0
- data/lib/aidp/harness/state/metrics.rb +219 -0
- data/lib/aidp/harness/state/persistence.rb +128 -0
- data/lib/aidp/harness/state/provider_state.rb +132 -0
- data/lib/aidp/harness/state/ui_state.rb +68 -0
- data/lib/aidp/harness/state/workflow_state.rb +123 -0
- data/lib/aidp/harness/state_manager.rb +586 -0
- data/lib/aidp/harness/status_display.rb +888 -0
- data/lib/aidp/harness/ui/base.rb +16 -0
- data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
- data/lib/aidp/harness/ui/error_handler.rb +132 -0
- data/lib/aidp/harness/ui/frame_manager.rb +361 -0
- data/lib/aidp/harness/ui/job_monitor.rb +500 -0
- data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
- data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
- data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
- data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
- data/lib/aidp/harness/ui/progress_display.rb +280 -0
- data/lib/aidp/harness/ui/question_collector.rb +141 -0
- data/lib/aidp/harness/ui/spinner_group.rb +184 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
- data/lib/aidp/harness/ui/status_manager.rb +312 -0
- data/lib/aidp/harness/ui/status_widget.rb +280 -0
- data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
- data/lib/aidp/harness/user_interface.rb +2381 -0
- data/lib/aidp/provider_manager.rb +131 -7
- data/lib/aidp/providers/anthropic.rb +28 -109
- data/lib/aidp/providers/base.rb +170 -0
- data/lib/aidp/providers/cursor.rb +52 -183
- data/lib/aidp/providers/gemini.rb +24 -109
- data/lib/aidp/providers/macos_ui.rb +99 -5
- data/lib/aidp/providers/opencode.rb +194 -0
- data/lib/aidp/storage/csv_storage.rb +172 -0
- data/lib/aidp/storage/file_manager.rb +214 -0
- data/lib/aidp/storage/json_storage.rb +140 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp.rb +56 -35
- data/templates/ANALYZE/06a_tree_sitter_scan.md +217 -0
- data/templates/COMMON/AGENT_BASE.md +11 -0
- data/templates/EXECUTE/00_PRD.md +4 -4
- data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
- data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
- data/templates/EXECUTE/08_TASKS.md +4 -4
- data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
- data/templates/README.md +279 -0
- data/templates/aidp-development.yml.example +373 -0
- data/templates/aidp-minimal.yml.example +48 -0
- data/templates/aidp-production.yml.example +475 -0
- data/templates/aidp.yml.example +598 -0
- metadata +106 -64
- data/lib/aidp/analyze/agent_personas.rb +0 -71
- data/lib/aidp/analyze/agent_tool_executor.rb +0 -445
- data/lib/aidp/analyze/data_retention_manager.rb +0 -426
- data/lib/aidp/analyze/database.rb +0 -260
- data/lib/aidp/analyze/dependencies.rb +0 -335
- data/lib/aidp/analyze/export_manager.rb +0 -425
- data/lib/aidp/analyze/focus_guidance.rb +0 -517
- data/lib/aidp/analyze/incremental_analyzer.rb +0 -543
- data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
- data/lib/aidp/analyze/large_analysis_progress.rb +0 -504
- data/lib/aidp/analyze/memory_manager.rb +0 -365
- data/lib/aidp/analyze/metrics_storage.rb +0 -336
- data/lib/aidp/analyze/parallel_processor.rb +0 -460
- data/lib/aidp/analyze/performance_optimizer.rb +0 -694
- data/lib/aidp/analyze/repository_chunker.rb +0 -704
- data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
- data/lib/aidp/analyze/storage.rb +0 -662
- data/lib/aidp/analyze/tool_configuration.rb +0 -456
- data/lib/aidp/analyze/tool_modernization.rb +0 -750
- data/lib/aidp/database/pg_adapter.rb +0 -148
- data/lib/aidp/database_config.rb +0 -69
- data/lib/aidp/database_connection.rb +0 -72
- data/lib/aidp/database_migration.rb +0 -158
- data/lib/aidp/job_manager.rb +0 -41
- data/lib/aidp/jobs/base_job.rb +0 -47
- data/lib/aidp/jobs/provider_execution_job.rb +0 -96
- data/lib/aidp/project_detector.rb +0 -117
- data/lib/aidp/providers/agent_supervisor.rb +0 -348
- data/lib/aidp/providers/supervised_base.rb +0 -317
- data/lib/aidp/providers/supervised_cursor.rb +0 -22
- data/lib/aidp/sync.rb +0 -13
- data/lib/aidp/workspace.rb +0 -19
@@ -1,9 +1,138 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "harness/provider_factory"
|
4
|
+
|
3
5
|
module Aidp
|
4
6
|
class ProviderManager
|
5
7
|
class << self
|
6
|
-
def get_provider(provider_type)
|
8
|
+
def get_provider(provider_type, options = {})
|
9
|
+
# Use harness factory if available
|
10
|
+
if options[:use_harness] != false
|
11
|
+
factory = get_harness_factory
|
12
|
+
return factory.create_provider(provider_type, options) if factory
|
13
|
+
end
|
14
|
+
|
15
|
+
# Fallback to legacy method
|
16
|
+
create_legacy_provider(provider_type)
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_from_config(config = {}, options = {})
|
20
|
+
provider_type = config["provider"] || "cursor"
|
21
|
+
get_provider(provider_type, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get harness factory instance
|
25
|
+
def get_harness_factory
|
26
|
+
@harness_factory ||= begin
|
27
|
+
require_relative "harness/config_manager"
|
28
|
+
Aidp::Harness::ProviderFactory.new
|
29
|
+
rescue LoadError
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create provider using harness configuration
|
35
|
+
def create_harness_provider(provider_name, options = {})
|
36
|
+
factory = get_harness_factory
|
37
|
+
raise "Harness factory not available" unless factory
|
38
|
+
|
39
|
+
factory.create_provider(provider_name, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get all configured providers
|
43
|
+
def get_all_providers(options = {})
|
44
|
+
factory = get_harness_factory
|
45
|
+
return [] unless factory
|
46
|
+
|
47
|
+
factory.create_all_providers(options)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get providers by priority
|
51
|
+
def get_providers_by_priority(options = {})
|
52
|
+
factory = get_harness_factory
|
53
|
+
return [] unless factory
|
54
|
+
|
55
|
+
factory.create_providers_by_priority(options)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get enabled providers
|
59
|
+
def get_enabled_providers(options = {})
|
60
|
+
factory = get_harness_factory
|
61
|
+
return [] unless factory
|
62
|
+
|
63
|
+
enabled_names = factory.get_enabled_providers(options)
|
64
|
+
factory.create_providers(enabled_names, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if provider is configured
|
68
|
+
def provider_configured?(provider_name, options = {})
|
69
|
+
factory = get_harness_factory
|
70
|
+
return false unless factory
|
71
|
+
|
72
|
+
factory.get_configured_providers(options).include?(provider_name.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Check if provider is enabled
|
76
|
+
def provider_enabled?(provider_name, options = {})
|
77
|
+
factory = get_harness_factory
|
78
|
+
return false unless factory
|
79
|
+
|
80
|
+
factory.get_enabled_providers(options).include?(provider_name.to_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get provider capabilities
|
84
|
+
def get_provider_capabilities(provider_name, options = {})
|
85
|
+
factory = get_harness_factory
|
86
|
+
return [] unless factory
|
87
|
+
|
88
|
+
factory.get_provider_capabilities(provider_name, options)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Check if provider supports feature
|
92
|
+
def provider_supports_feature?(provider_name, feature, options = {})
|
93
|
+
factory = get_harness_factory
|
94
|
+
return false unless factory
|
95
|
+
|
96
|
+
factory.provider_supports_feature?(provider_name, feature, options)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Get provider models
|
100
|
+
def get_provider_models(provider_name, options = {})
|
101
|
+
factory = get_harness_factory
|
102
|
+
return [] unless factory
|
103
|
+
|
104
|
+
factory.get_provider_models(provider_name, options)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Validate provider configuration
|
108
|
+
def validate_provider_config(provider_name, options = {})
|
109
|
+
factory = get_harness_factory
|
110
|
+
return ["Harness factory not available"] unless factory
|
111
|
+
|
112
|
+
factory.validate_provider_config(provider_name, options)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Validate all provider configurations
|
116
|
+
def validate_all_provider_configs(options = {})
|
117
|
+
factory = get_harness_factory
|
118
|
+
return {} unless factory
|
119
|
+
|
120
|
+
factory.validate_all_provider_configs(options)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Clear provider cache
|
124
|
+
def clear_cache
|
125
|
+
@harness_factory&.clear_cache
|
126
|
+
end
|
127
|
+
|
128
|
+
# Reload configuration
|
129
|
+
def reload_config
|
130
|
+
@harness_factory&.reload_config
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def create_legacy_provider(provider_type)
|
7
136
|
case provider_type
|
8
137
|
when "cursor"
|
9
138
|
Aidp::Providers::Cursor.new
|
@@ -12,14 +141,9 @@ module Aidp
|
|
12
141
|
when "gemini"
|
13
142
|
Aidp::Providers::Gemini.new
|
14
143
|
when "macos_ui"
|
15
|
-
Aidp::Providers::
|
144
|
+
Aidp::Providers::MacOSUI.new
|
16
145
|
end
|
17
146
|
end
|
18
|
-
|
19
|
-
def load_from_config(config = {})
|
20
|
-
provider_type = config["provider"] || "cursor"
|
21
|
-
get_provider(provider_type)
|
22
|
-
end
|
23
147
|
end
|
24
148
|
end
|
25
149
|
end
|
@@ -1,113 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "base"
|
4
|
+
require_relative "../debug_mixin"
|
4
5
|
|
5
6
|
module Aidp
|
6
7
|
module Providers
|
7
8
|
class Anthropic < Base
|
9
|
+
include Aidp::DebugMixin
|
10
|
+
|
8
11
|
def self.available?
|
9
12
|
!!Aidp::Util.which("claude")
|
10
13
|
end
|
11
14
|
|
12
|
-
def name
|
15
|
+
def name
|
16
|
+
"anthropic"
|
17
|
+
end
|
18
|
+
|
19
|
+
def available?
|
20
|
+
self.class.available?
|
21
|
+
end
|
13
22
|
|
14
23
|
def send(prompt:, session: nil)
|
15
24
|
raise "claude CLI not available" unless self.class.available?
|
16
25
|
|
17
|
-
require "open3"
|
18
|
-
|
19
|
-
# Use Claude CLI for non-interactive mode
|
20
|
-
cmd = ["claude", "--print"]
|
21
|
-
|
22
|
-
puts "📝 Sending prompt to claude..."
|
23
|
-
|
24
26
|
# Smart timeout calculation
|
25
27
|
timeout_seconds = calculate_timeout
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
stuck_detection_thread = Thread.new do
|
34
|
-
loop do
|
35
|
-
sleep 10 # Check every 10 seconds
|
36
|
-
|
37
|
-
if stuck?
|
38
|
-
puts "⚠️ claude appears stuck (no activity for #{stuck_timeout} seconds)"
|
39
|
-
puts " You can:"
|
40
|
-
puts " 1. Wait longer (press Enter)"
|
41
|
-
puts " 2. Abort (Ctrl+C)"
|
42
|
-
|
43
|
-
# Give user a chance to respond
|
44
|
-
begin
|
45
|
-
Timeout.timeout(30) do
|
46
|
-
gets
|
47
|
-
puts "🔄 Continuing to wait..."
|
48
|
-
end
|
49
|
-
rescue Timeout::Error
|
50
|
-
puts "⏰ No response received, continuing to wait..."
|
51
|
-
rescue Interrupt
|
52
|
-
puts "🛑 Aborting claude..."
|
53
|
-
Process.kill("TERM", wait.pid)
|
54
|
-
raise Interrupt, "User aborted claude execution"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Stop checking if the process is done
|
59
|
-
break if wait.value
|
60
|
-
rescue
|
61
|
-
break
|
62
|
-
end
|
63
|
-
end
|
29
|
+
debug_provider("claude", "Starting execution", {timeout: timeout_seconds})
|
30
|
+
debug_log("📝 Sending prompt to claude...", level: :info)
|
31
|
+
|
32
|
+
begin
|
33
|
+
# Use debug_execute_command for better debugging
|
34
|
+
result = debug_execute_command("claude", args: ["--print"], input: prompt, timeout: timeout_seconds)
|
64
35
|
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
if result.success?
|
74
|
-
output = stdout.read
|
75
|
-
puts "✅ Claude analysis completed"
|
76
|
-
mark_completed
|
77
|
-
return output.empty? ? :ok : output
|
78
|
-
else
|
79
|
-
error_output = stderr.read
|
80
|
-
mark_failed("claude failed with exit code #{result.exitstatus}: #{error_output}")
|
81
|
-
raise "claude failed with exit code #{result.exitstatus}: #{error_output}"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
rescue Timeout::Error
|
85
|
-
# Stop stuck detection thread
|
86
|
-
stuck_detection_thread&.kill
|
87
|
-
|
88
|
-
# Kill the process if it's taking too long
|
89
|
-
begin
|
90
|
-
Process.kill("TERM", wait.pid)
|
91
|
-
rescue
|
92
|
-
nil
|
93
|
-
end
|
94
|
-
|
95
|
-
mark_failed("claude timed out after #{timeout_seconds} seconds")
|
96
|
-
raise Timeout::Error, "claude timed out after #{timeout_seconds} seconds"
|
97
|
-
rescue Interrupt
|
98
|
-
# Stop stuck detection thread
|
99
|
-
stuck_detection_thread&.kill
|
100
|
-
|
101
|
-
# Kill the process
|
102
|
-
begin
|
103
|
-
Process.kill("TERM", wait.pid)
|
104
|
-
rescue
|
105
|
-
nil
|
106
|
-
end
|
107
|
-
|
108
|
-
mark_failed("claude execution was interrupted")
|
109
|
-
raise
|
36
|
+
# Log the results
|
37
|
+
debug_command("claude", args: ["--print"], input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
|
38
|
+
|
39
|
+
if result.exit_status == 0
|
40
|
+
result.out
|
41
|
+
else
|
42
|
+
debug_error(StandardError.new("claude failed"), {exit_code: result.exit_status, stderr: result.err})
|
43
|
+
raise "claude failed with exit code #{result.exit_status}: #{result.err}"
|
110
44
|
end
|
45
|
+
rescue => e
|
46
|
+
debug_error(e, {provider: "claude", prompt_length: prompt.length})
|
47
|
+
raise
|
111
48
|
end
|
112
49
|
end
|
113
50
|
|
@@ -142,25 +79,7 @@ module Aidp
|
|
142
79
|
end
|
143
80
|
|
144
81
|
def get_adaptive_timeout
|
145
|
-
#
|
146
|
-
begin
|
147
|
-
require_relative "../analyze/metrics_storage"
|
148
|
-
storage = Aidp::Analyze::MetricsStorage.new(Dir.pwd)
|
149
|
-
recommendations = storage.calculate_timeout_recommendations
|
150
|
-
|
151
|
-
# Get current step name from environment or context
|
152
|
-
step_name = ENV["AIDP_CURRENT_STEP"] || "unknown"
|
153
|
-
|
154
|
-
if recommendations[step_name]
|
155
|
-
recommended = recommendations[step_name][:recommended_timeout]
|
156
|
-
# Add 20% buffer for safety
|
157
|
-
return (recommended * 1.2).ceil
|
158
|
-
end
|
159
|
-
rescue => e
|
160
|
-
puts "⚠️ Could not get adaptive timeout: #{e.message}" if ENV["AIDP_DEBUG"]
|
161
|
-
end
|
162
|
-
|
163
|
-
# Fallback timeouts based on step type patterns
|
82
|
+
# Timeout recommendations based on step type patterns
|
164
83
|
step_name = ENV["AIDP_CURRENT_STEP"] || ""
|
165
84
|
|
166
85
|
case step_name
|
data/lib/aidp/providers/base.rb
CHANGED
@@ -27,6 +27,17 @@ module Aidp
|
|
27
27
|
@output_count = 0
|
28
28
|
@last_output_time = Time.now
|
29
29
|
@job_context = nil
|
30
|
+
@harness_context = nil
|
31
|
+
@harness_metrics = {
|
32
|
+
total_requests: 0,
|
33
|
+
successful_requests: 0,
|
34
|
+
failed_requests: 0,
|
35
|
+
rate_limited_requests: 0,
|
36
|
+
total_tokens_used: 0,
|
37
|
+
total_cost: 0.0,
|
38
|
+
average_response_time: 0.0,
|
39
|
+
last_request_time: nil
|
40
|
+
}
|
30
41
|
end
|
31
42
|
|
32
43
|
def name
|
@@ -135,6 +146,141 @@ module Aidp
|
|
135
146
|
# Get stuck timeout for this provider
|
136
147
|
attr_reader :stuck_timeout
|
137
148
|
|
149
|
+
# Harness integration methods
|
150
|
+
|
151
|
+
# Set harness context for provider
|
152
|
+
def set_harness_context(harness_runner)
|
153
|
+
@harness_context = harness_runner
|
154
|
+
end
|
155
|
+
|
156
|
+
# Check if provider is operating in harness mode
|
157
|
+
def harness_mode?
|
158
|
+
!@harness_context.nil?
|
159
|
+
end
|
160
|
+
|
161
|
+
# Get harness metrics
|
162
|
+
def harness_metrics
|
163
|
+
@harness_metrics.dup
|
164
|
+
end
|
165
|
+
|
166
|
+
# Record harness request metrics
|
167
|
+
def record_harness_request(success:, tokens_used: 0, cost: 0.0, response_time: 0.0, rate_limited: false)
|
168
|
+
@harness_metrics[:total_requests] += 1
|
169
|
+
@harness_metrics[:last_request_time] = Time.now
|
170
|
+
|
171
|
+
if success
|
172
|
+
@harness_metrics[:successful_requests] += 1
|
173
|
+
else
|
174
|
+
@harness_metrics[:failed_requests] += 1
|
175
|
+
end
|
176
|
+
|
177
|
+
if rate_limited
|
178
|
+
@harness_metrics[:rate_limited_requests] += 1
|
179
|
+
end
|
180
|
+
|
181
|
+
@harness_metrics[:total_tokens_used] += tokens_used
|
182
|
+
@harness_metrics[:total_cost] += cost
|
183
|
+
|
184
|
+
# Update average response time
|
185
|
+
total_time = @harness_metrics[:average_response_time] * (@harness_metrics[:total_requests] - 1) + response_time
|
186
|
+
@harness_metrics[:average_response_time] = total_time / @harness_metrics[:total_requests]
|
187
|
+
|
188
|
+
# Notify harness context if available
|
189
|
+
@harness_context&.record_provider_metrics(name, @harness_metrics)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Get provider health status for harness
|
193
|
+
def harness_health_status
|
194
|
+
{
|
195
|
+
provider: name,
|
196
|
+
activity_state: @activity_state,
|
197
|
+
stuck: stuck?,
|
198
|
+
success_rate: calculate_success_rate,
|
199
|
+
average_response_time: @harness_metrics[:average_response_time],
|
200
|
+
total_requests: @harness_metrics[:total_requests],
|
201
|
+
rate_limit_ratio: calculate_rate_limit_ratio,
|
202
|
+
last_activity: @last_activity_time,
|
203
|
+
health_score: calculate_health_score
|
204
|
+
}
|
205
|
+
end
|
206
|
+
|
207
|
+
# Check if provider is healthy for harness use
|
208
|
+
def harness_healthy?
|
209
|
+
return false if stuck?
|
210
|
+
return false if @harness_metrics[:total_requests] > 0 && calculate_success_rate < 0.5
|
211
|
+
return false if calculate_rate_limit_ratio > 0.3
|
212
|
+
|
213
|
+
true
|
214
|
+
end
|
215
|
+
|
216
|
+
# Get provider configuration for harness
|
217
|
+
def harness_config
|
218
|
+
{
|
219
|
+
name: name,
|
220
|
+
supports_activity_monitoring: supports_activity_monitoring?,
|
221
|
+
default_timeout: @stuck_timeout,
|
222
|
+
available: available?,
|
223
|
+
health_status: harness_health_status
|
224
|
+
}
|
225
|
+
end
|
226
|
+
|
227
|
+
# Check if provider is available (override in subclasses)
|
228
|
+
def available?
|
229
|
+
true # Default to true, override in subclasses
|
230
|
+
end
|
231
|
+
|
232
|
+
# Enhanced send method that integrates with harness
|
233
|
+
def send_with_harness(prompt:, session: nil, _options: {})
|
234
|
+
start_time = Time.now
|
235
|
+
success = false
|
236
|
+
rate_limited = false
|
237
|
+
tokens_used = 0
|
238
|
+
cost = 0.0
|
239
|
+
error_message = nil
|
240
|
+
|
241
|
+
begin
|
242
|
+
# Call the original send method
|
243
|
+
result = send(prompt: prompt, session: session)
|
244
|
+
success = true
|
245
|
+
|
246
|
+
# Extract token usage and cost if available
|
247
|
+
if result.is_a?(Hash) && result[:token_usage]
|
248
|
+
tokens_used = result[:token_usage][:total] || 0
|
249
|
+
cost = result[:token_usage][:cost] || 0.0
|
250
|
+
end
|
251
|
+
|
252
|
+
# Check for rate limiting in result
|
253
|
+
if result.is_a?(Hash) && result[:rate_limited]
|
254
|
+
rate_limited = true
|
255
|
+
end
|
256
|
+
|
257
|
+
result
|
258
|
+
rescue => e
|
259
|
+
error_message = e.message
|
260
|
+
|
261
|
+
# Check if error is rate limiting
|
262
|
+
if e.message.match?(/rate.?limit/i) || e.message.match?(/quota/i)
|
263
|
+
rate_limited = true
|
264
|
+
end
|
265
|
+
|
266
|
+
raise e
|
267
|
+
ensure
|
268
|
+
response_time = Time.now - start_time
|
269
|
+
record_harness_request(
|
270
|
+
success: success,
|
271
|
+
tokens_used: tokens_used,
|
272
|
+
cost: cost,
|
273
|
+
response_time: response_time,
|
274
|
+
rate_limited: rate_limited
|
275
|
+
)
|
276
|
+
|
277
|
+
# Log to harness context if available
|
278
|
+
if @harness_context && error_message
|
279
|
+
@harness_context.record_provider_error(name, error_message, rate_limited)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
138
284
|
protected
|
139
285
|
|
140
286
|
# Log message to job if in background mode
|
@@ -157,6 +303,30 @@ module Aidp
|
|
157
303
|
metadata
|
158
304
|
)
|
159
305
|
end
|
306
|
+
|
307
|
+
# Calculate success rate for harness metrics
|
308
|
+
def calculate_success_rate
|
309
|
+
return 1.0 if @harness_metrics[:total_requests] == 0
|
310
|
+
@harness_metrics[:successful_requests].to_f / @harness_metrics[:total_requests]
|
311
|
+
end
|
312
|
+
|
313
|
+
# Calculate rate limit ratio for harness metrics
|
314
|
+
def calculate_rate_limit_ratio
|
315
|
+
return 0.0 if @harness_metrics[:total_requests] == 0
|
316
|
+
@harness_metrics[:rate_limited_requests].to_f / @harness_metrics[:total_requests]
|
317
|
+
end
|
318
|
+
|
319
|
+
# Calculate overall health score for harness
|
320
|
+
def calculate_health_score
|
321
|
+
return 100.0 if @harness_metrics[:total_requests] == 0
|
322
|
+
|
323
|
+
success_rate = calculate_success_rate
|
324
|
+
rate_limit_ratio = calculate_rate_limit_ratio
|
325
|
+
response_time_score = [100 - (@harness_metrics[:average_response_time] * 10), 0].max
|
326
|
+
|
327
|
+
# Weighted health score
|
328
|
+
(success_rate * 50) + ((1 - rate_limit_ratio) * 30) + (response_time_score * 0.2)
|
329
|
+
end
|
160
330
|
end
|
161
331
|
end
|
162
332
|
end
|