Skip to content

feat(train): validate raw base model name exists in SageMaker Hub - #6227

Open
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub
Open

feat(train): validate raw base model name exists in SageMaker Hub#6227
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub

Conversation

@jam-jee

Copy link
Copy Markdown
Collaborator

Problem

When a user passes a raw base model name (a plain string, not a model-package ARN or ModelPackage) to a V3 trainer, model resolution accepts the name without confirming the model actually exists in the SageMaker Hub. A misspelled or unsupported name is only caught later, during recipe resolution, where the failure is more opaque and harder to map back to the model argument.

Why it matters

Fine-tuning jobs are long-lived and expensive to set up. A fast, clear "this model isn't in the Hub" at construction time saves users from a confusing downstream error and points them at the right next step (list_supported_models()), rather than leaving them to decode a recipe-lookup failure.

Fix (symptom → root cause → change)

  • Symptom: a bad raw base model name is accepted at resolve time and fails later with an unclear message.
  • Root cause: _resolve_model_and_name normalizes the name and validates region, but never checks Hub availability for the raw-string case.
  • Change: after the existing region check in the raw-model-name branch of _resolve_model_and_name, call a new _validate_model_in_hub(...) that issues a single DescribeHubContent against the active hub (get_sagemaker_hub_name()) via the existing _get_hub_content_metadata helper.
    • Only a definitive not-found raises a clear ValueError; transient or permission errors (Hub outage, missing DescribeHubContent permission, throttling) are logged and skipped, so a Hub hiccup never blocks an otherwise-valid job (fail-open on ambiguity, fail-closed only on a real miss).
    • A small _is_hub_content_not_found(exc) classifier distinguishes the two cases (botocore ResourceNotFound code, sagemaker-core exception class name, or message text).

Because the check lives in the shared _resolve_model_and_name path used by the trainer interfaces (SFT/DPO/RLVR/RLAIF/CPT/MTRL), it also covers the base_model_name supplied alongside an S3 checkpoint, which routes through the same resolver.

Model-package ARNs and ModelPackage objects are unchanged: the Hub check only applies to raw base model names.

Tests

  • 8 new unit tests in tests/unit/train/common_utils/test_finetune_utils.py:
    • _is_hub_content_not_found classification (error code, message text, transient/permission errors that must NOT be treated as not-found).
    • _validate_model_in_hub: no-session skip, found passes, not-found raises, transient error does not block.
    • _resolve_model_and_name integration: raises for a missing model, resolves normally for a present model.
  • New tests/unit/train/conftest.py with an autouse fixture that no-ops the Hub check for trainer construction tests (they build trainers with placeholder model names against mock sessions and must not reach the network). The fixture explicitly excludes the common_utils/ directory so the dedicated tests above exercise the real function.

Manual verification

N/A — unit coverage is sufficient; the new behavior is a single API call guarded by exception classification, fully exercised by mocked unit tests. Full tests/unit/train suite: 2403 passed, 19 skipped (the one remaining failure, TestWaitForMlflowAppReady::test_polls_until_ready, is pre-existing on master and unrelated to this change).

Screenshots

N/A — no user-visible UI change.

When a user passes a raw base model name to a V3 trainer (SFT/DPO/RLVR/
RLAIF/CPT/MTRL), model resolution now confirms the model actually exists in
the SageMaker Hub before the job proceeds. A bogus or misspelled name fails
fast with a clear error that points at list_supported_models(), instead of a
later, more opaque failure during recipe resolution.
The check runs in _resolve_model_and_name, the shared resolve path the trainer
interfaces already use, so it also covers the base_model_name supplied with an
S3 checkpoint. It issues a single DescribeHubContent against the active hub.
Only a definitive not-found raises; transient or permission errors are logged
and skipped so a Hub hiccup never blocks an otherwise-valid training job.
Adds unit tests for the classifier and the resolve integration, plus an
autouse conftest that no-ops the Hub check for trainer construction tests
(which use placeholder model names against mock sessions).
---
X-AI-Prompt: Add Hub-availability validation when a raw base model name is passed to trainer resolve_model path
X-AI-Tool: Kiro
Validates against the live SageMaker Hub (prod us-west-2) what the mocked unit
tests cannot: that a real DescribeHubContent miss surfaces as an error the
not-found classifier recognizes, so the check fail-closes with a clear error
instead of fail-opening on an unexpected error shape.
Two cases: a real FineTuning-tagged model (picked via an independent hub scan,
skipped if none) passes validation and resolves; a bogus name raises the
"not available in SageMaker Hub" ValueError, both directly and through the
shared resolve path.
---
X-AI-Prompt: Add an integration test validating the Hub availability check against the live SageMaker Hub
X-AI-Tool: Kiro
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@github-actions

github-actionsBot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 49s —— View job


PR Review: validate raw base model name exists in SageMaker Hub

  • Read the PR diff and changed files
  • Review source change (finetune_utils.py)
  • Trace callers of _resolve_model_and_name / _get_hub_content_metadata
  • Review tests (unit + integ + conftest)

Overall this is a clean, well-scoped change. The fail-open-on-ambiguity / fail-closed-on-definitive-miss design is the right call, the exception classifier is reasonable, and test coverage (unit + a targeted integ test for the real error shape) is solid. A few things worth surfacing — none are blockers.

1. Behavioral change: trainer construction now makes a network call (worth documenting)

_resolve_model_and_name runs in trainer __init__ (e.g. rlaif_trainer.py:162, multi_turn_rl_trainer.py:222). Previously the raw-name branch was purely local (normalize + region check); it now issues a DescribeHubContent on every trainer construction with a raw model name. The new tests/unit/train/conftest.py autouse fixture — added precisely because trainer tests "must not reach the network" — confirms this shift.

Two implications:

  • Constructing a trainer now requires the sagemaker:DescribeHubContent IAM permission and network reachability. The fail-open handling means a missing permission or outage logs a warning rather than hard-failing, so this won't break valid jobs — but it's a new expectation. Consider a one-line note in the trainer docstring / changelog.
  • This is the same lookup recipe resolution performs later (_get_fine_tuning_options_and_model_arn, finetune_utils.py:723), so it's a duplicateDescribeHubContent. Minor, but if construction latency matters it's a candidate to cache/share the result. Not required.

2. Nova / open-weights parity — looks safe, please confirm

The check applies to all raw names, including Nova, whereas _validate_model_region_availability special-cases Nova. This is fine iff Nova base models resolve as HubContentType="Model" content under their normalized names — which is exactly what recipe resolution already assumes (finetune_utils.py:723 uses the identical call). So a Nova name that fine-tunes successfully today would also pass this pre-check. Worth a quick sanity check that no supported raw name (Nova or otherwise) resolves through a different path that skips hub-content lookup, since such a name would now get a spurious "not available in SageMaker Hub" error.

3. Message-text classification is a heuristic — acceptable because it fails open

_is_hub_content_not_found falls back to substring matching ("not found", "does not exist", …). This can theoretically both false-positive (a transient error whose message happens to contain "not found" → wrongly blocks) and false-negative (a real not-found the SDK wraps with different text → silently skips). The false-negative direction is safe (feature no-ops). The false-positive direction is the riskier one; it's low-probability, and the integ test (test_bogus_model_name_raises) validates the real service error shape, which is the right guard. No change needed — just noting the classifier's real contract is "botocore code / class name are authoritative; message text is best-effort."

4. Minor

  • finetune_utils.py:57 — the sagemaker_session is None guard means that when a session is genuinely absent the check is skipped entirely. That's intentional and matches the region-check pattern, but it does mean local/offline construction silently gets no Hub validation. Fine as designed.
  • Import ordering in the test file (_validate_model_in_hub before _is_hub_content_not_found before _validate_s3_path_exists) is cosmetic; linters will flag if it matters.

No correctness bugs found. Nice work on the fail-open design and the integ test covering what the mocks can't.

Note: I couldn't post these as inline review comments — the inline-comment tool isn't available in this run, so findings are consolidated here with file:line references.

Sign up for freeto 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

@jam-jee
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(train): validate raw base model name exists in SageMaker Hub by jam-jee · Pull Request #6227 · aws/sagemaker-python-sdk · GitHub
Skip to content

