Core Concept: Context
What is Context?
Context is the orchestrator that brings together all aiNXT components - Factories, Loaders, Parsers, Datasets, and Models - into a unified, easy-to-use interface. It's your main entry point for creating objects from configuration.
Real-world analogy: Think of Context as the conductor of an orchestra: - The Factories are different instrument sections (strings, brass, woodwinds) - The Parsers are the sheet music translators - The conductor (Context) coordinates everything, ensuring all sections work together harmoniously
Source: ainxt/scripts/context.py
Why Do We Need Context?
Without Context (Complex)
# You'd need to manage everything separately
from ainxt.serving import DATASETS, MODELS, PARSERS, parse_config
# Parse configuration
parsed_dataset_config = parse_config(dataset_config, PARSERS)
parsed_model_config = parse_config(model_config, PARSERS)
# Build objects
dataset = DATASETS.build(**parsed_dataset_config)
model = MODELS.build(**parsed_model_config)
# Pass parsers around everywhere...
With Context (Simple)
# Context manages everything for you
from myapp.context import CONTEXT
# Just load what you need
dataset = CONTEXT.load_dataset(dataset_config)
model = CONTEXT.load_model(model_config)
# Context automatically handles parsing, factory selection, etc.
Creating a Context
Basic Context Setup
from ainxt.scripts.context import Context
from ainxt.serving.serialization import ainxtJSONEncoder, ainxtJSONDecoder
from myapp.serving import DATASETS, MODELS, PARSERS
# Specify your data type
DataType = str # Or Image, np.ndarray, etc.
# Create context
CONTEXT = Context[DataType](
encoder=ainxtJSONEncoder(),
decoder=ainxtJSONDecoder(),
dataset_builder=DATASETS,
model_builder=MODELS,
parsers=PARSERS
)
Real-World Example: DigitalNXT.Vision
# File: DigitalNXT.Vision/context.py
from ainxt.scripts.context import Context
from vision.data import Image
from vision.serving import (
VisionJSONEncoder,
VisionJSONDecoder,
DATASETS,
MODELS,
PARSERS,
METRICS,
VISUALIZATIONS
)
# Vision works with Image data type
CONTEXT = Context[Image](
encoder=VisionJSONEncoder(),
decoder=VisionJSONDecoder(),
dataset_builder=DATASETS,
model_builder=MODELS,
metric_builder=METRICS,
visualization_builder=VISUALIZATIONS,
parsers=PARSERS
)
# Export for use in scripts
__all__ = ("CONTEXT",)
Now in any script:
from context import CONTEXT
# Simple and clean!
dataset = CONTEXT.load_dataset(config["dataset"])
model = CONTEXT.load_model(config["model"])
Context Components
1. Encoders and Decoders
Handle serialization/deserialization of your data type:
# For text data
from ainxt.serving import ainxtJSONEncoder, ainxtJSONDecoder
context = Context[str](
encoder=ainxtJSONEncoder(), # Str → JSON
decoder=ainxtJSONDecoder() # JSON → Str
)
# For custom data types (like images)
context = Context[Image](
encoder=VisionJSONEncoder(), # Image → JSON
decoder=VisionJSONDecoder() # JSON → Image
)
2. Builders (Factories)
Connect your factories to the Context:
from myapp.serving import DATASETS, MODELS, METRICS
context = Context[DataType](
...,
dataset_builder=DATASETS, # For loading datasets
model_builder=MODELS, # For loading models
metric_builder=METRICS, # For loading metrics
visualization_builder=VISUALS # For loading visualizations
)
3. Parsers
Register parsers for configuration transformation:
from myapp.serving import PARSERS
context = Context[DataType](
...,
parsers=PARSERS # Transform config → objects
)
Using Context
Loading Datasets
# Configuration
dataset_config = {
"task": "classification",
"name": "imagenet",
"path": "/data/imagenet",
"augmentation": {
"name": "flip",
"probability": 0.5
}
}
# Load dataset - Context handles parsing and creation
dataset = CONTEXT.load_dataset(dataset_config)
# Dataset is ready to use
for instance in dataset:
print(instance)
What Context does internally:
1. Parse configuration with registered parsers
2. Transform augmentation dict into augmenter object
3. Use dataset_builder (Factory) to create dataset
4. Apply any decorators
5. Return the final dataset
Loading Models
# Configuration
model_config = {
"task": "classification",
"name": "resnet50",
"num_classes": 1000,
"pretrained": True
}
# Load model
model = CONTEXT.load_model(model_config)
# Use model
predictions = model(instance)
Loading Metrics
# Configuration
metrics_config = [
{"name": "accuracy"},
{"name": "precision", "average": "macro"},
{"name": "recall", "average": "macro"}
]
# Load metrics
metrics = CONTEXT.load_metrics(metrics_config, task="classification")
# Use metrics
for metric in metrics:
score = metric(predictions, ground_truth)
print(f"{metric.name}: {score}")
Loading Visualizations
# Configuration
viz_config = [
{"name": "confusion_matrix"},
{"name": "roc_curve"}
]
# Load visualizations
visualizations = CONTEXT.load_visualizations(viz_config, task="classification")
# Create visualizations
for viz in visualizations:
figure = viz(predictions, ground_truth)
figure.savefig(f"{viz.name}.png")
Complete Example: Training Script
# train.py
import yaml
from context import CONTEXT
def main():
# Load configuration
with open("config/experiment.yaml") as f:
config = yaml.safe_load(f)
# Load all components through Context
dataset = CONTEXT.load_dataset(config["dataset"])
model = CONTEXT.load_model(config["model"])
metrics = CONTEXT.load_metrics(config["metrics"], task=config["task"])
# Parse training configuration
from ainxt.serving import parse_config
training_kwargs = parse_config(config["training"], CONTEXT.parsers)
# Train model (optimizer, loss, etc. already parsed!)
model.fit(dataset, **training_kwargs)
# Evaluate
predictions = model(test_dataset)
for metric in metrics:
score = metric(predictions, ground_truth)
print(f"{metric.name}: {score:.3f}")
if __name__ == "__main__":
main()
Configuration file:
# config/experiment.yaml
task: classification
dataset:
task: classification
name: imagenet
path: /data/imagenet
augmentation: # Parsed by Context
name: standard
flip: true
rotate: 15
model:
task: classification
name: resnet50
num_classes: 1000
training:
epochs: 100
batch_size: 32
optimizer: # Parsed by Context
name: adam
learning_rate: 0.001
loss_function: # Parsed by Context
name: cross_entropy
metrics:
- name: accuracy
- name: f1_score
average: macro
Context Benefits
1. Centralized Configuration
All factories and parsers in one place:
# Single source of truth
CONTEXT = Context[DataType](
encoder=...,
decoder=...,
dataset_builder=DATASETS,
model_builder=MODELS,
parsers=PARSERS
)
# Use everywhere
from context import CONTEXT
2. Automatic Parsing
Context automatically applies parsers:
# Configuration with nested objects
config = {
"name": "my_model",
"optimizer": { # Will be parsed automatically
"name": "adam",
"learning_rate": 0.001
}
}
# Context handles parsing
model = CONTEXT.load_model(config)
# model.optimizer is already an Adam object!
3. Type Safety
Generic type parameter ensures consistency:
# Context knows it works with Images
CONTEXT = Context[Image](...)
# Type-safe loading
dataset: Dataset[Instance[Image]] = CONTEXT.load_dataset(config)
model: Model[Instance[Image]] = CONTEXT.load_model(config)
# IDE autocomplete and type checking work!
4. Simplified Scripts
Scripts become cleaner and more readable:
# Old way - verbose
from ainxt.serving import DATASETS, MODELS, PARSERS, parse_config
parsed_config = parse_config(config, PARSERS)
dataset = DATASETS.build(**parsed_config)
# With Context - clean
from context import CONTEXT
dataset = CONTEXT.load_dataset(config)
Multi-Package Context
When extending aiNXT (like DigitalNXT.Vision does), combine contexts:
# vision/serving/singletons.py
from ainxt.serving import (
DATASETS as CORE_DATASETS,
MODELS as CORE_MODELS,
PARSERS as CORE_PARSERS
)
# Extend with vision-specific components
DATASETS = CORE_DATASETS + create_dataset_factory("vision", Task)
MODELS = CORE_MODELS + create_model_factory("vision", Task)
PARSERS = {**CORE_PARSERS, **create_parsers("vision")}
# vision/context.py
CONTEXT = Context[Image](
encoder=VisionJSONEncoder(),
decoder=VisionJSONDecoder(),
dataset_builder=DATASETS, # Has both core + vision datasets
model_builder=MODELS, # Has both core + vision models
parsers=PARSERS # Has both core + vision parsers
)
Now you have access to everything: - Core aiNXT datasets and models - Vision-specific datasets and models - All parsers from both packages
Default Values
Context provides sensible defaults if you don't specify builders:
# Minimal context
context = Context[str](
encoder=ainxtJSONEncoder(),
decoder=ainxtJSONDecoder()
)
# Automatically uses:
# - dataset_builder = ainxt.serving.DATASETS
# - model_builder = ainxt.serving.MODELS
# - metric_builder = ainxt.serving.METRICS
# - visualization_builder = ainxt.serving.VISUALIZATIONS
# - parsers = {} (empty)
This is handled in context.py:111-121:
def __post_init__(self):
if self.dataset_builder is None:
self.dataset_builder = DATASETS
if self.model_builder is None:
self.model_builder = MODELS
# ... etc
Best Practices
1. One Context Per Package
# GOOD - single context for your package
# myapp/context.py
CONTEXT = Context[DataType](...)
# myapp/__init__.py
from myapp.context import CONTEXT
__all__ = ("CONTEXT",)
# Usage
from myapp import CONTEXT
2. Type Your Context
# GOOD - explicit data type
from myapp.data import CustomData
CONTEXT = Context[CustomData](...)
# BAD - no type information
CONTEXT = Context(...) # What data type?
3. Export Context Properly
# GOOD - clear export
# context.py
CONTEXT = Context[Image](...)
__all__ = ("CONTEXT",)
# scripts can import directly
from context import CONTEXT
# BAD - hidden in serving
# serving/singletons.py
CONTEXT = Context[Image](...) # Hard to find!
4. Use Context Methods
# GOOD - use Context methods
dataset = CONTEXT.load_dataset(config)
# AVOID - bypass Context
from myapp.serving import DATASETS, PARSERS, parse_config
parsed = parse_config(config, PARSERS)
dataset = DATASETS.build(**parsed) # Verbose and error-prone
Troubleshooting
Issue 1: "Context has no attribute 'load_X'"
Problem: Typo or missing builder
Solution: Check Context initialization
# Ensure all builders are set
CONTEXT = Context[DataType](
...,
dataset_builder=DATASETS, # Check this exists
model_builder=MODELS, # Check this exists
)
Issue 2: Parsers Not Working
Problem: Parsers not registered with Context
Solution: Pass parsers to Context
# Make sure parsers are included
CONTEXT = Context[DataType](
...,
parsers=PARSERS # Don't forget this!
)
Issue 3: Type Mismatches
Problem: Context type doesn't match data
Solution: Ensure consistent types
# BAD - mismatch
CONTEXT = Context[str](...) # Says str
dataset: Dataset[Image] = CONTEXT.load_dataset(...) # But loads Images
# GOOD - consistent
CONTEXT = Context[Image](...)
dataset: Dataset[Instance[Image]] = CONTEXT.load_dataset(...)
Summary
- Context orchestrates Factories, Loaders, and Parsers
- Simplifies object creation from configuration
- Centralizes all aiNXT components in one place
- Type-safe with generic data type parameter
- Automatic parsing of nested configuration
- Clean scripts with simple, readable code
Context is the glue that makes aiNXT's configuration-driven architecture practical and easy to use.
See Also
- Factory - Object creation system
- Loaders - Automatic component discovery
- Parsers - Configuration transformation
- Datasets and Models - Base classes used by Context