A Python package for tracing and exporting multi-step PyTorch computational graphs. Annotate your existing code with lightweight markers, and LEAPP captures the graph structure, exports each stage as an individual model, and generates a deployment-ready YAML specification.
LEAPP stands for Lightweight Export Annotations for Policy Pipelines. It is designed for pipelines that chain multiple PyTorch models or processing stages together — where you need to export the whole pipeline, not just a single model.
LEAPP works by tracing real execution of your code. It records which tensors flow between stages, exports each stage independently, and writes a YAML that describes how to wire them back together at inference time. Unlike other solutions, LEAPP is designed to be non-invasive and eliminates the need to have another export-specific implementation of your processing.
- Export to Multiple Formats: TorchScript and ONNX
- Flexible Structuring: Easily define complex node boundaries or multi-node graph structures
- Lightweight: Minimal insertions — no rewrites of existing model code required, annotations safely noop if not exporting
- YAML Specification: Complete metadata for deployment and downstream frameworks
- BYOM (Bring Your Own Model): Integrate pre-compiled models into the graph without recompiling
- Automatic Graph Visualization: Generate a diagram of your pipeline
pip:
pip install leapp
For detailed guides and API reference, see the docs/ directory.
LEAPP operates using traced datatypes. Traced datatypes extend the original datatype and silently add tracing capabilities to them. The result is the ability to define node boundaries explicitly, including across helper functions or when inputs come from multiple call sites.
import torch
import leapp
import env # mock environment setup
from leapp import annotate
def get_obs1_from_env():
obs1 = env.get_from_env('obs')
obs1 = annotate.input_tensors("preprocess", {"obs1_name": obs1}) #inserted annotation
# **some processing**
return obs1
def get_obs2_from_env():
obs2 = env.get_from_env('obs2')
obs2 = annotate.input_tensors("preprocess", {"obs2_name": obs2}) #inserted annotation
# **some processing**
return obs2
leapp.start(name="my_pipeline")
obs1 = get_obs1_from_env()
obs2 = get_obs2_from_env()
# Simple feature construction from the two observation tensors.
obs1_centered = obs1 - obs1.mean()
obs2_scaled = obs2 / (obs2.abs().max() + 1e-6)
features = torch.cat([obs1_centered, obs2_scaled], dim=-1)
annotate.output_tensors("preprocess", {"processed_features": features}, export_with="jit") #inserted annotation
features = annotate.input_tensors("predict", {"features": features}) #inserted annotation
output = torch.relu(featues @ torch.randn(16, 4))
annotate.output_tensors("predict", {"output": output}, export_with="onnx") #inserted annotation
leapp.stop()
leapp.compile_graph()On top of simply tracing inputs and outputs, leapp provides various annotations to accommodate a variety of policies. Please refer to docs/api.md for more details.
Running compile_graph() creates a directory named after your graph containing:
{graph_name}.yaml— complete pipeline specification (node I/O, model paths, data flow){graph_name}.png— visual diagram of the pipeline- Individual model files — one exported model per node (
.pt,.onnx, etc...)
import leapp
from leapp import annotateLifecycle
leapp.start(name, save_path=".", verbose=False, dry_run=False, non_traced=None, max_cached_io=5, global_patching=True)leapp.stop()leapp.compile_graph(visualize=True, verbose=None, validate=True, dry_run=False, rtol=1e-3, atol=1e-5, strict=True)
Annotations
annotate.input_tensors(node_name, tensors)— begin a traced tensor nodeannotate.output_tensors(node_name, tensors, export_with, ...)— finalize a traced tensor nodeannotate.method(export_with, node_name, ...)— decorator, shorthand for annotating i/o with function structureannotate.state_tensors(node_name, tensors)— declare recurrent/state inputsannotate.update_state(node_name, tensors)— update recurrent state outputsannotate.register_buffer(node_name, tensors)— register traced persistent buffers for in-place writesannotate.module(node_name, model, ...)— trace annn.Modulewith buffer trackingannotate.mirror_leapp_tags(source, target)— copy tracing tags between tensors
See docs/api.md for the full reference and docs/ for advanced features.
- Python ≥ 3.8
- PyTorch ≥ 2.6.0
- PyYAML ≥ 6.0
- matplotlib ≥ 3.5.0
- networkx ≥ 2.6
- onnx ≥ 1.19.0
- onnxruntime ≥ 1.20.0
- onnxscript ≥ 0.1.0
- safetensors ≥ 0.5.0
Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
Licensed under the Apache License, Version 2.0.