Class: Ragnar::Config

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/ragnar/config.rb

Constant Summary collapse

CONFIG_FILENAMES =
['.ragnar.yml', '.ragnarrc.yml', 'ragnar.yml'].freeze

Instance Method Summary collapse

Constructor Details

#initializeConfig

Returns a new instance of Config.



14
15
16
17
# File 'lib/ragnar/config.rb', line 14

def initialize
  @config = load_config
  ensure_directories_exist
end

Instance Method Details

#available_profilesObject



97
98
99
# File 'lib/ragnar/config.rb', line 97

def available_profiles
  llm_profiles.keys
end

#chunk_overlapObject



56
57
58
# File 'lib/ragnar/config.rb', line 56

def chunk_overlap
  get('embeddings.chunk_overlap', Ragnar::DEFAULT_CHUNK_OVERLAP)
end

#chunk_sizeObject



52
53
54
# File 'lib/ragnar/config.rb', line 52

def chunk_size
  get('embeddings.chunk_size', Ragnar::DEFAULT_CHUNK_SIZE)
end

#config_exists?Boolean

Returns:

  • (Boolean)


163
164
165
# File 'lib/ragnar/config.rb', line 163

def config_exists?
  !@config_file_path.nil?
end

#config_file_pathObject

Config file management



159
160
161
# File 'lib/ragnar/config.rb', line 159

def config_file_path
  @config_file_path
end

#create_chatObject

Create a new RubyLLM chat instance with the active profile’s settings



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ragnar/config.rb', line 102

def create_chat
  api_key = llm_api_key
  provider = llm_provider.to_sym

  # Configure RubyLLM with the API key if present
  if api_key
    configure_provider_api_key(provider, api_key)
  end

  RubyLLM.chat(provider: provider, model: llm_model)
end

#database_pathObject

Common config accessors



36
37
38
# File 'lib/ragnar/config.rb', line 36

def database_path
  get('storage.database_path', default_database_path)
end

#embedding_modelObject



48
49
50
# File 'lib/ragnar/config.rb', line 48

def embedding_model
  get('embeddings.model', Ragnar::DEFAULT_EMBEDDING_MODEL)
end

#enable_query_rewriting?Boolean

Returns:

  • (Boolean)


146
147
148
# File 'lib/ragnar/config.rb', line 146

def enable_query_rewriting?
  get('query.enable_query_rewriting', true)
end

#enable_reranking?Boolean

Returns:

  • (Boolean)


150
151
152
# File 'lib/ragnar/config.rb', line 150

def enable_reranking?
  get('query.enable_reranking', true)
end

#generate_config_file(path = nil) ⇒ Object

Generate a config file with current/default settings



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
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/ragnar/config.rb', line 173

def generate_config_file(path = nil)
  path ||= File.expand_path('~/.ragnar.yml')
  
  config_content = {
    'storage' => {
      'database_path' => '~/.cache/ragnar/database',
      'models_dir' => '~/.cache/ragnar/models', 
      'history_file' => '~/.cache/ragnar/history'
    },
    'embeddings' => {
      'model' => Ragnar::DEFAULT_EMBEDDING_MODEL,
      'chunk_size' => Ragnar::DEFAULT_CHUNK_SIZE,
      'chunk_overlap' => Ragnar::DEFAULT_CHUNK_OVERLAP
    },
    'umap' => {
      'reduced_dimensions' => Ragnar::DEFAULT_REDUCED_DIMENSIONS,
      'n_neighbors' => 15,
      'min_dist' => 0.1,
      'model_filename' => 'umap_model.bin'
    },
    'llm' => {
      'default_profile' => 'red_candle',
      'profiles' => {
        'red_candle' => {
          'provider' => 'red_candle',
          'model' => 'MaziyarPanahi/Qwen3-4B-GGUF'
        },
        'opus' => {
          'provider' => 'anthropic',
          'model' => 'claude-opus-4-6'
        },
        'sonnet' => {
          'provider' => 'anthropic',
          'model' => 'claude-sonnet-4-6'
        }
      }
    },
    'query' => {
      'top_k' => 3,
      'enable_query_rewriting' => true,
      'enable_reranking' => true,
      'reranker_model' => 'BAAI/bge-reranker-base'
    },
    'interactive' => {
      'prompt' => 'ragnar> ',
      'quiet_mode' => true
    },
    'output' => {
      'show_progress' => true
    }
  }
  
  # Ensure parent directory exists
  FileUtils.mkdir_p(File.dirname(path))
  
  # Write config file with comments
  File.write(path, generate_yaml_with_comments(config_content))
  path
