Add getall/getrawall for nested params, ROOT_KEY constant, tests and docs

This commit is contained in:
2026-04-15 20:04:04 +03:00
parent b8f34faff7
commit 3beccbf35e
6 changed files with 235 additions and 49 deletions

View File

@@ -12,6 +12,10 @@ config = src.utils.config_manager.config
- `config.register(name, val, default, desc, cat, env, validator)` - register a parameter
- `config.get(name, cat)` - get parameter value
- `config.set(name, value, cat)` - set parameter value (triggers validator if present)
- `config.getraw(key)` - get by full key path (no cat substitution)
- `config.setraw(key, value)` - set by full key path
- `config.getall(name, cat)` - get root + all nested params as dict with `ROOT_KEY`
- `config.getrawall(cat)` - get all params under category as nested dict
- `config.get_parameter(name, cat)` - get ConfigParameter object
- `config.get_description(name, cat)` - get parameter description
- `config.load(path?)` - load from file or env
@@ -31,6 +35,19 @@ On failure: logs error via `logger.error()` and raises exception. Value unchange
- `DEFAULT_CONFIG_FILE = "config/global.yaml"`
- `DEFAULT_CONFIG_ENV = "KOMAI_CONFIG_FILE"`
- `DEFAULT_CATEGORY = "global"`
- `ROOT_KEY = "$root$"` - key name for root value in nested getall/getrawall results
## Nested Parameters
Params with dots in name create nested structures:
```python
config.register(name="coder", val="llama-coder", cat="models")
config.register(name="coder.thinking", val="full", cat="models")
config.get("coder", cat="models") # "llama-coder"
config.getall("coder", cat="models") # {"$root$": "llama-coder", "thinking": "full"}
config.getrawall("models") # {"coder": {"$root$": "llama-coder", "thinking": "full"}}
```
## ConfigParameter Properties
@@ -40,6 +57,6 @@ On failure: logs error via `logger.error()` and raises exception. Value unchange
## Implementation Notes
- Single global instance created at module import (`config` object in `__init__.py`)
- All `__init__.py` files re-export only `config`
- `ROOT_KEY` exported from module for custom notation if needed
- Validator called via property setter on `_val` attribute
- `reset()` re-creates params dict and reloads from file

View File

@@ -7,6 +7,7 @@ from typing import Callable, Optional
DEFAULT_CONFIG_FILE = "config/global.yaml"
DEFAULT_CONFIG_ENV = "KOMAI_CONFIG_FILE"
DEFAULT_CATEGORY = "global"
ROOT_KEY = "$root$"
logger = logging.getLogger(__name__)
@@ -121,32 +122,97 @@ class ConfigManager:
self._params[key] = param
return param
def get(self, name: str, cat: str = DEFAULT_CATEGORY) -> Optional[str]:
def get(self, name: str, cat: str = None) -> Optional[str]:
cat = cat or DEFAULT_CATEGORY
key = f"{cat}.{name}"
param = self._params.get(key)
if param is None:
return None
return param.val
def get_description(self, name: str, cat: str = DEFAULT_CATEGORY) -> Optional[str]:
def getraw(self, key: str):
param = self._params.get(key)
if param is None:
return None
return param.val
def get_description(self, name: str, cat: str = None) -> Optional[str]:
cat = cat or DEFAULT_CATEGORY
key = f"{cat}.{name}"
param = self._params.get(key)
if param is None:
return None
return param.desc
def get_parameter(
self, name: str, cat: str = DEFAULT_CATEGORY
) -> Optional[ConfigParameter]:
def get_parameter(self, name: str, cat: str = None) -> Optional[ConfigParameter]:
cat = cat or DEFAULT_CATEGORY
key = f"{cat}.{name}"
return self._params.get(key)
def set(self, name: str, value, cat: str = DEFAULT_CATEGORY) -> None:
def set(self, name: str, value, cat: str = None) -> None:
cat = cat or DEFAULT_CATEGORY
key = f"{cat}.{name}"
param = self._params.get(key)
if param is not None:
param.val = value
def setraw(self, key: str, value) -> None:
param = self._params.get(key)
if param is not None:
param.val = value
def getall(self, name: str, cat: str = None) -> Optional[dict]:
cat = cat or DEFAULT_CATEGORY
root_key = f"{cat}.{name}"
root_param = self._params.get(root_key)
if root_param is None:
return None
result = {ROOT_KEY: root_param.val}
prefix = root_key + "."
for key, param in self._params.items():
if key.startswith(prefix):
nested_key = key[len(prefix) :]
parts = nested_key.split(".")
if len(parts) == 1:
result[parts[0]] = param.val
else:
current = result
for part in parts[:-1]:
if part not in current:
current[part] = {}
current = current[part]
current[parts[-1]] = param.val
return result
def getrawall(self, key: str) -> Optional[dict]:
if not key.endswith("."):
key = key + "."
result = {}
for param_key, param in self._params.items():
if not param_key.startswith(key):
continue
nested_key = param_key[len(key) :]
parts = nested_key.split(".")
if len(parts) == 1:
result[parts[0]] = param.val
else:
current = result
for i, part in enumerate(parts[:-1]):
if part not in current:
current[part] = {}
elif isinstance(current[part], str):
current[part] = {ROOT_KEY: current[part]}
current = current[part]
current[parts[-1]] = param.val
return result if result else None
def load(self, path: str = None) -> dict:
if path:
self._config_path = Path(path)