Core Concepts¶
Ginkgo is easiest to understand if you keep one distinction clear:
flows build graphs
tasks produce values when the runtime evaluates those graphs
@task() Produces Deferred Work¶
A function decorated with @task() does not execute immediately when you call
it. Instead, it returns an expression node describing the work to be done.
from ginkgo import task
@task()
def clean_sample(sample_id: str) -> str:
return sample_id.strip().lower()
Calling clean_sample(sample_id="A01") inside a flow does not return the final
string yet. It returns an Expr[str] that can be wired into downstream tasks.
@flow Builds The Initial Graph¶
A @flow function is the workflow entrypoint. Its job is to construct the
initial expression tree.
from ginkgo import flow
@flow
def main():
first = clean_sample(sample_id="A01")
return first
The flow body executes as ordinary Python, but individual tasks remain deferred.
Dynamic DAG Expansion Happens Inside Task Bodies¶
Task bodies receive resolved values at execution time. That means a task can inspect its inputs and return new expressions conditionally.
This is the mechanism behind Ginkgo’s dynamic DAG expansion. It lets workflows branch based on real data without moving orchestration logic into the flow body.
Because the flow body holds no real computation, Ginkgo can build the whole graph before any task runs, then validate it and compute cache keys up front.
.map() Expresses Fan-Out¶
Use partial application plus .map() when one task should run independently for
multiple inputs.
@task()
def qc(sample_id: str, min_length: int) -> str:
return sample_id
results = qc(min_length=8).map(sample_id=["sample_a", "sample_b"])
The result is an ExprList of independent task expressions that Ginkgo can
schedule concurrently.
Use .product_map() when you want the Cartesian product of multiple varying
arguments instead of positional pairing.
@task()
def train(sample_id: str, lr: float) -> str:
return sample_id
results = train().product_map(sample_id=["sample_a", "sample_b"], lr=[0.01, 0.1])
Chained fan-out stays flat: existing branches are the outer loop, and new rows
introduced by .map() or .product_map() are the inner loop.
Path Marker Types Matter¶
Ginkgo uses a few special path-oriented annotations to define runtime behavior:
filefoldertmp_dir
These types influence validation, hashing, artifact handling, and scratch-space lifecycle.
The Runtime Is Local-First¶
Today, Ginkgo’s orchestration logic stays in the local Python process. Graph construction, scheduling, caching decisions, and provenance recording all happen locally. Python task bodies run in a spawned subprocess worker pool; shell, script, and notebook tasks run via Pixi environments or containers.
See Also¶
Tasks and Flows — the full authoring model for Python tasks, shell tasks, and fan-out.