end

#get(key_path, default = nil) ⇒ Object

Main config access method



20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/ragnar/config.rb', line 20

def get(key_path, default = nil)
  keys = key_path.split('.')
  value = keys.reduce(@config) { |config, key| config&.dig(key) }
  
  # Use default only if value is nil (not false)
  result = value.nil? ? default : value
  
  # Expand paths that start with ~
  if result.is_a?(String) && result.start_with?('~')
    File.expand_path(result)
  else
    result
  end
end

#history_fileObject



40
41
42
# File 'lib/ragnar/config.rb', line 40

def history_file
  get('storage.history_file', default_history_file)
end

#interactive_promptObject



130
131
132
# File 'lib/ragnar/config.rb', line 130

def interactive_prompt
  get('interactive.prompt', "ragnar> ")
end

#llm_api_keyObject



126
127
128
# File 'lib/ragnar/config.rb', line 126

def llm_api_key
  llm_profile&.dig('api_key') || get('llm.api_key', nil)
end

#llm_gguf_fileObject



122
123
124
# File 'lib/ragnar/config.rb', line 122

def llm_gguf_file
  get('llm.default_gguf_file', "Qwen3-4B.Q4_K_M.gguf")
end

#llm_modelObject



118
119
120
# File 'lib/ragnar/config.rb', line 118

def llm_model
  llm_profile&.dig('model') || get('llm.default_model', 'MaziyarPanahi/Qwen3-4B-GGUF')
end

#llm_profileObject



93
94
95
# File 'lib/ragnar/config.rb', line 93

def llm_profile
  llm_profiles[llm_profile_name] || llm_profiles.values.first
end

#llm_profile_nameObject



74
75
76
# File 'lib/ragnar/config.rb', line 74

def llm_profile_name
  @active_profile || get('llm.default_profile', nil) || llm_profiles.keys.first || 'default'
end

#llm_profilesObject



78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/ragnar/config.rb', line 78

def llm_profiles
  configured = get('llm.profiles', nil)
  if configured.is_a?(Hash) && !configured.empty?
    configured
  else
    # Backwards compat: synthesize a profile from flat keys
    {
      'default' => {
        'provider' => get('llm.provider', 'red_candle'),
        'model' => get('llm.default_model', 'MaziyarPanahi/Qwen3-4B-GGUF')
      }
    }
  end
end

#llm_providerObject



114
115
116
# File 'lib/ragnar/config.rb', line 114

def llm_provider
  llm_profile&.dig('provider') || get('llm.provider', 'red_candle')
end

#models_dirObject



44
45
46
# File 'lib/ragnar/config.rb', line 44

def models_dir
  get('storage.models_dir', default_models_dir)
end

#query_top_kObject



142
143
144
# File 'lib/ragnar/config.rb', line 142

def query_top_k
  get('query.top_k', 3)
end

#quiet_mode?Boolean

Returns:

  • (Boolean)


134
135
136
# File 'lib/ragnar/config.rb', line 134

def quiet_mode?
  get('interactive.quiet_mode', true)
end

#reload!Object



167
168
169
170
# File 'lib/ragnar/config.rb', line 167

def reload!
  @config = load_config
  ensure_directories_exist
end

#reranker_modelObject



154
155
156
# File 'lib/ragnar/config.rb', line 154

def reranker_model
  get('query.reranker_model', 'BAAI/bge-reranker-base')
end

#set_active_profile(name) ⇒ Object

LLM Profile support Profiles allow switching between LLM providers/models via –profile flag Backwards compatible: flat llm.provider/llm.default_model still work if no profiles defined



64
65
66
67
68
69
70
71
72
# File 'lib/ragnar/config.rb', line 64

def set_active_profile(name)
  name = name.to_s
  profiles = llm_profiles
  unless profiles.key?(name)
    available = profiles.keys.join(', ')
    raise ArgumentError, "Unknown profile '#{name}'. Available profiles: #{available}"
  end
  @active_profile = name
end

#show_progress?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/ragnar/config.rb', line 138

def show_progress?
  get('output.show_progress', true)
end