SkyRL
API ReferenceSkyRLSft

Configuration

Supervised Fine-Tuning configuration.

Configuration

class SFTPlacementConfig

SFTPlacementConfig(num_nodes: int = 1, num_gpus_per_node: int = 4) -> None

Bases: BaseConfig

Placement configuration for SFT training

Functions:

NameDescription
from_dict_configConstruct a typed BaseConfig from a Hydra DictConfig.

Attributes:

NameTypeDescription
num_nodesint
num_gpus_per_nodeint
Source code in skyrl/train/config/sft_config.py:50-55
@dataclass
class SFTPlacementConfig(BaseConfig):
    """Placement configuration for SFT training"""

    num_nodes: int = 1
    num_gpus_per_node: int = 4

from_dict_config

from_dict_config(cfg: DictConfig) -> BaseConfig

Construct a typed BaseConfig from a Hydra DictConfig.

attr num_nodes

num_nodes: int = 1

attr num_gpus_per_node

num_gpus_per_node: int = 4

class SFTConfig

SFTConfig(model: ModelConfig = (lambda: ModelConfig(path='Qwen/Qwen3-0.6B'))(), optimizer_config: OptimizerConfig = OptimizerConfig(), placement: SFTPlacementConfig = SFTPlacementConfig(), megatron_config: MegatronConfig = (lambda: MegatronConfig(tensor_model_parallel_size=2, pipeline_model_parallel_size=2))(), fsdp_config: FSDPConfig = FSDPConfig(), sequence_parallel_size: int = 1, model_config_kwargs: dict = dict(), use_torch_compile: bool = False, record_memory: bool = False, torch_profiler_config: TorchProfilerConfig = TorchProfilerConfig(), strategy: str = 'megatron', dataset_name: Optional[str] = None, dataset_split: Optional[str] = None, train_datasets: Optional[List[str]] = None, train_dataset_splits: Optional[List[str]] = None, train_dataset_weights: Optional[List[float]] = None, pretokenized_dataset_paths: Optional[List[str]] = None, messages_key: str = 'messages', tools_key: str = 'tools', system_key: str = 'system', eval_dataset_name: Optional[str] = None, eval_dataset_split: Optional[str] = None, eval_datasets: Optional[List[str]] = None, eval_dataset_splits: Optional[List[str]] = None, eval_dataset_names: Optional[List[str]] = None, eval_pretokenized_dataset_paths: Optional[List[str]] = None, eval_interval: int = 0, eval_before_train: bool = False, max_length: Optional[int] = None, num_steps: Optional[int] = None, num_epochs: Optional[int] = 1, batch_size: int = 4, micro_train_batch_size_per_gpu: int = 2, logger: str = 'console', project_name: str = 'skyrl_sft', run_name: str = 'skyrl_sft_run', tags: Optional[List[str]] = None, ckpt_path: str = '', ckpt_interval: int = 0, enable_ray_gpu_monitor: bool = True, max_ckpts_to_keep: int = -1, resume_from: str = '', hf_save_interval: int = 0, export_path: str = '', seed: int = 42, num_workers: int = 8, async_batch_collation: bool = True, dataloader_num_workers: int = 0, dataloader_persistent_workers: bool = False, sampler: str = 'random', sampler_class_path: Optional[str] = None, sampler_kwargs: dict = dict(), cache_dir: str = os.path.join(os.environ.get('XDG_CACHE_HOME', os.path.expanduser('~/.cache')), 'skyrl', 'tokenized_datasets'), force_recache: bool = False, disable_cache: bool = False, train_on_what: TrainOnWhat = TrainOnWhat.LAST_ASSISTANT_MESSAGE, remove_microbatch_padding: bool = True, use_sequence_packing: bool = False, max_tokens_per_microbatch: Optional[int] = None, dummy_run_full_ctx: bool = False, dummy_run_max_steps: int = 5, max_training_steps: Optional[int] = None) -> None

Bases: BaseConfig

Configuration for SFT training.

Usage::

cfg = SFTConfig( strategy="megatron", placement=SFTPlacementConfig(num_gpus_per_node=4), megatron_config=MegatronConfig(tensor_model_parallel_size=2, pipeline_model_parallel_size=2), )

Or from CLI::

cfg = SFTConfig.from_cli_overrides(sys.argv[1:])

Functions:

NameDescription
from_dict_configConstruct a typed BaseConfig from a Hydra DictConfig.
from_cli_overridesConstruct an SFTConfig from CLI arguments or a dict of overrides.
resolved_bin_capacityFFD bin capacity (max tokens per bin) when sequence packing is enabled.

Attributes:

