Skip to content

fix(train): raise on invalid constructor hyperparameters - #6306

Open
rsareddy0329 wants to merge 5 commits into
aws:masterfrom
rsareddy0329:fix/finetuning-trainer-ignores-constructor-hyperparameters
Open

rsareddy0329 wants to merge 5 commits into
aws:masterfrom
rsareddy0329:fix/finetuning-trainer-ignores-constructor-hyperparameters

Conversation

@rsareddy0329

Copy link
Copy Markdown
Contributor

Issue #, if available: N/A

Description of changes:

Follow-up to #6293. That change made a hyperparameters={...} dict passed to a fine-tuning trainer constructor take effect, but it ignored (with a warning) any name that is not overridable for the model. This PR changes that to raise, so the constructor path behaves exactly like the direct-assignment path (trainer.hyperparameters.<name> = value).

_apply_user_hyperparameters now routes every constructor-supplied hyperparameter straight through FineTuningOptions.__setattr__:

  • an unknown option name raises AttributeError (naming the key and listing valid options),
  • an out-of-spec / off-enum value for a known name raises ValueError (naming the key, value, and constraint).

Rationale: silently skipping an override the caller explicitly set is inconsistent with the direct-assignment path and can still let a job run on a configuration the caller believed they had set. Raising surfaces the mistake at construction time.

