Class: Ace::Review::Molecules::PresetManager
- Inherits:
-
Object
- Object
- Ace::Review::Molecules::PresetManager
- Defined in:
- lib/ace/review/molecules/preset_manager.rb
Overview
Manages loading and resolving review presets from configuration
Constant Summary collapse
- COMPOSITION_METADATA_KEYS =
Metadata keys that are added during composition and should be stripped before use
%w[success composed composed_from].freeze
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#config_path ⇒ Object
readonly
Returns the value of attribute config_path.
-
#project_root ⇒ Object
readonly
Returns the value of attribute project_root.
Instance Method Summary collapse
-
#available_presets ⇒ Object
Get list of available preset names.
-
#default_context ⇒ Object
Get the default context from configuration.
-
#default_model ⇒ Object
Get the default model from configuration.
-
#default_output_format ⇒ Object
Get the default output format.
-
#initialize(config_path: nil, project_root: nil) ⇒ PresetManager
constructor
A new instance of PresetManager.
-
#load_preset(preset_name) ⇒ Object
Load a specific preset by name Cached results are returned immediately to avoid redundant composition.
-
#load_preset_with_composition(name, visited = Set.new) ⇒ Object
Load a preset with composition support Returns fully composed preset data with all dependent presets merged Composition order: base presets first, then composing preset (last wins for scalars) Uses intermediate caching to avoid redundant composition of shared dependencies.
-
#preset_exists?(preset_name) ⇒ Boolean
Check if a preset exists.
-
#resolve_preset(preset_name, overrides = {}) ⇒ Object
Resolve a preset configuration into actionable components.
-
#review_base_path ⇒ Object
Get the base path for storing reviews.
-
#storage_config ⇒ Object
Get storage configuration (user config only, no defaults).
Constructor Details
#initialize(config_path: nil, project_root: nil) ⇒ PresetManager
Returns a new instance of PresetManager.
17 18 19 20 21 22 23 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 17 def initialize(config_path: nil, project_root: nil) @project_root = project_root || find_project_root @config_path = resolve_config_path(config_path) @config = load_configuration @preset_cache = {} # Final preset cache (after merging with defaults) @composition_cache = {} # Intermediate composition cache (before defaults merge) end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
12 13 14 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 12 def config @config end |
#config_path ⇒ Object (readonly)
Returns the value of attribute config_path.
12 13 14 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 12 def config_path @config_path end |
#project_root ⇒ Object (readonly)
Returns the value of attribute project_root.
12 13 14 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 12 def project_root @project_root end |
Instance Method Details
#available_presets ⇒ Object
Get list of available preset names
53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 53 def available_presets presets = [] # Add presets from main config presets.concat(config_presets) if config # Add presets from preset directory presets.concat(file_presets) presets.uniq.sort end |
#default_context ⇒ Object
Get the default context from configuration
77 78 79 80 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 77 def default_context config&.dig("defaults", "bundle") || Ace::Review.get("defaults", "bundle") end |
#default_model ⇒ Object
Get the default model from configuration
71 72 73 74 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 71 def default_model config&.dig("defaults", "model") || Ace::Review.get("defaults", "model") end |
#default_output_format ⇒ Object
Get the default output format
83 84 85 86 87 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 83 def default_output_format config&.dig("defaults", "output_format") || Ace::Review.get("defaults", "output_format") || "markdown" end |
#load_preset(preset_name) ⇒ Object
Load a specific preset by name Cached results are returned immediately to avoid redundant composition
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 27 def load_preset(preset_name) return nil unless preset_name # Check cache first (composition can be expensive for deeply nested presets) return @preset_cache[preset_name] if @preset_cache.key?(preset_name) # Load with composition support result = load_preset_with_composition(preset_name) # Handle composition errors unless result && result["success"] # Log composition failure for debugging if result && result["error"] warn "Failed to compose preset '#{preset_name}': #{result["error"]}" if Ace::Review.debug? end return nil end # Extract preset data (remove composition metadata) preset = (result) # Merge with defaults and cache @preset_cache[preset_name] = merge_with_defaults(preset) end |
#load_preset_with_composition(name, visited = Set.new) ⇒ Object
Load a preset with composition support Returns fully composed preset data with all dependent presets merged Composition order: base presets first, then composing preset (last wins for scalars) Uses intermediate caching to avoid redundant composition of shared dependencies
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 131 def load_preset_with_composition(name, visited = Set.new) start_time = Time.now if Ace::Review.debug? # Check circular dependency first (before cache to prevent caching incomplete compositions) validation = Atoms::PresetValidator.check_circular_dependency(name, visited.to_a) unless validation[:success] return { "error" => validation[:error], "success" => false } end # Check composition cache (enables intermediate caching for shared base presets) if @composition_cache.key?(name) warn "[COMPOSITION] Cache hit for '#{name}'" if Ace::Review.debug? return @composition_cache[name].dup end # Load preset from file or config preset = load_preset_from_file(name) || load_preset_from_config(name) unless preset return { "error" => "Preset '#{name}' not found. Available presets: #{available_presets.join(", ")}", "success" => false } end # Mark this preset as visited new_visited = visited.dup.add(name) # Extract preset references preset_refs = Atoms::PresetValidator.extract_preset_references(preset) # If no references, return preset as-is if preset_refs.empty? # Ensure consistent string keys result = deep_stringify_keys(preset) result["success"] = true return result end # Load all referenced presets recursively composed_presets = [] errors = [] preset_refs.each do |ref_name| composed = load_preset_with_composition(ref_name, new_visited) if composed["success"] composed_presets << composed else errors << composed["error"] end end # If there were errors loading dependencies, return error if errors.any? return { "error" => "Failed to load preset dependencies: #{errors.join(", ")}", "success" => false, "partial_presets" => composed_presets } end # Strip metadata from composed presets before merging clean_composed = composed_presets.map { |p| (p) } # Merge all composed presets with current preset # Order: dependencies first, then current preset (last wins for scalars) merged = merge_preset_data(clean_composed + [preset]) # Ensure consistent string keys and add composition metadata merged = deep_stringify_keys(merged) merged["success"] = true merged["composed"] = true merged["composed_from"] = preset_refs + [name] # Cache the composed result for future reuse (enables intermediate caching) @composition_cache[name] = merged.dup # Log composition performance metrics in debug mode if Ace::Review.debug? elapsed = Time.now - start_time depth = visited.size + 1 ref_count = preset_refs.size warn "[COMPOSITION] Composed '#{name}' in #{(elapsed * 1000).round(2)}ms (depth: #{depth}, refs: #{ref_count})" end merged end |
#preset_exists?(preset_name) ⇒ Boolean
Check if a preset exists
66 67 68 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 66 def preset_exists?(preset_name) available_presets.include?(preset_name.to_s) end |
#resolve_preset(preset_name, overrides = {}) ⇒ Object
Resolve a preset configuration into actionable components
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 90 def resolve_preset(preset_name, overrides = {}) preset = load_preset(preset_name) return nil unless preset models_config = resolve_models_config(preset, overrides) # Support both bundle: and context: for context resolution preset_context = preset["bundle"] || preset["context"] { description: preset["description"], # Extract prompt composition for ace-bundle frontmatter (but let ace-bundle process it) system_prompt: preset["system_prompt"] || preset["prompt_composition"], # Preserve instructions field for section-based context generation instructions: preset["instructions"], context: resolve_context_config(preset_context, overrides[:context]), subject: resolve_subject_config(preset["subject"], overrides[:subject]), models: models_config, output_format: overrides[:output_format] || preset["output_format"] || default_output_format } end |
#review_base_path ⇒ Object
Get the base path for storing reviews
118 119 120 121 122 123 124 125 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 118 def review_base_path # 1. Check for configured path first (user config only) configured_path = storage_config["base_path"] return (configured_path) if configured_path # 2. Fallback to cache directory File.join(project_root, ".ace-local/review/sessions") end |
#storage_config ⇒ Object
Get storage configuration (user config only, no defaults)
113 114 115 |
# File 'lib/ace/review/molecules/preset_manager.rb', line 113 def storage_config config&.dig("storage") || {} end |