Skip to content

Commit a322ca1

Browse files
committed
fix(serve): remove IC data-source collapse hack from recommendation deploy
Inference Components now support AdditionalModelDataSources natively (kernel tuning / speculative decoding channels), so _deploy_recommendation no longer needs to collapse an optimized recommendation's base_model + draft channels into a single primary ModelDataSource. Deploy the recommendation's ModelPackage directly and let the hosting stack resolve the channels. Removes the base_model-promotion / OPTION_SPECULATIVE_DRAFT_MODEL rewiring block and its now-unused shape imports (AdditionalModelDataSource, ModelDataSource, S3ModelDataSource, SPECULATIVE_DRAFT_MODEL). Updates the speculative-decoding unit tests to assert the ModelPackage pass-through.
1 parent 5a09756 commit a322ca1

2 files changed

Lines changed: 24 additions & 121 deletions

File tree

‎sagemaker-serve/src/sagemaker/serve/model_builder.py‎

Lines changed: 10 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
from sagemaker.serve.validations.check_image_uri import is_1p_image_uri
8383
from sagemaker.core.inference_config import ResourceRequirements
8484
from sagemaker.serve.inference_recommendation_mixin import _InferenceRecommenderMixin
85-
from sagemaker.serve.model_builder_utils import _ModelBuilderUtils, SPECULATIVE_DRAFT_MODEL
85+
from sagemaker.serve.model_builder_utils import _ModelBuilderUtils
8686
from sagemaker.serve.model_builder_servers import _ModelBuilderServers
8787
from sagemaker.serve.validations.optimization import _validate_optimization_configuration
8888
from sagemaker.core.enums import Tag
@@ -5120,12 +5120,9 @@ def _deploy_recommendation(
51205120
import time as _time
51215121
import uuid as _uuid
51225122
from sagemaker.core.shapes.shapes import (
5123-
AdditionalModelDataSource as _AdditionalModelDataSource,
51245123
ContainerDefinition as _ContainerDefinition,
5125-
ModelDataSource as _ModelDataSource,
51265124
ProductionVariant as _ProductionVariant,
51275125
ProductionVariantRoutingConfig as _ProductionVariantRoutingConfig,
5128-
S3ModelDataSource as _S3ModelDataSource,
51295126
)
51305127

51315128
role = role or self.role_arn
@@ -5237,65 +5234,15 @@ def _deploy_recommendation(
52375234
)
52385235
resolved_endpoint_name = endpoint_name or f"sm-rec-endpoint-{ts}-{suffix}"
52395236

5240-
# Optimized recommendations put the base weights (and any draft model)
5241-
# in additional model data sources with an empty primary source. Promote
5242-
# base_model to the primary source, and keep any draft channel attached
5243-
# so OPTION_SPECULATIVE_DRAFT_MODEL points at a populated path.
5244-
pkg_container = (
5245-
described.get("InferenceSpecification", {}).get("Containers", [{}]) or [{}]
5246-
)[0]
5247-
additional_sources = pkg_container.get("AdditionalModelDataSources") or []
5248-
by_channel = {s.get("ChannelName"): s for s in additional_sources}
5249-
base_source = by_channel.get("base_model")
5250-
5251-
primary_model_dir = "/opt/ml/model"
5252-
if base_source:
5253-
base_s3 = base_source.get("S3DataSource", {})
5254-
base_channel_path = f"{SPECULATIVE_DRAFT_MODEL}/base_model"
5255-
# Repoint env vars (e.g. HF_MODEL_ID) that referenced the old
5256-
# channel path to the primary mount now holding the base weights.
5257-
env = {
5258-
k: (primary_model_dir if v == base_channel_path else v)
5259-
for k, v in (pkg_container.get("Environment") or {}).items()
5260-
}
5261-
# Keep any draft channel attached and point the env var at its mount.
5262-
draft_sources = []
5263-
for channel_name, source in by_channel.items():
5264-
if channel_name == "base_model":
5265-
continue
5266-
draft_s3 = source.get("S3DataSource", {})
5267-
draft_sources.append(
5268-
_AdditionalModelDataSource(
5269-
channel_name=channel_name,
5270-
s3_data_source=_S3ModelDataSource(
5271-
s3_uri=draft_s3.get("S3Uri"),
5272-
s3_data_type=draft_s3.get("S3DataType", "S3Prefix"),
5273-
compression_type=draft_s3.get("CompressionType", "None"),
5274-
),
5275-
)
5276-
)
5277-
env["OPTION_SPECULATIVE_DRAFT_MODEL"] = (
5278-
f"{SPECULATIVE_DRAFT_MODEL}/{channel_name}/"
5279-
)
5280-
primary_container = _ContainerDefinition(
5281-
image=pkg_container.get("Image"),
5282-
model_data_source=_ModelDataSource(
5283-
s3_data_source=_S3ModelDataSource(
5284-
s3_uri=base_s3.get("S3Uri"),
5285-
s3_data_type=base_s3.get("S3DataType", "S3Prefix"),
5286-
compression_type=base_s3.get("CompressionType", "None"),
5287-
)
5288-
),
5289-
additional_model_data_sources=draft_sources or None,
5290-
environment=env,
5291-
)
5292-
else:
5293-
container_def_kwargs = {"model_package_name": model_package_arn}
5294-
if inference_specification_name:
5295-
container_def_kwargs[
5296-
"inference_specification_name"
5297-
] = inference_specification_name
5298-
primary_container = _ContainerDefinition(**container_def_kwargs)
5237+
# Deploy directly from the recommendation's ModelPackage. Optimized
5238+
# recommendations (kernel tuning / speculative decoding) carry the base
5239+
# weights and any draft model as AdditionalModelDataSources on the
5240+
# package; the hosting stack resolves those channels itself, so no
5241+
# client-side collapsing is needed.
5242+
container_def_kwargs = {"model_package_name": model_package_arn}
5243+
if inference_specification_name:
5244+
container_def_kwargs["inference_specification_name"] = inference_specification_name
5245+
primary_container = _ContainerDefinition(**container_def_kwargs)
52995246

53005247
Model.create(
53015248
model_name=resolved_model_name,

‎sagemaker-serve/tests/unit/test_ai_inference_recommender/test_model_builder_recommendations.py‎

Lines changed: 14 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,11 @@ def test_builder_role_used_when_role_omitted(self, mb_class):
247247

248248

249249
class TestDeployRecommendationSpeculativeDecoding:
250-
"""Speculative-decoding / kernel-tuning recommendations (ModelPackage carries
251-
AdditionalModelDataSources) are collapsed to a single ModelDataSource +
252-
OPTION_SPECULATIVE_DRAFT_MODEL env so the model can be hosted (Inference
253-
Components reject AdditionalModelDataSources)."""
250+
"""Optimized recommendations (kernel tuning / speculative decoding) carry
251+
the base weights and any draft model as AdditionalModelDataSources on the
252+
ModelPackage. The hosting stack (including Inference Components) now resolves
253+
those channels itself, so deploy just passes the ModelPackage through — no
254+
client-side collapsing of the additional sources into a primary source."""
254255

255256
def _make_builder(self, mb_class, described):
256257
mb = mb_class(sagemaker_session=MagicMock())
@@ -291,52 +292,16 @@ def _channel(name, uri):
291292
},
292293
}
293294