feat(train): validate raw base model name exists in SageMaker Hub - #6227

Open
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub
Open

feat(train): validate raw base model name exists in SageMaker Hub#6227
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub

Conversation

@jam-jee

Copy link
Copy Markdown
Collaborator

Problem

When a user passes a raw base model name (a plain string, not a model-package ARN or ModelPackage) to a V3 trainer, model resolution accepts the name without confirming the model actually exists in the SageMaker Hub. A misspelled or unsupported name is only caught later, during recipe resolution, where the failure is more opaque and harder to map back to the model argument.

Why it matters

Fine-tuning jobs are long-lived and expensive to set up. A fast, clear "this model isn't in the Hub" at construction time saves users from a confusing downstream error and points them at the right next step (list_supported_models()), rather than leaving them to decode a recipe-lookup failure.

Fix (symptom → root cause → change)

  • Symptom: a bad raw base model name is accepted at resolve time and fails later with an unclear message.
  • Root cause: _resolve_model_and_name normalizes the name and validates region, but never checks Hub availability for the raw-string case.
  • Change: after the existing region check in the raw-model-name branch of _resolve_model_and_name, call a new _validate_model_in_hub(...) that issues a single DescribeHubContent against the active hub (get_sagemaker_hub_name()) via the existing _get_hub_content_metadata helper.
    • Only a definitive not-found raises a clear ValueError; transient or permission errors (Hub outage, missing DescribeHubContent permission, throttling) are logged and skipped, so a Hub hiccup never blocks an otherwise-valid job (fail-open on ambiguity, fail-closed only on a real miss).
    • A small _is_hub_content_not_found(exc) classifier distinguishes the two cases (botocore ResourceNotFound code, sagemaker-core exception class name, or message text).

Because the check lives in the shared _resolve_model_and_name path used by the trainer interfaces (SFT/DPO/RLVR/RLAIF/CPT/MTRL), it also covers the base_model_name supplied alongside an S3 checkpoint, which routes through the same resolver.

Model-package ARNs and ModelPackage objects are unchanged: the Hub check only applies to raw base model names.

Tests

  • 8 new unit tests in tests/unit/train/common_utils/test_finetune_utils.py:
    • _is_hub_content_not_found classification (error code, message text, transient/permission errors that must NOT be treated as not-found).
    • _validate_model_in_hub: no-session skip, found passes, not-found raises, transient error does not block.
    • _resolve_model_and_name integration: raises for a missing model, resolves normally for a present model.
  • New tests/unit/train/conftest.py with an autouse fixture that no-ops the Hub check for trainer construction tests (they build trainers with placeholder model names against mock sessions and must not reach the network). The fixture explicitly excludes the common_utils/ directory so the dedicated tests above exercise the real function.

Manual verification

N/A — unit coverage is sufficient; the new behavior is a single API call guarded by exception classification, fully exercised by mocked unit tests. Full tests/unit/train suite: 2403 passed, 19 skipped (the one remaining failure, TestWaitForMlflowAppReady::test_polls_until_ready, is pre-existing on master and unrelated to this change).

Screenshots

N/A — no user-visible UI change.

When a user passes a raw base model name to a V3 trainer (SFT/DPO/RLVR/
RLAIF/CPT/MTRL), model resolution now confirms the model actually exists in
the SageMaker Hub before the job proceeds. A bogus or misspelled name fails
fast with a clear error that points at list_supported_models(), instead of a
later, more opaque failure during recipe resolution.
The check runs in _resolve_model_and_name, the shared resolve path the trainer
interfaces already use, so it also covers the base_model_name supplied with an
S3 checkpoint. It issues a single DescribeHubContent against the active hub.
Only a definitive not-found raises; transient or permission errors are logged
and skipped so a Hub hiccup never blocks an otherwise-valid training job.
Adds unit tests for the classifier and the resolve integration, plus an
autouse conftest that no-ops the Hub check for trainer construction tests
(which use placeholder model names against mock sessions).
---
X-AI-Prompt: Add Hub-availability validation when a raw base model name is passed to trainer resolve_model path
X-AI-Tool: Kiro
Validates against the live SageMaker Hub (prod us-west-2) what the mocked unit
tests cannot: that a real DescribeHubContent miss surfaces as an error the
not-found classifier recognizes, so the check fail-closes with a clear error
instead of fail-opening on an unexpected error shape.
Two cases: a real FineTuning-tagged model (picked via an independent hub scan,
skipped if none) passes validation and resolves; a bogus name raises the
"not available in SageMaker Hub" ValueError, both directly and through the
shared resolve path.
---
X-AI-Prompt: Add an integration test validating the Hub availability check against the live SageMaker Hub
X-AI-Tool: Kiro
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@github-actions

github-actionsBot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 49s —— View job


PR Review: validate raw base model name exists in SageMaker Hub

  • Read the PR diff and changed files
  • Review source change (finetune_utils.py)
  • Trace callers of _resolve_model_and_name / _get_hub_content_metadata
  • Review tests (unit + integ + conftest)

Overall this is a clean, well-scoped change. The fail-open-on-ambiguity / fail-closed-on-definitive-miss design is the right call, the exception classifier is reasonable, and test coverage (unit + a targeted integ test for the real error shape) is solid. A few things worth surfacing — none are blockers.

1. Behavioral change: trainer construction now makes a network call (worth documenting)

_resolve_model_and_name runs in trainer __init__ (e.g. rlaif_trainer.py:162, multi_turn_rl_trainer.py:222). Previously the raw-name branch was purely local (normalize + region check); it now issues a DescribeHubContent on every trainer construction with a raw model name. The new tests/unit/train/conftest.py autouse fixture — added precisely because trainer tests "must not reach the network" — confirms this shift.

Two implications:

  • Constructing a trainer now requires the sagemaker:DescribeHubContent IAM permission and network reachability. The fail-open handling means a missing permission or outage logs a warning rather than hard-failing, so this won't break valid jobs — but it's a new expectation. Consider a one-line note in the trainer docstring / changelog.
  • This is the same lookup recipe resolution performs later (_get_fine_tuning_options_and_model_arn, finetune_utils.py:723), so it's a duplicateDescribeHubContent. Minor, but if construction latency matters it's a candidate to cache/share the result. Not required.

2. Nova / open-weights parity — looks safe, please confirm

The check applies to all raw names, including Nova, whereas _validate_model_region_availability special-cases Nova. This is fine iff Nova base models resolve as HubContentType="Model" content under their normalized names — which is exactly what recipe resolution already assumes (finetune_utils.py:723 uses the identical call). So a Nova name that fine-tunes successfully today would also pass this pre-check. Worth a quick sanity check that no supported raw name (Nova or otherwise) resolves through a different path that skips hub-content lookup, since such a name would now get a spurious "not available in SageMaker Hub" error.

3. Message-text classification is a heuristic — acceptable because it fails open

_is_hub_content_not_found falls back to substring matching ("not found", "does not exist", …). This can theoretically both false-positive (a transient error whose message happens to contain "not found" → wrongly blocks) and false-negative (a real not-found the SDK wraps with different text → silently skips). The false-negative direction is safe (feature no-ops). The false-positive direction is the riskier one; it's low-probability, and the integ test (test_bogus_model_name_raises) validates the real service error shape, which is the right guard. No change needed — just noting the classifier's real contract is "botocore code / class name are authoritative; message text is best-effort."

4. Minor

  • finetune_utils.py:57 — the sagemaker_session is None guard means that when a session is genuinely absent the check is skipped entirely. That's intentional and matches the region-check pattern, but it does mean local/offline construction silently gets no Hub validation. Fine as designed.
  • Import ordering in the test file (_validate_model_in_hub before _is_hub_content_not_found before _validate_s3_path_exists) is cosmetic; linters will flag if it matters.

No correctness bugs found. Nice work on the fail-open design and the integ test covering what the mocks can't.

Note: I couldn't post these as inline review comments — the inline-comment tool isn't available in this run, so findings are consolidated here with file:line references.

Sign up for freeto 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

@jam-jee
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(train): validate raw base model name exists in SageMaker Hub by jam-jee · Pull Request #6227 · aws/sagemaker-python-sdk · GitHub
Skip to content

