Skip to content

User Tasks

A Task defines custom logic for a single train or inference step.

Each Task may implement Stateful protocol, so you may store some mutable state here.

TrainTask

It is responsible for logging metrics, mapping batch inputs before they are fed into the model, and for computing the task loss function value.

Init: create_metrics(...), dump_hparams(...).

Lifecycle:

  1. build_forward_inputs(...) (will be called once) ->
  2. compute_loss(...) (will be called multiple times if pipelining is enabled - once for each pipeline microbatch) ->
  3. update_metrics(...) (will be called once).

Exit: finalize(...).

State Management: state_dict(...), load_state_dict(...).

Events Registration: register_events(...) allows you to link specific custom methods to framework-wide Event Hooks.

InferenceTask

The InferenceTask defines the logic for a single inference step.

It is designed to handle the forward-only flow, processing the raw tensors synthesized by the model (e.g., logits, hidden states).

Lifecycle:

  1. build_forward_inputs(...) (called once) ->
  2. process_outputs(...) (called once per pipeline microbatch).

Exit: finalize(...).

State Management: state_dict(...), load_state_dict(...).

Events Registration: register_events(...) allows hooking into the Event Bus alongside regular execution.

Task State

You may note that batch is only accessible in build_forward_inputs(...), but not in the later stages. Don't worry!

The state carries side-data from build_forward_inputs to the later stages of the same microbatch — labels, masks, token counts, or anything else the model forward does not return but the loss or metrics need.

It is declared by TState, the last type parameter of TrainTask / InferenceTask. TState is any PyTree, so you can use whatever shape fits:

  • a dataclass — when you want attribute access and strict typing;
  • a TypedDict — when you prefer dict access but still want keys checked;
  • a plain dict — for quick, untyped side-data;
  • None — for tasks that carry nothing.

build_forward_inputs returns the state; compute_loss / process_outputs / update_metrics read it back, fully typed.

1
2
3
4
5
6
7
8
class MyState(TypedDict):
    target: torch.Tensor

# in build_forward_inputs:
return BuildForwardInputsResult(input=..., shared=..., state=MyState(target=ctx.batch["target"]))

# later, in update_metrics / compute_loss:
metrics["accuracy"].update(ctx.state["target"])  # ctx.state is typed as MyState

Tensors stored in the state are detached from the autograd graph automatically, so caching them across the pipeline never keeps the graph alive.

Example Implementation

A task's IO is typed by the same four PyTree roles the model pipeline uses (see Pipeline Parallelism). TrainTask is generic over [TBatch, TPipelineInput, TSharedInput, TPipelineOutput, TState]:

  • TPipelineInput — the PipelineInput fed to the first stage (built here).
  • TSharedInput — the SharedInput broadcast to every stage.
  • TPipelineOutput — the PipelineOutput produced by the last stage, read in compute_loss; for a single-head model this is that head's output, and for a model composed with several named heads it is each head's output keyed by head name.

build_forward_inputs returns a BuildForwardInputsResult with input / shared / state fields — no dict keys.

import torch
from typing import TypedDict

from d9d.core.dist_context import DistributedContext
from d9d.core.types import ScalarTree
from d9d.module.block.head import LM_IGNORE_INDEX, SequenceCausalLMHeadShared, SequenceCausalLMOutput
from d9d.module.model.io import SequenceHeadShared, SequenceInput, SequenceShared
from d9d.loop.control import *


class SFTState(TypedDict):  # it also could be a dataclass
    labels: torch.Tensor


class SFTTask(
    TrainTask[
        dict[str, torch.Tensor],
        SequenceInput,
        SequenceHeadShared[SequenceCausalLMHeadShared],
        SequenceCausalLMOutput,
        SFTState,
    ]
):
    def __init__(self, dist_ctx: DistributedContext):
        self._dist_ctx = dist_ctx

    def build_forward_inputs(
        self, ctx: BuildForwardInputsContext
    ) -> BuildForwardInputsResult[SequenceInput, SequenceHeadShared[SequenceCausalLMHeadShared], SFTState]:
        # ctx.batch contains the output of the Collator.

        # Return the PipelineInput, the SharedInput and the typed
        # side-data carried to loss computation for this same microbatch.
        # The SharedInput routes position ids to the backbone and labels to the head.
        return BuildForwardInputsResult(
            input=SequenceInput(input_ids=ctx.batch["input_ids"]),
            shared=SequenceHeadShared(
                sequence=SequenceShared(position_ids=ctx.batch["position_ids"]),
                head=SequenceCausalLMHeadShared(labels=ctx.batch["labels"]),
            ),
            state=SFTState(labels=ctx.batch["labels"]),
        )

    def dump_hparams(self) -> ScalarTree:
        return super().dump_hparams()

    def compute_loss(self, ctx: ComputeLossContext[SequenceCausalLMOutput, SFTState]) -> ComputeLossResult:
        # Retrieve log_probs calculated by the model pipeline
        logps = ctx.pipeline_results.logps

        # Calculate number of valid tokens (ignoring the -100 padding)
        # This is crucial for variable length batches.
        num_loss_tokens = (ctx.state["labels"] != LM_IGNORE_INDEX).sum()

        # Calculate average loss per valid token
        total_loss = logps.sum() / num_loss_tokens

        return ComputeLossResult(
            loss=total_loss,
            # loss_weight is used for gradient accumulation across the distributed world.
            # If batches have different token counts, we weigh the gradient
            # by token count to get a mathematical true average over the accumulation steps.
            loss_weight=num_loss_tokens / 1000
        )

