Commit 1f4f83e
refactor: expose shared recipe builders from components (#2190)
* refactor: expose shared recipe builders from components
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor: share recipe optimizer builder
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor: reuse shared distributed builder in mining recipe
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor: keep config targets out of components
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* revert: restore recipe files to main (defer recipe changes to follow-up)
Revert all recipe-side changes introduced by the shared-builder commits.
This PR now only moves builder helpers into components/ and adds
_component_builders.py — recipes remain unchanged and will be updated
in the follow-up Engine PR.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(components): standardize builder files to api.py, replace Any with concrete types
Rename all five component builder files to `api.py` to follow the
reviewer-requested `config.py` + `api.py` two-file convention:
checkpoint/config_builder.py → checkpoint/api.py
distributed/build.py → distributed/api.py
loss/build.py → loss/api.py
optim/build.py → optim/api.py
training/step_builder.py → training/api.py
Replace `Any` type annotations with concrete types:
- optim/api.py: DistributedConfig, DeviceMesh, StepScheduler,
list[Optimizer] | Optimizer (under TYPE_CHECKING guard)
- loss/api.py: Callable[..., nn.Module] and nn.Module return
- training/api.py: DataLoader (under TYPE_CHECKING guard)
Fix stale build_mlflow import in _component_builders.py after the
merge-main resolution reverted mlflow_utils.py to configure_mlflow.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): resolve ConfigNode at recipe boundary for optimizer builder
Use _callable_and_kwargs() to extract (optimizer_factory, optimizer_kwargs)
from the ConfigNode before calling is_dion_optimizer / build_dion_optimizer,
so the component-layer utils receive typed Python args instead of ConfigNodes.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(components): add config.py re-exports for checkpoint, optim, training
Standardize the config.py + api.py two-file convention:
checkpoint/config.py → re-exports CheckpointingConfig
optim/config.py → re-exports OptimizerParamScheduler
training/config.py → re-exports StepScheduler
distributed/config.py already existed. loss/ and loggers/ have no config
dataclasses to expose.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): extract LRSchedulerConfig dataclass into config.py
Pull the LR scheduler parameters out of the OptimizerParamScheduler
constructor into a standalone LRSchedulerConfig dataclass that lives in
config.py. build_lr_scheduler in api.py now accepts LRSchedulerConfig
and resolves defaults (warmup, decay, LR fractions) from the training
schedule + optimizer base LR.
This completes the config.py + api.py two-file convention for the optim
component: config.py defines what you can configure, api.py builds it.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(training): extract StepSchedulerConfig dataclass into config.py
Pull the YAML-configurable step scheduler parameters (global_batch_size,
num_epochs, max_steps, checkpoint/val/gc intervals, etc.) into a
StepSchedulerConfig dataclass in config.py. build_step_scheduler in
api.py now accepts StepSchedulerConfig plus runtime-only args
(dataloader, dp_group_size, local_batch_size).
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(checkpoint): extract CheckpointConfig dataclass into config.py
Pull the YAML-configurable checkpoint parameters (checkpoint_dir,
model_save_format, save_consolidated, is_async, etc.) into a
CheckpointConfig dataclass in config.py. build_checkpoint_config in
api.py now accepts CheckpointConfig plus runtime-only args (cache_dir,
model_repo_id, is_peft) and produces the internal CheckpointingConfig.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(loggers): add config.py + api.py for WandB, MLflow, Comet
Add typed config dataclasses:
WandbConfig — project, entity, name, group, tags, save_dir, notes
MLflowConfig — experiment_name, run_name, tracking_uri, tags, resume, etc.
CometConfig — project_name, workspace, api_key, experiment_name, tags
Add api.py with build_wandb(config, ...) and build_comet(config).
MLflow stays in mlflow_utils.py (configure_mlflow) for now since it
takes the full config object with resume/sidecar logic; MLflowConfig
documents the YAML surface for discoverability.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(loggers): add build_mlflow to api.py with typed MLflowConfig
Implement build_mlflow(config, checkpoint_dir, run_config) that takes
MLflowConfig instead of the full ConfigNode. Handles experiment
creation, run resume via sidecar, failure hook installation, and
config logging — same behaviour as configure_mlflow but without
ConfigNode coupling.
The recipe-facing _component_builders.build_mlflow now bridges the
ConfigNode → MLflowConfig boundary, extracting checkpoint_dir and
run_config before delegating to the typed builder.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): route wandb/mlflow through _component_builders
Delete the inline build_wandb() from train_ft.py and vlm/finetune.py.
Replace configure_mlflow() calls with build_mlflow() from
_component_builders. All downstream recipes (diffusion, retrieval,
train_seq_cls) now import build_wandb from _component_builders instead
of from train_ft.py.
Remove the superseded build_wandb from wandb_utils.py — the typed
version in loggers/api.py is the canonical implementation.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(checkpoint): drop CheckpointConfig, re-export CheckpointingConfig in config.py
CheckpointingConfig is already a clean dataclass — no need for a
separate user-facing wrapper. config.py now re-exports it directly.
api.py reverts to accepting Mapping[str, Any] kwargs (same as before
the CheckpointConfig detour).
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(checkpoint): move CheckpointingConfig into config.py
Move the CheckpointingConfig dataclass and its _is_geq_torch_2_9 helper
from checkpointing.py into config.py (the canonical config surface).
checkpointing.py re-imports both so existing callers are unaffected.
This avoids pulling in checkpointing.py's heavy dependencies (DCP,
safetensors, etc.) when only the config type is needed.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* feat(optim): add OptimizerConfig with string-based optimizer resolution
Add OptimizerConfig dataclass to config.py with:
name: dotted import path (e.g. "torch.optim.AdamW")
lr, weight_decay: common hyper-parameters as explicit fields
extra_kwargs: pass-through dict for optimizer-specific params
build_optimizer in api.py now accepts either OptimizerConfig (preferred
for external integrations like veRL — zero importlib boilerplate) or
the existing (optimizer_factory, optimizer_kwargs) pair for backward
compat with _component_builders.
This follows the veRL/VeOmni pattern where a single config with a name
string + common fields is the most integration-friendly approach.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* feat(optim): add per-optimizer typed config hierarchy
Replace the flat OptimizerConfig with a hierarchy of typed subclasses
so that every parameter is discoverable by reading config.py:
OptimizerConfig (generic fallback — name + extra_kwargs)
├── AdamConfig (betas, eps, amsgrad)
├── AdamWConfig (betas, eps, amsgrad, fused)
├── FusedAdamConfig (betas, eps, adam_w_mode, bias_correction,
│ master_weights, master_weight_dtype)
├── FlashAdamWConfig (betas, eps, master_weight_bits)
└── MuonConfig (mu, betas, epsilon, adjust_lr, scalar_opt,
scalar_betas, scalar_eps)
Each config has a to_kwargs() method that returns the full kwargs dict
for the optimizer constructor. The base OptimizerConfig still works as
a generic fallback for third-party optimizers via extra_kwargs.
Covers all 5 optimizer classes currently used across Automodel's YAML
configs (188× Adam, 95× AdamW, 9× FlashAdamW, 9× FusedAdam, 1× Muon).
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* feat(optim): add OptimizerConfig.from_name() registry for string-based dispatch
Add a classmethod factory that resolves an optimizer name string to the
correct typed config subclass:
OptimizerConfig.from_name("torch.optim.AdamW", lr=1e-4, betas=(0.9, 0.95))
→ AdamWConfig(lr=1e-4, betas=(0.9, 0.95))
OptimizerConfig.from_name("some.custom.Optimizer", lr=1e-4, momentum=0.9)
→ OptimizerConfig(name=..., extra_kwargs={"momentum": 0.9})
Known optimizers (Adam, AdamW, FusedAdam, FlashAdamW, Muon) return
typed subclasses with full field validation. Unknown optimizers fall
back to the base OptimizerConfig with extra_kwargs.
This is the single entry point for all callers — veRL CLI, YAML
recipes, Python scripts — no integration-specific code needed.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* feat(loss): add typed LossConfig hierarchy with from_name() registry
Add config.py with per-loss typed configs following the same pattern
as OptimizerConfig:
LossConfig (generic fallback — name + extra_kwargs)
├── MaskedCrossEntropyConfig (fp32_upcast, ignore_index, reduction)
├── FusedLinearCEConfig (ignore_index, logit_softcapping, reduction)
├── TEParallelCEConfig (ignore_index, reduction)
└── KDLossConfig (ignore_index, temperature, fp32_upcast)
LossConfig.from_name() resolves the dotted name string to the correct
typed subclass. build_loss_fn() accepts either LossConfig or the
existing (loss_factory, loss_kwargs) pair for backward compat.
Covers all 4 loss classes used across Automodel's YAML configs
(290× MaskedCE, 4× TEParallelCE, 2× FusedLinearCE, 2× KDLoss).
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): route build_loss_fn through _component_builders
Delete inline build_loss_fn from train_ft.py and vlm/finetune.py.
Both now import from _component_builders, same pattern as build_wandb
and build_mlflow.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* test: add unit tests for config.py + api.py across all components
68 new tests covering:
optim/ — OptimizerConfig hierarchy, from_name() registry dispatch,
_resolve_optimizer, to_kwargs for all 5 subclasses,
LRSchedulerConfig defaults
loss/ — LossConfig hierarchy, from_name() dispatch, _resolve_loss,
to_kwargs for all 4 subclasses, build_loss_fn with config
loggers/ — WandbConfig, MLflowConfig, CometConfig defaults + custom
training/ — StepSchedulerConfig defaults, custom values, resume fields
checkpoint/ — CheckpointingConfig construction + __post_init__ validation,
backward compat import from checkpointing.py,
build_checkpoint_config with defaults/overrides/PEFT fallback
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(loss): remove string-based resolution from loss component
Move dotted-path resolution (_resolve_loss, _LOSS_REGISTRY, from_name)
out of the component layer. Loss config.py is now pure dataclasses;
api.py maps config type → loss class directly via _get_loss_class.
String-to-class resolution belongs in the integration layer (recipes,
veRL adapter), not in the component.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): remove string-based resolution from optim component
Remove _resolve_optimizer, _OPTIMIZER_REGISTRY, and from_name() from
optim config.py. When using OptimizerConfig, the caller must also
provide optimizer_factory (the resolved class). String-to-class
resolution belongs in the integration layer.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): TorchTitan-style typed configs with build() + RL escape hatch
Collapse optim config.py + api.py into a single optimizer.py. Each typed
optimizer config (Adam/AdamW/FusedAdam/FlashAdamW/Muon) exposes its full
parameter surface as named fields and a build() method — no extra_kwargs,
no to_kwargs(), no dotted-path resolution.
- build_optimizer(model, config, ...): Automodel-native orchestration
(parts loop, TP foreach, Dion grouping, Megatron-FSDP sharding).
- build_optimizer_for_rl(optimizer, model, **kwargs): kwargs-accepting
escape hatch for external integrations (veRL). Accepts a dotted path or a
resolved class; adding new typed configs never requires changing callers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): collapse build_optimizer + build_optimizer_for_rl into one
Single build_optimizer(model, optimizer, **kwargs) orchestration entry that
dispatches on its second argument:
- OptimizerConfig instance -> native typed path (delegates per-part to
config.build); rejects stray **kwargs.
- dotted path / class + **kwargs -> integration escape hatch (veRL); unknown
optimizers work without a typed config.
The per-part loop (TP foreach, Dion grouping, Megatron-FSDP sharding) now lives
in exactly one place. cfg.build(params) remains the clean per-optimizer
constructor. Guards reject a config passed with kwargs and a config class
passed instead of an instance.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): inline build_optimizer dispatch, drop make_one/make_dion closures
Replace the two per-branch closures with a single pre-loop prep block plus an
explicit 4-way dispatch inside the parts loop ({config, factory} x {dion,
normal}). All construction paths are now visible at the point of use, matching
the repo's explicit-over-implicit style. No behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): rename optim/utils.py to optim/dion.py
The module is 100% Dion (is_dion_optimizer, _separate_param_groups,
_get_dion_mesh, build_dion_optimizer); "utils" was a junk-drawer name.
Rename to dion.py so the Dion construction subsystem is self-describing and
sits beside optimizer.py/scheduler.py. Update all importers; MuonConfig.build_dion
still delegates to dion.build_dion_optimizer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): route train_ft through component build APIs
Replace train_ft.py's local build_optimizer / build_lr_scheduler /
build_checkpoint_config / build_step_scheduler / build_distributed
reimplementations with re-export imports from _component_builders (and
distributed.api). The recipe now consumes the refactored component layer; the
~210 lines of duplicated builder logic are deleted. Names remain importable from
train_ft, so sibling recipes (train_seq_cls, diffusion, kd, retrieval) and tests
that do `from ...train_ft import build_optimizer` keep working.
Remove obsolete recipe tests that asserted build_optimizer mutates cfg_opt in
place (an abandoned contract; already failing on the branch). dtype-string
resolution and TP-foreach behavior are covered at the component level in
tests/unit_tests/optim/test_optim_config.py.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): route vlm/finetune through component build APIs
VLM's build_optimizer was an outdated copy of the LLM one: git history shows it
never received the TE FusedAdam dtype-string resolution (PR #1417) or Dion
support that the LLM recipe gained, and its Megatron path discarded the sharded
optimizer's return value. Replace vlm/finetune.py's local build_optimizer /
build_lr_scheduler / build_checkpoint_config / build_step_scheduler /
build_distributed with re-export imports from _component_builders (and
distributed.api), bringing VLM to parity with LLM and ending the drift. Names
stay importable from vlm.finetune for siblings (kd) and tests.
Remove two obsolete tests asserting build_optimizer mutates cfg_opt.foreach in
place; repoint the dist-env test's monkeypatch to distributed.api where
build_distributed now resolves initialize_distributed. TP-foreach/dtype behavior
is covered at the component level.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(loss): TorchTitan-style typed configs with build(), single builder
Mirror the optim refactor: collapse loss config.py + api.py into a single
loss/loss.py. Each typed loss config (MaskedCrossEntropy / FusedLinearCE /
TEParallelCE / KDLoss) exposes its full parameter surface as named fields and a
build() method — no name/extra_kwargs/to_kwargs, no _get_loss_class mapping.
build_loss_fn(loss, **kwargs) is the single entry point, dispatching on:
- a typed LossConfig instance -> native path (delegates to config.build());
rejects stray **kwargs.
- a dotted path / class + **kwargs -> integration / YAML escape hatch; unknown
losses work without a typed config.
Guards reject a config passed with kwargs and a config class passed instead of
an instance. Update _component_builders + loss/__init__ + tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(components): single-file style for checkpoint/distributed/loggers/training
Eliminate the config.py + api.py two-file split in the four remaining components,
matching the optim/loss style:
- training: StepSchedulerConfig + StepSchedulerConfig.build() -> step_scheduler.py
- loggers: Wandb/MLflow/Comet configs each gain .build(); -> loggers/loggers.py
(closed, section-named set: no free builder / dispatcher needed)
- checkpoint: build_checkpoint_config -> config.py (kept light; checkpointing.py
is the heavy engine and imports CheckpointingConfig from here)
- distributed: build_distributed -> init_utils.py (beside initialize_distributed);
config.py untouched (central strategy-config module, 16+ importers)
Loggers and step-scheduler put build() on the config (no _target_ dispatch, no
cross-cutting orchestration). _component_builders constructs the typed config and
calls .build(); its recipe-facing names/signatures are unchanged so recipes and
sibling re-exports are unaffected. No api.py remains under components/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim,loss): drop dotted-path resolution from build_optimizer/build_loss_fn
The dotted-path string branch (_resolve_dotted_path) was the YAML _target_
convention leaking into the component layer, and was exercised only by tests —
the recipe path passes an already-resolved callable via _component_builders.
build_optimizer / build_loss_fn now accept only a typed config or a resolved
class/callable (OptimizerConfig|Callable, LossConfig|Callable); a non-callable
raises TypeError directing the caller to resolve dotted paths itself. Components
now carry zero _target_/importlib knowledge. Update tests to pass the class
object instead of a dotted-path string.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): move build_lr_scheduler onto LRSchedulerConfig.build()
The LR scheduler has no _target_ dispatch and no special orchestration, so it
fits the config.build() pattern like step-scheduler/loggers. Replace the free
build_lr_scheduler(config, optimizer, step_scheduler) with
LRSchedulerConfig.build(optimizer, step_scheduler), which always builds; the
"disabled" case (cfg is None -> None) moves to the _component_builders adapter
where YAML `lr_scheduler: null` is interpreted.
build_optimizer is now the only free function in the optim component (it alone
needs the config-or-callable dispatch + Dion/Megatron orchestration).
_component_builders.build_lr_scheduler keeps its name/signature, so recipes and
sibling re-exports are unaffected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* fix(recipes): strip runtime args from step_scheduler YAML block before typed config
build_step_scheduler built StepSchedulerConfig(**_as_dict(cfg)) directly, but
real YAML step_scheduler blocks carry local_batch_size (a runtime arg passed
separately to .build()), which is not a StepSchedulerConfig field — so it
crashed with TypeError on configs like gptoss/deepseekv3/nanogpt.
Regression introduced when train_ft was routed through
_component_builders.build_step_scheduler (the prior recipe-local builder fed
StepScheduler directly, which does accept local_batch_size). Pop the runtime
sizing args (local_batch_size, dp_size, dataloader) before constructing the
typed config. Add a regression test with local_batch_size in the YAML block.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): typed RecipeConfig boundary for train_ft (reference)
Reviewer-requested structure: __init__ converts YAML to a typed container;
setup/methods consume it and call .build() there (not in a per-call adapter).
- Add recipes/_typed_config.py: RecipeConfig wraps the raw ConfigNode and exposes
wandb / mlflow / step_scheduler / lr_scheduler as typed component configs
(cached_property); everything else (model, optimizer, loss, ... — _target_-based
or untyped) falls through to the raw ConfigNode. YAML-schema knowledge (e.g.
stripping the step_scheduler block's runtime keys) lives here, not in components.
- train_ft.__init__ wraps cfg in RecipeConfig; setup calls
self.cfg.<section>.build(...) for those four sections.
- dist_env (build_distributed), comet (comet_utils), optimizer/loss/checkpoint
(_target_-based) remain via their builders for now — documented exceptions to
extend in the full migration. The corresponding _component_builders names stay
re-exported from train_ft for sibling recipes (noqa).
This is a reference for one recipe; the other recipes + remaining sections follow
the same shape. _component_builders is retired as recipes migrate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(checkpoint): add CheckpointingConfig.build() and rewire recipes
Give CheckpointingConfig a `.build(dp_rank, tp_rank, pp_rank, moe_mesh)
-> Checkpointer` method so the checkpoint component follows the same
`config.build()` surface as the optim/loss/loggers/training components.
Checkpointer is imported lazily inside build() to avoid the
config.py<->checkpointing.py circular import.
Rewire all 7 recipes (train_ft, vlm/finetune, train_seq_cls, diffusion,
retrieval/bi_encoder, eagle1, eagle3) to call `<config>.build(...)`
instead of constructing `Checkpointer(...)` directly, and drop the
now-unused `Checkpointer` imports.
Update the three recipe tests that stubbed `build_checkpoint_config` to
expose a `.build()` on the stub config; the recipes no longer reference
the `Checkpointer` symbol so the separate Checkpointer monkeypatches are
removed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(distributed): move build_distributed out of the component into the recipe adapter
build_distributed parses a YAML `dist_env` dict (backend/timeout_minutes)
and delegates to initialize_distributed. Per the component-purity rule,
config-dict parsing must not live in components/, so move build_distributed
to recipes/_component_builders.py alongside the other YAML->component
adapters (build_optimizer/build_loss_fn/build_checkpoint_config/...).
The component now exposes only initialize_distributed (explicit kwargs);
build_distributed is dropped from the distributed package's public surface.
train_ft and vlm/finetune import it from the adapter; sibling recipes and
functional tests that import it via train_ft keep working through the
re-export, so no call sites or tests change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* Revert "refactor(distributed): move build_distributed out of the component into the recipe adapter"
This reverts commit 1260de9.
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): type optimizer/loss/checkpoint in RecipeConfig; migrate train_ft (Stage 1)
Make RecipeConfig the home for YAML->component resolution. Add OptimizerSpec,
LossSpec and CheckpointSpec (recipe-layer wrappers that resolve _target_ /
assemble checkpoint kwargs and delegate to the pure component builders), and
expose cfg.optimizer / cfg.loss_fn / cfg.checkpoint cached_properties. The
_target_ resolution helpers (_as_dict, _callable_and_kwargs, _model_name_from_cfg)
are relocated into _typed_config.py; components stay YAML-free.
train_ft.setup() now calls self.cfg.loss_fn.build() / self.cfg.optimizer.build(...)
/ self.cfg.checkpoint.build(...) instead of the free builders. The
_component_builders import block is reduced to a transitional re-export shim
(build_checkpoint_config/build_lr_scheduler/build_optimizer/build_step_scheduler)
so the not-yet-migrated sibling recipes keep working; removed in Stage 3.
test_train_ft setup stubs now patch OptimizerSpec/LossSpec/CheckpointSpec.build
instead of train_ft.build_*. Direct resolution tests still use the shim
re-export and migrate in Stage 3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): migrate vlm/seq_cls/diffusion/retrieval to RecipeConfig (Stage 2)
Wrap each recipe's cfg in RecipeConfig in __init__ and consume the typed
sections in setup(): optimizer/loss_fn/checkpoint/wandb/mlflow/step_scheduler/
lr_scheduler now go through self.cfg.<section>.build(...) instead of the free
_component_builders functions. Drop the per-recipe builder imports.
Gotchas handled:
- hasattr(cfg, "wandb"/"mlflow") -> cfg.wandb/.mlflow is not None (the typed
accessors always exist and return None when the section is absent).
- retrieval keeps self.cfg.get("optimizer").instantiate(params=...) (raw node)
for its param-group optimizer construction (OptimizerSpec has no .instantiate).
- diffusion reads step_scheduler.local_batch_size (a runtime key absent from the
typed StepSchedulerConfig) via self.cfg.get("step_scheduler.local_batch_size").
- diffusion keeps its own local build_lr_scheduler and direct StepScheduler().
Tests: vlm-helpers/cp_wiring setup stubs now patch the spec/config .build
methods (LossSpec/OptimizerSpec/CheckpointSpec/StepSchedulerConfig/
LRSchedulerConfig.build). The behavior tests that exercise the builders are
repointed to import them from _component_builders (still present until Stage 3).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): delete _component_builders.py; finalize tests on RecipeConfig (Stage 3)
All recipes now consume the typed RecipeConfig, so the free builder module is
removed along with the transitional re-export shim in train_ft. The YAML/_target_
resolution that lived in _component_builders now lives only in _typed_config
(specs + _as_dict/_callable_and_kwargs/_model_name_from_cfg) and the pure
component builders.
Behavior tests that exercised the old builders are repointed onto the production
path: small module-level helpers in test_train_ft.py / test_finetune_vlm_helpers.py
mirror RecipeConfig.<section>.build via OptimizerSpec/CheckpointSpec/
StepSchedulerConfig/LRSchedulerConfig, so the test bodies are unchanged. The
step_scheduler "_target_" test now asserts _target_ is dropped by the typed
boundary (RecipeConfig drops it via _section_kwargs) rather than rejected.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(recipes): import build_distributed/calculate_loss from their components, not train_ft
These two names only lived in train_ft as re-exports; point consumers at their
real home so sibling recipes stop reaching into train_ft for them:
- build_distributed -> nemo_automodel.components.distributed.init_utils
(diffusion/train, retrieval/train_bi_encoder, llm/train_seq_cls)
- calculate_loss -> nemo_automodel.components.loss.utils (llm/kd)
Also repoint the functional tests that imported these from train_ft
(megatron data/checkpoint tests for build_distributed; checkpoint dcp/peft/
hf_sharded/hf_consolidated_llm tests for calculate_loss), keeping the other
names (build_dataloader, TrainFinetuneRecipeForNextTokenPrediction) sourced
from train_ft unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): unify optimizer config normalization and type the bf16 Adam warning
Replace the recipe-layer OptimizerSpec with a single component-level
build_optimizer_config that normalizes any optimizer target (OptimizerConfig
instance/subclass, registry name or import path, or optimizer callable) into an
OptimizerConfig. RecipeConfig.optimizer now returns the typed OptimizerConfig
directly, and build_optimizer delegates string/tuple resolution to it. Add the
Dion-family configs and move Megatron-FSDP optimizer sharding into
maybe_shard_optimizer.
Type warn_if_torch_adam_with_bf16_params' optimizer_cfg as OptimizerConfig and
detect torch Adam/AdamW via the typed config (OptimizerFromFactoryConfig.factory
and AdamConfig/AdamWConfig) instead of a raw _target_ node; update recipe call
sites accordingly.
Update unit tests for the new build_optimizer_config API and the typed warning.
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
* refactor(loss): typed loss configs with registry and unified builder
Mirror the optimizer refactor for losses: add a LossConfig hierarchy
(MaskedCrossEntropy, FusedLinearCE, TEParallelCE, KDLoss) exposing each
loss's full constructor surface as typed fields, plus LossFromFactoryConfig
as the escape hatch for arbitrary callables.
- Add LOSS_CONFIG_REGISTRY and build_loss_config to normalize a LossConfig
instance, a registry name, or a loss class/callable into a LossConfig,
upgrading registered classes to their typed config when kwargs fit and
falling back to the factory wrapper otherwise.
- Rename build_loss_fn -> build_loss_module (thin build_loss_config(...).build()).
- Deprecate LossSpec: RecipeConfig.loss_fn now returns a LossConfig directly.
- Update tests, docs, and the recipe-development skill accordingly.
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
* refactor(checkpoint): construct CheckpointingConfig directly, drop builder + spec
Remove the component-level build_checkpoint_config and the recipe-layer
CheckpointSpec wrapper in favor of constructing CheckpointingConfig directly.
- Give every CheckpointingConfig field a default and move the cache-dir
fallback and the PEFT/torch_save -> safetensors fallback into __post_init__.
- RecipeConfig.checkpoint now returns a CheckpointingConfig, filling the
model-derived model_repo_id/model_cache_dir/is_peft from the surrounding
YAML (and dropping restore_from, which is consumed separately).
- Recipes use self.cfg.checkpoint directly instead of .build(cache_dir, ...).
- Update checkpoint/recipe tests and the recipe-development skill doc.
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
* refactor(distributed): drop build_distributed wrapper, call initialize_distributed directly
build_distributed only read backend/timeout_minutes from the dist_env dict and
delegated, so remove the wrapper and have recipes call the pure
initialize_distributed entry point directly (matching what eagle1/eagle3
already do; defaults preserved as nccl / 1). De-export it from the distributed
package __all__ and drop the now-unused `Any` import in init_utils.
Inlined in: train_ft, vlm/finetune, train_seq_cls, retrieval/train_bi_encoder,
diffusion. retrieval/mine_hard_negatives keeps its own local build_distributed
(separate recipe-local helper). Tests repoint the build_distributed monkeypatches
to initialize_distributed, drop the wrapper-only test_setup_initializes_dist_env,
and inline the two functional megatron tests.
Also drop a stray import of the non-existent
nemo_automodel.components.datasets.loader.Dataloader (and its use as a base class
of DummyIterableDataset) in test_train_ft.py, which was breaking test collection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(loss): add typed MTPLossConfig; build MTP via the typed boundary
Represent the Multi-Token-Prediction auxiliary loss as a typed MTPLossConfig
(scaling_factor / ignore_index) in components/loss/mtp.py, consistent with the
other typed loss/optimizer/checkpoint configs. PipelineCausalLMLoss gains
optional scaling_factor/ignore_index whose defaults preserve the existing
model-driven behavior (scaling_factor=None -> model value; ignore_index=-100).
RecipeConfig exposes cfg.mtp as a default MTPLossConfig — MTP params are
model-driven and intentionally not exposed via YAML, so no mtp: block is read
and no config schema changes. train_ft and vlm/finetune now build the
pipeline-schedule MTP loss via cfg.mtp.build(...) instead of constructing
PipelineCausalLMLoss directly. Behavior is identical for all existing configs.
test_train_ft imports PipelineCausalLMLoss from the component.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* fix(recipes): recognize RecipeConfig in base_recipe checkpoint I/O + benchmark step_scheduler access
Two regressions from the RecipeConfig refactor, surfaced by a 3-step smoke sweep
(unit tests missed them: they stub the checkpointer and don't run the benchmark
recipe).
- base_recipe.py: state-tracking, checkpoint-save config discovery, and load
only recognized ConfigNode, so self.cfg (now a RecipeConfig) was neither
tracked nor found, and save_checkpoint hit "AttributeError: 'NoneType' object
has no attribute 'raw_config'". Recognize RecipeConfig alongside ConfigNode at
the 3 sites (RecipeConfig.raw_config already delegates to the ConfigNode).
- llm/benchmark.py: read self.cfg.step_scheduler.local_batch_size off the typed
StepSchedulerConfig (which strips runtime keys) at 3 sites -> use
self.cfg.get("step_scheduler.local_batch_size"), matching the other recipes.
Verified by 3-step smokes (8x H100): qwen3.5-4b VL, qwen3.5-35b-A3B VL,
qwen3-30b-A3B, qwen3.5-35b-A3B benchmark, moonlight-16b, qwen2.5-7b, llama3.1-8b
all train + checkpoint cleanly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* test(benchmark): make ConfigNamespace.get resolve dotted keys (match RecipeConfig.get)
The benchmark.py fix (8fed671) switched step_scheduler.local_batch_size to
self.cfg.get("step_scheduler.local_batch_size"); the test shim's .get only did
flat getattr, so it returned None and broke 9 benchmark tests. Make the shim
resolve dotted keys like the real ConfigNode/RecipeConfig.get.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* fix(optim): epoch_len-based LR scheduler, dion _target_ routing, foreach gating
- LRSchedulerConfig.build: derive total_steps from step_scheduler.epoch_len
(already grad-acc'd + ceil'd) instead of calling len(dataloader)
unconditionally, which crashed on iterable/streaming datasets; fall back to
max_steps and raise a clear ValueError when neither is set.
- build_optimizer_config: route a resolved dion.* class (YAML _target_: dion.Muon)
to its typed Muon/NorMuon/Dion2/Dion config (parameter grouping) instead of the
flat-params factory escape hatch.
- OptimizerFromFactoryConfig.build: only inject foreach when the factory signature
accepts it (TE FusedAdam under TP>1 no longer gets a spurious foreach).
- Fold the four near-identical dion builds into _DionConfigBase: strips
grouping-only fields (scalar_*/*_lr) before the dion ctor and asserts Dion is
incompatible with Megatron-FSDP sharding via maybe_shard_optimizer(allow=False)
instead of silently returning an unsharded optimizer; add scalar_lr/embed_lr/
lm_head_lr and Muon nesterov/flatten/use_triton fields.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* fix(loggers): wandb passthrough, wire CometConfig, dedup configure_mlflow
- WandbConfig: forward arbitrary wandb.init kwargs (mode/dir/...) via an `extra`
passthrough + from_kwargs() that routes unknown keys there; drop the save_dir
field so wandb's native `dir` flows through. The closed dataclass previously
crashed RecipeConfig.wandb on any non-field key.
- CometConfig: fold the model-tag + experiment-name derivation into build(model_name)
and wire it through RecipeConfig.comet + train_ft (self.cfg.comet.build(...)).
build_comet is now a thin back-compat shim over the typed config.
- configure_mlflow: reduce to a back-compat shim that builds an MLflowConfig from a
raw cfg and delegates to MLflowConfig.build (the single implementation), removing
the duplicate ~100-line reimplementation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* fix(checkpoint): clarify PEFT+torch_save coercion preserves other fields
The PEFT + torch_save path flips only model_save_format -> safetensors and
save_consolidated -> True; all other user-set fields (staging_dir,
single_rank_consolidation, v4_compatible, ...) are preserved. Reword the now
misleading "preserving checkpoint_dir" docstring/warning and add a test asserting
those fields survive the coercion.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* test(loss): cover MTPLossConfig defaults and scaling_factor override
Asserts MTPLossConfig() defaults (scaling_factor None, ignore_index -100) and that
MTPLossConfig(scaling_factor=0.5).build(loss_fn, model) overrides the model-provided
factor while the default falls back to it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
* refactor(optim): keep optim free of distributed/training cross-imports
Resolve the two import-linter "Components must not import each other"
violations and the ty errors on the optim component, on top of the dion-config
restructure.
- Move all Megatron-FSDP optimizer sharding out of OptimizerConfig /
_DionConfigBase / OptimizerFromFactoryConfig build() into the recipe layer
(recipes/_dist_setup.shard_optimizers_for_megatron_fsdp); optim no longer
imports components.distributed.megatron_fsdp. supports_megatron_fsdp_sharding
(True for Adam/factory, False for dion) is threaded to the helper as `allow`,
preserving the "Dion cannot use Megatron-FSDP" assert at the recipe layer.
- Move precision_warnings into components/optim and emit the bf16/torch-Adam
warning from build() (object-based detection), dropping the
components.training -> components.optim import. Recipes building via
cfg.optimizer.build no longer call it explicitly.
- Fix ty: dion-family config Protocol, Optimizer-narrowed list in
LRSchedulerConfig.build, and a mesh_dim_names None-guard in _foreach_for_mesh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
* fix(recipes): guard supports_megatron_fsdp_sharding lookup; repoint stale diffusion test patch
Fix L0_Unit_Tests_CPU failures:
- Recipes read cfg.optimizer.supports_megatron_fsdp_sharding, which broke unit
tests that stub cfg.optimizer as a SimpleNamespace. Use getattr(..., True) so
stubs default to shard-capable; the recipe-layer shard helper is a no-op for
non-MegatronFSDP configs (distributed_config=None in those tests).
- test_diffusion_train_metrics monkeypatched diffusion.train.build_distributed,
which an earlier branch commit replaced with initialize_distributed; repoint
the patch. Pre-existing breakage that only surfaced now that the import-linter
fix lets the test stage run.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
* test(train_ft): stop forwarding distributed_config in the build_optimizer helper
L0_Unit_Tests_GPU: 7 GPU-marked tests in test_train_ft.py call the local
build_optimizer helper, which still forwarded distributed_config to
OptimizerConfig.build() (removed in the optim refactor) -> TypeError. Drop the
forward (matches the equivalent fix already applied to test_finetune_vlm_helpers).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
* fix(recipes): assign self.optimizer once (shard a local, not a re-assign)
L2 functional tests (HF_Transformer/PEFT/DCP, Pretrain, KD, Seq_Cls) crashed with
"RuntimeError: State key 'optimizer' is already tracked". BaseRecipe.__setattr__
state-tracks self.optimizer (is_optimizer), and the recipe assigned it twice: once
from cfg.optimizer.build(), then again from the Megatron-FSDP shard helper. Build
into a local `optimizer` and assign self.optimizer exactly once (the sharded list).
Unit tests passed earlier because their SimpleNamespace stub optimizers aren't
detected by is_optimizer, so neither assignment was tracked.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
* style(recipes): collapse single-line optimizer build (ruff format)
The previous fix shortened `self.optimizer = ...build(` to `optimizer = ...build(`,
which now fits in 120 cols, so ruff format collapses the call to one line. Pure
formatting; no behavior change. (Fixes the `linting` job; everything else was green.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
---------
Signed-off-by: HuiyingLi <willwin.lee@gmail.com>
Signed-off-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Alexandros Koumparoulis <akoumparouli@nvidia.com>
Co-authored-by: Alexandros Koumparoulis <153118171+akoumpa@users.noreply.github.com>1 parent e2c8895 commit 1f4f83e
52 files changed
Lines changed: 3627 additions & 1672 deletions
File tree
- docs/guides/llm
- fern/versions
- nightly/pages/guides/llm
- v0.4/pages/guides/llm
- nemo_automodel
- components
- checkpoint
- distributed
- loggers
- loss
- optim
- training
- recipes
- diffusion
- llm
- retrieval
- vlm
- skills/nemo-automodel-recipe-development
- tests
- functional_tests
- checkpoint
- training
- unit_tests
- checkpoint
- components/optim
- loggers
- loss
- optim
- recipes
- llm
- training
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
103 | | - | |
| 103 | + | |
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
106 | | - | |
| 106 | + | |
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
106 | | - | |
| 106 | + | |
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
19 | 22 | | |
20 | 23 | | |
21 | 24 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
22 | 21 | | |
23 | 22 | | |
24 | 23 | | |
| |||
32 | 31 | | |
33 | 32 | | |
34 | 33 | | |
35 | | - | |
36 | 34 | | |
37 | 35 | | |
38 | 36 | | |
| |||
72 | 70 | | |
73 | 71 | | |
74 | 72 | | |
| 73 | + | |
| 74 | + | |
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | 81 | | |
111 | 82 | | |
112 | 83 | | |
| |||
128 | 99 | | |
129 | 100 | | |
130 | 101 | | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | 102 | | |
139 | 103 | | |
140 | 104 | | |
| |||
207 | 171 | | |
208 | 172 | | |
209 | 173 | | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
216 | | - | |
217 | | - | |
218 | | - | |
219 | | - | |
220 | | - | |
221 | | - | |
222 | | - | |
223 | | - | |
224 | | - | |
225 | | - | |
226 | | - | |
227 | | - | |
228 | | - | |
229 | | - | |
230 | | - | |
231 | | - | |
232 | | - | |
233 | | - | |
234 | | - | |
235 | | - | |
236 | | - | |
237 | | - | |
238 | | - | |
239 | | - | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | 174 | | |
270 | 175 | | |
271 | 176 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
0 commit comments