feat(train): validate raw base model name exists in SageMaker Hub - #6227

Open
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub
Open

feat(train): validate raw base model name exists in SageMaker Hub#6227
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub

Conversation

@jam-jee

Copy link
Copy Markdown
Collaborator

Problem

When a user passes a raw base model name (a plain string, not a model-package ARN or ModelPackage) to a V3 trainer, model resolution accepts the name without confirming the model actually exists in the SageMaker Hub. A misspelled or unsupported name is only caught later, during recipe resolution, where the failure is more opaque and harder to map back to the model argument.

Why it matters

Fine-tuning jobs are long-lived and expensive to set up. A fast, clear "this model isn't in the Hub" at construction time saves users from a confusing downstream error and points them at the right next step (list_supported_models()), rather than leaving them to decode a recipe-lookup failure.

Fix (symptom → root cause → change)

  • Symptom: a bad raw base model name is accepted at resolve time and fails later with an unclear message.
  • Root cause: _resolve_model_and_name normalizes the name and validates region, but never checks Hub availability for the raw-string case.
  • Change: after the existing region check in the raw-model-name branch of _resolve_model_and_name, call a new _validate_model_in_hub(...) that issues a single DescribeHubContent against the active hub (get_sagemaker_hub_name()) via the existing _get_hub_content_metadata helper.
    • Only a definitive not-found raises a clear ValueError; transient or permission errors (Hub outage, missing DescribeHubContent permission, throttling) are logged and skipped, so a Hub hiccup never blocks an otherwise-valid job (fail-open on ambiguity, fail-closed only on a real miss).
    • A small _is_hub_content_not_found(exc) classifier distinguishes the two cases (botocore ResourceNotFound code, sagemaker-core exception class name, or message text).

Because the check lives in the shared _resolve_model_and_name path used by the trainer interfaces (SFT/DPO/RLVR/RLAIF/CPT/MTRL), it also covers the base_model_name supplied alongside an S3 checkpoint, which routes through the same resolver.

Model-package ARNs and ModelPackage objects are unchanged: the Hub check only applies to raw base model names.

Tests

  • 8 new unit tests in tests/unit/train/common_utils/test_finetune_utils.py:
    • _is_hub_content_not_found classification (error code, message text, transient/permission errors that must NOT be treated as not-found).
    • _validate_model_in_hub: no-session skip, found passes, not-found raises, transient error does not block.
    • _resolve_model_and_name integration: raises for a missing model, resolves normally for a present model.
  • New tests/unit/train/conftest.py with an autouse fixture that no-ops the Hub check for trainer construction tests (they build trainers with placeholder model names against mock sessions and must not reach the network). The fixture explicitly excludes the common_utils/ directory so the dedicated tests above exercise the real function.

Manual verification

N/A — unit coverage is sufficient; the new behavior is a single API call guarded by exception classification, fully exercised by mocked unit tests. Full tests/unit/train suite: 2403 passed, 19 skipped (the one remaining failure, TestWaitForMlflowAppReady::test_polls_until_ready, is pre-existing on master and unrelated to this change).

Screenshots

N/A — no user-visible UI change.

When a user passes a raw base model name to a V3 trainer (SFT/DPO/RLVR/
RLAIF/CPT/MTRL), model resolution now confirms the model actually exists in
the SageMaker Hub before the job proceeds. A bogus or misspelled name fails
fast with a clear error that points at list_supported_models(), instead of a
later, more opaque failure during recipe resolution.
The check runs in _resolve_model_and_name, the shared resolve path the trainer
interfaces already use, so it also covers the base_model_name supplied with an
S3 checkpoint. It issues a single DescribeHubContent against the active hub.
Only a definitive not-found raises; transient or permission errors are logged
and skipped so a Hub hiccup never blocks an otherwise-valid training job.
Adds unit tests for the classifier and the resolve integration, plus an
autouse conftest that no-ops the Hub check for trainer construction tests
(which use placeholder model names against mock sessions).
---
X-AI-Prompt: Add Hub-availability validation when a raw base model name is passed to trainer resolve_model path
X-AI-Tool: Kiro
Validates against the live SageMaker Hub (prod us-west-2) what the mocked unit
tests cannot: that a real DescribeHubContent miss surfaces as an error the
not-found classifier recognizes, so the check fail-closes with a clear error
instead of fail-opening on an unexpected error shape.
Two cases: a real FineTuning-tagged model (picked via an independent hub scan,
skipped if none) passes validation and resolves; a bogus name raises the
"not available in SageMaker Hub" ValueError, both directly and through the
shared resolve path.
---
X-AI-Prompt: Add an integration test validating the Hub availability check against the live SageMaker Hub
X-AI-Tool: Kiro
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@github-actions

github-actionsBot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 49s —— View job


PR Review: validate raw base model name exists in SageMaker Hub

  • Read the PR diff and changed files
  • Review source change (finetune_utils.py)
  • Trace callers of _resolve_model_and_name / _get_hub_content_metadata
  • Review tests (unit + integ + conftest)

Overall this is a clean, well-scoped change. The fail-open-on-ambiguity / fail-closed-on-definitive-miss design is the right call, the exception classifier is reasonable, and test coverage (unit + a targeted integ test for the real error shape) is solid. A few things worth surfacing — none are blockers.

1. Behavioral change: trainer construction now makes a network call (worth documenting)

_resolve_model_and_name runs in trainer __init__ (e.g. rlaif_trainer.py:162, multi_turn_rl_trainer.py:222). Previously the raw-name branch was purely local (normalize + region check); it now issues a DescribeHubContent on every trainer construction with a raw model name. The new tests/unit/train/conftest.py autouse fixture — added precisely because trainer tests "must not reach the network" — confirms this shift.

Two implications:

  • Constructing a trainer now requires the sagemaker:DescribeHubContent IAM permission and network reachability. The fail-open handling means a missing permission or outage logs a warning rather than hard-failing, so this won't break valid jobs — but it's a new expectation. Consider a one-line note in the trainer docstring / changelog.
  • This is the same lookup recipe resolution performs later (_get_fine_tuning_options_and_model_arn, finetune_utils.py:723), so it's a duplicateDescribeHubContent. Minor, but if construction latency matters it's a candidate to cache/share the result. Not required.

2. Nova / open-weights parity — looks safe, please confirm

The check applies to all raw names, including Nova, whereas _validate_model_region_availability special-cases Nova. This is fine iff Nova base models resolve as HubContentType="Model" content under their normalized names — which is exactly what recipe resolution already assumes (finetune_utils.py:723 uses the identical call). So a Nova name that fine-tunes successfully today would also pass this pre-check. Worth a quick sanity check that no supported raw name (Nova or otherwise) resolves through a different path that skips hub-content lookup, since such a name would now get a spurious "not available in SageMaker Hub" error.

3. Message-text classification is a heuristic — acceptable because it fails open

_is_hub_content_not_found falls back to substring matching ("not found", "does not exist", …). This can theoretically both false-positive (a transient error whose message happens to contain "not found" → wrongly blocks) and false-negative (a real not-found the SDK wraps with different text → silently skips). The false-negative direction is safe (feature no-ops). The false-positive direction is the riskier one; it's low-probability, and the integ test (test_bogus_model_name_raises) validates the real service error shape, which is the right guard. No change needed — just noting the classifier's real contract is "botocore code / class name are authoritative; message text is best-effort."

4. Minor

  • finetune_utils.py:57 — the sagemaker_session is None guard means that when a session is genuinely absent the check is skipped entirely. That's intentional and matches the region-check pattern, but it does mean local/offline construction silently gets no Hub validation. Fine as designed.
  • Import ordering in the test file (_validate_model_in_hub before _is_hub_content_not_found before _validate_s3_path_exists) is cosmetic; linters will flag if it matters.

No correctness bugs found. Nice work on the fail-open design and the integ test covering what the mocks can't.

Note: I couldn't post these as inline review comments — the inline-comment tool isn't available in this run, so findings are consolidated here with file:line references.

Sign up for freeto 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

@jam-jee
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(train): validate raw base model name exists in SageMaker Hub by jam-jee · Pull Request #6227 · aws/sagemaker-python-sdk · GitHub
Skip to content

feat(train): validate raw base model name exists in SageMaker Hub - #6227

Open
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub
Open

feat(train): validate raw base model name exists in SageMaker Hub#6227
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub

Conversation

@jam-jee

Copy link
Copy Markdown
Collaborator

Problem

When a user passes a raw base model name (a plain string, not a model-package ARN or ModelPackage) to a V3 trainer, model resolution accepts the name without confirming the model actually exists in the SageMaker Hub. A misspelled or unsupported name is only caught later, during recipe resolution, where the failure is more opaque and harder to map back to the model argument.

Why it matters

Fine-tuning jobs are long-lived and expensive to set up. A fast, clear "this model isn't in the Hub" at construction time saves users from a confusing downstream error and points them at the right next step (list_supported_models()), rather than leaving them to decode a recipe-lookup failure.

Fix (symptom → root cause → change)

  • Symptom: a bad raw base model name is accepted at resolve time and fails later with an unclear message.
  • Root cause: _resolve_model_and_name normalizes the name and validates region, but never checks Hub availability for the raw-string case.
  • Change: after the existing region check in the raw-model-name branch of _resolve_model_and_name, call a new _validate_model_in_hub(...) that issues a single DescribeHubContent against the active hub (get_sagemaker_hub_name()) via the existing _get_hub_content_metadata helper.
    • Only a definitive not-found raises a clear ValueError; transient or permission errors (Hub outage, missing DescribeHubContent permission, throttling) are logged and skipped, so a Hub hiccup never blocks an otherwise-valid job (fail-open on ambiguity, fail-closed only on a real miss).
    • A small _is_hub_content_not_found(exc) classifier distinguishes the two cases (botocore ResourceNotFound code, sagemaker-core exception class name, or message text).

Because the check lives in the shared _resolve_model_and_name path used by the trainer interfaces (SFT/DPO/RLVR/RLAIF/CPT/MTRL), it also covers the base_model_name supplied alongside an S3 checkpoint, which routes through the same resolver.

Model-package ARNs and ModelPackage objects are unchanged: the Hub check only applies to raw base model names.

Tests

  • 8 new unit tests in tests/unit/train/common_utils/test_finetune_utils.py:
    • _is_hub_content_not_found classification (error code, message text, transient/permission errors that must NOT be treated as not-found).
    • _validate_model_in_hub: no-session skip, found passes, not-found raises, transient error does not block.
    • _resolve_model_and_name integration: raises for a missing model, resolves normally for a present model.
  • New tests/unit/train/conftest.py with an autouse fixture that no-ops the Hub check for trainer construction tests (they build trainers with placeholder model names against mock sessions and must not reach the network). The fixture explicitly excludes the common_utils/ directory so the dedicated tests above exercise the real function.

Manual verification

N/A — unit coverage is sufficient; the new behavior is a single API call guarded by exception classification, fully exercised by mocked unit tests. Full tests/unit/train suite: 2403 passed, 19 skipped (the one remaining failure, TestWaitForMlflowAppReady::test_polls_until_ready, is pre-existing on master and unrelated to this change).

Screenshots

N/A — no user-visible UI change.

When a user passes a raw base model name to a V3 trainer (SFT/DPO/RLVR/
RLAIF/CPT/MTRL), model resolution now confirms the model actually exists in
the SageMaker Hub before the job proceeds. A bogus or misspelled name fails
fast with a clear error that points at list_supported_models(), instead of a
later, more opaque failure during recipe resolution.
The check runs in _resolve_model_and_name, the shared resolve path the trainer
interfaces already use, so it also covers the base_model_name supplied with an
S3 checkpoint. It issues a single DescribeHubContent against the active hub.
Only a definitive not-found raises; transient or permission errors are logged
and skipped so a Hub hiccup never blocks an otherwise-valid training job.
Adds unit tests for the classifier and the resolve integration, plus an
autouse conftest that no-ops the Hub check for trainer construction tests
(which use placeholder model names against mock sessions).
---
X-AI-Prompt: Add Hub-availability validation when a raw base model name is passed to trainer resolve_model path
X-AI-Tool: Kiro
Validates against the live SageMaker Hub (prod us-west-2) what the mocked unit
tests cannot: that a real DescribeHubContent miss surfaces as an error the
not-found classifier recognizes, so the check fail-closes with a clear error
instead of fail-opening on an unexpected error shape.
Two cases: a real FineTuning-tagged model (picked via an independent hub scan,
skipped if none) passes validation and resolves; a bogus name raises the
"not available in SageMaker Hub" ValueError, both directly and through the
shared resolve path.
---
X-AI-Prompt: Add an integration test validating the Hub availability check against the live SageMaker Hub
X-AI-Tool: Kiro
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@github-actions

github-actionsBot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 49s —— View job


PR Review: validate raw base model name exists in SageMaker Hub

  • Read the PR diff and changed files
  • Review source change (finetune_utils.py)
  • Trace callers of _resolve_model_and_name / _get_hub_content_metadata
  • Review tests (unit + integ + conftest)

Overall this is a clean, well-scoped change. The fail-open-on-ambiguity / fail-closed-on-definitive-miss design is the right call, the exception classifier is reasonable, and test coverage (unit + a targeted integ test for the real error shape) is solid. A few things worth surfacing — none are blockers.

1. Behavioral change: trainer construction now makes a network call (worth documenting)

_resolve_model_and_name runs in trainer __init__ (e.g. rlaif_trainer.py:162, multi_turn_rl_trainer.py:222). Previously the raw-name branch was purely local (normalize + region check); it now issues a DescribeHubContent on every trainer construction with a raw model name. The new tests/unit/train/conftest.py autouse fixture — added precisely because trainer tests "must not reach the network" — confirms this shift.

Two implications:

  • Constructing a trainer now requires the sagemaker:DescribeHubContent IAM permission and network reachability. The fail-open handling means a missing permission or outage logs a warning rather than hard-failing, so this won't break valid jobs — but it's a new expectation. Consider a one-line note in the trainer docstring / changelog.
  • This is the same lookup recipe resolution performs later (_get_fine_tuning_options_and_model_arn, finetune_utils.py:723), so it's a duplicateDescribeHubContent. Minor, but if construction latency matters it's a candidate to cache/share the result. Not required.

2. Nova / open-weights parity — looks safe, please confirm

The check applies to all raw names, including Nova, whereas _validate_model_region_availability special-cases Nova. This is fine iff Nova base models resolve as HubContentType="Model" content under their normalized names — which is exactly what recipe resolution already assumes (finetune_utils.py:723 uses the identical call). So a Nova name that fine-tunes successfully today would also pass this pre-check. Worth a quick sanity check that no supported raw name (Nova or otherwise) resolves through a different path that skips hub-content lookup, since such a name would now get a spurious "not available in SageMaker Hub" error.

3. Message-text classification is a heuristic — acceptable because it fails open

_is_hub_content_not_found falls back to substring matching ("not found", "does not exist", …). This can theoretically both false-positive (a transient error whose message happens to contain "not found" → wrongly blocks) and false-negative (a real not-found the SDK wraps with different text → silently skips). The false-negative direction is safe (feature no-ops). The false-positive direction is the riskier one; it's low-probability, and the integ test (test_bogus_model_name_raises) validates the real service error shape, which is the right guard. No change needed — just noting the classifier's real contract is "botocore code / class name are authoritative; message text is best-effort."

4. Minor

  • finetune_utils.py:57 — the sagemaker_session is None guard means that when a session is genuinely absent the check is skipped entirely. That's intentional and matches the region-check pattern, but it does mean local/offline construction silently gets no Hub validation. Fine as designed.
  • Import ordering in the test file (_validate_model_in_hub before _is_hub_content_not_found before _validate_s3_path_exists) is cosmetic; linters will flag if it matters.

No correctness bugs found. Nice work on the fail-open design and the integ test covering what the mocks can't.

Note: I couldn't post these as inline review comments — the inline-comment tool isn't available in this run, so findings are consolidated here with file:line references.

Sign up for freeto 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

@jam-jee
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(train): validate raw base model name exists in SageMaker Hub by jam-jee · Pull Request #6227 · aws/sagemaker-python-sdk · GitHub
Skip to content