d9d.loop.control.task

BaseTask

Bases: ABC, Stateful, Generic[TBatch, TPipelineInput, TSharedInput, TState]

Abstract base class representing a unit of work (Task) in the training/inference loop.

Class Type Parameters:

Name Bound or Constraints Description Default
TBatch

The raw microbatch type produced by the data stream.

required
TPipelineInput

The PipelineInput fed to the first pipeline stage.

required
TSharedInput

The SharedInput fed to every pipeline stage.

required
TState

The per-microbatch side-data carried from input building to loss/output processing. Tasks that carry nothing use None.

required

build_forward_inputs(ctx) abstractmethod

Transforms one raw microbatch into arguments for the model.

Called once per microbatch in the step's pack.

Parameters:

Name Type Description Default
ctx BuildForwardInputsContext[TBatch]

Context object.

required

Returns:

Type Description
BuildForwardInputsResult[TPipelineInput, TSharedInput, TState]

Result object, including the state side-data carried to loss/output processing.

finalize(ctx)

Performs cleanup or final actions when the task execution finishes.

Parameters:

Name Type Description Default
ctx FinalizeContext

Context object.

required

load_state_dict(state_dict)

Restores the task's state from the provided dictionary.

Parameters:

Name Type Description Default
state_dict dict[str, Any]

The state dictionary to load.

required

register_events(context)

Register task-specific event subscriptions.

Parameters:

Name Type Description Default
context RegisterTaskEventsContext

Context providing access to the distributed environment and the event bus.

required

state_dict()

Returns the state dictionary for checkpointing this task.

Returns:

Type Description
dict[str, Any]

A dictionary containing the task's state.

BuildForwardInputsContext dataclass

Bases: Generic[TBatch]

Context data to prepare inputs for the model forward pass of a single microbatch.

Attributes:

Name Type Description
batch TBatch

One raw microbatch of data produced by the data stream.

BuildForwardInputsResult dataclass

Bases: Generic[TPipelineInput, TSharedInput, TState]

The result of processing one raw microbatch into model inputs.

Attributes:

Name Type Description
input TPipelineInput

The PipelineInput passed to the model pipeline as input data (first stage only if using pipeline parallelism).

shared TSharedInput

The SharedInput passed to every pipeline stage.

state TState

Side-data (a PyTree, e.g. a TypedDict) carried to loss/output processing for this same microbatch — labels, masks, anything the model forward does not return but the loss needs.

ComputeLossContext dataclass

Bases: Generic[TPipelineOutput, TState]

Context data provided to calculate the loss during training.

Attributes:

Name Type Description
pipeline_results TPipelineOutput

The PipelineOutput returned by the model's forward pass.

state TState

The side-data this microbatch's build_forward_inputs returned.

schedule JobSchedule

Component tracking the current step.

ComputeLossResult dataclass

The result of the loss computation.

Attributes:

Name Type Description
loss Tensor

The scalar tensor representing the loss to be backpropagated.

loss_weight Tensor | None

The weight to apply to the loss (for synchronizing gradients using weighted mean). None for 1.0.

CreateMetricsContext dataclass

Context data provided to initialize metrics.

CreateMetricsResult dataclass

Result of metric initialization.

Attributes:

Name Type Description
metrics dict[str, Metric]

A dictionary mapping metric names to Metric instances.

FinalizeContext dataclass

Context data provided when the task is being finalized.

InferenceTask

Bases: BaseTask[TBatch, TPipelineInput, TSharedInput, TState], ABC, Generic[TBatch, TPipelineInput, TSharedInput, TPipelineOutput, TState]

Abstract base class for defining inference-specific logic.

build_forward_inputs(ctx) abstractmethod

Transforms one raw microbatch into arguments for the model.

Called once per microbatch in the step's pack.

Parameters:

Name Type Description Default
ctx BuildForwardInputsContext[TBatch]

Context object.

required

Returns:

Type Description
BuildForwardInputsResult[TPipelineInput, TSharedInput, TState]

Result object, including the state side-data carried to loss/output processing.

finalize(ctx)