NameTypeDescription
modelModelConfig
optimizer_configOptimizerConfig
placementSFTPlacementConfig
megatron_configMegatronConfig
fsdp_configFSDPConfig
sequence_parallel_sizeintUlysses sequence parallelism size
model_config_kwargsdictPass-through kwargs for the HuggingFace model config (FSDP backends).
use_torch_compileboolApply torch.compile to logits calculation.
record_memoryboolSave memory snapshots to {ckpt_path}/memory_snapshots/.
torch_profiler_configTorchProfilerConfigtorch.profiler config for policy training steps.
strategystr
dataset_nameOptional[str]Deprecated: use train_datasets instead. Translated to train_datasets=[dataset_name]
dataset_splitOptional[str]Deprecated: use train_dataset_splits instead.
train_datasetsOptional[List[str]]HuggingFace dataset names (or paths) to train on. With multiple datasets, batches are
train_dataset_splitsOptional[List[str]]Split to load for each entry of train_datasets (e.g. "train[:50000]"). Must match
train_dataset_weightsOptional[List[float]]Per-dataset sampling weights: the approximate per-batch ratio of samples drawn from each
pretokenized_dataset_pathsOptional[List[str]]Local paths to pretokenized training datasets, each a file or
messages_keystr
tools_keystrColumn name holding per-row tool/function schemas for tool-calling datasets
system_keystrColumn name holding a per-row system prompt to prepend when messages
eval_dataset_nameOptional[str]Deprecated: use eval_datasets instead. Translated to eval_datasets=[eval_dataset_name]
eval_dataset_splitOptional[str]Deprecated: use eval_dataset_splits instead.
eval_datasetsOptional[List[str]]HuggingFace dataset names (or paths) used to compute eval loss during training.
eval_dataset_splitsOptional[List[str]]Split to load for each entry of eval_datasets (e.g. "validation", "test[:500]").
eval_dataset_namesOptional[List[str]]Optional shorthand names used only for logging (eval/{name}/loss). Must be unique and
eval_pretokenized_dataset_pathsOptional[List[str]]Paths to pretokenized eval datasets (same formats and schema as
eval_intervalintRun eval every N training steps. Eval also runs once at the end of training
eval_before_trainboolIf True, run a baseline eval pass before training begins (logged at step 0).
max_lengthOptional[int]Maximum length of tokenized sequences. If specified, all sequences will be truncated to this value
num_stepsOptional[int]Number of training steps. If None, num_epochs is used to derive the step count.
num_epochsOptional[int]Number of training epochs. Used when num_steps is None. Default: 1 epoch.
batch_sizeint
micro_train_batch_size_per_gpuint
loggerstr
project_namestr
run_namestr
tagsOptional[List[str]]Optional list of tags to apply to the W&B run. Has no effect on other backends.
ckpt_pathstr
ckpt_intervalint
enable_ray_gpu_monitorboolEnable background Ray GPU/RAM metrics collection and logging to wandb.
max_ckpts_to_keepint-1 to keep all checkpoints, N to keep only the last N.
resume_fromstr
hf_save_intervalintSave HuggingFace-format weights every N steps. 0 = disabled.
export_pathstrDirectory for HF-format exports. Defaults to ckpt_path/hf_exports if empty.
seedint
num_workersintNumber of worker processes for parallel tokenization during dataset loading. Set to 0 for single-threaded.
async_batch_collationboolOverlap the next stateful-dataloader batch with the current GPU step.
dataloader_num_workersintNumber of worker processes for the training/eval StatefulDataLoader. 0 loads in the main process.
dataloader_persistent_workersboolKeep dataloader workers alive across epochs. Only takes effect when dataloader_num_workers > 0.
samplerstrTraining sampler: "random" (shuffle each epoch), "sequential" (in-order), or "custom"
sampler_class_pathOptional[str]Import path ("module.path.ClassName") to a custom stateful sampler. Required when sampler='custom'.
sampler_kwargsdictKeyword arguments forwarded to the custom sampler constructor.
cache_dirstrDirectory to cache tokenized datasets. For multi-node training, set this to an NFS-mounted path so all nodes can
force_recacheboolIf True, ignore existing cache and re-tokenize the dataset.
disable_cacheboolIf True, disable cache completely (always tokenize from scratch).
train_on_whatTrainOnWhatWhich tokens to compute loss on. See :class:TrainOnWhat for options.
remove_microbatch_paddingbool
use_sequence_packingboolEnable controller-level FFD bin-packing across the global mini-batch.
max_tokens_per_microbatchOptional[int]FFD bin capacity (max tokens per bin) when use_sequence_packing=True.
dummy_run_full_ctxbool
dummy_run_max_stepsint
max_training_stepsOptional[int]If set, stop training after this many steps regardless of num_steps or num_epochs.
Source code in skyrl/train/config/sft_config.py:58-327
@dataclass
class SFTConfig(BaseConfig):
    """Configuration for SFT training.

    Usage::

        cfg = SFTConfig(
            strategy="megatron",
            placement=SFTPlacementConfig(num_gpus_per_node=4),
            megatron_config=MegatronConfig(tensor_model_parallel_size=2,
                                    pipeline_model_parallel_size=2),
        )

    Or from CLI::

        cfg = SFTConfig.from_cli_overrides(sys.argv[1:])
    """

    @classmethod
    def from_cli_overrides(cls, args: Union[List[str], dict]) -> "SFTConfig":
        """Construct an SFTConfig from CLI arguments or a dict of overrides.

        Parses CLI dotlist arguments via OmegaConf and builds a typed config.
        Dataclass field defaults are used for any values not specified.

        Args:
            args: Either a list of CLI arguments in 'key.path=value' format, or a dict
                  mapping dot-notation keys to values.
                  Example list: ['strategy=megatron', 'model.path=Qwen/Qwen3-0.6B']
                  Example dict: {'strategy': 'megatron', 'model.path': 'Qwen/Qwen3-0.6B'}
                  Dict values are serialized as JSON, so ``None``, bools, strings,
                  lists and nested dicts keep their types.

        Returns:
            A fully constructed SFTConfig with CLI overrides applied.

        Raises:
            ValueError: If both ``num_epochs`` and ``num_steps`` are explicitly provided.
        """
        if isinstance(args, dict):
            args = overrides_dict_to_dotlist(args)

        overrides = OmegaConf.from_cli(args)
        # Check for mutual exclusion before constructing the full config
        if "num_epochs" in overrides and "num_steps" in overrides:
            raise ValueError("Cannot specify both num_epochs and num_steps")
        # Accept the deprecated ``use_sample_packing`` key as an alias for
        # ``remove_microbatch_padding``. Remap it before construction so the
        # strict key validation does not reject the old name.
        if "use_sample_packing" in overrides:
            if "remove_microbatch_padding" in overrides:
                raise ValueError(
                    "Specify only one of use_sample_packing (deprecated) and remove_microbatch_padding, not both."
                )
            import warnings

            warnings.warn(
                "use_sample_packing has been renamed to remove_microbatch_padding; "
                "use remove_microbatch_padding instead.",
                DeprecationWarning,
                stacklevel=2,
            )
            overrides["remove_microbatch_padding"] = overrides["use_sample_packing"]
            del overrides["use_sample_packing"]
        return cls.from_dict_config(overrides)

    # ---- Reused SkyRL config objects ----
    model: ModelConfig = field(default_factory=lambda: ModelConfig(path="Qwen/Qwen3-0.6B"))
    optimizer_config: OptimizerConfig = field(default_factory=OptimizerConfig)
    placement: SFTPlacementConfig = field(default_factory=SFTPlacementConfig)
    megatron_config: MegatronConfig = field(
        default_factory=lambda: MegatronConfig(
            tensor_model_parallel_size=2,
            pipeline_model_parallel_size=2,
        )
    )
    fsdp_config: FSDPConfig = field(default_factory=FSDPConfig)

    # Ulysses sequence parallelism
    sequence_parallel_size: int = 1
    """Ulysses sequence parallelism size"""

    model_config_kwargs: dict = field(default_factory=dict)
    """Pass-through kwargs for the HuggingFace model config (FSDP backends).
    For Megatron, use ``megatron_config.transformer_config_kwargs`` instead."""
    use_torch_compile: bool = False
    """Apply torch.compile to logits calculation."""
    record_memory: bool = False
    """Save memory snapshots to ``{ckpt_path}/memory_snapshots/``.
    Visualize by dragging pickle files to https://docs.pytorch.org/memory_viz."""
    torch_profiler_config: TorchProfilerConfig = field(default_factory=TorchProfilerConfig)
    """torch.profiler config for policy training steps."""

    # ---- SFT-specific flat fields ----
    strategy: str = "megatron"  # "megatron" or "fsdp"
    dataset_name: Optional[str] = None
    """Deprecated: use ``train_datasets`` instead. Translated to ``train_datasets=[dataset_name]``
    with a DeprecationWarning. Cannot be combined with ``train_datasets``."""
    dataset_split: Optional[str] = None
    """Deprecated: use ``train_dataset_splits`` instead."""
    train_datasets: Optional[List[str]] = None
    """HuggingFace dataset names (or paths) to train on. With multiple datasets, batches are
    mixed per-source by :class:`~skyrl.train.dataset.samplers.DataMixingSampler` according to
    ``train_dataset_weights``. Defaults to ``["yahma/alpaca-cleaned"]``. All datasets must share
    the same ``messages_key``/``tools_key``/``system_key`` columns and modality."""
    train_dataset_splits: Optional[List[str]] = None
    """Split to load for each entry of ``train_datasets`` (e.g. ``"train[:50000]"``). Must match
    ``train_datasets`` in length. Defaults to ``["train[:100]"]``."""
    train_dataset_weights: Optional[List[float]] = None
    """Per-dataset sampling weights: the approximate per-batch ratio of samples drawn from each
    dataset, independent of dataset sizes. Only supported with ``sampler="random"`` (custom
    samplers receive ratios via ``sampler_kwargs``). Defaults to equal mixing (``1/N`` each)."""
    pretokenized_dataset_paths: Optional[List[str]] = None
    """Local paths to *pretokenized* training datasets, each a file or
    directory holding parquet/JSONL/arrow files or a HF
    ``Dataset.save_to_disk`` directory. Rows must carry unpadded
    ``input_ids`` and a full-sequence 0/1 ``loss_mask`` (``num_actions`` is
    inferred); VLM rows additionally carry ``pixel_values`` /
    ``image_grid_thw``. See ``skyrl.train.dataset.pretokenized``. When set,
    online tokenization is skipped; cannot be combined with ``train_datasets``.
    Multiple stores are concatenated and mixed per ``train_dataset_weights``
    (like ``train_datasets``)."""
    messages_key: str = "messages"  # column name for chat-format datasets
    tools_key: str = "tools"
    """Column name holding per-row tool/function schemas for tool-calling datasets
    (e.g. APIGen-MT, xLAM, ToolACE). May be a list[dict] or a JSON-encoded string.
    Ignored if the column is absent from the dataset."""
    system_key: str = "system"
    """Column name holding a per-row system prompt to prepend when ``messages``
    does not already start with a system turn. Ignored if absent."""

    # ---- Evaluation datasets ----
    eval_dataset_name: Optional[str] = None
    """Deprecated: use ``eval_datasets`` instead. Translated to ``eval_datasets=[eval_dataset_name]``
    with a DeprecationWarning. Cannot be combined with ``eval_datasets``."""
    eval_dataset_split: Optional[str] = None
    """Deprecated: use ``eval_dataset_splits`` instead."""
    eval_datasets: Optional[List[str]] = None
    """HuggingFace dataset names (or paths) used to compute eval loss during training.
    When ``None`` (default), eval is disabled. Metrics are logged per dataset under
    ``eval/{name}/`` (nested even with a single eval dataset)."""
    eval_dataset_splits: Optional[List[str]] = None
    """Split to load for each entry of ``eval_datasets`` (e.g. ``"validation"``, ``"test[:500]"``).
    Must match ``eval_datasets`` in length. Defaults to ``["validation"]`` on the deprecated path."""
    eval_dataset_names: Optional[List[str]] = None
    """Optional shorthand names used only for logging (``eval/{name}/loss``). Must be unique and
    match ``eval_datasets`` (or ``eval_pretokenized_dataset_paths``) in length. Defaults to each
    dataset name with ``/`` replaced by ``_`` (path basenames for pretokenized stores)."""
    eval_pretokenized_dataset_paths: Optional[List[str]] = None
    """Paths to *pretokenized* eval datasets (same formats and schema as
    ``pretokenized_dataset_paths``). Cannot be combined with ``eval_datasets``.
    Metrics are logged under ``eval/{name}/`` where the names come from
    ``eval_dataset_names`` when set, defaulting to each path's basename."""
    eval_interval: int = 0
    """Run eval every N training steps. Eval also runs once at the end of training
    when an eval dataset is configured. ``0`` disables periodic eval."""
    eval_before_train: bool = False
    """If True, run a baseline eval pass before training begins (logged at step 0)."""
    max_length: Optional[int] = None
    """Maximum length of tokenized sequences. If specified, all sequences will be truncated to this value
    By default, no truncation is performed"""
    num_steps: Optional[int] = None
    """Number of training steps. If None, num_epochs is used to derive the step count."""
    num_epochs: Optional[int] = 1
    """Number of training epochs. Used when num_steps is None. Default: 1 epoch."""
    batch_size: int = 4
    micro_train_batch_size_per_gpu: int = 2
    logger: str = "console"  # "console" or "wandb"
    project_name: str = "skyrl_sft"
    run_name: str = "skyrl_sft_run"
    tags: Optional[List[str]] = None
    """Optional list of tags to apply to the W&B run. Has no effect on other backends."""
    ckpt_path: str = ""
    ckpt_interval: int = 0  # <= 0 -> no checkpointing
    enable_ray_gpu_monitor: bool = True
    """Enable background Ray GPU/RAM metrics collection and logging to wandb."""
    max_ckpts_to_keep: int = -1
    """-1 to keep all checkpoints, N to keep only the last N."""
    resume_from: str = ""  # "" = no resume, "latest" = latest checkpoint, or path to global_step_N dir

    # ---- HF export ----
    hf_save_interval: int = 0
    """Save HuggingFace-format weights every N steps. 0 = disabled."""
    export_path: str = ""
    """Directory for HF-format exports. Defaults to ckpt_path/hf_exports if empty."""

    seed: int = 42

    # ---- Data loading ----
    num_workers: int = 8
    """Number of worker processes for parallel tokenization during dataset loading. Set to 0 for single-threaded."""
    async_batch_collation: bool = True
    """Overlap the next stateful-dataloader batch with the current GPU step.

    Checkpoint state remains pinned after the current batch. Set to False for
    serial data loading."""

    # ---- Dataloader / sampler ----
    dataloader_num_workers: int = 0
    """Number of worker processes for the training/eval ``StatefulDataLoader``. ``0`` loads in the main process."""
    dataloader_persistent_workers: bool = False
    """Keep dataloader workers alive across epochs. Only takes effect when ``dataloader_num_workers > 0``."""
    sampler: str = "random"
    """Training sampler: ``"random"`` (shuffle each epoch), ``"sequential"`` (in-order), or ``"custom"``
    (load from ``sampler_class_path``)."""
    sampler_class_path: Optional[str] = None
    """Import path (``"module.path.ClassName"``) to a custom stateful sampler. Required when ``sampler='custom'``.
    Instantiated as ``ClassName(tokenized, **sampler_kwargs)``."""
    sampler_kwargs: dict = field(default_factory=dict)
    """Keyword arguments forwarded to the custom sampler constructor."""

    # ---- Tokenized dataset caching ----
    cache_dir: str = os.path.join(
        os.environ.get("XDG_CACHE_HOME", os.path.expanduser("~/.cache")), "skyrl", "tokenized_datasets"
    )
    """Directory to cache tokenized datasets. For multi-node training, set this to an NFS-mounted path so all nodes can
    share the cache."""
    force_recache: bool = False
    """If True, ignore existing cache and re-tokenize the dataset."""
    disable_cache: bool = False
    """If True, disable cache completely (always tokenize from scratch)."""

    # ---- Training target ----
    train_on_what: TrainOnWhat = TrainOnWhat.LAST_ASSISTANT_MESSAGE
    """Which tokens to compute loss on. See :class:`TrainOnWhat` for options."""

    # ---- Packing ----
    remove_microbatch_padding: bool = True  # Pack multiple sequences per microbatch (requires flash_attn)
    use_sequence_packing: bool = False
    """Enable controller-level FFD bin-packing across the global mini-batch.
    Requires ``remove_microbatch_padding=True`` and the Megatron backend. When
    enabled, ``SFTTrainer`` uses ``PackedDataCollator`` instead of
    ``DefaultCollator``. Each bin row becomes one row in the dispatched batch
    and one worker micro-batch.
    """
    max_tokens_per_microbatch: Optional[int] = None
    """FFD bin capacity (max tokens per bin) when ``use_sequence_packing=True``.
    Each bin row becomes one worker micro-batch, so this is the token budget for
    one micro-batch. Must be ``>= max_length`` so any single sequence fits in a
    bin. ``None`` (default) resolves to ``max_length`` (each bin holds one
    sequence)."""

    # ---- Dummy run / benchmarking ----
    dummy_run_full_ctx: bool = False  # Skip real data; fabricate full-context sequences
    dummy_run_max_steps: int = 5  # Number of steps to run in dummy mode

    # ---- CI / smoke test support ----
    max_training_steps: Optional[int] = None
    """If set, stop training after this many steps regardless of num_steps or num_epochs.
    Useful for CI smoke tests and quick validation runs."""

    def resolved_bin_capacity(self) -> int:
        """FFD bin capacity (max tokens per bin) when sequence packing is enabled.

        Resolves ``max_tokens_per_microbatch`` against ``max_length``: when the
        token budget is ``None`` it falls back to ``max_length`` (each bin holds
        one sequence). Requires ``max_length`` to be set and the resolved budget
        to be ``>= max_length`` so any single sequence fits in a bin.
        """
        if self.max_length is None:
            raise ValueError("max_tokens_per_microbatch requires max_length to be set.")
        max_tokens = self.max_tokens_per_microbatch
        if max_tokens is None:
            max_tokens = self.max_length
        if max_tokens < self.max_length:
            raise ValueError(
                f"max_tokens_per_microbatch ({max_tokens}) must be >= max_length "
                f"({self.max_length}) so any single sequence fits in a bin."
            )
        return max_tokens

from_dict_config

from_dict_config(cfg: DictConfig) -> BaseConfig

Construct a typed BaseConfig from a Hydra DictConfig.

method classmethod from_cli_overrides

from_cli_overrides(args: Union[List[str], dict]) -> SFTConfig

Construct an SFTConfig from CLI arguments or a dict of overrides.

Parses CLI dotlist arguments via OmegaConf and builds a typed config. Dataclass field defaults are used for any values not specified.

Parameters:

NameTypeDescriptionDefault
argsUnion[List[str], dict]Either a list of CLI arguments in 'key.path=value' format, or a dict mapping dot-notation keys to values. Example list: ['strategy=megatron', 'model.path=Qwen/Qwen3-0.6B'] Example dict: {'strategy': 'megatron', 'model.path': 'Qwen/Qwen3-0.6B'} Dict values are serialized as JSON, so None, bools, strings, lists and nested dicts keep their types.required

Returns:

TypeDescription
SFTConfigA fully constructed SFTConfig with CLI overrides applied.

Raises:

TypeDescription
ValueErrorIf both num_epochs and num_steps are explicitly provided.
Source code in skyrl/train/config/sft_config.py:76-122
    @classmethod
    def from_cli_overrides(cls, args: Union[List[str], dict]) -> "SFTConfig":
        """Construct an SFTConfig from CLI arguments or a dict of overrides.

        Parses CLI dotlist arguments via OmegaConf and builds a typed config.
        Dataclass field defaults are used for any values not specified.

        Args:
            args: Either a list of CLI arguments in 'key.path=value' format, or a dict
                  mapping dot-notation keys to values.
                  Example list: ['strategy=megatron', 'model.path=Qwen/Qwen3-0.6B']
                  Example dict: {'strategy': 'megatron', 'model.path': 'Qwen/Qwen3-0.6B'}
                  Dict values are serialized as JSON, so ``None``, bools, strings,
                  lists and nested dicts keep their types.

        Returns:
            A fully constructed SFTConfig with CLI overrides applied.

        Raises:
            ValueError: If both ``num_epochs`` and ``num_steps`` are explicitly provided.
        """
        if isinstance(args, dict):
            args = overrides_dict_to_dotlist(args)

        overrides = OmegaConf.from_cli(args)
        # Check for mutual exclusion before constructing the full config
        if "num_epochs" in overrides and "num_steps" in overrides:
            raise ValueError("Cannot specify both num_epochs and num_steps")
        # Accept the deprecated ``use_sample_packing`` key as an alias for
        # ``remove_microbatch_padding``. Remap it before construction so the
        # strict key validation does not reject the old name.
        if "use_sample_packing" in overrides:
            if "remove_microbatch_padding" in overrides:
                raise ValueError(
                    "Specify only one of use_sample_packing (deprecated) and remove_microbatch_padding, not both."
                )
            import warnings

            warnings.warn(
                "use_sample_packing has been renamed to remove_microbatch_padding; "
                "use remove_microbatch_padding instead.",
                DeprecationWarning,
                stacklevel=2,
            )
            overrides["remove_microbatch_padding"] = overrides["use_sample_packing"]
            del overrides["use_sample_packing"]
        return cls.from_dict_config(overrides)

attr model

model: ModelConfig = field(default_factory=(lambda: ModelConfig(path='Qwen/Qwen3-0.6B')))

attr optimizer_config

optimizer_config: OptimizerConfig = field(default_factory=OptimizerConfig)

attr placement

placement: SFTPlacementConfig = field(default_factory=SFTPlacementConfig)

attr megatron_config

megatron_config: MegatronConfig = field(default_factory=(lambda: MegatronConfig(tensor_model_parallel_size=2, pipeline_model_parallel_size=2)))

attr fsdp_config

fsdp_config: FSDPConfig = field(default_factory=FSDPConfig)

attr sequence_parallel_size

sequence_parallel_size: int = 1

Ulysses sequence parallelism size

attr model_config_kwargs

model_config_kwargs: dict = field(default_factory=dict)

Pass-through kwargs for the HuggingFace model config (FSDP backends). For Megatron, use megatron_config.transformer_config_kwargs instead.

attr use_torch_compile

use_torch_compile: bool = False

Apply torch.compile to logits calculation.

attr record_memory

record_memory: bool = False

Save memory snapshots to {ckpt_path}/memory_snapshots/. Visualize by dragging pickle files to https://docs.pytorch.org/memory_viz.

attr torch_profiler_config

torch_profiler_config: TorchProfilerConfig = field(default_factory=TorchProfilerConfig)

torch.profiler config for policy training steps.

attr strategy

strategy: str = 'megatron'

attr dataset_name

dataset_name: Optional[str] = None

Deprecated: use train_datasets instead. Translated to train_datasets=[dataset_name] with a DeprecationWarning. Cannot be combined with train_datasets.

attr dataset_split

dataset_split: Optional[str] = None

Deprecated: use train_dataset_splits instead.

attr train_datasets

train_datasets: Optional[List[str]] = None

HuggingFace dataset names (or paths) to train on. With multiple datasets, batches are mixed per-source by :class:~skyrl.train.dataset.samplers.DataMixingSampler according to train_dataset_weights. Defaults to ["yahma/alpaca-cleaned"]. All datasets must share the same messages_key/tools_key/system_key columns and modality.

attr train_dataset_splits

train_dataset_splits: Optional[List[str]] = None

Split to load for each entry of train_datasets (e.g. "train[:50000]"). Must match train_datasets in length. Defaults to ["train[:100]"].

attr train_dataset_weights

train_dataset_weights: Optional[List[float]] = None

Per-dataset sampling weights: the approximate per-batch ratio of samples drawn from each dataset, independent of dataset sizes. Only supported with sampler="random" (custom samplers receive ratios via sampler_kwargs). Defaults to equal mixing (1/N each).

attr pretokenized_dataset_paths

pretokenized_dataset_paths: Optional[List[str]] = None

Local paths to pretokenized training datasets, each a file or directory holding parquet/JSONL/arrow files or a HF Dataset.save_to_disk directory. Rows must carry unpadded input_ids and a full-sequence 0/1 loss_mask (num_actions is inferred); VLM rows additionally carry pixel_values / image_grid_thw. See skyrl.train.dataset.pretokenized. When set, online tokenization is skipped; cannot be combined with train_datasets. Multiple stores are concatenated and mixed per train_dataset_weights (like train_datasets).

attr messages_key

messages_key: str = 'messages'

attr tools_key

tools_key: str = 'tools'

Column name holding per-row tool/function schemas for tool-calling datasets (e.g. APIGen-MT, xLAM, ToolACE). May be a list[dict] or a JSON-encoded string. Ignored if the column is absent from the dataset.

attr system_key

system_key: str = 'system'

Column name holding a per-row system prompt to prepend when messages does not already start with a system turn. Ignored if absent.

attr eval_dataset_name

eval_dataset_name: Optional[str] = None

Deprecated: use eval_datasets instead. Translated to eval_datasets=[eval_dataset_name] with a DeprecationWarning. Cannot be combined with eval_datasets.

attr eval_dataset_split

eval_dataset_split: Optional[str] = None

Deprecated: use eval_dataset_splits instead.

attr eval_datasets

eval_datasets: Optional[List[str]] = None

HuggingFace dataset names (or paths) used to compute eval loss during training. When None (default), eval is disabled. Metrics are logged per dataset under eval/{name}/ (nested even with a single eval dataset).

attr eval_dataset_splits

eval_dataset_splits: Optional[List[str]] = None

Split to load for each entry of eval_datasets (e.g. "validation", "test[:500]"). Must match eval_datasets in length. Defaults to ["validation"] on the deprecated path.

attr eval_dataset_names

eval_dataset_names: Optional[List[str]] = None

Optional shorthand names used only for logging (eval/{name}/loss). Must be unique and match eval_datasets (or eval_pretokenized_dataset_paths) in length. Defaults to each dataset name with / replaced by _ (path basenames for pretokenized stores).

attr eval_pretokenized_dataset_paths

eval_pretokenized_dataset_paths: Optional[List[str]] = None

Paths to pretokenized eval datasets (same formats and schema as pretokenized_dataset_paths). Cannot be combined with eval_datasets. Metrics are logged under eval/{name}/ where the names come from eval_dataset_names when set, defaulting to each path's basename.

attr eval_interval

eval_interval: int = 0

Run eval every N training steps. Eval also runs once at the end of training when an eval dataset is configured. 0 disables periodic eval.

attr eval_before_train

eval_before_train: bool = False

If True, run a baseline eval pass before training begins (logged at step 0).

attr max_length

max_length: Optional[int] = None

Maximum length of tokenized sequences. If specified, all sequences will be truncated to this value By default, no truncation is performed

attr num_steps

num_steps: Optional[int] = None

Number of training steps. If None, num_epochs is used to derive the step count.

attr num_epochs

num_epochs: Optional[int] = 1

Number of training epochs. Used when num_steps is None. Default: 1 epoch.

attr batch_size

batch_size: int = 4

attr micro_train_batch_size_per_gpu

micro_train_batch_size_per_gpu: int = 2

attr logger

logger: str = 'console'

attr project_name

project_name: str = 'skyrl_sft'

attr run_name

run_name: str = 'skyrl_sft_run'

attr tags

tags: Optional[List[str]] = None

Optional list of tags to apply to the W&B run. Has no effect on other backends.

attr ckpt_path

ckpt_path: str = ''

attr ckpt_interval

ckpt_interval: int = 0

attr enable_ray_gpu_monitor

enable_ray_gpu_monitor: bool = True

Enable background Ray GPU/RAM metrics collection and logging to wandb.

attr max_ckpts_to_keep

max_ckpts_to_keep: int = -1

-1 to keep all checkpoints, N to keep only the last N.

attr resume_from

resume_from: str = ''

attr hf_save_interval

hf_save_interval: int = 0

Save HuggingFace-format weights every N steps. 0 = disabled.

attr export_path

export_path: str = ''

Directory for HF-format exports. Defaults to ckpt_path/hf_exports if empty.

attr seed

seed: int = 42

attr num_workers

num_workers: int = 8

Number of worker processes for parallel tokenization during dataset loading. Set to 0 for single-threaded.

attr async_batch_collation

async_batch_collation: bool = True

Overlap the next stateful-dataloader batch with the current GPU step.

Checkpoint state remains pinned after the current batch. Set to False for serial data loading.

attr dataloader_num_workers

dataloader_num_workers: int = 0

Number of worker processes for the training/eval StatefulDataLoader. 0 loads in the main process.

attr dataloader_persistent_workers

dataloader_persistent_workers: bool = False

Keep dataloader workers alive across epochs. Only takes effect when dataloader_num_workers > 0.

attr sampler

sampler: str = 'random'

Training sampler: "random" (shuffle each epoch), "sequential" (in-order), or "custom" (load from sampler_class_path).

attr sampler_class_path

sampler_class_path: Optional[str] = None

Import path ("module.path.ClassName") to a custom stateful sampler. Required when sampler='custom'. Instantiated as ClassName(tokenized, **sampler_kwargs).

attr sampler_kwargs

sampler_kwargs: dict = field(default_factory=dict)

Keyword arguments forwarded to the custom sampler constructor.

attr cache_dir

cache_dir: str = os.path.join(os.environ.get('XDG_CACHE_HOME', os.path.expanduser('~/.cache')), 'skyrl', 'tokenized_datasets')

Directory to cache tokenized datasets. For multi-node training, set this to an NFS-mounted path so all nodes can share the cache.

attr force_recache

force_recache: bool = False

If True, ignore existing cache and re-tokenize the dataset.

attr disable_cache

disable_cache: bool = False

If True, disable cache completely (always tokenize from scratch).

attr train_on_what

train_on_what: TrainOnWhat = TrainOnWhat.LAST_ASSISTANT_MESSAGE

Which tokens to compute loss on. See :class:TrainOnWhat for options.

attr remove_microbatch_padding

remove_microbatch_padding: bool = True

attr use_sequence_packing

use_sequence_packing: bool = False

Enable controller-level FFD bin-packing across the global mini-batch. Requires remove_microbatch_padding=True and the Megatron backend. When enabled, SFTTrainer uses PackedDataCollator instead of DefaultCollator. Each bin row becomes one row in the dispatched batch and one worker micro-batch.

attr max_tokens_per_microbatch

max_tokens_per_microbatch: Optional[int] = None

FFD bin capacity (max tokens per bin) when use_sequence_packing=True. Each bin row becomes one worker micro-batch, so this is the token budget for one micro-batch. Must be >= max_length so any single sequence fits in a bin. None (default) resolves to max_length (each bin holds one sequence).

attr dummy_run_full_ctx

dummy_run_full_ctx: bool = False

attr dummy_run_max_steps

dummy_run_max_steps: int = 5

attr max_training_steps

max_training_steps: Optional[int] = None

If set, stop training after this many steps regardless of num_steps or num_epochs. Useful for CI smoke tests and quick validation runs.

method resolved_bin_capacity

resolved_bin_capacity() -> int

FFD bin capacity (max tokens per bin) when sequence packing is enabled.

Resolves max_tokens_per_microbatch against max_length: when the token budget is None it falls back to max_length (each bin holds one sequence). Requires max_length to be set and the resolved budget to be >= max_length so any single sequence fits in a bin.

Source code in skyrl/train/config/sft_config.py:309-327
    def resolved_bin_capacity(self) -> int:
        """FFD bin capacity (max tokens per bin) when sequence packing is enabled.

        Resolves ``max_tokens_per_microbatch`` against ``max_length``: when the
        token budget is ``None`` it falls back to ``max_length`` (each bin holds
        one sequence). Requires ``max_length`` to be set and the resolved budget
        to be ``>= max_length`` so any single sequence fits in a bin.
        """
        if self.max_length is None:
            raise ValueError("max_tokens_per_microbatch requires max_length to be set.")
        max_tokens = self.max_tokens_per_microbatch
        if max_tokens is None:
            max_tokens = self.max_length
        if max_tokens < self.max_length:
            raise ValueError(
                f"max_tokens_per_microbatch ({max_tokens}) must be >= max_length "
                f"({self.max_length}) so any single sequence fits in a bin."
            )
        return max_tokens

class TrainOnWhat

Bases: StrEnum

Enum controlling which parts of the sequence to compute loss on.

Members:

LAST_ASSISTANT_MESSAGE: Train only on the final assistant message. ALL_ASSISTANT_MESSAGES: Train on every assistant message in the conversation.

Attributes:

Source code in skyrl/train/config/sft_config.py:33-42
class TrainOnWhat(StrEnum):
    """Enum controlling which parts of the sequence to compute loss on.

    Members:
        LAST_ASSISTANT_MESSAGE: Train only on the final assistant message.
        ALL_ASSISTANT_MESSAGES: Train on every assistant message in the conversation.
    """

    LAST_ASSISTANT_MESSAGE = "last_assistant_message"
    ALL_ASSISTANT_MESSAGES = "all_assistant_messages"

attr LAST_ASSISTANT_MESSAGE

LAST_ASSISTANT_MESSAGE = 'last_assistant_message'

attr ALL_ASSISTANT_MESSAGES

ALL_ASSISTANT_MESSAGES = 'all_assistant_messages'

Config Bridge

method validate_sft_cfg

validate_sft_cfg(cfg: SFTConfig) -> None

Validate SFT-specific configuration.

Only checks fields that are relevant to SFT training, unlike validate_cfg which includes RL-specific validations.

method build_skyrl_config_for_sft

build_skyrl_config_for_sft(sft_cfg: SFTConfig) -> SkyRLTrainConfig

Map user-facing SFTConfig to the internal SkyRL backend config.

On this page

Configurationclass SFTPlacementConfigfrom_dict_configattr num_nodesattr num_gpus_per_nodeclass SFTConfigfrom_dict_configmethod classmethod from_cli_overridesattr modelattr optimizer_configattr placementattr megatron_configattr fsdp_configattr sequence_parallel_sizeattr model_config_kwargsattr use_torch_compileattr record_memoryattr torch_profiler_configattr strategyattr dataset_nameattr dataset_splitattr train_datasetsattr train_dataset_splitsattr train_dataset_weightsattr pretokenized_dataset_pathsattr messages_keyattr tools_keyattr system_keyattr eval_dataset_nameattr eval_dataset_splitattr eval_datasetsattr eval_dataset_splitsattr eval_dataset_namesattr eval_pretokenized_dataset_pathsattr eval_intervalattr eval_before_trainattr max_lengthattr num_stepsattr num_epochsattr batch_sizeattr micro_train_batch_size_per_gpuattr loggerattr project_nameattr run_nameattr tagsattr ckpt_pathattr ckpt_intervalattr enable_ray_gpu_monitorattr max_ckpts_to_keepattr resume_fromattr hf_save_intervalattr export_pathattr seedattr num_workersattr async_batch_collationattr dataloader_num_workersattr dataloader_persistent_workersattr samplerattr sampler_class_pathattr sampler_kwargsattr cache_dirattr force_recacheattr disable_cacheattr train_on_whatattr remove_microbatch_paddingattr use_sequence_packingattr max_tokens_per_microbatchattr dummy_run_full_ctxattr dummy_run_max_stepsattr max_training_stepsmethod resolved_bin_capacityclass TrainOnWhatattr LAST_ASSISTANT_MESSAGEattr ALL_ASSISTANT_MESSAGESConfig Bridgemethod validate_sft_cfgmethod build_skyrl_config_for_sft