Repository navigation
Conversation
…s every deploy path
`deploy()` took `tags` only through `**kwargs`, so `inspect.signature` could
not validate it, and two deploy paths dropped it: the fine-tuned model package
path read only `self._tags`, and the Nova path did not read the kwarg at all.
The accumulated tags also had two shapes in one SDK. `add_tags` compared
`tag["Key"]` and raised `KeyError: 'Key'` for the `{"key": ..., "value": ...}`
form that the `sagemaker.core.resources` classes take, and raised `TypeError`
for a `sagemaker.core.shapes.Tag`, while the fine-tuned path converted the
stored tags back to lowercase by hand.
Add `tags` as a named `deploy()` parameter. Normalize every accepted form (a
`{key: value}` mapping, boto-style dicts, core-style dicts, `Tag` objects) once
in `format_tags`, and make `add_tags`, `tag_exists`, `_validate_new_tags`, and
`remove_tag_with_key` go through it. Merge the caller's tags into the builder
before dispatch so the built-model, inference-component, recommendation,
fine-tuned, restricted-package, and Nova paths all tag the endpoint, and
replace the two hand-written case conversions with one helper.
---
X-AI-Prompt: Why does ModelBuilder.deploy drop my tags on a fine-tuned model, and why does add_tags raise KeyError: 'Key'?
X-AI-Tool: claude-code
|
Superseded by #6533, which combines the four changes into one PR and moves the hub-model metadata accessors onto |
There was a problem hiding this comment.
🤖 AI code review
This PR makes ModelBuilder.deploy(tags=...) a named parameter, centralizes tag normalization in format_tags/_normalize_tag so all accepted representations (boto dicts, core-style dicts, Tag objects, {key: value} mappings) become boto-style {"Key","Value"} dicts, and threads a merged tag list through every deploy path (built model, IC, recommendation, fine-tuned, restricted-package, Nova) via add_tags/_core_endpoint_tags. It aligns with prior precedent (PR #6179) and adds focused unit tests. The change is sound; I verified callers of format_tags, _validate_new_tags, and remove_tag_with_key in sagemaker-core, the _deploy_core_endpoint flow, imports, and the existing tag tests. Only one minor edge case worth noting.
Reviewed commit 825999b. Automated review; verify before acting.
| if isinstance(tags, dict): | ||
| if "Key" in tags or "key" in tags: | ||
| return [_normalize_tag(tags)] | ||
| return [{"Key": str(k), "Value": str(v)} for k, v in tags.items()] |
There was a problem hiding this comment.
🟡 Low: The dict branch now treats any mapping containing a "Key" or "key" entry as a single tag rather than a {tag_key: tag_value} mapping. This correctly disambiguates a single boto/core tag dict, but it silently changes behavior for a legitimate {tag_key: value} mapping that happens to use "Key"/"key" as one of its tag keys: e.g. format_tags({"Key": "prod", "team": "ml"}) now yields a single tag {"Key": "prod", "Value": None} and drops the "team" entry, whereas before it produced two tags. This is an inherent ambiguity of overloading dict, and unlikely in practice, but it is a new silent data-loss path and may be worth a short note in the docstring or a test guarding the expected precedence.
Problem
ModelBuilder.deploy()takestagsonly through**kwargs, soinspect.signature(ModelBuilder.deploy)does not list it and a typo in the name is a silent no-op. Two deploy paths drop it: the fine-tuned model package path reads onlyself._tags, and the Nova path reads neither. The accumulated tags also have two shapes in one SDK.add_tagscomparestag["Key"], so the{"key": ..., "value": ...}form that thesagemaker.core.resourcesclasses take raisesKeyError: 'Key', and asagemaker.core.shapes.TagraisesTypeError: 'Tag' object is not subscriptable, while the fine-tuned and Nova paths convert the stored tags back to lowercase by hand. The SageMaker agent skills carry a commit whose only purpose is to pass{"Key", "Value"}dicts to dodge theKeyError.Reproduction on
origin/master:Solution
deploy()gets a namedtags: Optional[Tags] = Noneparameter.format_tagsnormalizes every accepted form once (a{key: value}mapping, boto-style dicts, core-style dicts,Tagobjects, or a mix) into boto-style dicts, raisesTypeErrorfor a tag with no key, andadd_tags,tag_exists,_validate_new_tags, andremove_tag_with_keygo through it.deploy()merges the caller's tags into the builder before dispatch, so the built-model, inference-component, recommendation, fine-tuned, restricted-package, and Nova paths all tag the endpoint from the same list. One helper,_core_endpoint_tags(), replaces the two hand-written case conversions.build(tags=...)from #6510 uses the same normalization.Tests
New
sagemaker-serve/tests/unit/test_model_builder_deploy_tags.py, 13 tests:format_tagson every representation and itsTypeErrorcases,_validate_new_tagswith mixed casing andTagobjects,remove_tag_with_keyon core casing,add_tagsnormalization, thetagsparameter in the signature, the merge into the builder before the core path,_deploy_core_endpointnormalizing kwargs tags, the recommendation path receiving the builder's tags,_core_endpoint_tags()casing, the restricted-package endpoint being tagged, and the Nova endpoint merging the builder's tags.On
origin/masterthe same file reports13 failed(KeyError: 'Key'in five tests, the rest on the missing parameter and the dropped tags).Existing suites on this branch:
sagemaker-coretests/unit/test_common_utils.py -k tag:16 passed.sagemaker-servetest_model_builder_build_tags.py,test_model_builder_deploy.py,test_merged_model_deployment.py,test_model_builder_core.py,test_model_builder_methods.py,test_model_builder_utils_methods.py,test_model_builder_register.py,test_inference_recommendation_mixin.py,test_model_builder_additional.py:188 passed, 6 skipped, the one failure (test_get_client_translators_with_torch_tensor,No module named 'torch') fails identically onorigin/master.black --line-length 100,flake8, andpydocstylepass on the changed files.Merge Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.