Class: Aidp::Evaluations::EvaluationStorage

Inherits:
Object
  • Object
show all
Includes:
RescueLogging
Defined in:
lib/aidp/evaluations/evaluation_storage.rb

Overview

Storage manager for evaluation records

Stores evaluations in ‘.aidp/evaluations/` with append-only semantics:

  • Individual evaluations stored as JSON files: ‘eval_YYYYMMDD_HHMMSS_xxxx.json`

  • Indexed summary file for efficient lookups: ‘index.json`

Examples:

Storing an evaluation

storage = EvaluationStorage.new(project_dir: Dir.pwd)
storage.store(record)

Listing evaluations

storage.list(limit: 10)
storage.list(rating: "bad")

Instance Method Summary collapse

Methods included from RescueLogging

__log_rescue_impl, log_rescue, #log_rescue

Constructor Details

#initialize(project_dir: Dir.pwd) ⇒ EvaluationStorage

Returns a new instance of EvaluationStorage.



27
28
29
30
31
32
33
34
# File 'lib/aidp/evaluations/evaluation_storage.rb', line 27

def initialize(project_dir: Dir.pwd)
  @project_dir = project_dir
  @evaluations_dir = ConfigPaths.evaluations_dir(project_dir)
  @index_file = ConfigPaths.evaluations_index_file(project_dir)

  Aidp.log_debug("evaluation_storage", "initialize",
    project_dir: project_dir, evaluations_dir: @evaluations_dir)
end

Instance Method Details

#any?Boolean

Check if evaluations directory exists and has evaluations

Returns:

  • (Boolean)


188
189
190
# File 'lib/aidp/evaluations/evaluation_storage.rb', line 188

def any?
  Dir.exist?(@evaluations_dir) && Dir.glob(File.join(@evaluations_dir, "eval_*.json")).any?
end

#clearHash

Clear all evaluations

Returns:

  • (Hash)

    Result with :success and :count keys



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/aidp/evaluations/evaluation_storage.rb', line 170

def clear
  Aidp.log_debug("evaluation_storage", "clear")

  return {success: true, count: 0} unless Dir.exist?(@evaluations_dir)

  count = Dir.glob(File.join(@evaluations_dir, "eval_*.json")).size
  FileUtils.rm_rf(@evaluations_dir)

  {success: true, count: count}
rescue => error
  log_rescue(error,
    component: "evaluation_storage",
    action: "clear",
    fallback: {success: false})
  {success: false, error: error.message}
end

#delete(id) ⇒ Hash

Delete an evaluation by ID

Parameters:

  • id (String)

    The evaluation ID

Returns:

  • (Hash)

    Result with :success key



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/aidp/evaluations/evaluation_storage.rb', line 148

def delete(id)
  file_path = File.join(@evaluations_dir, "#{id}.json")
  return {success: true, message: "Evaluation not found"} unless File.exist?(file_path)

  Aidp.log_debug("evaluation_storage", "delete", id: id)

  File.delete(file_path)
  remove_from_index(id)

  {success: true, id: id}
rescue => error
  log_rescue(error,
    component: "evaluation_storage",
    action: "delete",
    fallback: {success: false},
    id: id)
  {success: false, error: error.message}
end

#list(limit: 50, rating: nil, target_type: nil) ⇒ Array<EvaluationRecord>

List evaluations with optional filtering

Parameters:

  • limit (Integer) (defaults to: 50)

    Maximum number of records to return

  • rating (String, nil) (defaults to: nil)

    Filter by rating (good/neutral/bad)

  • target_type (String, nil) (defaults to: nil)

    Filter by target type

Returns:



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/aidp/evaluations/evaluation_storage.rb', line 87

def list(limit: 50, rating: nil, target_type: nil)
  Aidp.log_debug("evaluation_storage", "list",
    limit: limit, rating: rating, target_type: target_type)

  index = load_index
  entries = index[:entries] || []

  # Apply filters
  entries = entries.select { |e| e[:rating] == rating } if rating
  entries = entries.select { |e| e[:target_type] == target_type } if target_type

  # Sort by created_at descending, take limit
  entries = entries.sort_by { |e| e[:created_at] || "" }.reverse.take(limit)

  # Load full records
  entries.filter_map { |entry| load(entry[:id]) }
rescue => error
  log_rescue(error,
    component: "evaluation_storage",
    action: "list",
    fallback: [],
    limit: limit)
  []
end

#load(id) ⇒ EvaluationRecord?

Load a specific evaluation by ID

Parameters:

  • id (String)

    The evaluation ID

Returns:



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/aidp/evaluations/evaluation_storage.rb', line 64

def load(id)
  file_path = File.join(@evaluations_dir, "#{id}.json")
  return nil unless File.exist?(file_path)

  Aidp.log_debug("evaluation_storage", "load", id: id)

  data = JSON.parse(File.read(file_path))
  EvaluationRecord.from_h(data)
rescue => error
  log_rescue(error,
    component: "evaluation_storage",
    action: "load",
    fallback: nil,
    id: id)
  nil
end

#statsHash

Get statistics about evaluations

Returns:

  • (Hash)

    Statistics including counts by rating



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/aidp/evaluations/evaluation_storage.rb', line 115

def stats
  Aidp.log_debug("evaluation_storage", "stats")

  index = load_index
  entries = index[:entries] || []

  total = entries.size
  by_rating = entries.group_by { |e| e[:rating] }
  by_target = entries.group_by { |e| e[:target_type] }

  {
    total: total,
    by_rating: {
      good: (by_rating["good"] || []).size,
      neutral: (by_rating["neutral"] || []).size,
      bad: (by_rating["bad"] || []).size
    },
    by_target_type: by_target.transform_values(&:size),
    first_evaluation: entries.min_by { |e| e[:created_at] || "" }&.dig(:created_at),
    last_evaluation: entries.max_by { |e| e[:created_at] || "" }&.dig(:created_at)
  }
rescue => error
  log_rescue(error,
    component: "evaluation_storage",
    action: "stats",
    fallback: {total: 0, by_rating: {good: 0, neutral: 0, bad: 0}})
  {total: 0, by_rating: {good: 0, neutral: 0, bad: 0}, by_target_type: {}}
end

#store(record) ⇒ Hash

Store a new evaluation record

Parameters:

Returns:

  • (Hash)

    Result with :success and :id keys



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/aidp/evaluations/evaluation_storage.rb', line 40

def store(record)
  ensure_directory
  file_path = File.join(@evaluations_dir, "#{record.id}.json")

  Aidp.log_debug("evaluation_storage", "store",
    id: record.id, rating: record.rating, file_path: file_path)

  File.write(file_path, JSON.pretty_generate(record.to_h))
  update_index(record)

  {success: true, id: record.id, file_path: file_path}
rescue => error
  log_rescue(error,
    component: "evaluation_storage",
    action: "store",
    fallback: {success: false},
    id: record.id)
  {success: false, error: error.message, id: record.id}
end