diff --git a/model-engine/model_engine_server/infra/services/live_endpoint_builder_service.py b/model-engine/model_engine_server/infra/services/live_endpoint_builder_service.py index bef91df0a..aecef2c75 100644 --- a/model-engine/model_engine_server/infra/services/live_endpoint_builder_service.py +++ b/model-engine/model_engine_server/infra/services/live_endpoint_builder_service.py @@ -78,6 +78,7 @@ ECR_AWS_PROFILE: str = os.getenv("ECR_READ_AWS_PROFILE", "default") # type: ignore GIT_TAG: str = os.getenv("GIT_TAG") # type: ignore ENV: str = os.getenv("DD_ENV") # type: ignore +WORKSPACE_PATH = os.getenv("WORKSPACE", ".") INITIAL_K8S_CACHE_TTL_SECONDS: int = 60 MAX_IMAGE_TAG_LEN = 128 @@ -494,7 +495,6 @@ def get_base_image_params( # The context should be whatever WORKDIR is in the container running the build app itself. inference_folder = "model-engine/model_engine_server/inference" - base_path: str = os.getenv("WORKSPACE") # type: ignore logger_adapter.info(f"inference_folder: {inference_folder}") logger_adapter.info(f"dockerfile: {inference_folder}/{dockerfile}") @@ -502,7 +502,7 @@ def get_base_image_params( repo=hmi_config.user_inference_base_repository, image_tag=resulting_image_tag[:MAX_IMAGE_TAG_LEN], aws_profile=ECR_AWS_PROFILE, # type: ignore - base_path=base_path, + base_path=WORKSPACE_PATH, dockerfile=f"{inference_folder}/{dockerfile}", base_image=base_image, requirements_folder=None, @@ -557,9 +557,7 @@ def _get_user_image_params( # The context should be whatever WORKDIR is in the container running the build app itself. inference_folder = "model-engine/model_engine_server/inference" - base_path: str = os.getenv("WORKSPACE") # type: ignore - - requirements_folder = os.path.join(base_path, f"requirements_{requirements_hash}") + requirements_folder = os.path.join(WORKSPACE_PATH, f"requirements_{requirements_hash}") try: os.mkdir(requirements_folder) except FileExistsError: @@ -577,7 +575,7 @@ def _get_user_image_params( repo=ecr_repo, image_tag=service_image_tag[:MAX_IMAGE_TAG_LEN], aws_profile=ECR_AWS_PROFILE, - base_path=base_path, + base_path=WORKSPACE_PATH, dockerfile=f"{inference_folder}/{dockerfile}", base_image=base_image, requirements_folder=requirements_folder, @@ -609,9 +607,7 @@ def _get_inject_bundle_image_params( # The context should be whatever WORKDIR is in the container running the build app itself. dockerfile = "inject_bundle.Dockerfile" inference_folder = "model-engine/model_engine_server/inference" - base_path: str = os.getenv("WORKSPACE") # type: ignore - - bundle_folder = os.path.join(base_path, f"bundle_{service_image_hash}") + bundle_folder = os.path.join(WORKSPACE_PATH, f"bundle_{service_image_hash}") try: os.mkdir(bundle_folder) except FileExistsError: @@ -635,7 +631,7 @@ def _get_inject_bundle_image_params( repo=ecr_repo, image_tag=service_image_tag[:MAX_IMAGE_TAG_LEN], aws_profile=ECR_AWS_PROFILE, - base_path=base_path, + base_path=WORKSPACE_PATH, dockerfile=f"{inference_folder}/{dockerfile}", base_image=base_image, requirements_folder=bundle_folder, diff --git a/model-engine/requirements.in b/model-engine/requirements.in index 5cf95a515..2ef63150f 100644 --- a/model-engine/requirements.in +++ b/model-engine/requirements.in @@ -12,7 +12,7 @@ azure-storage-blob~=12.19.0 boto3-stubs[essential]~=1.26.67 boto3~=1.21 botocore~=1.24 -build==0.8.0 +build~=1.0.3 celery[redis,sqs,tblib]~=5.3.6 click~=8.1 cloudpickle==2.1.0 @@ -37,7 +37,7 @@ protobuf~=3.20 psycopg2-binary==2.9.3 py-xid==0.3.0 pycurl~=7.44 # For celery[sqs] -pydantic~=1.10.11 +pydantic==1.10.14 python-multipart~=0.0.7 quart==0.18.3 requests-auth-aws-sigv4~=0.7 diff --git a/model-engine/requirements.txt b/model-engine/requirements.txt index 623b12af1..71e7440d8 100644 --- a/model-engine/requirements.txt +++ b/model-engine/requirements.txt @@ -8,14 +8,14 @@ aiofiles==23.1.0 # via quart aiohttp==3.9.2 # via - # -r requirements.in + # -r model-engine/requirements.in # kubernetes-asyncio aioredis==2.0.1 - # via -r requirements.in + # via -r model-engine/requirements.in aiosignal==1.3.1 # via aiohttp alembic==1.8.1 - # via -r requirements.in + # via -r model-engine/requirements.in amqp==5.1.1 # via kombu anyio==3.7.1 @@ -33,7 +33,7 @@ async-timeout==4.0.2 # aioredis # redis asyncpg==0.27.0 - # via -r requirements.in + # via -r model-engine/requirements.in attrs==23.1.0 # via # aiohttp @@ -44,7 +44,7 @@ attrs==23.1.0 azure-common==1.1.28 # via azure-keyvault-secrets azure-containerregistry==1.2.0 - # via -r requirements.in + # via -r model-engine/requirements.in azure-core==1.29.6 # via # azure-containerregistry @@ -53,13 +53,13 @@ azure-core==1.29.6 # azure-servicebus # azure-storage-blob azure-identity==1.15.0 - # via -r requirements.in + # via -r model-engine/requirements.in azure-keyvault-secrets==4.7.0 - # via -r requirements.in + # via -r model-engine/requirements.in azure-servicebus==7.11.4 - # via -r requirements.in + # via -r model-engine/requirements.in azure-storage-blob==12.19.0 - # via -r requirements.in + # via -r model-engine/requirements.in backports-zoneinfo[tzdata]==0.2.1 # via # celery @@ -72,20 +72,22 @@ blinker==1.6.2 # via quart boto3==1.28.1 # via - # -r requirements.in + # -r model-engine/requirements.in # celery # kombu boto3-stubs[essential]==1.26.67 - # via -r requirements.in + # via + # -r model-engine/requirements.in + # boto3-stubs botocore==1.31.1 # via - # -r requirements.in + # -r model-engine/requirements.in # boto3 # s3transfer botocore-stubs==1.29.165 # via boto3-stubs -build==0.8.0 - # via -r requirements.in +build==1.0.3 + # via -r model-engine/requirements.in bytecode==0.14.2 # via ddtrace cachetools==5.3.1 @@ -93,7 +95,9 @@ cachetools==5.3.1 cattrs==23.1.2 # via ddtrace celery[redis,sqs,tblib]==5.3.6 - # via -r requirements.in + # via + # -r model-engine/requirements.in + # celery certifi==2023.7.22 # via # datadog-api-client @@ -108,7 +112,7 @@ charset-normalizer==3.2.0 # via requests click==8.1.4 # via - # -r requirements.in + # -r model-engine/requirements.in # celery # click-didyoumean # click-plugins @@ -122,35 +126,35 @@ click-plugins==1.1.1 click-repl==0.3.0 # via celery cloudpickle==2.1.0 - # via -r requirements.in + # via -r model-engine/requirements.in colorama==0.4.6 # via twine commonmark==0.9.1 # via rich croniter==1.4.1 - # via -r requirements.in + # via -r model-engine/requirements.in cryptography==42.0.5 # via - # -r requirements.in + # -r model-engine/requirements.in # azure-identity # azure-storage-blob # msal # pyjwt # secretstorage dataclasses-json==0.5.9 - # via -r requirements.in + # via -r model-engine/requirements.in datadog==0.47.0 - # via -r requirements.in + # via -r model-engine/requirements.in datadog-api-client==2.11.0 - # via -r requirements.in + # via -r model-engine/requirements.in ddsketch==2.0.4 # via ddtrace ddtrace==1.8.3 - # via -r requirements.in + # via -r model-engine/requirements.in deprecation==2.1.0 - # via -r requirements.in + # via -r model-engine/requirements.in docker==5.0.3 - # via -r requirements.in + # via -r model-engine/requirements.in docutils==0.20.1 # via readme-renderer envier==0.4.0 @@ -160,7 +164,7 @@ exceptiongroup==1.2.0 # anyio # cattrs fastapi==0.110.0 - # via -r requirements.in + # via -r model-engine/requirements.in filelock==3.13.1 # via # huggingface-hub @@ -174,15 +178,15 @@ fsspec==2023.10.0 gitdb==4.0.10 # via gitpython gitdb2==2.0.6 - # via -r requirements.in + # via -r model-engine/requirements.in gitpython==3.1.41 - # via -r requirements.in + # via -r model-engine/requirements.in google-auth==2.21.0 # via kubernetes greenlet==2.0.2 # via sqlalchemy gunicorn==20.1.0 - # via -r requirements.in + # via -r model-engine/requirements.in h11==0.14.0 # via # httpcore @@ -196,7 +200,7 @@ hpack==4.0.0 httpcore==1.0.4 # via httpx httptools==0.5.0 - # via -r requirements.in + # via -r model-engine/requirements.in httpx==0.27.0 # via starlette huggingface-hub==0.20.3 @@ -216,6 +220,7 @@ idna==3.7 importlib-metadata==6.8.0 # via # alembic + # build # keyring # quart # twine @@ -243,7 +248,7 @@ jeepney==0.8.0 # secretstorage jinja2==3.0.3 # via - # -r requirements.in + # -r model-engine/requirements.in # quart # starlette jmespath==1.0.1 @@ -251,7 +256,7 @@ jmespath==1.0.1 # boto3 # botocore json-log-formatter==0.5.2 - # via -r requirements.in + # via -r model-engine/requirements.in jsonschema==4.19.0 # via ddtrace jsonschema-specifications==2023.7.1 @@ -261,11 +266,11 @@ keyring==24.2.0 kombu[sqs]==5.3.5 # via celery kubeconfig==1.1.1 - # via -r requirements.in + # via -r model-engine/requirements.in kubernetes==25.3.0 - # via -r requirements.in + # via -r model-engine/requirements.in kubernetes-asyncio==25.11.0 - # via -r requirements.in + # via -r model-engine/requirements.in mako==1.2.4 # via alembic markupsafe==2.1.3 @@ -313,7 +318,7 @@ numpy==1.24.4 oauthlib==3.2.2 # via requests-oauthlib orjson==3.9.15 - # via -r requirements.in + # via -r model-engine/requirements.in packaging==23.1 # via # build @@ -323,8 +328,6 @@ packaging==23.1 # marshmallow # msal-extensions # transformers -pep517==0.13.0 - # via build pg8000==1.29.8 # via testing-postgresql pkginfo==1.9.6 @@ -339,13 +342,13 @@ prompt-toolkit==3.0.39 # via click-repl protobuf==3.20.3 # via - # -r requirements.in + # -r model-engine/requirements.in # ddsketch # ddtrace psycopg2-binary==2.9.3 - # via -r requirements.in + # via -r model-engine/requirements.in py-xid==0.3.0 - # via -r requirements.in + # via -r model-engine/requirements.in pyasn1==0.5.0 # via # pyasn1-modules @@ -356,12 +359,12 @@ pycparser==2.21 # via cffi pycurl==7.45.2 # via - # -r requirements.in + # -r model-engine/requirements.in # celery # kombu -pydantic==1.10.11 +pydantic==1.10.14 # via - # -r requirements.in + # -r model-engine/requirements.in # fastapi pygments==2.15.1 # via @@ -371,6 +374,8 @@ pyjwt[crypto]==2.8.0 # via # msal # pyjwt +pyproject-hooks==1.0.0 + # via build python-dateutil==2.8.2 # via # botocore @@ -382,7 +387,7 @@ python-dateutil==2.8.2 # pg8000 python-multipart==0.0.7 # via - # -r requirements.in + # -r model-engine/requirements.in # starlette pyyaml==6.0.1 # via @@ -393,7 +398,7 @@ pyyaml==6.0.1 # starlette # transformers quart==0.18.3 - # via -r requirements.in + # via -r model-engine/requirements.in readme-renderer==40.0 # via twine redis==4.6.0 @@ -406,7 +411,7 @@ regex==2023.10.3 # via transformers requests==2.31.0 # via - # -r requirements.in + # -r model-engine/requirements.in # azure-core # datadog # docker @@ -419,7 +424,7 @@ requests==2.31.0 # transformers # twine requests-auth-aws-sigv4==0.7 - # via -r requirements.in + # via -r model-engine/requirements.in requests-oauthlib==1.3.1 # via kubernetes requests-toolbelt==1.0.0 @@ -427,7 +432,7 @@ requests-toolbelt==1.0.0 rfc3986==2.0.0 # via twine rich==12.6.0 - # via -r requirements.in + # via -r model-engine/requirements.in rpds-py==0.10.0 # via # jsonschema @@ -443,9 +448,9 @@ scramp==1.4.4 secretstorage==3.3.3 # via keyring sentencepiece==0.1.99 - # via -r requirements.in + # via -r model-engine/requirements.in sh==1.14.3 - # via -r requirements.in + # via -r model-engine/requirements.in six==1.16.0 # via # azure-core @@ -459,7 +464,7 @@ six==1.16.0 # python-dateutil # tenacity smart-open==5.2.1 - # via -r requirements.in + # via -r model-engine/requirements.in smmap==5.0.0 # via # gitdb @@ -472,48 +477,50 @@ sniffio==1.3.0 # httpx sqlalchemy[asyncio]==2.0.4 # via - # -r requirements.in + # -r model-engine/requirements.in # alembic + # sqlalchemy sse-starlette==1.6.1 - # via -r requirements.in + # via -r model-engine/requirements.in sseclient-py==1.7.2 - # via -r requirements.in + # via -r model-engine/requirements.in starlette[full]==0.36.3 # via - # -r requirements.in + # -r model-engine/requirements.in # fastapi # sse-starlette + # starlette stringcase==1.2.0 - # via -r requirements.in + # via -r model-engine/requirements.in tblib==2.0.0 # via celery tenacity==6.2.0 # via - # -r requirements.in + # -r model-engine/requirements.in # ddtrace testing-common-database==2.0.3 # via testing-postgresql testing-postgresql==1.3.0 - # via -r requirements.in + # via -r model-engine/requirements.in tokenizers==0.15.2 # via - # -r requirements.in + # -r model-engine/requirements.in # transformers tomli==2.0.1 # via # build # hypercorn - # pep517 + # pyproject-hooks tqdm==4.65.0 # via - # -r requirements.in + # -r model-engine/requirements.in # huggingface-hub # transformers # twine transformers==4.38.0 - # via -r requirements.in + # via -r model-engine/requirements.in twine==3.7.1 - # via -r requirements.in + # via -r model-engine/requirements.in types-awscrt==0.16.23 # via # botocore-stubs @@ -566,9 +573,9 @@ urllib3==1.26.16 # kubernetes-asyncio # requests uvicorn==0.17.6 - # via -r requirements.in + # via -r model-engine/requirements.in uvloop==0.17.0 - # via -r requirements.in + # via -r model-engine/requirements.in vine==5.1.0 # via # amqp @@ -590,7 +597,7 @@ xmltodict==0.13.0 # via ddtrace yarl==1.9.2 # via - # -r requirements.in + # -r model-engine/requirements.in # aiohttp zipp==3.16.0 # via diff --git a/model-engine/tests/unit/conftest.py b/model-engine/tests/unit/conftest.py index 4b57afa1d..fd6b9f0ea 100644 --- a/model-engine/tests/unit/conftest.py +++ b/model-engine/tests/unit/conftest.py @@ -3383,6 +3383,48 @@ def build_endpoint_request_async_custom( return build_endpoint_request +@pytest.fixture +def build_endpoint_request_async_zipartifact_highpri( + test_api_key: str, model_bundle_3: ModelBundle +) -> BuildEndpointRequest: + build_endpoint_request = BuildEndpointRequest( + model_endpoint_record=ModelEndpointRecord( + id="test_model_endpoint_id_3", + name="test_model_endpoint_name_3", + created_by=test_api_key, + created_at=datetime(2022, 1, 4), + last_updated_at=datetime(2022, 1, 4), + metadata={}, + creation_task_id="test_creation_task_id", + endpoint_type=ModelEndpointType.ASYNC, + destination="test_destination", + status=ModelEndpointStatus.READY, + current_model_bundle=model_bundle_3, + owner=test_api_key, + ), + high_priority=True, + deployment_name=f"{test_api_key}-test_model_endpoint_name_3", + aws_role="default", + results_s3_bucket="test_s3_bucket", + child_fn_info=None, + post_inference_hooks=None, + labels=dict(team="test_team", product="test_product"), + min_workers=1, + max_workers=3, + per_worker=2, + cpus=1, + gpus=0, + memory="1G", + gpu_type=None, + storage=None, + optimize_costs=True, + broker_type=BrokerType.SQS, + default_callback_url=None, + default_callback_auth=None, + ) + return build_endpoint_request + + @pytest.fixture def build_endpoint_request_sync_custom( test_api_key: str, model_bundle_3: ModelBundle diff --git a/model-engine/tests/unit/infra/services/test_live_endpoint_builder_service.py b/model-engine/tests/unit/infra/services/test_live_endpoint_builder_service.py index bf568c9a9..a0e876eb7 100644 --- a/model-engine/tests/unit/infra/services/test_live_endpoint_builder_service.py +++ b/model-engine/tests/unit/infra/services/test_live_endpoint_builder_service.py @@ -102,8 +102,11 @@ def set_env_vars(): live_endpoint_builder_service.ECR_AWS_PROFILE = "default" live_endpoint_builder_service.GIT_TAG = "test_tag" live_endpoint_builder_service.ENV = "test_env" + live_endpoint_builder_service.WORKSPACE_PATH = ".." live_endpoint_builder_service.open = mock_open() live_endpoint_builder_service.os.mkdir = Mock() + live_endpoint_builder_service.open_wrapper = mock_open() + live_endpoint_builder_service.tempfile.mkstemp = Mock(return_value=["", ""]) @pytest.mark.asyncio @@ -114,6 +117,7 @@ async def test_build_endpoint( build_endpoint_request_async_runnable_image: BuildEndpointRequest, build_endpoint_request_sync_runnable_image: BuildEndpointRequest, build_endpoint_request_streaming_runnable_image: BuildEndpointRequest, + build_endpoint_request_async_zipartifact_highpri: BuildEndpointRequest, endpoint_builder_service_empty_docker_built: LiveEndpointBuilderService, endpoint_builder_service_empty_docker_not_built: LiveEndpointBuilderService, fake_model_endpoint_cache_repository: ModelEndpointCacheRepository, @@ -131,6 +135,7 @@ async def test_build_endpoint( build_endpoint_request_async_runnable_image, build_endpoint_request_sync_runnable_image, build_endpoint_request_streaming_runnable_image, + build_endpoint_request_async_zipartifact_highpri, ]: fake_monitoring_metrics_gateway.reset() repo.add_model_endpoint_record(request.model_endpoint_record) diff --git a/requirements-dev.txt b/requirements-dev.txt index f6e4d22cd..f785f8de0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,5 +5,5 @@ ipython==8.12.0 # 8.12.0 is the last version to support Python 3.8 isort==5.12.0 mypy==1.3.0 pip-tools==7.0.0 -poetry==1.5.1 -pre-commit==3.3.3 \ No newline at end of file +poetry==1.8.2 +pre-commit==3.3.3