294-
def test_base_model_channel_promoted_to_primary_source(self, mb_class):
295-
# Optimized recs put the weights in a base_model channel with an empty
296-
# primary ModelDataSource; the base_model S3 uri becomes the primary source,
297-
# and env vars that pointed at the old channel path are repointed to
298-
# /opt/ml/model (where the primary source mounts).
295+
def test_optimized_recommendation_deploys_via_model_package(self, mb_class):
296+
# An optimized rec exposes base_model + draft_model as additional data
297+
# sources; deploy no longer collapses them — it passes the ModelPackage
298+
# through and lets the hosting stack resolve the channels.
299299
described = {
300300
"ModelApprovalStatus": "Approved",
301301
"InferenceSpecification": {
302302
"Containers": [
303303
{
304304
"Image": "123.dkr.ecr.us-west-2.amazonaws.com/djl-inference:latest",
305-
"Environment": {
306-
"OPTION_TENSOR_PARALLEL_DEGREE": "1",
307-
"HF_MODEL_ID": "/opt/ml/additional-model-data-sources/base_model",
308-
"option.model_id": "/opt/ml/additional-model-data-sources/base_model",
309-
},
310-
"AdditionalModelDataSources": [
311-
self._channel("base_model", "s3://base/weights/")
312-
],
313-
}
314-
]
315-
},
316-
}
317-
mb = self._make_builder(mb_class, described)
318-
container = self._deploy(mb).call_args.kwargs["primary_container"]
319-
320-
# No AdditionalModelDataSources on the created model (IC would reject it).
321-
assert not getattr(container, "additional_model_data_sources", None)
322-
assert not getattr(container, "model_package_name", None)
323-
# base_model weights promoted to the primary source.
324-
assert container.model_data_source.s3_data_source.s3_uri == "s3://base/weights/"
325-
# Env vars that referenced the old channel path now point at the primary mount.
326-
assert container.environment["HF_MODEL_ID"] == "/opt/ml/model"
327-
assert container.environment["option.model_id"] == "/opt/ml/model"
328-
# No draft channel here, so no draft env.
329-
assert "OPTION_SPECULATIVE_DRAFT_MODEL" not in container.environment
330-
assert container.environment["OPTION_TENSOR_PARALLEL_DEGREE"] == "1"
331-
332-
def test_draft_model_channel_mounted_via_env(self, mb_class):
333-
described = {
334-
"ModelApprovalStatus": "Approved",
335-
"InferenceSpecification": {
336-
"Containers": [
337-
{
338-
"Image": "123.dkr.ecr.us-west-2.amazonaws.com/djl-inference:latest",
339-
"Environment": {},
340305
"AdditionalModelDataSources": [
341306
self._channel("base_model", "s3://base/weights/"),
342307
self._channel("draft_model", "s3://draft/model/"),
@@ -348,19 +313,10 @@ def test_draft_model_channel_mounted_via_env(self, mb_class):
348313
mb = self._make_builder(mb_class, described)
349314
container = self._deploy(mb).call_args.kwargs["primary_container"]
350315

351-
# base_model is the primary source.
352-
assert container.model_data_source.s3_data_source.s3_uri == "s3://base/weights/"
353-
# The draft channel stays attached as an additional model data source so
354-
# the speculative-decoding env path is actually populated at runtime
355-
# (a model-based endpoint supports additional model data sources).
356-
assert len(container.additional_model_data_sources) == 1
357-
draft = container.additional_model_data_sources[0]
358-
assert draft.channel_name == "draft_model"
359-
assert draft.s3_data_source.s3_uri == "s3://draft/model/"
360-
assert (
361-
container.environment["OPTION_SPECULATIVE_DRAFT_MODEL"]
362-
== "/opt/ml/additional-model-data-sources/draft_model/"
363-
)
316+
# Deployed straight from the ModelPackage — no client-side collapsing.
317+
assert container.model_package_name == "arn:.../p/A"
318+
assert not getattr(container, "model_data_source", None)
319+
assert not getattr(container, "additional_model_data_sources", None)
364320

365321
def test_no_additional_sources_uses_model_package(self, mb_class):
366322
described = {
@@ -370,7 +326,7 @@ def test_no_additional_sources_uses_model_package(self, mb_class):
370326
mb = self._make_builder(mb_class, described)
371327
model_create = self._deploy(mb)
372328
container = model_create.call_args.kwargs["primary_container"]
373-
# Plain (non-speculative-decoding) recommendation still deploys via the ModelPackage.
329+
# Plain (non-optimized) recommendation also deploys via the ModelPackage.
374330
assert container.model_package_name == "arn:.../p/A"
375331

376332

0 commit comments

Comments
 (0)