Class: Kodo::Config
- Inherits:
-
Object
- Object
- Kodo::Config
- Defined in:
- lib/kodo/config.rb
Constant Summary collapse
- PROVIDER_KEY_MAP =
Map of Kodo config key → RubyLLM config setter name
{ 'anthropic' => 'anthropic', 'openai' => 'openai', 'gemini' => 'gemini', 'deepseek' => 'deepseek', 'mistral' => 'mistral', 'openrouter' => 'openrouter', 'perplexity' => 'perplexity', 'xai' => 'xai' }.freeze
- DEFAULTS =
{ 'daemon' => { 'port' => 7377, 'heartbeat_interval' => 60 }, 'llm' => { 'model' => 'claude-sonnet-4-6', 'utility_model' => 'claude-haiku-4-5-20251001', 'providers' => { 'anthropic' => { 'api_key_env' => 'ANTHROPIC_API_KEY' } } }, 'channels' => { 'telegram' => { 'enabled' => false, 'bot_token_env' => 'TELEGRAM_BOT_TOKEN' } }, 'memory' => { 'encryption' => false, 'passphrase_env' => 'KODO_PASSPHRASE', 'store' => 'file' }, 'search' => { 'provider' => nil, 'providers' => { 'tavily' => { 'api_key_env' => 'TAVILY_API_KEY' } } }, 'logging' => { 'level' => 'info', 'audit' => true }, 'web' => { 'fetch_url_enabled' => true, 'web_search_enabled' => true, 'injection_scan' => true, 'audit_urls' => true, 'fetch_blocklist' => [], 'fetch_allowlist' => [], 'ssrf_bypass_hosts' => [] } }.freeze
Instance Attribute Summary collapse
-
#data ⇒ Object
readonly
Returns the value of attribute data.
Class Method Summary collapse
Instance Method Summary collapse
- #audit_enabled? ⇒ Boolean
- #heartbeat_interval ⇒ Object
-
#initialize(data) ⇒ Config
constructor
A new instance of Config.
-
#llm_api_keys ⇒ Object
Returns a hash of { "provider_name" => "actual_api_key" } for all configured providers.
-
#llm_model ⇒ Object
--- LLM ---.
-
#log_level ⇒ Object
--- Logging ---.
-
#memory_encryption? ⇒ Boolean
--- Memory / Encryption ---.
- #memory_passphrase ⇒ Object
- #memory_passphrase_env ⇒ Object
-
#ollama_api_base ⇒ Object
Optional: Ollama base URL for local models.
-
#port ⇒ Object
--- Daemon ---.
- #search_api_key ⇒ Object
- #search_configured? ⇒ Boolean
-
#search_provider ⇒ Object
--- Search ---.
- #search_provider_instance ⇒ Object
-
#secrets_passphrase ⇒ Object
--- Secrets ---.
-
#telegram_bot_token ⇒ Object
--- Channels ---.
- #telegram_enabled? ⇒ Boolean
- #utility_model ⇒ Object
- #web_audit_urls? ⇒ Boolean
- #web_fetch_allowlist ⇒ Object
- #web_fetch_blocklist ⇒ Object
-
#web_fetch_url_enabled? ⇒ Boolean
--- Web ---.
- #web_injection_scan? ⇒ Boolean
- #web_search_enabled? ⇒ Boolean
- #web_ssrf_bypass_hosts ⇒ Object
Constructor Details
#initialize(data) ⇒ Config
Returns a new instance of Config.
67 68 69 |
# File 'lib/kodo/config.rb', line 67 def initialize(data) @data = data end |
Instance Attribute Details
#data ⇒ Object (readonly)
Returns the value of attribute data.
65 66 67 |
# File 'lib/kodo/config.rb', line 65 def data @data end |
Class Method Details
.config_path ⇒ Object
79 80 81 |
# File 'lib/kodo/config.rb', line 79 def config_path File.join(Kodo.home_dir, 'config.yml') end |
.ensure_home_dir! ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/kodo/config.rb', line 83 def ensure_home_dir! dirs = [ Kodo.home_dir, File.join(Kodo.home_dir, 'memory', 'conversations'), File.join(Kodo.home_dir, 'memory', 'knowledge'), File.join(Kodo.home_dir, 'memory', 'reminders'), File.join(Kodo.home_dir, 'memory', 'audit'), File.join(Kodo.home_dir, 'skills') ] dirs.each { |d| FileUtils.mkdir_p(d) } return if File.exist?(config_path) File.write(config_path, YAML.dump(DEFAULTS)) end |
.load(path = nil) ⇒ Object
72 73 74 75 76 77 |
# File 'lib/kodo/config.rb', line 72 def load(path = nil) path ||= config_path user_config = File.exist?(path) ? YAML.safe_load_file(path) : {} merged = deep_merge(DEFAULTS, user_config || {}) new(merged) end |
Instance Method Details
#audit_enabled? ⇒ Boolean
178 |
# File 'lib/kodo/config.rb', line 178 def audit_enabled? = data.dig('logging', 'audit') != false |
#heartbeat_interval ⇒ Object
114 |
# File 'lib/kodo/config.rb', line 114 def heartbeat_interval = data.dig('daemon', 'heartbeat_interval') |
#llm_api_keys ⇒ Object
Returns a hash of { "provider_name" => "actual_api_key" } for all configured providers
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/kodo/config.rb', line 121 def llm_api_keys providers = data.dig('llm', 'providers') || {} keys = {} providers.each do |provider, settings| env_var = settings['api_key_env'] next unless env_var key = ENV[env_var] if key && !key.empty? ruby_llm_name = PROVIDER_KEY_MAP[provider] || provider keys[ruby_llm_name] = key end end raise Error, 'No LLM API keys found. Set at least one provider key (e.g. ANTHROPIC_API_KEY)' if keys.empty? keys end |
#llm_model ⇒ Object
--- LLM ---
117 |
# File 'lib/kodo/config.rb', line 117 def llm_model = data.dig('llm', 'model') |
#log_level ⇒ Object
--- Logging ---
177 |
# File 'lib/kodo/config.rb', line 177 def log_level = data.dig('logging', 'level')&.to_sym || :info |
#memory_encryption? ⇒ Boolean
--- Memory / Encryption ---
147 |
# File 'lib/kodo/config.rb', line 147 def memory_encryption? = data.dig('memory', 'encryption') == true |
#memory_passphrase ⇒ Object
153 154 155 156 157 158 159 160 |
# File 'lib/kodo/config.rb', line 153 def memory_passphrase passphrase = ENV[memory_passphrase_env] if memory_encryption? && (passphrase.nil? || passphrase.empty?) raise Error, "Memory encryption is enabled but #{memory_passphrase_env} is not set" end passphrase end |
#memory_passphrase_env ⇒ Object
149 150 151 |
# File 'lib/kodo/config.rb', line 149 def memory_passphrase_env data.dig('memory', 'passphrase_env') || 'KODO_PASSPHRASE' end |
#ollama_api_base ⇒ Object
Optional: Ollama base URL for local models
142 143 144 |
# File 'lib/kodo/config.rb', line 142 def ollama_api_base data.dig('llm', 'providers', 'ollama', 'api_base') || ENV['OLLAMA_API_BASE'] end |
#port ⇒ Object
--- Daemon ---
113 |
# File 'lib/kodo/config.rb', line 113 def port = data.dig('daemon', 'port') |
#search_api_key ⇒ Object
199 200 201 202 203 204 205 206 207 |
# File 'lib/kodo/config.rb', line 199 def search_api_key return nil unless search_configured? provider = search_provider env_var = data.dig('search', 'providers', provider, 'api_key_env') return nil unless env_var ENV[env_var] end |
#search_configured? ⇒ Boolean
195 196 197 |
# File 'lib/kodo/config.rb', line 195 def search_configured? !search_provider.nil? && !search_provider.empty? end |
#search_provider ⇒ Object
--- Search ---
191 192 193 |
# File 'lib/kodo/config.rb', line 191 def search_provider data.dig('search', 'provider') end |
#search_provider_instance ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/kodo/config.rb', line 227 def search_provider_instance return nil unless search_configured? case search_provider when 'tavily' api_key = search_api_key unless api_key raise Error, "Tavily API key not set (#{data.dig('search', 'providers', 'tavily', 'api_key_env')})" end Search::Tavily.new(api_key: api_key) else raise Error, "Unknown search provider: #{search_provider}" end end |
#secrets_passphrase ⇒ Object
--- Secrets ---
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/kodo/config.rb', line 163 def secrets_passphrase passphrase_path = File.join(Kodo.home_dir, '.passphrase') if File.exist?(passphrase_path) File.read(passphrase_path).strip else passphrase = SecureRandom.hex(32) FileUtils.mkdir_p(Kodo.home_dir) File.write(passphrase_path, passphrase) File.chmod(0o600, passphrase_path) passphrase end end |
#telegram_bot_token ⇒ Object
--- Channels ---
181 182 183 184 |
# File 'lib/kodo/config.rb', line 181 def telegram_bot_token env_var = data.dig('channels', 'telegram', 'bot_token_env') ENV.fetch(env_var) { raise Error, "Missing environment variable: #{env_var}" } end |
#telegram_enabled? ⇒ Boolean
186 187 188 |
# File 'lib/kodo/config.rb', line 186 def telegram_enabled? data.dig('channels', 'telegram', 'enabled') == true end |
#utility_model ⇒ Object
118 |
# File 'lib/kodo/config.rb', line 118 def utility_model = data.dig('llm', 'utility_model') || llm_model |
#web_audit_urls? ⇒ Boolean
213 |
# File 'lib/kodo/config.rb', line 213 def web_audit_urls? = data.dig('web', 'audit_urls') != false |
#web_fetch_allowlist ⇒ Object
219 220 221 |
# File 'lib/kodo/config.rb', line 219 def web_fetch_allowlist data.dig('web', 'fetch_allowlist') || [] end |
#web_fetch_blocklist ⇒ Object
215 216 217 |
# File 'lib/kodo/config.rb', line 215 def web_fetch_blocklist data.dig('web', 'fetch_blocklist') || [] end |
#web_fetch_url_enabled? ⇒ Boolean
--- Web ---
210 |
# File 'lib/kodo/config.rb', line 210 def web_fetch_url_enabled? = data.dig('web', 'fetch_url_enabled') != false |
#web_injection_scan? ⇒ Boolean
212 |
# File 'lib/kodo/config.rb', line 212 def web_injection_scan? = data.dig('web', 'injection_scan') != false |
#web_search_enabled? ⇒ Boolean
211 |
# File 'lib/kodo/config.rb', line 211 def web_search_enabled? = data.dig('web', 'web_search_enabled') != false |
#web_ssrf_bypass_hosts ⇒ Object
223 224 225 |
# File 'lib/kodo/config.rb', line 223 def web_ssrf_bypass_hosts data.dig('web', 'ssrf_bypass_hosts') || [] end |