Models Module¶
signalflow.models is the pinned-inference delivery layer: declarative, versioned references to forecast-model artefacts plus lazy resolution of their weights.
Forecast models are trained elsewhere and arrive in the trading pipeline as versioned, reproducible artefacts. This package keeps the trading pipeline decoupled from training — a reference carries no weights, only enough metadata to resolve the artefact later.
Lazy by design
Importing signalflow.models does not require mlflow. Weights load
only on an explicit resolve / get call.
Overview¶
| Component | Role |
|---|---|
ModelRef |
Declarative, versioned pointer to an artefact (frozen, hashable). |
Resolver (Protocol) |
Port: turn a ModelRef into a loaded model. |
MlflowResolver |
MLflow-backed Resolver (lazy mlflow import). |
ModelRegistry (Protocol) |
Port: fetch loaded models by ModelRef. |
CachingModelRegistry |
In-process registry that resolves once and caches. |
from signalflow.models import (
ModelRef,
Resolver,
MlflowResolver,
ModelRegistry,
CachingModelRegistry,
)
ModelRef¶
A ModelRef is a frozen dataclass: ModelRef(name, version, source="mlflow").
name— registered model name (non-empty).version— mandatory. Usually a numeric string/int ("3").source— backing registry, one of{"mlflow", "hf"}(default"mlflow").
Why version is mandatory¶
A floating version="latest" silently breaks parity and reproducibility
between training and live inference, so it is rejected unless the
environment variable SF_ALLOW_LATEST=1 is set (dev opt-in only).
Parsing¶
ModelRef.parse(spec, *, source="mlflow") accepts two compact forms:
from signalflow.models import ModelRef
ModelRef.parse("models:/revert/3") # -> ModelRef(name='revert', version='3', source='mlflow')
ModelRef.parse("revert@3") # -> ModelRef(name='revert', version='3', source='mlflow')
ModelRef.parse("revert@3", source="hf") # at-spec uses the given source
models:/<name>/<version>always forcessource="mlflow".<name>@<version>uses thesourceargument.
URI¶
Resolver¶
Resolver is a runtime_checkable Protocol with one method:
MlflowResolver(tracking_uri=None) is the MLflow-backed implementation.
Loading is fully lazy: mlflow is imported only inside resolve, and the
underlying loader is isolated in _load so tests can override it without a
real MLflow server. If tracking_uri is set it is applied on the first
resolve call. resolve raises ValueError if ref.source != "mlflow".
from signalflow.models import ModelRef, MlflowResolver
resolver = MlflowResolver(tracking_uri="http://mlflow:5000")
model = resolver.resolve(ModelRef.parse("models:/revert/3")) # loads weights here
ModelRegistry¶
ModelRegistry is the consumer-facing Protocol:
def get(self, ref: ModelRef) -> Any: ... # resolve lazily if needed
def has(self, ref: ModelRef) -> bool: ... # already cached?
CachingModelRegistry(resolver) is a simple, lazy, in-process implementation.
It holds a Resolver and a cache keyed by ModelRef (frozen → hashable). The
first get for a ref triggers resolution; subsequent calls return the cached
artefact without re-resolving. has never triggers resolution.
from signalflow.models import ModelRef, MlflowResolver, CachingModelRegistry
registry = CachingModelRegistry(MlflowResolver())
ref = ModelRef.parse("models:/revert/3")
registry.has(ref) # False
model = registry.get(ref) # cache miss -> resolves and caches
registry.has(ref) # True
model is registry.get(ref) # True (served from cache)
See Also¶
- Model Integration guide — registering forecast artefacts in a flow via
.forecast()and consuming them withforecasts=/forecast_window=. - Feature API —
FeatureSpec/ModelFeaturesPipelineand thefeature_hashdrift detector that guards train↔serve reproducibility.
API Reference¶
signalflow.models.model_ref.ModelRef
dataclass
¶
A pinned, versioned reference to a model artifact.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Registered model name (e.g. |
version |
str | int
|
Mandatory version. Usually a numeric string/int ( |
source |
str
|
Backing registry, one of |
Construction is cheap and never touches the network or weights — use a
:class:~signalflow.models.resolver.Resolver to actually load the model.
Example
ModelRef.parse("models:/revert/3") ModelRef(name='revert', version='3', source='mlflow') ModelRef.parse("revert@3") ModelRef(name='revert', version='3', source='mlflow')
__post_init__ ¶
Source code in src/signalflow/models/model_ref.py
parse
classmethod
¶
Parse a ModelRef from a compact string spec.
Supported forms
- MLflow URI:
"models:/<name>/<version>"(forcessource="mlflow"). - At-spec:
"<name>@<version>"(uses thesourceargument).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spec
|
str
|
The string to parse. |
required |
source
|
str
|
Source to use for non-URI specs. Default |
'mlflow'
|
Returns:
| Type | Description |
|---|---|
ModelRef
|
A validated ModelRef. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the spec is malformed or violates ModelRef invariants. |
Source code in src/signalflow/models/model_ref.py
signalflow.models.resolver.Resolver ¶
Bases: Protocol
Port: resolve a ModelRef into a loaded, ready-to-use model object.
signalflow.models.resolver.MlflowResolver ¶
Resolver backed by the MLflow Model Registry.
Loading is fully lazy: mlflow is imported only when :meth:resolve is
called, and the underlying loader is isolated in :meth:_load so tests can
override it without a real MLflow server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tracking_uri
|
str | None
|
Optional MLflow tracking URI. If set, applied on the first resolve call. |
None
|
Source code in src/signalflow/models/resolver.py
_load ¶
Load a model from an MLflow models:/ URI (lazy mlflow import).
Overridable for testing. Uses the generic pyfunc loader so any model flavor registered under the URI can be loaded.
Source code in src/signalflow/models/resolver.py
resolve ¶
Resolve ref to a loaded MLflow model.
Builds the URI models:/{ref.name}/{ref.version} and delegates the
actual load to :meth:_load.
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/signalflow/models/resolver.py
signalflow.models.registry.ModelRegistry ¶
signalflow.models.registry.CachingModelRegistry ¶
In-process registry that lazily resolves and caches models by ModelRef.
Holds a :class:Resolver and a cache keyed by ModelRef (which is frozen and
hashable). The first :meth:get for a ref triggers resolution; subsequent
calls return the cached artifact without re-resolving.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resolver
|
Resolver
|
The Resolver used to load uncached refs. |
required |
Source code in src/signalflow/models/registry.py
get ¶
Return the model for ref, resolving and caching on first access.