Skip to content

Commit e19d73b

Browse files
committed
Add ConfigManager for unified configuration management
- Introduced a new `config_manager.py` module to handle reading and writing configurations from `config.yml` and `.env` files, ensuring backward compatibility. - Refactored `ChronicleSetup` in `backends/advanced/init.py` to utilize `ConfigManager` for loading and updating configurations, simplifying the setup process. - Removed redundant methods for loading and saving `config.yml` directly in `ChronicleSetup`, as these are now managed by `ConfigManager`. - Enhanced user feedback during configuration updates, including success messages for changes made to configuration files.
1 parent 1bde611 commit e19d73b

File tree

2 files changed

+383
-65
lines changed

2 files changed

+383
-65
lines changed

backends/advanced/init.py

Lines changed: 35 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,21 @@ def __init__(self, args=None):
3636
self.console.print("[red][ERROR][/red] Please run this script from the backends/advanced directory")
3737
sys.exit(1)
3838

39-
# Load config.yml if it exists
40-
self.load_config_yml()
39+
# Initialize ConfigManager
40+
repo_root = Path.cwd().parent.parent # backends/advanced -> repo root
41+
if str(repo_root) not in sys.path:
42+
sys.path.insert(0, str(repo_root))
43+
44+
from config_manager import ConfigManager
45+
46+
self.config_manager = ConfigManager(service_path="backends/advanced")
47+
self.console.print(f"[blue][INFO][/blue] Using config.yml at: {self.config_manager.config_yml_path}")
48+
49+
# Load existing config or create default structure
50+
self.config_yml_data = self.config_manager.get_full_config()
51+
if not self.config_yml_data:
52+
self.console.print("[yellow][WARNING][/yellow] config.yml not found, will create default structure")
53+
self.config_yml_data = self._get_default_config_structure()
4154

4255
def print_header(self, title: str):
4356
"""Print a colorful header"""
@@ -126,21 +139,6 @@ def mask_api_key(self, key: str, show_chars: int = 5) -> str:
126139

127140
return f"{key_clean[:show_chars]}{'*' * min(15, len(key_clean) - show_chars * 2)}{key_clean[-show_chars:]}"
128141

129-
def load_config_yml(self):
130-
"""Load config.yml from repository root"""
131-
if not self.config_yml_path.exists():
132-
self.console.print(f"[yellow][WARNING][/yellow] config.yml not found at {self.config_yml_path}")
133-
self.console.print("[yellow]Will create a new config.yml during setup[/yellow]")
134-
self.config_yml_data = self._get_default_config_structure()
135-
return
136-
137-
try:
138-
with open(self.config_yml_path, 'r') as f:
139-
self.config_yml_data = yaml.safe_load(f)
140-
self.console.print(f"[blue][INFO][/blue] Loaded existing config.yml")
141-
except Exception as e:
142-
self.console.print(f"[red][ERROR][/red] Failed to load config.yml: {e}")
143-
self.config_yml_data = self._get_default_config_structure()
144142

145143
def _get_default_config_structure(self) -> Dict[str, Any]:
146144
"""Return default config.yml structure if file doesn't exist"""
@@ -163,36 +161,6 @@ def _get_default_config_structure(self) -> Dict[str, Any]:
163161
}
164162
}
165163

166-
def save_config_yml(self):
167-
"""Save config.yml back to repository root"""
168-
try:
169-
# Backup existing config.yml if it exists
170-
if self.config_yml_path.exists():
171-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
172-
backup_path = self.config_yml_path.parent / f"config.yml.backup.{timestamp}"
173-
shutil.copy2(self.config_yml_path, backup_path)
174-
self.console.print(f"[blue][INFO][/blue] Backed up config.yml to {backup_path.name}")
175-
176-
# Write updated config
177-
with open(self.config_yml_path, 'w') as f:
178-
yaml.dump(self.config_yml_data, f, default_flow_style=False, sort_keys=False)
179-
180-
self.console.print("[green][SUCCESS][/green] config.yml updated successfully")
181-
except Exception as e:
182-
self.console.print(f"[red][ERROR][/red] Failed to save config.yml: {e}")
183-
raise
184-
185-
def update_config_default(self, key: str, value: str):
186-
"""Update a default value in config.yml"""
187-
if "defaults" not in self.config_yml_data:
188-
self.config_yml_data["defaults"] = {}
189-
self.config_yml_data["defaults"][key] = value
190-
191-
def update_memory_config(self, updates: Dict[str, Any]):
192-
"""Update memory configuration in config.yml"""
193-
if "memory" not in self.config_yml_data:
194-
self.config_yml_data["memory"] = {}
195-
self.config_yml_data["memory"].update(updates)
196164

197165
def setup_authentication(self):
198166
"""Configure authentication settings"""
@@ -306,8 +274,8 @@ def setup_llm(self):
306274
if api_key:
307275
self.config["OPENAI_API_KEY"] = api_key
308276
# Update config.yml to use OpenAI models
309-
self.update_config_default("llm", "openai-llm")
310-
self.update_config_default("embedding", "openai-embed")
277+
self.config_manager.update_config_defaults({"llm": "openai-llm", "embedding": "openai-embed"})
278+
self.config_yml_data = self.config_manager.get_full_config() # Reload to stay in sync
311279
self.console.print("[green][SUCCESS][/green] OpenAI configured in config.yml")
312280
self.console.print("[blue][INFO][/blue] Set defaults.llm: openai-llm")
313281
self.console.print("[blue][INFO][/blue] Set defaults.embedding: openai-embed")
@@ -317,8 +285,8 @@ def setup_llm(self):
317285
elif choice == "2":
318286
self.console.print("[blue][INFO][/blue] Ollama selected")
319287
# Update config.yml to use Ollama models
320-
self.update_config_default("llm", "local-llm")
321-
self.update_config_default("embedding", "local-embed")
288+
self.config_manager.update_config_defaults({"llm": "local-llm", "embedding": "local-embed"})
289+
self.config_yml_data = self.config_manager.get_full_config() # Reload to stay in sync
322290
self.console.print("[green][SUCCESS][/green] Ollama configured in config.yml")
323291
self.console.print("[blue][INFO][/blue] Set defaults.llm: local-llm")
324292
self.console.print("[blue][INFO][/blue] Set defaults.embedding: local-embed")
@@ -327,7 +295,8 @@ def setup_llm(self):
327295
elif choice == "3":
328296
self.console.print("[blue][INFO][/blue] Skipping LLM setup - memory extraction disabled")
329297
# Disable memory extraction in config.yml
330-
self.update_memory_config({"extraction": {"enabled": False}})
298+
self.config_manager.update_memory_config({"extraction": {"enabled": False}})
299+
self.config_yml_data = self.config_manager.get_full_config() # Reload to stay in sync
331300

332301
def setup_memory(self):
333302
"""Configure memory provider - updates config.yml"""
@@ -347,9 +316,10 @@ def setup_memory(self):
347316
qdrant_url = self.prompt_value("Qdrant URL", "qdrant")
348317
self.config["QDRANT_BASE_URL"] = qdrant_url
349318

350-
# Update config.yml
351-
self.update_memory_config({"provider": "chronicle"})
352-
self.console.print("[green][SUCCESS][/green] Chronicle memory provider configured in config.yml")
319+
# Update config.yml (also updates .env automatically)
320+
self.config_manager.update_memory_config({"provider": "chronicle"})
321+
self.config_yml_data = self.config_manager.get_full_config() # Reload to stay in sync
322+
self.console.print("[green][SUCCESS][/green] Chronicle memory provider configured in config.yml and .env")
353323

354324
elif choice == "2":
355325
self.console.print("[blue][INFO][/blue] OpenMemory MCP selected")
@@ -359,8 +329,8 @@ def setup_memory(self):
359329
user_id = self.prompt_value("OpenMemory user ID", "openmemory")
360330
timeout = self.prompt_value("OpenMemory timeout (seconds)", "30")
361331

362-
# Update config.yml with OpenMemory MCP settings
363-
self.update_memory_config({
332+
# Update config.yml with OpenMemory MCP settings (also updates .env automatically)
333+
self.config_manager.update_memory_config({
364334
"provider": "openmemory_mcp",
365335
"openmemory_mcp": {
366336
"server_url": mcp_url,
@@ -369,7 +339,8 @@ def setup_memory(self):
369339
"timeout": int(timeout)
370340
}
371341
})
372-
self.console.print("[green][SUCCESS][/green] OpenMemory MCP configured in config.yml")
342+
self.config_yml_data = self.config_manager.get_full_config() # Reload to stay in sync
343+
self.console.print("[green][SUCCESS][/green] OpenMemory MCP configured in config.yml and .env")
373344
self.console.print("[yellow][WARNING][/yellow] Remember to start OpenMemory: cd ../../extras/openmemory-mcp && docker compose up -d")
374345

375346
elif choice == "3":
@@ -378,15 +349,16 @@ def setup_memory(self):
378349
mycelia_url = self.prompt_value("Mycelia API URL", "http://localhost:5173")
379350
timeout = self.prompt_value("Mycelia timeout (seconds)", "30")
380351

381-
# Update config.yml with Mycelia settings
382-
self.update_memory_config({
352+
# Update config.yml with Mycelia settings (also updates .env automatically)
353+
self.config_manager.update_memory_config({
383354
"provider": "mycelia",
384355
"mycelia": {
385356
"api_url": mycelia_url,
386357
"timeout": int(timeout)
387358
}
388359
})
389-
self.console.print("[green][SUCCESS][/green] Mycelia memory provider configured in config.yml")
360+
self.config_yml_data = self.config_manager.get_full_config() # Reload to stay in sync
361+
self.console.print("[green][SUCCESS][/green] Mycelia memory provider configured in config.yml and .env")
390362
self.console.print("[yellow][WARNING][/yellow] Make sure Mycelia is running at the configured URL")
391363

392364
def setup_optional_services(self):
@@ -604,10 +576,8 @@ def generate_env_file(self):
604576

605577
self.console.print("[green][SUCCESS][/green] .env file configured successfully with secure permissions")
606578

607-
# Save config.yml with all updates
608-
self.console.print()
609-
self.console.print("[blue][INFO][/blue] Saving configuration to config.yml...")
610-
self.save_config_yml()
579+
# Note: config.yml is automatically saved by ConfigManager when updates are made
580+
self.console.print("[blue][INFO][/blue] Configuration saved to config.yml and .env (via ConfigManager)")
611581

612582
def copy_config_templates(self):
613583
"""Copy other configuration files"""

0 commit comments

Comments
 (0)