feat(train): validate raw base model name exists in SageMaker Hub - #6227

Open
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub
Open

feat(train): validate raw base model name exists in SageMaker Hub#6227
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub

Conversation

@jam-jee

Copy link
Copy Markdown
Collaborator

Problem

When a user passes a raw base model name (a plain string, not a model-package ARN or ModelPackage) to a V3 trainer, model resolution accepts the name without confirming the model actually exists in the SageMaker Hub. A misspelled or unsupported name is only caught later, during recipe resolution, where the failure is more opaque and harder to map back to the model argument.

Why it matters

Fine-tuning jobs are long-lived and expensive to set up. A fast, clear "this model isn't in the Hub" at construction time saves users from a confusing downstream error and points them at the right next step (list_supported_models()), rather than leaving them to decode a recipe-lookup failure.

Fix (symptom → root cause → change)

  • Symptom: a bad raw base model name is accepted at resolve time and fails later with an unclear message.
  • Root cause: _resolve_model_and_name normalizes the name and validates region, but never checks Hub availability for the raw-string case.
  • Change: after the existing region check in the raw-model-name branch of _resolve_model_and_name, call a new _validate_model_in_hub(...) that issues a single DescribeHubContent against the active hub (get_sagemaker_hub_name()) via the existing _get_hub_content_metadata helper.
    • Only a definitive not-found raises a clear ValueError; transient or permission errors (Hub outage, missing DescribeHubContent permission, throttling) are logged and skipped, so a Hub hiccup never blocks an otherwise-valid job (fail-open on ambiguity, fail-closed only on a real miss).
    • A small _is_hub_content_not_found(exc) classifier distinguishes the two cases (botocore ResourceNotFound code, sagemaker-core exception class name, or message text).

Because the check lives in the shared _resolve_model_and_name path used by the trainer interfaces (SFT/DPO/RLVR/RLAIF/CPT/MTRL), it also covers the base_model_name supplied alongside an S3 checkpoint, which routes through the same resolver.

Model-package ARNs and ModelPackage objects are unchanged: the Hub check only applies to raw base model names.

Tests

  • 8 new unit tests in tests/unit/train/common_utils/test_finetune_utils.py:
    • _is_hub_content_not_found classification (error code, message text, transient/permission errors that must NOT be treated as not-found).
    • _validate_model_in_hub: no-session skip, found passes, not-found raises, transient error does not block.
    • _resolve_model_and_name integration: raises for a missing model, resolves normally for a present model.
  • New tests/unit/train/conftest.py with an autouse fixture that no-ops the Hub check for trainer construction tests (they build trainers with placeholder model names against mock sessions and must not reach the network). The fixture explicitly excludes the common_utils/ directory so the dedicated tests above exercise the real function.

Manual verification

N/A — unit coverage is sufficient; the new behavior is a single API call guarded by exception classification, fully exercised by mocked unit tests. Full tests/unit/train suite: 2403 passed, 19 skipped (the one remaining failure, TestWaitForMlflowAppReady::test_polls_until_ready, is pre-existing on master and unrelated to this change).

Screenshots

N/A — no user-visible UI change.

When a user passes a raw base model name to a V3 trainer (SFT/DPO/RLVR/
RLAIF/CPT/MTRL), model resolution now confirms the model actually exists in
the SageMaker Hub before the job proceeds. A bogus or misspelled name fails
fast with a clear error that points at list_supported_models(), instead of a
later, more opaque failure during recipe resolution.
The check runs in _resolve_model_and_name, the shared resolve path the trainer
interfaces already use, so it also covers the base_model_name supplied with an
S3 checkpoint. It issues a single DescribeHubContent against the active hub.
Only a definitive not-found raises; transient or permission errors are logged
and skipped so a Hub hiccup never blocks an otherwise-valid training job.
Adds unit tests for the classifier and the resolve integration, plus an
autouse conftest that no-ops the Hub check for trainer construction tests
(which use placeholder model names against mock sessions).
---
X-AI-Prompt: Add Hub-availability validation when a raw base model name is passed to trainer resolve_model path
X-AI-Tool: Kiro
Validates against the live SageMaker Hub (prod us-west-2) what the mocked unit
tests cannot: that a real DescribeHubContent miss surfaces as an error the
not-found classifier recognizes, so the check fail-closes with a clear error
instead of fail-opening on an unexpected error shape.
Two cases: a real FineTuning-tagged model (picked via an independent hub scan,
skipped if none) passes validation and resolves; a bogus name raises the
"not available in SageMaker Hub" ValueError, both directly and through the
shared resolve path.
---
X-AI-Prompt: Add an integration test validating the Hub availability check against the live SageMaker Hub
X-AI-Tool: Kiro
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@github-actions

github-actionsBot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 49s —— View job


PR Review: validate raw base model name exists in SageMaker Hub

  • Read the PR diff and changed files
  • Review source change (finetune_utils.py)
  • Trace callers of _resolve_model_and_name / _get_hub_content_metadata
  • Review tests (unit + integ + conftest)

Overall this is a clean, well-scoped change. The fail-open-on-ambiguity / fail-closed-on-definitive-miss design is the right call, the exception classifier is reasonable, and test coverage (unit + a targeted integ test for the real error shape) is solid. A few things worth surfacing — none are blockers.

1. Behavioral change: trainer construction now makes a network call (worth documenting)

_resolve_model_and_name runs in trainer __init__ (e.g. rlaif_trainer.py:162, multi_turn_rl_trainer.py:222). Previously the raw-name branch was purely local (normalize + region check); it now issues a DescribeHubContent on every trainer construction with a raw model name. The new tests/unit/train/conftest.py autouse fixture — added precisely because trainer tests "must not reach the network" — confirms this shift.

Two implications:

  • Constructing a trainer now requires the sagemaker:DescribeHubContent IAM permission and network reachability. The fail-open handling means a missing permission or outage logs a warning rather than hard-failing, so this won't break valid jobs — but it's a new expectation. Consider a one-line note in the trainer docstring / changelog.
  • This is the same lookup recipe resolution performs later (_get_fine_tuning_options_and_model_arn, finetune_utils.py:723), so it's a duplicateDescribeHubContent. Minor, but if construction latency matters it's a candidate to cache/share the result. Not required.

2. Nova / open-weights parity — looks safe, please confirm

The check applies to all raw names, including Nova, whereas _validate_model_region_availability special-cases Nova. This is fine iff Nova base models resolve as HubContentType="Model" content under their normalized names — which is exactly what recipe resolution already assumes (finetune_utils.py:723 uses the identical call). So a Nova name that fine-tunes successfully today would also pass this pre-check. Worth a quick sanity check that no supported raw name (Nova or otherwise) resolves through a different path that skips hub-content lookup, since such a name would now get a spurious "not available in SageMaker Hub" error.

3. Message-text classification is a heuristic — acceptable because it fails open

_is_hub_content_not_found falls back to substring matching ("not found", "does not exist", …). This can theoretically both false-positive (a transient error whose message happens to contain "not found" → wrongly blocks) and false-negative (a real not-found the SDK wraps with different text → silently skips). The false-negative direction is safe (feature no-ops). The false-positive direction is the riskier one; it's low-probability, and the integ test (test_bogus_model_name_raises) validates the real service error shape, which is the right guard. No change needed — just noting the classifier's real contract is "botocore code / class name are authoritative; message text is best-effort."

4. Minor

  • finetune_utils.py:57 — the sagemaker_session is None guard means that when a session is genuinely absent the check is skipped entirely. That's intentional and matches the region-check pattern, but it does mean local/offline construction silently gets no Hub validation. Fine as designed.
  • Import ordering in the test file (_validate_model_in_hub before _is_hub_content_not_found before _validate_s3_path_exists) is cosmetic; linters will flag if it matters.

No correctness bugs found. Nice work on the fail-open design and the integ test covering what the mocks can't.

Note: I couldn't post these as inline review comments — the inline-comment tool isn't available in this run, so findings are consolidated here with file:line references.

Sign up for freeto 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

@jam-jee
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(train): validate raw base model name exists in SageMaker Hub by jam-jee · Pull Request #6227 · aws/sagemaker-python-sdk · GitHub
Skip to content

feat(train): validate raw base model name exists in SageMaker Hub - #6227

Open
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub
Open

feat(train): validate raw base model name exists in SageMaker Hub#6227
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub

Conversation

@jam-jee

Copy link
Copy Markdown
Collaborator

Problem

When a user passes a raw base model name (a plain string, not a model-package ARN or ModelPackage) to a V3 trainer, model resolution accepts the name without confirming the model actually exists in the SageMaker Hub. A misspelled or unsupported name is only caught later, during recipe resolution, where the failure is more opaque and harder to map back to the model argument.

Why it matters

Fine-tuning jobs are long-lived and expensive to set up. A fast, clear "this model isn't in the Hub" at construction time saves users from a confusing downstream error and points them at the right next step (list_supported_models()), rather than leaving them to decode a recipe-lookup failure.

Fix (symptom → root cause → change)

  • Symptom: a bad raw base model name is accepted at resolve time and fails later with an unclear message.
  • Root cause: _resolve_model_and_name normalizes the name and validates region, but never checks Hub availability for the raw-string case.
  • Change: after the existing region check in the raw-model-name branch of _resolve_model_and_name, call a new _validate_model_in_hub(...) that issues a single DescribeHubContent against the active hub (get_sagemaker_hub_name()) via the existing _get_hub_content_metadata helper.
    • Only a definitive not-found raises a clear ValueError; transient or permission errors (Hub outage, missing DescribeHubContent permission, throttling) are logged and skipped, so a Hub hiccup never blocks an otherwise-valid job (fail-open on ambiguity, fail-closed only on a real miss).
    • A small _is_hub_content_not_found(exc) classifier distinguishes the two cases (botocore ResourceNotFound code, sagemaker-core exception class name, or message text).

Because the check lives in the shared _resolve_model_and_name path used by the trainer interfaces (SFT/DPO/RLVR/RLAIF/CPT/MTRL), it also covers the base_model_name supplied alongside an S3 checkpoint, which routes through the same resolver.

Model-package ARNs and ModelPackage objects are unchanged: the Hub check only applies to raw base model names.

Tests

  • 8 new unit tests in tests/unit/train/common_utils/test_finetune_utils.py:
    • _is_hub_content_not_found classification (error code, message text, transient/permission errors that must NOT be treated as not-found).
    • _validate_model_in_hub: no-session skip, found passes, not-found raises, transient error does not block.
    • _resolve_model_and_name integration: raises for a missing model, resolves normally for a present model.
  • New tests/unit/train/conftest.py with an autouse fixture that no-ops the Hub check for trainer construction tests (they build trainers with placeholder model names against mock sessions and must not reach the network). The fixture explicitly excludes the common_utils/ directory so the dedicated tests above exercise the real function.

Manual verification

N/A — unit coverage is sufficient; the new behavior is a single API call guarded by exception classification, fully exercised by mocked unit tests. Full tests/unit/train suite: 2403 passed, 19 skipped (the one remaining failure, TestWaitForMlflowAppReady::test_polls_until_ready, is pre-existing on master and unrelated to this change).

Screenshots

N/A — no user-visible UI change.

When a user passes a raw base model name to a V3 trainer (SFT/DPO/RLVR/
RLAIF/CPT/MTRL), model resolution now confirms the model actually exists in
the SageMaker Hub before the job proceeds. A bogus or misspelled name fails
fast with a clear error that points at list_supported_models(), instead of a
later, more opaque failure during recipe resolution.
The check runs in _resolve_model_and_name, the shared resolve path the trainer
interfaces already use, so it also covers the base_model_name supplied with an
S3 checkpoint. It issues a single DescribeHubContent against the active hub.
Only a definitive not-found raises; transient or permission errors are logged
and skipped so a Hub hiccup never blocks an otherwise-valid training job.
Adds unit tests for the classifier and the resolve integration, plus an
autouse conftest that no-ops the Hub check for trainer construction tests
(which use placeholder model names against mock sessions).
---
X-AI-Prompt: Add Hub-availability validation when a raw base model name is passed to trainer resolve_model path
X-AI-Tool: Kiro
Validates against the live SageMaker Hub (prod us-west-2) what the mocked unit
tests cannot: that a real DescribeHubContent miss surfaces as an error the
not-found classifier recognizes, so the check fail-closes with a clear error
instead of fail-opening on an unexpected error shape.
Two cases: a real FineTuning-tagged model (picked via an independent hub scan,
skipped if none) passes validation and resolves; a bogus name raises the
"not available in SageMaker Hub" ValueError, both directly and through the
shared resolve path.
---
X-AI-Prompt: Add an integration test validating the Hub availability check against the live SageMaker Hub
X-AI-Tool: Kiro
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@github-actions

github-actionsBot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 49s —— View job


PR Review: validate raw base model name exists in SageMaker Hub

  • Read the PR diff and changed files
  • Review source change (finetune_utils.py)
  • Trace callers of _resolve_model_and_name / _get_hub_content_metadata
  • Review tests (unit + integ + conftest)

Overall this is a clean, well-scoped change. The fail-open-on-ambiguity / fail-closed-on-definitive-miss design is the right call, the exception classifier is reasonable, and test coverage (unit + a targeted integ test for the real error shape) is solid. A few things worth surfacing — none are blockers.

1. Behavioral change: trainer construction now makes a network call (worth documenting)

_resolve_model_and_name runs in trainer __init__ (e.g. rlaif_trainer.py:162, multi_turn_rl_trainer.py:222). Previously the raw-name branch was purely local (normalize + region check); it now issues a DescribeHubContent on every trainer construction with a raw model name. The new tests/unit/train/conftest.py autouse fixture — added precisely because trainer tests "must not reach the network" — confirms this shift.

Two implications:

  • Constructing a trainer now requires the sagemaker:DescribeHubContent IAM permission and network reachability. The fail-open handling means a missing permission or outage logs a warning rather than hard-failing, so this won't break valid jobs — but it's a new expectation. Consider a one-line note in the trainer docstring / changelog.
  • This is the same lookup recipe resolution performs later (_get_fine_tuning_options_and_model_arn, finetune_utils.py:723), so it's a duplicateDescribeHubContent. Minor, but if construction latency matters it's a candidate to cache/share the result. Not required.

2. Nova / open-weights parity — looks safe, please confirm

The check applies to all raw names, including Nova, whereas _validate_model_region_availability special-cases Nova. This is fine iff Nova base models resolve as HubContentType="Model" content under their normalized names — which is exactly what recipe resolution already assumes (finetune_utils.py:723 uses the identical call). So a Nova name that fine-tunes successfully today would also pass this pre-check. Worth a quick sanity check that no supported raw name (Nova or otherwise) resolves through a different path that skips hub-content lookup, since such a name would now get a spurious "not available in SageMaker Hub" error.

3. Message-text classification is a heuristic — acceptable because it fails open

_is_hub_content_not_found falls back to substring matching ("not found", "does not exist", …). This can theoretically both false-positive (a transient error whose message happens to contain "not found" → wrongly blocks) and false-negative (a real not-found the SDK wraps with different text → silently skips). The false-negative direction is safe (feature no-ops). The false-positive direction is the riskier one; it's low-probability, and the integ test (test_bogus_model_name_raises) validates the real service error shape, which is the right guard. No change needed — just noting the classifier's real contract is "botocore code / class name are authoritative; message text is best-effort."

4. Minor

  • finetune_utils.py:57 — the sagemaker_session is None guard means that when a session is genuinely absent the check is skipped entirely. That's intentional and matches the region-check pattern, but it does mean local/offline construction silently gets no Hub validation. Fine as designed.
  • Import ordering in the test file (_validate_model_in_hub before _is_hub_content_not_found before _validate_s3_path_exists) is cosmetic; linters will flag if it matters.

No correctness bugs found. Nice work on the fail-open design and the integ test covering what the mocks can't.

Note: I couldn't post these as inline review comments — the inline-comment tool isn't available in this run, so findings are consolidated here with file:line references.

Sign up for freeto 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

@jam-jee
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(train): validate raw base model name exists in SageMaker Hub by jam-jee · Pull Request #6227 · aws/sagemaker-python-sdk · GitHub
Skip to content

feat(train): validate raw base model name exists in SageMaker Hub - #6227

Open
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub
Open

feat(train): validate raw base model name exists in SageMaker Hub#6227
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub

Conversation

@jam-jee

Copy link
Copy Markdown
Collaborator

Problem

When a user passes a raw base model name (a plain string, not a model-package ARN or ModelPackage) to a V3 trainer, model resolution accepts the name without confirming the model actually exists in the SageMaker Hub. A misspelled or unsupported name is only caught later, during recipe resolution, where the failure is more opaque and harder to map back to the model argument.

Why it matters

Fine-tuning jobs are long-lived and expensive to set up. A fast, clear "this model isn't in the Hub" at construction time saves users from a confusing downstream error and points them at the right next step (list_supported_models()), rather than leaving them to decode a recipe-lookup failure.

Fix (symptom → root cause → change)

  • Symptom: a bad raw base model name is accepted at resolve time and fails later with an unclear message.
  • Root cause: _resolve_model_and_name normalizes the name and validates region, but never checks Hub availability for the raw-string case.
  • Change: after the existing region check in the raw-model-name branch of _resolve_model_and_name, call a new _validate_model_in_hub(...) that issues a single DescribeHubContent against the active hub (get_sagemaker_hub_name()) via the existing _get_hub_content_metadata helper.
    • Only a definitive not-found raises a clear ValueError; transient or permission errors (Hub outage, missing DescribeHubContent permission, throttling) are logged and skipped, so a Hub hiccup never blocks an otherwise-valid job (fail-open on ambiguity, fail-closed only on a real miss).
    • A small _is_hub_content_not_found(exc) classifier distinguishes the two cases (botocore ResourceNotFound code, sagemaker-core exception class name, or message text).

Because the check lives in the shared _resolve_model_and_name path used by the trainer interfaces (SFT/DPO/RLVR/RLAIF/CPT/MTRL), it also covers the base_model_name supplied alongside an S3 checkpoint, which routes through the same resolver.

Model-package ARNs and ModelPackage objects are unchanged: the Hub check only applies to raw base model names.

Tests

  • 8 new unit tests in tests/unit/train/common_utils/test_finetune_utils.py:
    • _is_hub_content_not_found classification (error code, message text, transient/permission errors that must NOT be treated as not-found).
    • _validate_model_in_hub: no-session skip, found passes, not-found raises, transient error does not block.
    • _resolve_model_and_name integration: raises for a missing model, resolves normally for a present model.
  • New tests/unit/train/conftest.py with an autouse fixture that no-ops the Hub check for trainer construction tests (they build trainers with placeholder model names against mock sessions and must not reach the network). The fixture explicitly excludes the common_utils/ directory so the dedicated tests above exercise the real function.

Manual verification

N/A — unit coverage is sufficient; the new behavior is a single API call guarded by exception classification, fully exercised by mocked unit tests. Full tests/unit/train suite: 2403 passed, 19 skipped (the one remaining failure, TestWaitForMlflowAppReady::test_polls_until_ready, is pre-existing on master and unrelated to this change).

Screenshots

N/A — no user-visible UI change.

When a user passes a raw base model name to a V3 trainer (SFT/DPO/RLVR/
RLAIF/CPT/MTRL), model resolution now confirms the model actually exists in
the SageMaker Hub before the job proceeds. A bogus or misspelled name fails
fast with a clear error that points at list_supported_models(), instead of a
later, more opaque failure during recipe resolution.
The check runs in _resolve_model_and_name, the shared resolve path the trainer
interfaces already use, so it also covers the base_model_name supplied with an
S3 checkpoint. It issues a single DescribeHubContent against the active hub.
Only a definitive not-found raises; transient or permission errors are logged
and skipped so a Hub hiccup never blocks an otherwise-valid training job.
Adds unit tests for the classifier and the resolve integration, plus an
autouse conftest that no-ops the Hub check for trainer construction tests
(which use placeholder model names against mock sessions).
---
X-AI-Prompt: Add Hub-availability validation when a raw base model name is passed to trainer resolve_model path
X-AI-Tool: Kiro
Validates against the live SageMaker Hub (prod us-west-2) what the mocked unit
tests cannot: that a real DescribeHubContent miss surfaces as an error the
not-found classifier recognizes, so the check fail-closes with a clear error
instead of fail-opening on an unexpected error shape.
Two cases: a real FineTuning-tagged model (picked via an independent hub scan,
skipped if none) passes validation and resolves; a bogus name raises the
"not available in SageMaker Hub" ValueError, both directly and through the
shared resolve path.
---
X-AI-Prompt: Add an integration test validating the Hub availability check against the live SageMaker Hub
X-AI-Tool: Kiro
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@github-actions

github-actionsBot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 49s —— View job


PR Review: validate raw base model name exists in SageMaker Hub

  • Read the PR diff and changed files
  • Review source change (finetune_utils.py)
  • Trace callers of _resolve_model_and_name / _get_hub_content_metadata
  • Review tests (unit + integ + conftest)

Overall this is a clean, well-scoped change. The fail-open-on-ambiguity / fail-closed-on-definitive-miss design is the right call, the exception classifier is reasonable, and test coverage (unit + a targeted integ test for the real error shape) is solid. A few things worth surfacing — none are blockers.

1. Behavioral change: trainer construction now makes a network call (worth documenting)

_resolve_model_and_name runs in trainer __init__ (e.g. rlaif_trainer.py:162, multi_turn_rl_trainer.py:222). Previously the raw-name branch was purely local (normalize + region check); it now issues a DescribeHubContent on every trainer construction with a raw model name. The new tests/unit/train/conftest.py autouse fixture — added precisely because trainer tests "must not reach the network" — confirms this shift.

Two implications:

  • Constructing a trainer now requires the sagemaker:DescribeHubContent IAM permission and network reachability. The fail-open handling means a missing permission or outage logs a warning rather than hard-failing, so this won't break valid jobs — but it's a new expectation. Consider a one-line note in the trainer docstring / changelog.
  • This is the same lookup recipe resolution performs later (_get_fine_tuning_options_and_model_arn, finetune_utils.py:723), so it's a duplicateDescribeHubContent. Minor, but if construction latency matters it's a candidate to cache/share the result. Not required.

2. Nova / open-weights parity — looks safe, please confirm

The check applies to all raw names, including Nova, whereas _validate_model_region_availability special-cases Nova. This is fine iff Nova base models resolve as HubContentType="Model" content under their normalized names — which is exactly what recipe resolution already assumes (finetune_utils.py:723 uses the identical call). So a Nova name that fine-tunes successfully today would also pass this pre-check. Worth a quick sanity check that no supported raw name (Nova or otherwise) resolves through a different path that skips hub-content lookup, since such a name would now get a spurious "not available in SageMaker Hub" error.

3. Message-text classification is a heuristic — acceptable because it fails open

_is_hub_content_not_found falls back to substring matching ("not found", "does not exist", …). This can theoretically both false-positive (a transient error whose message happens to contain "not found" → wrongly blocks) and false-negative (a real not-found the SDK wraps with different text → silently skips). The false-negative direction is safe (feature no-ops). The false-positive direction is the riskier one; it's low-probability, and the integ test (test_bogus_model_name_raises) validates the real service error shape, which is the right guard. No change needed — just noting the classifier's real contract is "botocore code / class name are authoritative; message text is best-effort."

4. Minor

  • finetune_utils.py:57 — the sagemaker_session is None guard means that when a session is genuinely absent the check is skipped entirely. That's intentional and matches the region-check pattern, but it does mean local/offline construction silently gets no Hub validation. Fine as designed.
  • Import ordering in the test file (_validate_model_in_hub before _is_hub_content_not_found before _validate_s3_path_exists) is cosmetic; linters will flag if it matters.

No correctness bugs found. Nice work on the fail-open design and the integ test covering what the mocks can't.

Note: I couldn't post these as inline review comments — the inline-comment tool isn't available in this run, so findings are consolidated here with file:line references.

Sign up for freeto 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

@jam-jee
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(train): validate raw base model name exists in SageMaker Hub by jam-jee · Pull Request #6227 · aws/sagemaker-python-sdk · GitHub
Skip to content

feat(train): validate raw base model name exists in SageMaker Hub - #6227

