aidp 0.34.1 → 0.39.3
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 +14 -14
- data/lib/aidp/agent_harness_adapter.rb +309 -0
- data/lib/aidp/cli/issue_importer.rb +20 -6
- data/lib/aidp/cli/jobs_command.rb +1 -1
- data/lib/aidp/cli/models_command.rb +9 -16
- data/lib/aidp/cli/prompts_command.rb +253 -0
- data/lib/aidp/cli/security_command.rb +4 -10
- data/lib/aidp/cli/storage_command.rb +277 -0
- data/lib/aidp/cli/temporal_command.rb +460 -0
- data/lib/aidp/cli.rb +24 -2
- data/lib/aidp/config.rb +15 -0
- data/lib/aidp/config_paths.rb +12 -2
- data/lib/aidp/database/migrations.rb +114 -0
- data/lib/aidp/database/repositories/checkpoint_repository.rb +174 -0
- data/lib/aidp/database/repositories/deprecated_models_repository.rb +206 -0
- data/lib/aidp/database/repositories/evaluation_repository.rb +208 -0
- data/lib/aidp/database/repositories/harness_state_repository.rb +140 -0
- data/lib/aidp/database/repositories/job_repository.rb +278 -0
- data/lib/aidp/database/repositories/model_cache_repository.rb +194 -0
- data/lib/aidp/database/repositories/progress_repository.rb +210 -0
- data/lib/aidp/database/repositories/prompt_archive_repository.rb +195 -0
- data/lib/aidp/database/repositories/prompt_feedback_repository.rb +266 -0
- data/lib/aidp/database/repositories/provider_info_cache_repository.rb +208 -0
- data/lib/aidp/database/repositories/provider_metrics_repository.rb +245 -0
- data/lib/aidp/database/repositories/secrets_repository.rb +187 -0
- data/lib/aidp/database/repositories/task_repository.rb +231 -0
- data/lib/aidp/database/repositories/template_version_repository.rb +383 -0
- data/lib/aidp/database/repositories/watch_state_repository.rb +472 -0
- data/lib/aidp/database/repositories/workstream_repository.rb +310 -0
- data/lib/aidp/database/repositories/worktree_repository.rb +240 -0
- data/lib/aidp/database/repository.rb +166 -0
- data/lib/aidp/database/schema.rb +347 -0
- data/lib/aidp/database/storage_migrator.rb +693 -0
- data/lib/aidp/database.rb +139 -0
- data/lib/aidp/debug_mixin.rb +2 -2
- data/lib/aidp/execute/checkpoint.rb +26 -0
- data/lib/aidp/execute/checkpoint_display.rb +16 -0
- data/lib/aidp/execute/prompt_evaluator.rb +30 -57
- data/lib/aidp/execute/work_loop_runner.rb +294 -46
- data/lib/aidp/firewall/provider_requirements_collector.rb +7 -23
- data/lib/aidp/harness/agent_harness_provider_manager.rb +275 -0
- data/lib/aidp/harness/ai_decision_engine.rb +78 -296
- data/lib/aidp/harness/config_schema.rb +59 -0
- data/lib/aidp/harness/configuration.rb +229 -10
- data/lib/aidp/harness/deprecation_cache.rb +53 -11
- data/lib/aidp/harness/enhanced_runner.rb +1 -1
- data/lib/aidp/harness/error_handler.rb +11 -5
- data/lib/aidp/harness/provider_config.rb +36 -0
- data/lib/aidp/harness/provider_factory.rb +26 -24
- data/lib/aidp/harness/provider_info.rb +9 -5
- data/lib/aidp/harness/provider_manager.rb +2 -2
- data/lib/aidp/harness/provider_metrics.rb +103 -1
- data/lib/aidp/harness/rspec_command_optimizer.rb +166 -0
- data/lib/aidp/harness/ruby_llm_registry.rb +97 -8
- data/lib/aidp/harness/runner.rb +7 -2
- data/lib/aidp/harness/state_manager.rb +1 -1
- data/lib/aidp/harness/test_runner.rb +167 -11
- data/lib/aidp/harness/thinking_depth_manager.rb +564 -5
- data/lib/aidp/harness/usage_limit.rb +179 -0
- data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
- data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
- data/lib/aidp/harness/usage_period.rb +234 -0
- data/lib/aidp/init/agent_instructions_generator.rb +317 -0
- data/lib/aidp/init/runner.rb +25 -4
- data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
- data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
- data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
- data/lib/aidp/interfaces/logger_interface.rb +150 -0
- data/lib/aidp/interfaces/ui_interface.rb +254 -0
- data/lib/aidp/logger.rb +25 -7
- data/lib/aidp/orchestration_adapter.rb +279 -0
- data/lib/aidp/pr_worktree_manager.rb +4 -15
- data/lib/aidp/prompts/feedback_collector.rb +198 -0
- data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
- data/lib/aidp/prompts/template_evolver.rb +305 -0
- data/lib/aidp/prompts/template_version_manager.rb +325 -0
- data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
- data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
- data/lib/aidp/setup/wizard.rb +616 -176
- data/lib/aidp/shell_executor.rb +58 -0
- data/lib/aidp/style_guide/selector.rb +56 -1
- data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
- data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
- data/lib/aidp/temporal/activities/base_activity.rb +78 -0
- data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
- data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
- data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
- data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
- data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
- data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
- data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
- data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
- data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
- data/lib/aidp/temporal/configuration.rb +169 -0
- data/lib/aidp/temporal/connection.rb +93 -0
- data/lib/aidp/temporal/worker.rb +119 -0
- data/lib/aidp/temporal/workflow_client.rb +171 -0
- data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
- data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
- data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
- data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
- data/lib/aidp/temporal.rb +120 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/base_processor.rb +35 -0
- data/lib/aidp/watch/build_processor.rb +132 -0
- data/lib/aidp/watch/github_state_extractor.rb +0 -8
- data/lib/aidp/watch/rebase_processor.rb +245 -0
- data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
- data/lib/aidp/watch/runner.rb +366 -285
- data/lib/aidp/watch/state_store.rb +80 -0
- data/lib/aidp/watch/work_item.rb +96 -0
- data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
- data/lib/aidp/watch/worktree_reconciler.rb +643 -0
- data/lib/aidp/workflows/guided_agent.rb +1 -1
- data/lib/aidp/worktree.rb +62 -0
- data/templates/aidp.yml.example +70 -0
- metadata +117 -16
- data/lib/aidp/analyze/json_file_storage.rb +0 -292
- data/lib/aidp/execute/future_work_backlog.rb +0 -411
- data/lib/aidp/providers/adapter.rb +0 -239
- data/lib/aidp/providers/aider.rb +0 -264
- data/lib/aidp/providers/anthropic.rb +0 -614
- data/lib/aidp/providers/base.rb +0 -617
- data/lib/aidp/providers/capability_registry.rb +0 -204
- data/lib/aidp/providers/codex.rb +0 -267
- data/lib/aidp/providers/cursor.rb +0 -304
- data/lib/aidp/providers/error_taxonomy.rb +0 -195
- data/lib/aidp/providers/gemini.rb +0 -110
- data/lib/aidp/providers/github_copilot.rb +0 -227
- data/lib/aidp/providers/kilocode.rb +0 -157
- data/lib/aidp/providers/opencode.rb +0 -139
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../repository"
|
|
4
|
+
|
|
5
|
+
module Aidp
|
|
6
|
+
module Database
|
|
7
|
+
module Repositories
|
|
8
|
+
# Repository for checkpoint and checkpoint_history tables
|
|
9
|
+
# Replaces checkpoint.yml and checkpoint_history.jsonl
|
|
10
|
+
class CheckpointRepository < Repository
|
|
11
|
+
def initialize(project_dir: Dir.pwd)
|
|
12
|
+
super(project_dir: project_dir, table_name: "checkpoints")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Save or update current checkpoint
|
|
16
|
+
#
|
|
17
|
+
# @param step_name [String]
|
|
18
|
+
# @param iteration [Integer]
|
|
19
|
+
# @param status [String]
|
|
20
|
+
# @param run_loop_started_at [String, nil]
|
|
21
|
+
# @param metrics [Hash]
|
|
22
|
+
# @return [Integer] Checkpoint ID
|
|
23
|
+
def save_checkpoint(step_name:, iteration: nil, status: nil, run_loop_started_at: nil, metrics: {})
|
|
24
|
+
data = {
|
|
25
|
+
step_name: step_name,
|
|
26
|
+
iteration: iteration,
|
|
27
|
+
status: status,
|
|
28
|
+
run_loop_started_at: run_loop_started_at,
|
|
29
|
+
metrics: metrics
|
|
30
|
+
}
|
|
31
|
+
existing = current_checkpoint
|
|
32
|
+
now = current_timestamp
|
|
33
|
+
|
|
34
|
+
if existing
|
|
35
|
+
existing_id = existing[:id]
|
|
36
|
+
execute(
|
|
37
|
+
<<~SQL,
|
|
38
|
+
UPDATE checkpoints SET
|
|
39
|
+
step_name = ?,
|
|
40
|
+
step_index = ?,
|
|
41
|
+
status = ?,
|
|
42
|
+
run_loop_started_at = ?,
|
|
43
|
+
metadata = ?,
|
|
44
|
+
updated_at = ?
|
|
45
|
+
WHERE id = ?
|
|
46
|
+
SQL
|
|
47
|
+
[
|
|
48
|
+
data[:step_name],
|
|
49
|
+
data[:iteration],
|
|
50
|
+
data[:status],
|
|
51
|
+
data[:run_loop_started_at],
|
|
52
|
+
serialize_json(data[:metrics]),
|
|
53
|
+
now,
|
|
54
|
+
existing_id
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
Aidp.log_debug("checkpoint_repository", "updated", id: existing_id)
|
|
58
|
+
existing_id
|
|
59
|
+
else
|
|
60
|
+
execute(
|
|
61
|
+
insert_sql([
|
|
62
|
+
:project_dir, :step_name, :step_index, :status,
|
|
63
|
+
:run_loop_started_at, :metadata, :created_at, :updated_at
|
|
64
|
+
]),
|
|
65
|
+
[
|
|
66
|
+
project_dir,
|
|
67
|
+
data[:step_name],
|
|
68
|
+
data[:iteration],
|
|
69
|
+
data[:status],
|
|
70
|
+
data[:run_loop_started_at],
|
|
71
|
+
serialize_json(data[:metrics]),
|
|
72
|
+
now,
|
|
73
|
+
now
|
|
74
|
+
]
|
|
75
|
+
)
|
|
76
|
+
id = last_insert_row_id
|
|
77
|
+
Aidp.log_debug("checkpoint_repository", "created", id: id)
|
|
78
|
+
id
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Get current checkpoint for project
|
|
83
|
+
#
|
|
84
|
+
# @return [Hash, nil] Current checkpoint or nil
|
|
85
|
+
def current_checkpoint
|
|
86
|
+
row = query_one(
|
|
87
|
+
"SELECT * FROM checkpoints WHERE project_dir = ? ORDER BY updated_at DESC LIMIT 1",
|
|
88
|
+
[project_dir]
|
|
89
|
+
)
|
|
90
|
+
return nil unless row
|
|
91
|
+
|
|
92
|
+
deserialize_checkpoint(row)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Append checkpoint to history
|
|
96
|
+
#
|
|
97
|
+
# @param step_name [String]
|
|
98
|
+
# @param iteration [Integer]
|
|
99
|
+
# @param status [String]
|
|
100
|
+
# @param timestamp [String, nil]
|
|
101
|
+
# @param metrics [Hash]
|
|
102
|
+
def append_history(step_name:, iteration: nil, status: nil, timestamp: nil, metrics: {})
|
|
103
|
+
execute(
|
|
104
|
+
<<~SQL,
|
|
105
|
+
INSERT INTO checkpoint_history (project_dir, step_name, step_index, status, timestamp, metadata)
|
|
106
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
107
|
+
SQL
|
|
108
|
+
[
|
|
109
|
+
project_dir,
|
|
110
|
+
step_name,
|
|
111
|
+
iteration,
|
|
112
|
+
status,
|
|
113
|
+
timestamp || current_timestamp,
|
|
114
|
+
serialize_json(metrics)
|
|
115
|
+
]
|
|
116
|
+
)
|
|
117
|
+
Aidp.log_debug("checkpoint_repository", "history_appended", step: step_name)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Get checkpoint history
|
|
121
|
+
#
|
|
122
|
+
# @param limit [Integer] Maximum entries to return
|
|
123
|
+
# @return [Array<Hash>] Checkpoint history entries
|
|
124
|
+
def history(limit: 100)
|
|
125
|
+
rows = query(
|
|
126
|
+
<<~SQL,
|
|
127
|
+
SELECT * FROM checkpoint_history
|
|
128
|
+
WHERE project_dir = ?
|
|
129
|
+
ORDER BY timestamp DESC, id DESC
|
|
130
|
+
LIMIT ?
|
|
131
|
+
SQL
|
|
132
|
+
[project_dir, limit]
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
rows.map { |row| deserialize_history_entry(row) }.reverse
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Clear all checkpoint data for project
|
|
139
|
+
def clear
|
|
140
|
+
transaction do
|
|
141
|
+
execute("DELETE FROM checkpoints WHERE project_dir = ?", [project_dir])
|
|
142
|
+
execute("DELETE FROM checkpoint_history WHERE project_dir = ?", [project_dir])
|
|
143
|
+
end
|
|
144
|
+
Aidp.log_debug("checkpoint_repository", "cleared", project_dir: project_dir)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
def deserialize_checkpoint(row)
|
|
150
|
+
{
|
|
151
|
+
id: row["id"],
|
|
152
|
+
step_name: row["step_name"],
|
|
153
|
+
iteration: row["step_index"],
|
|
154
|
+
status: row["status"],
|
|
155
|
+
run_loop_started_at: row["run_loop_started_at"],
|
|
156
|
+
metrics: deserialize_json(row["metadata"]) || {},
|
|
157
|
+
created_at: row["created_at"],
|
|
158
|
+
updated_at: row["updated_at"]
|
|
159
|
+
}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def deserialize_history_entry(row)
|
|
163
|
+
{
|
|
164
|
+
step_name: row["step_name"],
|
|
165
|
+
iteration: row["step_index"],
|
|
166
|
+
status: row["status"],
|
|
167
|
+
timestamp: row["timestamp"],
|
|
168
|
+
metrics: deserialize_json(row["metadata"]) || {}
|
|
169
|
+
}
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../repository"
|
|
4
|
+
|
|
5
|
+
module Aidp
|
|
6
|
+
module Database
|
|
7
|
+
module Repositories
|
|
8
|
+
# Repository for deprecated models cache
|
|
9
|
+
# Replaces: .aidp/deprecated_models.json
|
|
10
|
+
#
|
|
11
|
+
# Tracks models that have been deprecated by providers at runtime
|
|
12
|
+
class DeprecatedModelsRepository < Repository
|
|
13
|
+
def initialize(project_dir: Dir.pwd)
|
|
14
|
+
super(project_dir: project_dir, table_name: "deprecated_models")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Add a deprecated model to the cache
|
|
18
|
+
#
|
|
19
|
+
# @param provider_name [String] Provider name (e.g., "anthropic")
|
|
20
|
+
# @param model_name [String] Deprecated model ID
|
|
21
|
+
# @param replacement [String, nil] Replacement model ID
|
|
22
|
+
# @param reason [String, nil] Deprecation reason
|
|
23
|
+
# @return [Boolean] Success status
|
|
24
|
+
def add(provider_name:, model_name:, replacement: nil, reason: nil)
|
|
25
|
+
Aidp.log_debug("deprecated_models_repo", "add",
|
|
26
|
+
provider: provider_name, model: model_name, replacement: replacement)
|
|
27
|
+
|
|
28
|
+
execute(<<~SQL, [project_dir, provider_name, model_name, replacement, reason, current_timestamp])
|
|
29
|
+
INSERT INTO #{table_name}
|
|
30
|
+
(project_dir, provider_name, model_name, replacement, reason, detected_at)
|
|
31
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
32
|
+
ON CONFLICT(project_dir, provider_name, model_name) DO UPDATE SET
|
|
33
|
+
replacement = excluded.replacement,
|
|
34
|
+
reason = excluded.reason,
|
|
35
|
+
detected_at = excluded.detected_at
|
|
36
|
+
SQL
|
|
37
|
+
|
|
38
|
+
Aidp.log_info("deprecated_models_repo", "added_deprecated_model",
|
|
39
|
+
provider: provider_name, model: model_name, replacement: replacement)
|
|
40
|
+
true
|
|
41
|
+
rescue => e
|
|
42
|
+
Aidp.log_error("deprecated_models_repo", "add_failed",
|
|
43
|
+
provider: provider_name, model: model_name, error: e.message)
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Check if a model is deprecated
|
|
48
|
+
#
|
|
49
|
+
# @param provider_name [String] Provider name
|
|
50
|
+
# @param model_name [String] Model ID to check
|
|
51
|
+
# @return [Boolean]
|
|
52
|
+
def deprecated?(provider_name:, model_name:)
|
|
53
|
+
result = query_value(<<~SQL, [project_dir, provider_name, model_name])
|
|
54
|
+
SELECT 1 FROM #{table_name}
|
|
55
|
+
WHERE project_dir = ? AND provider_name = ? AND model_name = ?
|
|
56
|
+
SQL
|
|
57
|
+
|
|
58
|
+
!result.nil?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get replacement model for a deprecated model
|
|
62
|
+
#
|
|
63
|
+
# @param provider_name [String] Provider name
|
|
64
|
+
# @param model_name [String] Deprecated model ID
|
|
65
|
+
# @return [String, nil] Replacement model ID or nil
|
|
66
|
+
def replacement_for(provider_name:, model_name:)
|
|
67
|
+
query_value(<<~SQL, [project_dir, provider_name, model_name])
|
|
68
|
+
SELECT replacement FROM #{table_name}
|
|
69
|
+
WHERE project_dir = ? AND provider_name = ? AND model_name = ?
|
|
70
|
+
SQL
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Get all deprecated models for a provider
|
|
74
|
+
#
|
|
75
|
+
# @param provider_name [String] Provider name
|
|
76
|
+
# @return [Array<String>] List of deprecated model IDs
|
|
77
|
+
def deprecated_models(provider_name:)
|
|
78
|
+
rows = query(<<~SQL, [project_dir, provider_name])
|
|
79
|
+
SELECT model_name FROM #{table_name}
|
|
80
|
+
WHERE project_dir = ? AND provider_name = ?
|
|
81
|
+
ORDER BY model_name
|
|
82
|
+
SQL
|
|
83
|
+
|
|
84
|
+
rows.map { |row| row["model_name"] }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Get full deprecation info for a model
|
|
88
|
+
#
|
|
89
|
+
# @param provider_name [String] Provider name
|
|
90
|
+
# @param model_name [String] Model ID
|
|
91
|
+
# @return [Hash, nil] Deprecation metadata or nil
|
|
92
|
+
def info(provider_name:, model_name:)
|
|
93
|
+
row = query_one(<<~SQL, [project_dir, provider_name, model_name])
|
|
94
|
+
SELECT replacement, reason, detected_at
|
|
95
|
+
FROM #{table_name}
|
|
96
|
+
WHERE project_dir = ? AND provider_name = ? AND model_name = ?
|
|
97
|
+
SQL
|
|
98
|
+
|
|
99
|
+
return nil unless row
|
|
100
|
+
|
|
101
|
+
{
|
|
102
|
+
replacement: row["replacement"],
|
|
103
|
+
reason: row["reason"],
|
|
104
|
+
detected_at: row["detected_at"]
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Remove a model from the deprecated cache
|
|
109
|
+
#
|
|
110
|
+
# @param provider_name [String] Provider name
|
|
111
|
+
# @param model_name [String] Model ID to remove
|
|
112
|
+
def remove(provider_name:, model_name:)
|
|
113
|
+
Aidp.log_debug("deprecated_models_repo", "remove",
|
|
114
|
+
provider: provider_name, model: model_name)
|
|
115
|
+
|
|
116
|
+
execute(<<~SQL, [project_dir, provider_name, model_name])
|
|
117
|
+
DELETE FROM #{table_name}
|
|
118
|
+
WHERE project_dir = ? AND provider_name = ? AND model_name = ?
|
|
119
|
+
SQL
|
|
120
|
+
|
|
121
|
+
Aidp.log_info("deprecated_models_repo", "removed_deprecated_model",
|
|
122
|
+
provider: provider_name, model: model_name)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Clear all deprecations for project
|
|
126
|
+
def clear!
|
|
127
|
+
Aidp.log_debug("deprecated_models_repo", "clear")
|
|
128
|
+
delete_by_project
|
|
129
|
+
Aidp.log_info("deprecated_models_repo", "cleared_all")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Get cache statistics
|
|
133
|
+
#
|
|
134
|
+
# @return [Hash] Statistics
|
|
135
|
+
def stats
|
|
136
|
+
providers_rows = query(<<~SQL, [project_dir])
|
|
137
|
+
SELECT DISTINCT provider_name FROM #{table_name}
|
|
138
|
+
WHERE project_dir = ?
|
|
139
|
+
ORDER BY provider_name
|
|
140
|
+
SQL
|
|
141
|
+
|
|
142
|
+
providers = providers_rows.map { |row| row["provider_name"] }
|
|
143
|
+
|
|
144
|
+
by_provider = {}
|
|
145
|
+
providers.each do |provider|
|
|
146
|
+
count = query_value(<<~SQL, [project_dir, provider])
|
|
147
|
+
SELECT COUNT(*) FROM #{table_name}
|
|
148
|
+
WHERE project_dir = ? AND provider_name = ?
|
|
149
|
+
SQL
|
|
150
|
+
by_provider[provider] = count || 0
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
total = by_provider.values.sum
|
|
154
|
+
|
|
155
|
+
{
|
|
156
|
+
providers: providers,
|
|
157
|
+
total_deprecated: total,
|
|
158
|
+
by_provider: by_provider
|
|
159
|
+
}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# List all deprecated models with full info
|
|
163
|
+
#
|
|
164
|
+
# @return [Array<Hash>] All deprecated models
|
|
165
|
+
def list_all
|
|
166
|
+
rows = query(<<~SQL, [project_dir])
|
|
167
|
+
SELECT provider_name, model_name, replacement, reason, detected_at
|
|
168
|
+
FROM #{table_name}
|
|
169
|
+
WHERE project_dir = ?
|
|
170
|
+
ORDER BY provider_name, model_name
|
|
171
|
+
SQL
|
|
172
|
+
|
|
173
|
+
rows.map do |row|
|
|
174
|
+
{
|
|
175
|
+
provider: row["provider_name"],
|
|
176
|
+
model: row["model_name"],
|
|
177
|
+
replacement: row["replacement"],
|
|
178
|
+
reason: row["reason"],
|
|
179
|
+
detected_at: row["detected_at"]
|
|
180
|
+
}
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Find deprecated models with replacements
|
|
185
|
+
#
|
|
186
|
+
# @return [Array<Hash>] Models with replacement info
|
|
187
|
+
def with_replacements
|
|
188
|
+
rows = query(<<~SQL, [project_dir])
|
|
189
|
+
SELECT provider_name, model_name, replacement
|
|
190
|
+
FROM #{table_name}
|
|
191
|
+
WHERE project_dir = ? AND replacement IS NOT NULL
|
|
192
|
+
ORDER BY provider_name, model_name
|
|
193
|
+
SQL
|
|
194
|
+
|
|
195
|
+
rows.map do |row|
|
|
196
|
+
{
|
|
197
|
+
provider: row["provider_name"],
|
|
198
|
+
model: row["model_name"],
|
|
199
|
+
replacement: row["replacement"]
|
|
200
|
+
}
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../repository"
|
|
4
|
+
|
|
5
|
+
module Aidp
|
|
6
|
+
module Database
|
|
7
|
+
module Repositories
|
|
8
|
+
# Repository for evaluations table
|
|
9
|
+
# Replaces evaluations/*.json and index.json
|
|
10
|
+
class EvaluationRepository < Repository
|
|
11
|
+
VALID_RATINGS = %w[good neutral bad].freeze
|
|
12
|
+
|
|
13
|
+
def initialize(project_dir: Dir.pwd)
|
|
14
|
+
super(project_dir: project_dir, table_name: "evaluations")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Store a new evaluation
|
|
18
|
+
#
|
|
19
|
+
# @param record [Hash] Evaluation data with keys:
|
|
20
|
+
# - id [String] Evaluation ID
|
|
21
|
+
# - rating [String] good/neutral/bad
|
|
22
|
+
# - target_type [String] Type of item being evaluated
|
|
23
|
+
# - target_id [String] ID of item being evaluated
|
|
24
|
+
# - feedback [String, nil] User feedback text
|
|
25
|
+
# - context [Hash, nil] Additional context
|
|
26
|
+
# @return [Hash] Result with :success and :id
|
|
27
|
+
def store(record)
|
|
28
|
+
execute(
|
|
29
|
+
insert_sql([
|
|
30
|
+
:id, :project_dir, :rating, :target_type, :target_id,
|
|
31
|
+
:comment, :context
|
|
32
|
+
]),
|
|
33
|
+
[
|
|
34
|
+
record[:id],
|
|
35
|
+
project_dir,
|
|
36
|
+
record[:rating],
|
|
37
|
+
record[:target_type],
|
|
38
|
+
record[:target_id],
|
|
39
|
+
record[:feedback],
|
|
40
|
+
serialize_json(record[:context] || {})
|
|
41
|
+
]
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
Aidp.log_debug("evaluation_repository", "stored",
|
|
45
|
+
id: record[:id], rating: record[:rating])
|
|
46
|
+
|
|
47
|
+
{success: true, id: record[:id]}
|
|
48
|
+
rescue => e
|
|
49
|
+
Aidp.log_debug("evaluation_repository", "store_failed",
|
|
50
|
+
id: record[:id], error: e.message)
|
|
51
|
+
{success: false, error: e.message, id: record[:id]}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Load an evaluation by ID
|
|
55
|
+
#
|
|
56
|
+
# @param id [String] Evaluation ID
|
|
57
|
+
# @return [Hash, nil] Evaluation or nil
|
|
58
|
+
def load(id)
|
|
59
|
+
row = query_one(
|
|
60
|
+
"SELECT * FROM evaluations WHERE id = ? AND project_dir = ?",
|
|
61
|
+
[id, project_dir]
|
|
62
|
+
)
|
|
63
|
+
deserialize_evaluation(row)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# List evaluations with optional filtering
|
|
67
|
+
#
|
|
68
|
+
# @param limit [Integer] Maximum records
|
|
69
|
+
# @param rating [String, nil] Filter by rating
|
|
70
|
+
# @param target_type [String, nil] Filter by target type
|
|
71
|
+
# @return [Array<Hash>] Evaluations
|
|
72
|
+
def list(limit: 50, rating: nil, target_type: nil)
|
|
73
|
+
conditions = ["project_dir = ?"]
|
|
74
|
+
params = [project_dir]
|
|
75
|
+
|
|
76
|
+
if rating
|
|
77
|
+
conditions << "rating = ?"
|
|
78
|
+
params << rating
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if target_type
|
|
82
|
+
conditions << "target_type = ?"
|
|
83
|
+
params << target_type
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
params << limit
|
|
87
|
+
|
|
88
|
+
rows = query(
|
|
89
|
+
<<~SQL,
|
|
90
|
+
SELECT * FROM evaluations
|
|
91
|
+
WHERE #{conditions.join(" AND ")}
|
|
92
|
+
ORDER BY created_at DESC
|
|
93
|
+
LIMIT ?
|
|
94
|
+
SQL
|
|
95
|
+
params
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
rows.map { |row| deserialize_evaluation(row) }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Get statistics
|
|
102
|
+
#
|
|
103
|
+
# @return [Hash] Statistics
|
|
104
|
+
def stats
|
|
105
|
+
total = query_value(
|
|
106
|
+
"SELECT COUNT(*) FROM evaluations WHERE project_dir = ?",
|
|
107
|
+
[project_dir]
|
|
108
|
+
) || 0
|
|
109
|
+
|
|
110
|
+
by_rating = {}
|
|
111
|
+
%w[good neutral bad].each do |r|
|
|
112
|
+
by_rating[r.to_sym] = query_value(
|
|
113
|
+
"SELECT COUNT(*) FROM evaluations WHERE project_dir = ? AND rating = ?",
|
|
114
|
+
[project_dir, r]
|
|
115
|
+
) || 0
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
by_type_rows = query(
|
|
119
|
+
<<~SQL,
|
|
120
|
+
SELECT target_type, COUNT(*) as count
|
|
121
|
+
FROM evaluations
|
|
122
|
+
WHERE project_dir = ?
|
|
123
|
+
GROUP BY target_type
|
|
124
|
+
SQL
|
|
125
|
+
[project_dir]
|
|
126
|
+
)
|
|
127
|
+
by_target_type = by_type_rows.each_with_object({}) do |row, h|
|
|
128
|
+
h[row["target_type"]] = row["count"]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
first_row = query_one(
|
|
132
|
+
"SELECT created_at FROM evaluations WHERE project_dir = ? ORDER BY created_at ASC LIMIT 1",
|
|
133
|
+
[project_dir]
|
|
134
|
+
)
|
|
135
|
+
last_row = query_one(
|
|
136
|
+
"SELECT created_at FROM evaluations WHERE project_dir = ? ORDER BY created_at DESC LIMIT 1",
|
|
137
|
+
[project_dir]
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
{
|
|
141
|
+
total: total,
|
|
142
|
+
by_rating: by_rating,
|
|
143
|
+
by_target_type: by_target_type,
|
|
144
|
+
first_evaluation: first_row&.dig("created_at"),
|
|
145
|
+
last_evaluation: last_row&.dig("created_at")
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Delete an evaluation
|
|
150
|
+
#
|
|
151
|
+
# @param id [String] Evaluation ID
|
|
152
|
+
# @return [Hash] Result
|
|
153
|
+
def delete(id)
|
|
154
|
+
execute(
|
|
155
|
+
"DELETE FROM evaluations WHERE id = ? AND project_dir = ?",
|
|
156
|
+
[id, project_dir]
|
|
157
|
+
)
|
|
158
|
+
Aidp.log_debug("evaluation_repository", "deleted", id: id)
|
|
159
|
+
{success: true, id: id}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Clear all evaluations
|
|
163
|
+
#
|
|
164
|
+
# @return [Hash] Result with count
|
|
165
|
+
def clear
|
|
166
|
+
count = query_value(
|
|
167
|
+
"SELECT COUNT(*) FROM evaluations WHERE project_dir = ?",
|
|
168
|
+
[project_dir]
|
|
169
|
+
) || 0
|
|
170
|
+
|
|
171
|
+
execute("DELETE FROM evaluations WHERE project_dir = ?", [project_dir])
|
|
172
|
+
|
|
173
|
+
Aidp.log_debug("evaluation_repository", "cleared", count: count)
|
|
174
|
+
{success: true, count: count}
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Check if any evaluations exist
|
|
178
|
+
#
|
|
179
|
+
# @return [Boolean]
|
|
180
|
+
def any?
|
|
181
|
+
count = query_value(
|
|
182
|
+
"SELECT COUNT(*) FROM evaluations WHERE project_dir = ?",
|
|
183
|
+
[project_dir]
|
|
184
|
+
) || 0
|
|
185
|
+
count.positive?
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
def deserialize_evaluation(row)
|
|
191
|
+
return nil unless row
|
|
192
|
+
|
|
193
|
+
context = deserialize_json(row["context"]) || {}
|
|
194
|
+
|
|
195
|
+
{
|
|
196
|
+
id: row["id"],
|
|
197
|
+
rating: row["rating"],
|
|
198
|
+
target_type: row["target_type"],
|
|
199
|
+
target_id: row["target_id"],
|
|
200
|
+
feedback: row["comment"],
|
|
201
|
+
context: context,
|
|
202
|
+
created_at: row["created_at"]
|
|
203
|
+
}
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|