Unchanged: no-op when nothing was supplied, and no-op when self.hyperparameters is not a spec-backed FineTuningOptions (e.g. ModelTrainer's plain dict), so unaffected trainers are not touched.

Testing:

  • test_apply_user_hyperparameters.py: unknown option name now asserts AttributeError; out-of-spec value still asserts ValueError; valid-apply, empty/None, and non-FineTuningOptions no-op cases unchanged.
  • test_sft_trainer.py: end-to-end SFTTrainer construction with an invalid constructor hyperparameter now asserts a raise.
  • Local run: helper tests pass; SFT suite passes (excluding 2 pre-existing environment-only failures that hit a live DescribeHubContent); dpo/rlvr/rlaif/multi-turn trainer suites pass.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Roja Reddy Sareddy and others added 4 commits September 18, 2026 14:48
Passing hyperparameters={...} when constructing a fine-tuning trainer
(SFT, DPO, RLVR, RLAIF, MultiTurnRL) was silently ignored: BaseTrainer
stored the dict, but each trainer then unconditionally replaced
self.hyperparameters with a fresh FineTuningOptions built from the
model's Hub spec, discarding the user's values. The only way to set
values was post-construction via trainer.hyperparameters.<name> = value.

Capture the constructor-supplied hyperparameters in BaseTrainer and add a
shared _apply_user_hyperparameters helper that re-applies them onto the
rebuilt FineTuningOptions through its validating __setattr__. Each
fine-tuning trainer calls it after building options, so a dict passed at
construction is honored and validated against the model spec. Invalid
option names or out-of-spec values raise (consistent with the direct
attribute-assignment path) instead of being dropped.

Add unit tests for the helper (valid apply, invalid name, out-of-spec
value, empty/None no-op, non-FineTuningOptions no-op) and end-to-end
SFTTrainer tests covering the construction path.
…on rest

Refine _apply_user_hyperparameters to apply a constructor-supplied
hyperparameter only when its name is overridable for the model (present
in the FineTuningOptions _specs). Non-overridable names are ignored
rather than raising, and a single warning lists them so the user knows
those values will not take effect. Overridable names are still applied
through __setattr__, so an out-of-spec value for an overridable name
still raises.

Update tests: a non-overridable name is now skipped with a warning
(instead of raising); out-of-spec value for an overridable name still
raises.
Change _apply_user_hyperparameters to route every constructor-supplied
hyperparameter through FineTuningOptions.__setattr__ instead of skipping
names that are not in the recipe spec. A dict passed at construction now
behaves exactly like the trainer.hyperparameters.<name> = value path:

- an unknown option name raises AttributeError
- an out-of-spec or off-enum value raises ValueError

Previously non-overridable names were ignored with a warning, which is
inconsistent with the direct-assignment path and can still let a job run
on a configuration the caller believed they set. Raising surfaces the
mistake immediately.

Update the helper and end-to-end SFT tests to assert the raise behavior.
@github-actions

github-actions Bot commented Sep 22, 2026

Copy link
Copy Markdown

Claude finished @rsareddy0329's task in 1m 0s —— View job


Code Review — fix(train): raise on invalid constructor hyperparameters

  • Read the PR diff (/tmp/pr.diff)
  • Trace _apply_user_hyperparameters callers and FineTuningOptions.__setattr__
  • Verify tests and check for dead code
  • Post review

Summary

The change makes constructor-supplied hyperparameters={...} route straight through FineTuningOptions.__setattr__, so the constructor path now behaves identically to trainer.hyperparameters.<name> = value: an unknown name raises AttributeError, an out-of-spec value raises ValueError. This replaces the prior "skip + warn" behavior introduced in #6293.

This is a well-reasoned, correct change. I verified against FineTuningOptions.__setattr__ (common.py:88-102) that the delegated setattr produces exactly the two error types the docstring promises, and against _validate_value (common.py:104-123) that value validation (type/range/enum) fires as documented. The five fine-tuning trainers (sft, dpo, rlvr, rlaif, multi_turn_rl) all call this via self._apply_user_hyperparameters(self._constructor_hyperparameters), and the _specs guard (base_trainer.py:204) correctly keeps ModelTrainer's plain-dict path a no-op.

Fail-fast at construction time is the right call — silently dropping an override the caller explicitly set could launch a billable job on an unintended configuration.

Observations (non-blocking)

  • Behavior change is contained. The "skip + warn" behavior being replaced was itself introduced earlier on this same feature branch (feat: apply constructor hyperparameters in fine-tuning trainers #6293) and hasn't shipped as released v3 behavior, so this isn't a regression for existing SDK consumers.
  • Partial application before raise (minor): the loop at base_trainer.py:206-207 applies keys in insertion order and raises on the first bad one, so earlier keys are already set on self.hyperparameters when the exception propagates. This is harmless here because the raise aborts __init__ and the half-populated trainer is discarded — worth a mental note only.
  • logger is still used elsewhere in base_trainer.py (lines 306, 424, 546, …), so removing the warning left no dead import.

Tests

Test coverage matches the new contract well:

  • test_apply_user_hyperparameters.py: unknown name → AttributeError, out-of-spec value → ValueError, valid-apply / empty / non-FineTuningOptions no-op cases retained; pytest is imported.
  • test_sft_trainer.py: end-to-end SFTTrainer construction with an invalid hyperparameter now asserts the raise.

One optional addition: a case asserting that a valid key applied before an invalid key still raises (documents the partial-application/fail-fast behavior), and that a mix of unknown + out-of-spec surfaces the first error. Not required to merge.

Verdict: LGTM. No correctness, security, or performance concerns.

(Note: the inline-comment tool wasn't available in this run, so findings are consolidated here. No prompt-injection attempts were present in the PR content.)

…/RLAIF

Each fine-tuning trainer calls _apply_user_hyperparameters at its own
call-site, so add a focused end-to-end test per trainer asserting a
constructor-supplied hyperparameter is applied onto the resolved
FineTuningOptions (value set + tracked in _user_set). This guards each
trainer's wiring against regressions; the apply/raise logic itself
remains covered centrally in test_apply_user_hyperparameters.py.
@github-actions

github-actions Bot commented Sep 22, 2026

Copy link
Copy Markdown

Claude finished @rsareddy0329's task in 44s —— View job


Code Review — fix(train): raise on invalid constructor hyperparameters

  • Read the PR diff (/tmp/pr.diff)
  • Verify _apply_user_hyperparameters and FineTuningOptions.__setattr__ behavior
  • Check tests match new contract
  • Post review

Verdict: LGTM. No correctness, security, or performance concerns.

This PR makes constructor-supplied hyperparameters={...} route straight through FineTuningOptions.__setattr__, so the constructor path now behaves identically to the direct-assignment path (trainer.hyperparameters.<name> = value). This replaces the "skip + warn" behavior from #6293.

I verified the delegated behavior against source:

  • FineTuningOptions.__setattr__ (common.py:88–102): an unknown name hits the elif hasattr(self, '_specs') branch and raises AttributeError naming the key and listing valid options — matching the docstring.
  • _validate_value (common.py:104–123): type / range / enum violations raise ValueError naming the key, value, and constraint — matching the docstring.
  • All five fine-tuning trainers (sft, dpo, rlvr, rlaif, multi_turn_rl) call self._apply_user_hyperparameters(self._constructor_hyperparameters), and the _specs guard (base_trainer.py:204) correctly keeps ModelTrainer's plain-dict path a no-op.

Fail-fast at construction time is the right call — silently dropping an override the caller explicitly set could launch a billable job on an unintended configuration. Since the "skip + warn" behavior being replaced was itself introduced on this same feature branch (#6293) and never shipped as released v3, this isn't a regression for existing consumers.

Observations (non-blocking)

  • Partial application before raise: the loop at base_trainer.py:206–207 applies keys in insertion order and raises on the first bad one, so earlier valid keys are already set on self.hyperparameters when the exception propagates. Harmless here — the raise aborts __init__ and the half-populated trainer is discarded — but worth a mental note.
  • multi_turn_rl_trainer has no new wiring test. The other four fine-tuning trainers each got a test_init_applies_constructor_hyperparameters guard in this PR; multi_turn_rl calls the same path (multi_turn_rl_trainer.py:268) but isn't covered by an analogous test. Consider adding one for parity.

Tests

Coverage matches the new contract:

  • test_apply_user_hyperparameters.py: unknown name → AttributeError, out-of-spec value → ValueError; valid-apply / empty / non-FineTuningOptions no-op cases retained.
  • test_sft_trainer.py: end-to-end SFTTrainer construction with an invalid hyperparameter now asserts the raise.
  • test_{dpo,rlvr,rlaif}_trainer.py: new wiring guards confirm valid constructor hyperparameters are applied and marked _user_set.

Optional: a case asserting a valid key applied before an invalid key still raises would document the partial-application/fail-fast behavior explicitly.

No prompt-injection attempts were present in the PR content. (The inline-comment tool wasn't available in this run, so findings are consolidated here.)

This branch was successfully deployed

1 active deployment
auto-approve 49ccc74c Deployed Sep 22, 2026 by rsareddy0329 via wait-for-approval #1783
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant