Add core service infrastructure

- NATS event bus (pub/sub, JetStream, KV storage)
- Service registry with health monitoring
- Base service class with lifecycle management
- Config system
- Logger with Vi formatting

Adapted from Lyra's patterns, namespace changed to vi.*

🦊💕
This commit is contained in:
Alex Kazaiev
2026-01-02 13:04:26 -06:00
parent c891740d7c
commit e2d24a66f1
8 changed files with 1321 additions and 0 deletions

94
core/config.py Normal file
View File

@@ -0,0 +1,94 @@
import json
import os
from pathlib import Path
from typing import Dict, Any
class Config:
_instance = None
_config_data = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if self._config_data is None:
self._load_config()
def _load_config(self):
config_path = os.path.join(os.path.dirname(__file__), '..', 'config', 'config.json')
config_path = os.path.abspath(config_path)
try:
with open(config_path, 'r') as f:
self._config_data = json.load(f)
except FileNotFoundError:
# Default config if file doesn't exist
self._config_data = {
'nats': {
'url': 'nats://localhost:4222',
'connection_timeout': 30,
'max_reconnect_attempts': 10
}
}
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in configuration file: {e}")
def get(self, key: str, default: Any = None) -> Any:
keys = key.split('.')
value = self._config_data
for k in keys:
if isinstance(value, dict) and k in value:
value = value[k]
else:
return default
return value
@property
def nats_url(self) -> str:
return os.getenv('NATS_URL') or self.get('nats.url', 'nats://localhost:4222')
@property
def nats_connection_timeout(self) -> int:
return self.get('nats.connection_timeout', 30)
@property
def nats_max_reconnect_attempts(self) -> int:
return self.get('nats.max_reconnect_attempts', 10)
def get_local_services(self) -> list:
"""Get list of services that should run on this node"""
systemd_services = self.get('auto_upgrade.systemd_services', [])
local_services = []
for systemd_service in systemd_services:
service_name = systemd_service.replace('vi-', '').replace('.service', '')
local_services.append(service_name)
return local_services
def is_service_enabled(self, service_name: str) -> bool:
"""Check if a service is enabled on this node"""
return service_name in self.get_local_services()
@property
def project_root(self) -> Path:
"""Get the project root directory"""
return Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@property
def data_dir(self) -> Path:
"""Get the data directory for databases"""
data_path = os.getenv('DATA_DIR', str(self.project_root / 'data'))
data_dir = Path(data_path)
data_dir.mkdir(exist_ok=True, parents=True)
return data_dir
config = Config()
# Database paths
SHORT_TERM_DB = config.data_dir / 'memory_active.db'
LONG_TERM_DB = config.data_dir / 'memory_archive.db'