All docs
Examples
Example: live node metrics
Show live row and column counts on a node card with graph_outputs and ctx.set_output, plus progress reporting via StepContext.progress.
TL;DRDeclare graph_outputs={"rows": int, "cols": int} on a step and set them from inside run() with ctx.set_output(...). They render as read-only scalars on the node card, are excluded from the cache key, and StepContext.progress reports a progress bar — all cache-neutral side channels.
A node card can show live numbers — rows processed, a computed score — without those values ever
affecting caching. Declare them as graph_outputs, set them with ctx.set_output(...), and report
progress with ctx.progress(...).
The code#
Python
# tool.py
from lsdtools import Tool, StepContext, Table
tool = Tool("metrics", label="Metrics")
@tool.load
def demo(n: int = 100) -> Table:
"""A table to measure."""
return Table({"i": list(range(n)), "sq": [i * i for i in range(n)]})
@tool.shape(graph_outputs={"rows": int, "cols": int})
def summarize(t: Table, ctx: StepContext) -> Table:
"""Pass the table through unchanged, reporting its size to the node card."""
ctx.progress(0.3, "measuring")
ctx.set_output("rows", len(t))
ctx.set_output("cols", len(t.columns))
ctx.progress(1.0, "done")
return t
What each line does#
graph_outputs={"rows": int, "cols": int}— declares two read-only scalar outputs. They show on the node card, ride along in the step'sSTEP_FINISHEDevent, and are excluded from the cache key — setting them never invalidates a cached run.ctx: StepContext— an injected capability object. It is cache-neutral (progress, cancel, scratch dir,set_output) — unlike a fullContext, which a step may not read. Injected parameters never appear as form fields.ctx.progress(fraction, message)— reports a[0, 1]progress fraction; the desktop drives a progress bar from it. Best-effort — it never fails the step.ctx.set_output("rows", len(t))— records a graph-output value.len(t)is the row count;len(t.columns)the column count.- The step returns
tunchanged — the metrics are a pure side channel.
Run it in Python#
Python
from lsdtools import Engine
s = summarize(demo(n=100))
res = Engine().run(s)
res.output.print() # → the table, unchanged (100 rows)
print(s.rows, s.cols) # → 100 2
After the run, the graph outputs are readable straight off the step instance (s.rows, s.cols) —
the same values the desktop paints on the node card.
Try changing it#
- Add a
mean_sq: floatgraph output:ctx.set_output("mean_sq", ...)from agroup_byaggregate. - Check
ctx.cancelledinside a loop to bail out early on a long run. - Write a scratch file under
ctx.scratch_dirfor intermediate debugging artifacts.
Related#
Learn — Multiple outputs · Shape steps
API — Context · Table · Engine
Examples — Multi-output mesh · A dockable panel
By LSD Team · Last updated Jul 17, 2026
Ask on Discord
View as Markdown