Mindtrace Core
The foundational module of the Mindtrace ML framework, providing essential utilities, base classes, and core abstractions used across all other Mindtrace modules.
Purpose
mindtrace-core serves as the foundation layer (Level 1) in the Mindtrace architecture, offering:
- Base Classes: Abstract base classes and metaclasses for consistent architecture
- Configuration Management: Centralized configuration handling
- Event System: Observable patterns and event bus for inter-component communication
- Utilities: Common utility functions for dynamic imports, type checking, and more
- Logging: Structured logging capabilities
Installation
# Install as standalone package
uv add mindtrace-core
# Or install as part of full Mindtrace
uv add mindtrace
Architecture
The core module is organized into several submodules:
Base Classes (base/)
MindtraceABC: Abstract base class for all Mindtrace componentsMindtraceMeta: Metaclass providing common functionalityMindtrace: Main base class with core functionality
Configuration (config/)
Config: Centralized configuration management system
Observables (observables/)
EventBus: Publish-subscribe event systemObservableContext: Context management with observation capabilitiesContextListener: Event listening and handling
Utilities (utils/)
checks: Type checking and validation utilitiesdynamic: Dynamic class instantiation and import helpers
Logging (logging/)
- Structured logging configuration and utilities
Core Classes
Mindtrace
Base class for all Mindtrace components.
Config
The Config class in Mindtrace provides a configuration layer designed to unify various sources of configuration— Pydantic models, Settings, Dict objects in a single, easy-to-use object with attribute access, and dynamic overrides.
from mindtrace.core.config import Config
# Pass a dictionary
config = Config({"MINDTRACE_DIR_PATHS":{"TEMP":"~/tmp"}})
# Access config values
print(config["MINDTRACE_DIR_PATHS"]["TEMP"])
# Attribute-style access
print(config.MINDTRACE_DIR_PATHS.TEMP)
For detailed usage of the Config class—including how it's used within the Mindtrace class—refer to the Usage documentation
Logging
The get_logger function provides a unified way to configure logging across your application. It can return either a standard Python logger or a structlog
logger, based on user-defined arguments or below CoreConfig settings (lower priority).
[MINDTRACE_DIR_PATHS]
STRUCT_LOGGER_DIR = ${MINDTRACE_DIR_PATHS:ROOT}/structlogs
LOGGER_DIR = ${MINDTRACE_DIR_PATHS:ROOT}/logs
[MINDTRACE_LOGGER]
USE_STRUCTLOG = False
Basic Logger
Setup basic logger to produce logs in ~/.cache/mindtrace/logs.
Here, by default propogation is set to true, and you should be able to see logs in tail -f ~/.cache/mindtrace/logs/mindtrace.log and tail -f ~/.cache/mindtrace/logs/modules/mindtrace.core.module.log files
from mindtrace.core.logging.logger import get_logger
# Create a standard logger
logger = get_logger("core.module")
logger.info("Logger configured with custom settings.")
Structured Logger
Setup structlog to log structured events: dictionaries of key-value pairs that can later be searched, filtered, or transformed (e.g., into JSON).
Here, by default propogation is set to true, and you should be able to see structured logs in tail -f ~/.cache/mindtrace/structlogs/mindtrace.log and tail -f ~/.cache/mindtrace/structlogs/modules/mindtrace.core.module.log files
from mindtrace.core.logging.logger import get_logger
# Create a structlog logger with custom bindings
slogger = get_logger(
"core.module",
use_structlog=True,
structlog_bind={"service": "my-service"},
)
slogger.info("Structured log", user_id="123")
Mindtrace autolog
Mindtrace.autolog automatically logs function execution (start, end, duration, exceptions, and optional system metrics).
It supports sync, async, and static functions with both standard and structured logging formats. See a full usage example here
from mindtrace.core import Mindtrace
class DataProcessor(Mindtrace):
@Mindtrace.autolog()
def process_data(self, data_list, batch_size=100):
# Function automatically logged
return [item * 2 for item in data_list]
Observables
The Observables module enables lightweight observability and reactivity for class objects, automatically turning selected properties into observable variables. This framework allows external components (listeners) to be notified whenever specific values change, without hard-coding the coupling between the source and observers.
There are three main classes included in the observables module:
1. EventBus
The EventBus is a lightweight internal publish-subscribe system for event dispatching.
API:
Event buses expose three main methods, which may be used to subscribe/unsubscribe individual listeners and emit event messages.
subscribe(handler: Callable, event_name: str) -> str
unsubscribe(handler_or_id: Union[str, Callable], event_name: str)
emit(event_name: str, **kwargs)
Example Usage: To use an event bus, subscribe a handler to the bus with an associated event name. The handler will be called any time the event name is emitted.
from mindtrace.core import EventBus
bus = EventBus()
def handler(**kwargs):
print(kwargs)
bus.subscribe(handler, "event")
bus.emit("event", x="1", y="2") # {'x': '1', 'y': '2'}
bus.unsubscribe(handler, "event")
bus.emit("event", x="1", y="2") # No output
2. ObservableContext
The ObservableContext class decorator automatically turns specified properties into observable fields and wires up listener support.
The ObservableContext class supports two specific event types, with associated event names:
context_updated(source: str, var: str, old: any, new: any): May be used when any observed variable changes. The name of the variable will be given as thevarargument, with associated old and new values.{var}_updated(source: str, old: any, new: any): May be used to listen to specific variables.
API:
The ObservableContext decorator adds subscribe and unsubscribe methods onto a wrapped class, which may be used directly analogously to with the EventBus.
Example Usage:
When subscribing a class-derived listener, define a context_updated method which will be notified anytime any observable variable is updated, or specific {var}_updated methods, which will be notified when the associated variable is updated.
class MyListener:
def context_updated(self, source, var, old, new): # May be omitted, `{var}_updated` methods will be called automatically
if var == "x":
return self.x_updated(source, old, new)
elif var == "y":
return self.y_updated(source, old, new)
def x_updated(...): ...
def y_updated(...): ...
Listeners may subscribe to any class that has been decorated with the ObservableContext decorator, listening to any of the listed vars in the decorator.
from mindtrace.core import ObservableContext
@ObservableContext(vars=["x", "y"])
class MyContext:
def __init__(self):
self.x = 0
self.y = 0
class MyListener:
def x_changed(self, source, old, new):
print(f"[{source}] x changed from {old} to {new}")
my_context = MyContext()
my_context.subscribe(MyListener())
my_context.x = 1 # [MyContext] x changed from 0 to 1
3. ContextListener(Mindtrace)
The ContextListener class is a helper class for defining observers that respond to context changes. This class is meant to provide two benefits: (1) deriving from the Mindtrace base class, it provides for uniform logging of events and (2) the default ContextListener class can be used to automatically log changes to variables, optionally with a custom logger.
Example usage:
from mindtrace.core import ContextListener, ObservableContext
@ObservableContext(vars={"x": int, "y": int})
class MyContext:
def __init__(self):
self.x = 0
self.y = 0
my_context = MyContext()
my_context.subscribe(ContextListener(autolog=["x", "y"], logger=...)) # May provide custom logger if desired
my_context.x = 1
my_context.y = 2
# Logs:
# [MyContext] x changed: 0 → 1
# [MyContext] y changed: 0 → 2
Utility Functions
check_libs(*libs)
Verify required libraries are installed.
ifnone(value, default)
Return default if value is None.
instantiate_target(target, **kwargs)
Dynamically instantiate a class from string reference.