Performs cleanup or final actions when the task execution finishes.

Parameters:

Name Type Description Default
ctx FinalizeContext

Context object.

required

load_state_dict(state_dict)

Restores the task's state from the provided dictionary.

Parameters:

Name Type Description Default
state_dict dict[str, Any]

The state dictionary to load.

required

process_outputs(ctx) abstractmethod

Processes the model outputs (e.g. saving to disk, decoding tokens).

Parameters:

Name Type Description Default
ctx ProcessOutputsContext[TPipelineOutput, TState]

Context containing the model outputs and pipeline state.

required

register_events(context)

Register task-specific event subscriptions.

Parameters:

Name Type Description Default
context RegisterTaskEventsContext

Context providing access to the distributed environment and the event bus.

required

state_dict()

Returns the state dictionary for checkpointing this task.

Returns:

Type Description
dict[str, Any]

A dictionary containing the task's state.

InferenceTaskProvider

Bases: Protocol

Protocol for a callable that creates an InferenceTask instance.

__call__(ctx)

Creates and returns a new InferenceTask.

Parameters:

Name Type Description Default
ctx InferenceTaskProviderContext

Context providing distributed environment information.

required

Returns:

Type Description
InferenceTask

An instantiated InferenceTask.

InferenceTaskProviderContext dataclass

Context data provided to the factory creating an InferenceTask.

Attributes:

Name Type Description
dist_context DistributedContext

Information about the distributed environment.

ProcessOutputsContext dataclass

Bases: Generic[TPipelineOutput, TState]

Context data provided to process outputs during inference.

Attributes:

Name Type Description
pipeline_results TPipelineOutput

The PipelineOutput returned by the model's forward pass.

state TState

The side-data this microbatch's build_forward_inputs returned.

RegisterTaskEventsContext dataclass

Context for registering task-specific events.

Attributes:

Name Type Description
dist_context DistributedContext

The distributed execution context.

event_bus EventBus

The event bus for subscribing to events.

TrainTask

Bases: BaseTask[TBatch, TPipelineInput, TSharedInput, TState], ABC, Generic[TBatch, TPipelineInput, TSharedInput, TPipelineOutput, TState]

Abstract base class for defining training-specific logic.

build_forward_inputs(ctx) abstractmethod

Transforms one raw microbatch into arguments for the model.

Called once per microbatch in the step's pack.

Parameters:

Name Type Description Default
ctx BuildForwardInputsContext[TBatch]

Context object.

required

Returns:

Type Description
BuildForwardInputsResult[TPipelineInput, TSharedInput, TState]

Result object, including the state side-data carried to loss/output processing.

compute_loss(ctx) abstractmethod

Calculates the loss based on model outputs.

Parameters:

Name Type Description Default
ctx ComputeLossContext[TPipelineOutput, TState]

Context object.

required

Returns:

Type Description
ComputeLossResult

Result object.

create_metrics(ctx)

Initializes metrics to be tracked during training.

Parameters:

Name Type Description Default
ctx CreateMetricsContext

Context object.

required

Returns:

Type Description
CreateMetricsResult

Result object.

dump_hparams()

Exports hyperparameters associated with this task for logging.

Returns:

Type Description
ScalarTree

A dictionary of hyperparameter names and values.

finalize(ctx)

Performs cleanup or final actions when the task execution finishes.

Parameters:

Name Type Description Default
ctx FinalizeContext

Context object.

required

load_state_dict(state_dict)

Restores the task's state from the provided dictionary.

Parameters:

Name Type Description Default
state_dict dict[str, Any]

The state dictionary to load.

required

register_events(context)

Register task-specific event subscriptions.

Parameters:

Name Type Description Default
context RegisterTaskEventsContext

Context providing access to the distributed environment and the event bus.

required

state_dict()

Returns the state dictionary for checkpointing this task.

Returns:

Type Description
dict[str, Any]

A dictionary containing the task's state.

update_metrics(ctx)

Updates the state of the metrics at the end of training step.

Parameters:

Name Type Description Default
ctx UpdateMetricsContext[TState]

Context object.

required

TrainTaskProvider

Bases: Protocol

Protocol that creates a TrainTask instance.

__call__(ctx)

Creates and returns a new TrainTask.

Parameters:

Name Type Description Default
ctx TrainTaskProviderContext

Context object.

required

Returns:

Type Description
TrainTask

An instantiated TrainTask.

TrainTaskProviderContext dataclass

Context data provided to the factory creating a TrainTask.

Attributes:

Name Type Description
dist_context DistributedContext

Information about the distributed environment.

UpdateMetricsContext dataclass

Bases: Generic[TState]

Context data provided to update metrics after a step.

Attributes:

Name Type Description
state TState

The side-data this microbatch's build_forward_inputs returned.

metrics Mapping[str, Metric]

The dictionary of metrics to be updated.