Open
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub
Open

feat(train): validate raw base model name exists in SageMaker Hub#6227
jam-jee wants to merge 2 commits into
aws:masterfrom
jam-jee:feat/validate-base-model-in-hub

Conversation

@jam-jee

Copy link
Copy Markdown
Collaborator

Problem

When a user passes a raw base model name (a plain string, not a model-package ARN or ModelPackage) to a V3 trainer, model resolution accepts the name without confirming the model actually exists in the SageMaker Hub. A misspelled or unsupported name is only caught later, during recipe resolution, where the failure is more opaque and harder to map back to the model argument.

Why it matters

Fine-tuning jobs are long-lived and expensive to set up. A fast, clear "this model isn't in the Hub" at construction time saves users from a confusing downstream error and points them at the right next step (list_supported_models()), rather than leaving them to decode a recipe-lookup failure.

Fix (symptom → root cause → change)

  • Symptom: a bad raw base model name is accepted at resolve time and fails later with an unclear message.
  • Root cause: _resolve_model_and_name normalizes the name and validates region, but never checks Hub availability for the raw-string case.
  • Change: after the existing region check in the raw-model-name branch of _resolve_model_and_name, call a new _validate_model_in_hub(...) that issues a single DescribeHubContent against the active hub (get_sagemaker_hub_name()) via the existing _get_hub_content_metadata helper.
    • Only a definitive not-found raises a clear ValueError; transient or permission errors (Hub outage, missing DescribeHubContent permission, throttling) are logged and skipped, so a Hub hiccup never blocks an otherwise-valid job (fail-open on ambiguity, fail-closed only on a real miss).
    • A small _is_hub_content_not_found(exc) classifier distinguishes the two cases (botocore ResourceNotFound code, sagemaker-core exception class name, or message text).

Because the check lives in the shared _resolve_model_and_name path used by the trainer interfaces (SFT/DPO/RLVR/RLAIF/CPT/MTRL), it also covers the base_model_name supplied alongside an S3 checkpoint, which routes through the same resolver.

Model-package ARNs and ModelPackage objects are unchanged: the Hub check only applies to raw base model names.

Tests

  • 8 new unit tests in tests/unit/train/common_utils/test_finetune_utils.py:
    • _is_hub_content_not_found classification (error code, message text, transient/permission errors that must NOT be treated as not-found).
    • _validate_model_in_hub: no-session skip, found passes, not-found raises, transient error does not block.
    • _resolve_model_and_name integration: raises for a missing model, resolves normally for a present model.
  • New tests/unit/train/conftest.py with an autouse fixture that no-ops the Hub check for trainer construction tests (they build trainers with placeholder model names against mock sessions and must not reach the network). The fixture explicitly excludes the common_utils/ directory so the dedicated tests above exercise the real function.

Manual verification

N/A — unit coverage is sufficient; the new behavior is a single API call guarded by exception classification, fully exercised by mocked unit tests. Full tests/unit/train suite: 2403 passed, 19 skipped (the one remaining failure, TestWaitForMlflowAppReady::test_polls_until_ready, is pre-existing on master and unrelated to this change).

Screenshots

N/A — no user-visible UI change.

When a user passes a raw base model name to a V3 trainer (SFT/DPO/RLVR/
RLAIF/CPT/MTRL), model resolution now confirms the model actually exists in
the SageMaker Hub before the job proceeds. A bogus or misspelled name fails
fast with a clear error that points at list_supported_models(), instead of a
later, more opaque failure during recipe resolution.
The check runs in _resolve_model_and_name, the shared resolve path the trainer
interfaces already use, so it also covers the base_model_name supplied with an
S3 checkpoint. It issues a single DescribeHubContent against the active hub.
Only a definitive not-found raises; transient or permission errors are logged
and skipped so a Hub hiccup never blocks an otherwise-valid training job.
Adds unit tests for the classifier and the resolve integration, plus an
autouse conftest that no-ops the Hub check for trainer construction tests
(which use placeholder model names against mock sessions).
---
X-AI-Prompt: Add Hub-availability validation when a raw base model name is passed to trainer resolve_model path
X-AI-Tool: Kiro
Validates against the live SageMaker Hub (prod us-west-2) what the mocked unit
tests cannot: that a real DescribeHubContent miss surfaces as an error the
not-found classifier recognizes, so the check fail-closes with a clear error
instead of fail-opening on an unexpected error shape.
Two cases: a real FineTuning-tagged model (picked via an independent hub scan,
skipped if none) passes validation and resolves; a bogus name raises the
"not available in SageMaker Hub" ValueError, both directly and through the
shared resolve path.
---
X-AI-Prompt: Add an integration test validating the Hub availability check against the live SageMaker Hub
X-AI-Tool: Kiro
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@jam-jee
jam-jeedeployed to auto-approve August 31, 2026 20:44 — with GitHub Actions Active
@github-actions

github-actionsBot commented Aug 31, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 49s —— View job


PR Review: validate raw base model name exists in SageMaker Hub

  • Read the PR diff and changed files
  • Review source change (finetune_utils.py)
  • Trace callers of _resolve_model_and_name / _get_hub_content_metadata
  • Review tests (unit + integ + conftest)

Overall this is a clean, well-scoped change. The fail-open-on-ambiguity / fail-closed-on-definitive-miss design is the right call, the exception classifier is reasonable, and test coverage (unit + a targeted integ test for the real error shape) is solid. A few things worth surfacing — none are blockers.

1. Behavioral change: trainer construction now makes a network call (worth documenting)

_resolve_model_and_name runs in trainer __init__ (e.g. rlaif_trainer.py:162, multi_turn_rl_trainer.py:222). Previously the raw-name branch was purely local (normalize + region check); it now issues a DescribeHubContent on every trainer construction with a raw model name. The new tests/unit/train/conftest.py autouse fixture — added precisely because trainer tests "must not reach the network" — confirms this shift.

Two implications:

  • Constructing a trainer now requires the sagemaker:DescribeHubContent IAM permission and network reachability. The fail-open handling means a missing permission or outage logs a warning rather than hard-failing, so this won't break valid jobs — but it's a new expectation. Consider a one-line note in the trainer docstring / changelog.
  • This is the same lookup recipe resolution performs later (_get_fine_tuning_options_and_model_arn, finetune_utils.py:723), so it's a duplicateDescribeHubContent. Minor, but if construction latency matters it's a candidate to cache/share the result. Not required.

2. Nova / open-weights parity — looks safe, please confirm

The check applies to all raw names, including Nova, whereas _validate_model_region_availability special-cases Nova. This is fine iff Nova base models resolve as HubContentType="Model" content under their normalized names — which is exactly what recipe resolution already assumes (finetune_utils.py:723 uses the identical call). So a Nova name that fine-tunes successfully today would also pass this pre-check. Worth a quick sanity check that no supported raw name (Nova or otherwise) resolves through a different path that skips hub-content lookup, since such a name would now get a spurious "not available in SageMaker Hub" error.

3. Message-text classification is a heuristic — acceptable because it fails open

_is_hub_content_not_found falls back to substring matching ("not found", "does not exist", …). This can theoretically both false-positive (a transient error whose message happens to contain "not found" → wrongly blocks) and false-negative (a real not-found the SDK wraps with different text → silently skips). The false-negative direction is safe (feature no-ops). The false-positive direction is the riskier one; it's low-probability, and the integ test (test_bogus_model_name_raises) validates the real service error shape, which is the right guard. No change needed — just noting the classifier's real contract is "botocore code / class name are authoritative; message text is best-effort."

4. Minor

  • finetune_utils.py:57 — the sagemaker_session is None guard means that when a session is genuinely absent the check is skipped entirely. That's intentional and matches the region-check pattern, but it does mean local/offline construction silently gets no Hub validation. Fine as designed.
  • Import ordering in the test file (_validate_model_in_hub before _is_hub_content_not_found before _validate_s3_path_exists) is cosmetic; linters will flag if it matters.

No correctness bugs found. Nice work on the fail-open design and the integ test covering what the mocks can't.

Note: I couldn't post these as inline review comments — the inline-comment tool isn't available in this run, so findings are consolidated here with file:line references.

Sign up for freeto 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

@jam-jee