169 lines
5.0 KiB
Python
169 lines
5.0 KiB
Python
"""Configuration management module"""
|
|
import os
|
|
import yaml
|
|
from pathlib import Path
|
|
from typing import Any, Dict, Optional
|
|
|
|
|
|
class Config:
|
|
"""Configuration class (singleton pattern)"""
|
|
|
|
_instance: Optional["Config"] = None
|
|
_config: Dict[str, Any] = {}
|
|
|
|
def __new__(cls):
|
|
if cls._instance is None:
|
|
cls._instance = super().__new__(cls)
|
|
cls._instance._load_config()
|
|
return cls._instance
|
|
|
|
def _load_config(self) -> None:
|
|
"""Load configuration from YAML file"""
|
|
yaml_paths = [
|
|
Path("config.yaml"),
|
|
Path(__file__).parent.parent / "config.yaml",
|
|
Path.cwd() / "config.yaml",
|
|
]
|
|
|
|
for path in yaml_paths:
|
|
if path.exists():
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
self._config = yaml.safe_load(f) or {}
|
|
self._resolve_env_vars()
|
|
return
|
|
|
|
self._config = {}
|
|
|
|
def _resolve_env_vars(self) -> None:
|
|
"""Resolve environment variable references.
|
|
|
|
Supports:
|
|
- ${VAR_NAME} → os.environ.get("VAR_NAME", "")
|
|
- ${VAR_NAME:-default} → os.environ.get("VAR_NAME", "default")
|
|
"""
|
|
import re
|
|
env_pattern = re.compile(r'^\$\{([^}:-]+)(?::-([^}]*))?\}$')
|
|
|
|
def resolve(value: Any) -> Any:
|
|
if isinstance(value, str):
|
|
m = env_pattern.match(value)
|
|
if m:
|
|
var_name = m.group(1)
|
|
default_val = m.group(2) if m.group(2) is not None else ""
|
|
return os.environ.get(var_name, default_val)
|
|
return value
|
|
elif isinstance(value, dict):
|
|
return {k: resolve(v) for k, v in value.items()}
|
|
elif isinstance(value, list):
|
|
return [resolve(item) for item in value]
|
|
return value
|
|
|
|
self._config = resolve(self._config)
|
|
|
|
def get(self, key: str, default: Any = None) -> Any:
|
|
"""Get configuration value, supports dot-separated keys"""
|
|
keys = key.split(".")
|
|
value = self._config
|
|
for k in keys:
|
|
if isinstance(value, dict):
|
|
value = value.get(k)
|
|
else:
|
|
return default
|
|
if value is None:
|
|
return default
|
|
return value
|
|
|
|
# App configuration
|
|
@property
|
|
def secret_key(self) -> str:
|
|
return self.get("app.secret_key", "change-me-in-production")
|
|
|
|
@property
|
|
def debug(self) -> bool:
|
|
return self.get("app.debug", True)
|
|
|
|
@property
|
|
def app_host(self) -> str:
|
|
return self.get("app.host", "0.0.0.0")
|
|
|
|
@property
|
|
def app_port(self) -> int:
|
|
return self.get("app.port", 8000)
|
|
|
|
# Database configuration
|
|
@property
|
|
def database_url(self) -> str:
|
|
return self.get("database.url", "sqlite:///./chat.db")
|
|
|
|
# LLM configuration
|
|
@property
|
|
def llm_api_key(self) -> str:
|
|
return self.get("llm.api_key", "") or os.environ.get("DEEPSEEK_API_KEY", "")
|
|
|
|
@property
|
|
def llm_api_url(self) -> str:
|
|
return self.get("llm.api_url", "https://api.deepseek.com/v1")
|
|
|
|
@property
|
|
def llm_provider(self) -> str:
|
|
return self.get("llm.provider", "deepseek")
|
|
|
|
# Tools configuration
|
|
@property
|
|
def tools_enable_cache(self) -> bool:
|
|
return self.get("tools.enable_cache", True)
|
|
|
|
@property
|
|
def tools_cache_ttl(self) -> int:
|
|
return self.get("tools.cache_ttl", 300)
|
|
|
|
@property
|
|
def tools_max_workers(self) -> int:
|
|
return self.get("tools.max_workers", 4)
|
|
|
|
@property
|
|
def tools_max_iterations(self) -> int:
|
|
return self.get("tools.max_iterations", 10)
|
|
|
|
# Logging configuration
|
|
@property
|
|
def log_level(self) -> str:
|
|
return self.get("logging.level", "INFO")
|
|
|
|
@property
|
|
def log_format(self) -> str:
|
|
return self.get("logging.format", "%(asctime)s | %(levelname)-8s | %(message)s")
|
|
|
|
@property
|
|
def log_date_format(self) -> str:
|
|
return self.get("logging.date_format", "%Y-%m-%d %H:%M:%S")
|
|
|
|
# Workspace configuration
|
|
@property
|
|
def workspace_root(self) -> str:
|
|
return self.get("workspace.root", "./workspaces")
|
|
|
|
@property
|
|
def workspace_auto_create(self) -> bool:
|
|
return self.get("workspace.auto_create", True)
|
|
|
|
# Auth configuration
|
|
@property
|
|
def auth_default_permission_level(self) -> int:
|
|
"""Default permission level for new users (1=READ_ONLY, 2=WRITE, 3=EXECUTE, 4=ADMIN)"""
|
|
return self.get("auth.default_permission_level", 3)
|
|
|
|
@property
|
|
def auth_admin_username(self) -> str:
|
|
"""Default admin username for initial setup"""
|
|
return self.get("auth.admin_username", "admin")
|
|
|
|
@property
|
|
def auth_admin_password(self) -> str:
|
|
"""Default admin password for initial setup"""
|
|
return self.get("auth.admin_password", "admin123")
|
|
|
|
|
|
# Global configuration instance
|
|
config = Config()
|