qwak-sdk 0.5.62__py3-none-any.whl → 0.5.64__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of qwak-sdk might be problematic. Click here for more details.

Files changed (27) hide show
  1. qwak_sdk/__init__.py +1 -1
  2. qwak_sdk/commands/models/build/ui.py +5 -0
  3. qwak_sdk/commands/models/deployments/deploy/_logic/deploy_config.py +1 -1
  4. qwak_sdk/commands/models/deployments/deploy/_logic/deployment_message_helpers.py +1 -8
  5. qwak_sdk/commands/models/deployments/deploy/_logic/deployment_size_mapper.py +41 -3
  6. qwak_sdk/commands/models/deployments/deploy/_logic/local_deployment.py +193 -0
  7. qwak_sdk/commands/models/deployments/deploy/batch/_logic/deploy_executor.py +0 -1
  8. qwak_sdk/commands/models/deployments/deploy/batch/ui.py +1 -1
  9. qwak_sdk/commands/models/deployments/deploy/realtime/_logic/deploy_executor.py +0 -1
  10. qwak_sdk/commands/models/deployments/deploy/realtime/_logic/serving_strategy_mapper.py +0 -35
  11. qwak_sdk/commands/models/deployments/deploy/realtime/ui.py +25 -5
  12. qwak_sdk/commands/models/deployments/deploy/streaming/_logic/deploy_executor.py +0 -1
  13. qwak_sdk/commands/models/deployments/undeploy/_logic/request_undeploy.py +0 -27
  14. qwak_sdk/commands/models/runtime/runtime_commands_group.py +0 -2
  15. {qwak_sdk-0.5.62.dist-info → qwak_sdk-0.5.64.dist-info}/METADATA +5 -3
  16. {qwak_sdk-0.5.62.dist-info → qwak_sdk-0.5.64.dist-info}/RECORD +18 -26
  17. qwak_sdk/commands/models/_logic/__init__.py +0 -0
  18. qwak_sdk/commands/models/_logic/variations.py +0 -55
  19. qwak_sdk/commands/models/deployments/deploy/_logic/variations.py +0 -81
  20. qwak_sdk/commands/models/deployments/undeploy/_logic/variations.py +0 -74
  21. qwak_sdk/commands/models/runtime/traffic_update/__init__.py +0 -0
  22. qwak_sdk/commands/models/runtime/traffic_update/_logic/__init__.py +0 -0
  23. qwak_sdk/commands/models/runtime/traffic_update/_logic/execute_runtime_update_traffic.py +0 -54
  24. qwak_sdk/commands/models/runtime/traffic_update/_logic/variations.py +0 -84
  25. qwak_sdk/commands/models/runtime/traffic_update/ui.py +0 -37
  26. {qwak_sdk-0.5.62.dist-info → qwak_sdk-0.5.64.dist-info}/WHEEL +0 -0
  27. {qwak_sdk-0.5.62.dist-info → qwak_sdk-0.5.64.dist-info}/entry_points.txt +0 -0
qwak_sdk/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # fmt: off
2
2
  __author__ = '''Qwak.ai'''
3
- __version__ = '0.5.62'
3
+ __version__ = '0.5.64'
4
4
 
5
5
  from qwak.inner import wire_dependencies
6
6
 
@@ -110,6 +110,11 @@ from qwak_sdk.inner.tools.config_handler import config_handler
110
110
  type=str,
111
111
  help="[REMOTE BUILD] Custom IAM Role ARN.",
112
112
  )
113
+ @click.option(
114
+ "--service-account-key-secret-name",
115
+ type=str,
116
+ help="Custom service account for Gcp.",
117
+ )
113
118
  @click.option(
114
119
  "--cache/--no-cache",
115
120
  default=None,
@@ -246,7 +246,7 @@ class DeployConfig(YamlConfigMixin, QwakConfigBase):
246
246
  @dataclass
247
247
  class AdvancedOptions:
248
248
  iam_role_arn: str = field(default=None)
249
- purchase_option: str = field(default=PurchaseOption.SPOT.value)
249
+ purchase_option: str = field(default="")
250
250
  service_account_key_secret_name: str = field(default=None)
251
251
 
252
252
  model_id: str = field(default="")
@@ -11,7 +11,6 @@ from _qwak_proto.qwak.deployment.deployment_pb2 import (
11
11
  ServingStrategy,
12
12
  )
13
13
  from qwak.clients.administration.eco_system.client import EcosystemClient
14
- from qwak.clients.deployment.client import DeploymentManagementClient
15
14
  from qwak.clients.instance_template.client import InstanceTemplateManagementClient
16
15
  from qwak.exceptions import QwakException
17
16
 
@@ -40,12 +39,11 @@ UNKNOWN_SERVING_STRATEGY = (
40
39
  def get_env_to_deployment_message(
41
40
  deploy_config: DeployConfig,
42
41
  kube_deployment_type: KubeDeploymentType,
43
- deployment_client: DeploymentManagementClient,
44
42
  ecosystem_client: EcosystemClient,
45
43
  instance_template_client: InstanceTemplateManagementClient,
46
44
  ) -> Dict[str, EnvironmentDeploymentMessage]:
47
45
  deployment_size = deployment_size_from_deploy_config(
48
- deploy_config, instance_template_client
46
+ deploy_config, instance_template_client, ecosystem_client
49
47
  )
50
48
  advanced_deployment_options = get_advanced_deployment_options_from_deploy_config(
51
49
  deploy_config, kube_deployment_type
@@ -60,13 +58,8 @@ def get_env_to_deployment_message(
60
58
  )
61
59
 
62
60
  if kube_deployment_type == KubeDeploymentType.ONLINE:
63
- model_traffic = deployment_client.get_model_traffic_config(
64
- deploy_config.model_id
65
- )
66
-
67
61
  env_to_serving_strategy = create_realtime_serving_strategy_from_deploy_config(
68
62
  deploy_config,
69
- model_traffic,
70
63
  environment_name_to_config,
71
64
  )
72
65
  environment_variables = dictify_params(deploy_config.realtime.env_vars)
@@ -7,8 +7,11 @@ from _qwak_proto.qwak.user_application.common.v0.resources_pb2 import (
7
7
  GpuResources,
8
8
  PodComputeResourceTemplateSpec,
9
9
  )
10
+ from qwak.clients.administration.eco_system.client import EcosystemClient
10
11
  from qwak.clients.instance_template.client import InstanceTemplateManagementClient
12
+ from qwak.exceptions import QwakException
11
13
  from qwak.inner.instance_template.verify_template_id import verify_template_id
14
+ from qwak.inner.provider import Provider
12
15
 
13
16
  from qwak_sdk.commands.models.deployments.deploy._logic.deploy_config import (
14
17
  DeployConfig,
@@ -18,14 +21,37 @@ from qwak_sdk.commands.models.deployments.deploy._logic.deploy_config import (
18
21
  def deployment_size_from_deploy_config(
19
22
  deploy_config: DeployConfig,
20
23
  instance_template_client: InstanceTemplateManagementClient,
24
+ ecosystem_client: EcosystemClient,
21
25
  ) -> DeploymentSize:
22
26
  if deploy_config.resources.instance_size:
23
27
  deploy_config.resources.instance_size = (
24
28
  deploy_config.resources.instance_size.lower()
25
29
  )
26
- verify_template_id(
27
- deploy_config.resources.instance_size, instance_template_client
28
- )
30
+ account_details = ecosystem_client.get_account_details()
31
+ if deploy_config.environments:
32
+ environments_config = list(
33
+ ecosystem_client.get_environments_names_to_details(
34
+ deploy_config.environments
35
+ ).values()
36
+ )
37
+ else:
38
+ environments_config = [
39
+ account_details.environment_by_id[
40
+ account_details.default_environment_id
41
+ ]
42
+ ]
43
+ for environment_config in environments_config:
44
+ provider = __get_provider(environment_config)
45
+ try:
46
+ verify_template_id(
47
+ deploy_config.resources.instance_size,
48
+ instance_template_client,
49
+ provider=provider,
50
+ )
51
+ except QwakException as e:
52
+ raise QwakException(
53
+ f"Error with template {deploy_config.resources.instance_size} for environment {environment_config.name}: {e.message}"
54
+ )
29
55
 
30
56
  return DeploymentSize(
31
57
  number_of_pods=deploy_config.resources.pods,
@@ -56,3 +82,15 @@ def deployment_size_from_deploy_config(
56
82
  )
57
83
  ),
58
84
  )
85
+
86
+
87
+ def __get_provider(environment_config):
88
+ provider = None
89
+ cloud_type = environment_config.configuration.cloud_configuration.WhichOneof(
90
+ "configuration"
91
+ )
92
+ if cloud_type == "aws_cloud_configuration":
93
+ provider = Provider.AWS
94
+ elif cloud_type == "gcp_cloud_configuration":
95
+ provider = Provider.GCP
96
+ return provider
@@ -0,0 +1,193 @@
1
+ import base64
2
+
3
+ from _qwak_proto.qwak.ecosystem.v0.ecosystem_runtime_service_pb2 import (
4
+ GetCloudCredentialsParameters,
5
+ GetCloudCredentialsRequest,
6
+ PermissionSet,
7
+ PullModelsContainerRegistry,
8
+ )
9
+ from google.protobuf.duration_pb2 import Duration
10
+ from qwak.clients.administration.eco_system.client import EcosystemClient
11
+ from qwak.clients.build_orchestrator import BuildOrchestratorClient
12
+ from qwak.exceptions import QwakException
13
+ from rich.console import Console
14
+ from rich.layout import Layout
15
+ from rich.panel import Panel
16
+ from rich.progress import Progress
17
+
18
+ from qwak_sdk.commands.models.deployments.deploy._logic.deploy_config import (
19
+ DeployConfig,
20
+ )
21
+
22
+ tasks = {}
23
+
24
+
25
+ def local_deploy(config: DeployConfig):
26
+ try:
27
+ import docker
28
+ except ImportError:
29
+ raise QwakException(
30
+ "Error: 'docker' package is required to for local model deployment."
31
+ )
32
+
33
+ console = Console()
34
+ console.print(Panel(f"Deploying model {config.model_id} locally", style="green"))
35
+
36
+ client = docker.from_env()
37
+ build_image = _get_build_image(config)
38
+
39
+ if not client.images.list(name=build_image):
40
+ console.print(
41
+ "Pulling serving image from Qwak's remote repository. Might take a few minutes...",
42
+ style="yellow",
43
+ )
44
+ _docker_login(client)
45
+ _image_pull(client, build_image)
46
+
47
+ try:
48
+ container = start_local_qwak_container(build_image, client, config)
49
+ container.reload()
50
+ host_port = container.ports.get("5000/tcp")[0]["HostPort"]
51
+
52
+ layout = example_usage_layout(host_port)
53
+ console.print(layout)
54
+
55
+ for line in container.logs(stream=True):
56
+ console.print(str(line.strip().decode()), style="blue")
57
+
58
+ except KeyboardInterrupt:
59
+ console.print(Panel("Stopping container...", style="red"))
60
+ container.stop()
61
+ container.remove()
62
+
63
+
64
+ def example_usage_layout(host_port: str):
65
+ layout = Layout(size=6)
66
+ layout.split_row(
67
+ Layout(name="left"),
68
+ Layout(name="right"),
69
+ )
70
+ layout["left"].update(
71
+ Panel(
72
+ f"[bold]cURL usage:[bold]\n\n"
73
+ f"curl --location\n"
74
+ f"--request POST http://localhost:{host_port}/predict\n"
75
+ f"--header 'Content-Type: application/json'\n"
76
+ f"--data '...'",
77
+ style="green",
78
+ )
79
+ )
80
+ layout["right"].update(
81
+ Panel(
82
+ f"[bold]Python client usage:[bold]\n\n"
83
+ f"from qwak_inference import RealtimeClient\n\nfeature_vector=[...]\n"
84
+ f"client = RealTimeClient(model_api='http://localhost:{host_port}/predict')\n"
85
+ f"client.predict(feature_vector)",
86
+ style="green",
87
+ )
88
+ )
89
+ return layout
90
+
91
+
92
+ def start_local_qwak_container(build_image: str, docker_client, config: DeployConfig):
93
+ return docker_client.containers.run(
94
+ build_image,
95
+ command=["bentoml", "serve-gunicorn", "./"],
96
+ entrypoint="./docker-entrypoint.sh",
97
+ environment=[
98
+ "BENTOML_GUNICORN_WORKERS=2",
99
+ "QWAK_DEBUG_MODE=True",
100
+ f"QWAK_MODEL_ID={config.model_id}",
101
+ "BENTOML_DO_NOT_TRACK=True",
102
+ "PYTHONPATH=/qwak/model_dir:/qwak/model_dir/main:$PYTHONPATH",
103
+ f"QWAK_BUILD_ID={config.build_id}",
104
+ "BUNDLE_PATH=/home/bentoml/bundle",
105
+ "BENTOML_HOME=/home/bentoml/",
106
+ ],
107
+ stream=True,
108
+ detach=True,
109
+ publish_all_ports=True,
110
+ )
111
+
112
+
113
+ def _get_build_image(config: DeployConfig):
114
+ return (
115
+ BuildOrchestratorClient().get_build(config.build_id).build.build_destined_image
116
+ )
117
+
118
+
119
+ def _show_progress(line, progress):
120
+ if line["status"] == "Downloading":
121
+ idx = f'[red][Download {line["id"]}]'
122
+ elif line["status"] == "Extracting":
123
+ idx = f'[green][Extract {line["id"]}]'
124
+ else:
125
+ # skip other statuses
126
+ return
127
+
128
+ if idx not in tasks.keys():
129
+ tasks[idx] = progress.add_task(f"{idx}", total=line["progressDetail"]["total"])
130
+ else:
131
+ progress.update(tasks[idx], completed=line["progressDetail"]["current"])
132
+
133
+
134
+ def _image_pull(docker_client, image_name: str):
135
+ with Progress() as progress:
136
+ resp = docker_client.api.pull(image_name, stream=True, decode=True)
137
+ for line in resp:
138
+ _show_progress(line, progress)
139
+
140
+
141
+ def _docker_login(docker_client):
142
+ try:
143
+ import boto3
144
+ except ImportError:
145
+ raise QwakException(
146
+ "Error: The 'boto3' package is necessary for local model deployment. Install it directly or by running"
147
+ " 'pip install qwak[local-deployment]'"
148
+ )
149
+
150
+ # TODO: block if not an AWS based container registry
151
+ credentials = _get_aws_credentials()
152
+
153
+ aws_credentials = credentials.cloud_credentials.aws_temporary_credentials
154
+ ecr_client = boto3.Session(
155
+ aws_access_key_id=aws_credentials.access_key_id,
156
+ aws_secret_access_key=aws_credentials.secret_access_key,
157
+ aws_session_token=aws_credentials.session_token,
158
+ region_name=aws_credentials.region,
159
+ ).client("ecr")
160
+
161
+ ecr_credentials = ecr_client.get_authorization_token()["authorizationData"][0]
162
+
163
+ try:
164
+ docker_client.login(
165
+ username="AWS",
166
+ registry=ecr_credentials["proxyEndpoint"],
167
+ password=base64.b64decode(ecr_credentials["authorizationToken"])
168
+ .replace(b"AWS:", b"")
169
+ .decode("utf-8"),
170
+ )
171
+
172
+ except Exception as e:
173
+ raise QwakException(f"Failed to login to Qwak's container registry: {e}")
174
+
175
+
176
+ def _get_aws_credentials():
177
+ try:
178
+ eco_client = EcosystemClient()
179
+ credentials = eco_client.get_cloud_credentials(
180
+ request=GetCloudCredentialsRequest(
181
+ parameters=GetCloudCredentialsParameters(
182
+ duration=Duration(seconds=60 * 60, nanos=0), # 6 hours
183
+ permission_set=PermissionSet(
184
+ pull_models_container_registry=PullModelsContainerRegistry()
185
+ ),
186
+ )
187
+ )
188
+ )
189
+ return credentials
190
+ except Exception as e:
191
+ raise QwakException(
192
+ f"Failed to get credentials to pull image from Qwak's container registry: {e}."
193
+ )
@@ -14,7 +14,6 @@ class BatchDeployExecutor(BaseDeployExecutor):
14
14
  env_deployment_messages = get_env_to_deployment_message(
15
15
  self.config,
16
16
  KubeDeploymentType.BATCH,
17
- self.deploy_client,
18
17
  self.ecosystem_client,
19
18
  self.instance_template_client,
20
19
  )
@@ -64,7 +64,7 @@ from qwak_sdk.inner.tools.config_handler import config_handler
64
64
  @click.option(
65
65
  "--service-account-key-secret-name",
66
66
  type=str,
67
- help="Custom service accound for Gcp.",
67
+ help="Custom service account for Gcp.",
68
68
  )
69
69
  @click.option(
70
70
  "--sync",
@@ -14,7 +14,6 @@ class RealtimeDeployExecutor(BaseDeployExecutor):
14
14
  env_deployment_messages = get_env_to_deployment_message(
15
15
  self.config,
16
16
  KubeDeploymentType.ONLINE,
17
- self.deploy_client,
18
17
  self.ecosystem_client,
19
18
  self.instance_template_client,
20
19
  )
@@ -7,24 +7,14 @@ from _qwak_proto.qwak.deployment.deployment_pb2 import (
7
7
  RealTimeConfig,
8
8
  ServingStrategy,
9
9
  TrafficConfig,
10
- Variation,
11
- )
12
- from _qwak_proto.qwak.deployment.deployment_service_pb2 import (
13
- GetDeploymentStatusResponse,
14
10
  )
15
11
  from _qwak_proto.qwak.ecosystem.v0.ecosystem_pb2 import UserContextEnvironmentDetails
16
12
  from qwak.exceptions import QwakException
17
13
 
18
14
  from qwak_sdk.commands.audience._logic.config.v1.audience_config import AudienceConfig
19
- from qwak_sdk.commands.models._logic.variations import (
20
- create_variation_from_variation_config,
21
- )
22
15
  from qwak_sdk.commands.models.deployments.deploy._logic.deploy_config import (
23
16
  DeployConfig,
24
17
  )
25
- from qwak_sdk.commands.models.deployments.deploy._logic.variations import (
26
- get_variations_for_deploy,
27
- )
28
18
 
29
19
 
30
20
  def create_realtime_serving_strategy(
@@ -32,7 +22,6 @@ def create_realtime_serving_strategy(
32
22
  audiences: List[AudienceConfig],
33
23
  fallback_variation: str,
34
24
  variation_name: str,
35
- variations: List[Variation],
36
25
  variation_protected_state: bool = False,
37
26
  ) -> ServingStrategy:
38
27
  return ServingStrategy(
@@ -41,7 +30,6 @@ def create_realtime_serving_strategy(
41
30
  auto_scaling_config=auto_scaling,
42
31
  traffic_config=TrafficConfig(
43
32
  selected_variation_name=variation_name,
44
- variations=variations,
45
33
  audience_routes_entries=[
46
34
  audience.to_audience_route_entry(index)
47
35
  for index, audience in enumerate(audiences)
@@ -56,7 +44,6 @@ def create_realtime_serving_strategy(
56
44
 
57
45
  def create_realtime_serving_strategy_from_deploy_config(
58
46
  deploy_config: DeployConfig,
59
- model_traffic: GetDeploymentStatusResponse,
60
47
  environment_name_to_config: Dict[str, UserContextEnvironmentDetails],
61
48
  ) -> Dict[str, ServingStrategy]:
62
49
  serving_strategies = {}
@@ -70,34 +57,12 @@ def create_realtime_serving_strategy_from_deploy_config(
70
57
  else None
71
58
  )
72
59
  for env_name, env_config in environment_name_to_config.items():
73
- env_variations_response = model_traffic.environment_to_model_traffic.get(
74
- env_config.id
75
- )
76
- existing_variation = (
77
- env_variations_response.variations if env_variations_response else []
78
- )
79
60
  try:
80
- variations = []
81
- if not deploy_config.realtime.audiences:
82
- variations = get_variations_for_deploy(
83
- variation_name=variation_name,
84
- existing_variations=existing_variation,
85
- requested_variations=list(
86
- map(
87
- create_variation_from_variation_config,
88
- deploy_config.realtime.variations,
89
- )
90
- ),
91
- environment_name=env_name,
92
- variation_protected_state=variation_protected_state,
93
- )
94
-
95
61
  serving_strategies[env_config.id] = create_realtime_serving_strategy(
96
62
  auto_scaling,
97
63
  deploy_config.realtime.audiences,
98
64
  fallback_variation,
99
65
  variation_name,
100
- variations,
101
66
  variation_protected_state,
102
67
  )
103
68
 
@@ -14,6 +14,9 @@ from qwak_sdk.commands.models.deployments.deploy._logic.deploy_config import (
14
14
  from qwak_sdk.commands.models.deployments.deploy._logic.deployment_response_handler import (
15
15
  client_deployment,
16
16
  )
17
+ from qwak_sdk.commands.models.deployments.deploy._logic.local_deployment import (
18
+ local_deploy,
19
+ )
17
20
  from qwak_sdk.commands.models.deployments.deploy.realtime._logic.deploy_executor import (
18
21
  RealtimeDeployExecutor,
19
22
  )
@@ -87,7 +90,7 @@ logger = get_qwak_logger()
87
90
  @click.option(
88
91
  "--service-account-key-secret-name",
89
92
  type=str,
90
- help="Custom service accound for Gcp.",
93
+ help="Custom service account for Gcp.",
91
94
  )
92
95
  @click.option(
93
96
  "--max-batch-size",
@@ -113,6 +116,12 @@ logger = get_qwak_logger()
113
116
  default=False,
114
117
  help="Waiting for deployments to be ready",
115
118
  )
119
+ @click.option(
120
+ "--local",
121
+ is_flag=True,
122
+ default=False,
123
+ help="Deploy the model container locally, for development purposes only",
124
+ )
116
125
  @click.option(
117
126
  "-v",
118
127
  "--verbose",
@@ -170,13 +179,20 @@ logger = get_qwak_logger()
170
179
  help="Whether the deployment variation is protected. Default is false",
171
180
  )
172
181
  def realtime(
173
- verbose: bool, from_file: Optional[str], out_conf: bool, sync: bool, **kwargs
182
+ verbose: bool,
183
+ from_file: Optional[str],
184
+ out_conf: bool,
185
+ sync: bool,
186
+ local: bool,
187
+ **kwargs,
174
188
  ):
175
189
  set_qwak_logger_stdout_verbosity_level(verbose + 1)
176
- deploy_realtime(from_file, out_conf, sync, **kwargs)
190
+ deploy_realtime(from_file, out_conf, sync, local, **kwargs)
177
191
 
178
192
 
179
- def deploy_realtime(from_file: Optional[str], out_conf: bool, sync: bool, **kwargs):
193
+ def deploy_realtime(
194
+ from_file: Optional[str], out_conf: bool, sync: bool, local: bool, **kwargs
195
+ ):
180
196
  config: DeployConfig = config_handler(
181
197
  config=DeployConfig,
182
198
  from_file=from_file,
@@ -184,6 +200,10 @@ def deploy_realtime(from_file: Optional[str], out_conf: bool, sync: bool, **kwar
184
200
  sections=("realtime", "autoscaling"),
185
201
  **kwargs,
186
202
  )
187
- if not out_conf:
203
+
204
+ if local:
205
+ local_deploy(config)
206
+
207
+ elif not out_conf:
188
208
  deploy_executor = RealtimeDeployExecutor(config)
189
209
  client_deployment(deploy=deploy_executor, sync=sync)
@@ -14,7 +14,6 @@ class StreamDeployExecutor(BaseDeployExecutor):
14
14
  env_deployment_messages = get_env_to_deployment_message(
15
15
  self.config,
16
16
  KubeDeploymentType.STREAM,
17
- self.deploy_client,
18
17
  self.ecosystem_client,
19
18
  self.instance_template_client,
20
19
  )
@@ -8,7 +8,6 @@ from _qwak_proto.qwak.deployment.deployment_pb2 import (
8
8
  EnvironmentUndeploymentMessage,
9
9
  ModelDeploymentStatus,
10
10
  TrafficConfig,
11
- Variation,
12
11
  )
13
12
  from _qwak_proto.qwak.ecosystem.v0.ecosystem_pb2 import EnvironmentDetails
14
13
  from qwak.clients.administration.eco_system.client import EcosystemClient
@@ -16,15 +15,9 @@ from qwak.clients.deployment.client import DeploymentManagementClient
16
15
  from qwak.exceptions import QwakException
17
16
  from qwak.tools.logger.logger import get_qwak_logger
18
17
 
19
- from qwak_sdk.commands.models._logic.variations import (
20
- create_variation_from_variation_config,
21
- )
22
18
  from qwak_sdk.commands.models.deployments.deploy._logic.deploy_config import (
23
19
  DeployConfig,
24
20
  )
25
- from qwak_sdk.commands.models.deployments.undeploy._logic.variations import (
26
- validate_variations_for_undeploy,
27
- )
28
21
  from qwak_sdk.tools.utils import qwak_spinner
29
22
 
30
23
  NO_DEPLOYED_VARIATIONS_ERROR_MSG = (
@@ -45,12 +38,10 @@ def get_deployed_variation_name(existing_variations_names: Set[str]) -> str:
45
38
 
46
39
  def get_environment_undeploy_message(
47
40
  audiences: List[AudienceRoutesEntry],
48
- env_name: str,
49
41
  existing_variations_names: Set[str],
50
42
  fallback_variation: str,
51
43
  model_id: str,
52
44
  model_uuid: str,
53
- requested_variations: List[Variation],
54
45
  variation_name: str,
55
46
  ):
56
47
  if not variation_name and len(existing_variations_names) == 1:
@@ -58,20 +49,12 @@ def get_environment_undeploy_message(
58
49
  existing_variations_names=existing_variations_names
59
50
  )
60
51
 
61
- if not audiences:
62
- validate_variations_for_undeploy(
63
- variation_name,
64
- existing_variations_names,
65
- requested_variations,
66
- env_name,
67
- )
68
52
  return EnvironmentUndeploymentMessage(
69
53
  model_id=model_id,
70
54
  model_uuid=model_uuid,
71
55
  hosting_service_type=DeploymentHostingServiceType.KUBE_DEPLOYMENT,
72
56
  traffic_config=TrafficConfig(
73
57
  selected_variation_name=variation_name,
74
- variations=requested_variations,
75
58
  audience_routes_entries=audiences,
76
59
  fallback_variation=fallback_variation,
77
60
  ),
@@ -84,7 +67,6 @@ def get_env_to_undeploy_message(
84
67
  env_id_to_deployment_details: Dict[str, EnvironmentDeploymentDetailsMessage],
85
68
  env_name_to_env_details: Dict[str, EnvironmentDetails],
86
69
  model_id: str,
87
- requested_variations: List[Variation],
88
70
  variation_name: str,
89
71
  fallback_variation: str,
90
72
  ) -> Dict[str, EnvironmentUndeploymentMessage]:
@@ -111,12 +93,10 @@ def get_env_to_undeploy_message(
111
93
  env_details.id
112
94
  ] = get_environment_undeploy_message(
113
95
  audiences,
114
- env_name,
115
96
  existing_variations_names,
116
97
  fallback_variation,
117
98
  model_id,
118
99
  model_uuid,
119
- requested_variations,
120
100
  variation_name,
121
101
  )
122
102
  except QwakException as e:
@@ -138,12 +118,6 @@ def undeploy(
138
118
  audience.to_audience_route_entry(index)
139
119
  for index, audience in enumerate(config.realtime.audiences)
140
120
  ]
141
- requested_variations = list(
142
- map(
143
- create_variation_from_variation_config,
144
- config.realtime.variations if config.realtime else [],
145
- )
146
- )
147
121
 
148
122
  if not model_uuid:
149
123
  raise QwakException("missing argument model uuid")
@@ -164,7 +138,6 @@ def undeploy(
164
138
  env_id_to_deployment_details,
165
139
  env_name_to_env_details,
166
140
  model_id,
167
- requested_variations,
168
141
  config.realtime.variation_name,
169
142
  config.realtime.fallback_variation,
170
143
  )
@@ -1,7 +1,6 @@
1
1
  import click
2
2
 
3
3
  from qwak_sdk.commands.models.runtime.logs.ui import runtime_logs
4
- from qwak_sdk.commands.models.runtime.traffic_update.ui import runtime_traffic_update
5
4
  from qwak_sdk.commands.models.runtime.update.ui import runtime_update
6
5
 
7
6
 
@@ -15,5 +14,4 @@ def runtime_commands_group():
15
14
 
16
15
 
17
16
  runtime_commands_group.add_command(runtime_logs)
18
- runtime_commands_group.add_command(runtime_traffic_update)
19
17
  runtime_commands_group.add_command(runtime_update)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qwak-sdk
3
- Version: 0.5.62
3
+ Version: 0.5.64
4
4
  Summary: Qwak SDK and CLI for qwak models
5
5
  License: Apache-2.0
6
6
  Keywords: mlops,ml,deployment,serving,model
@@ -19,7 +19,8 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
19
19
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
20
  Provides-Extra: batch
21
21
  Provides-Extra: feedback
22
- Requires-Dist: boto3 (>=1.24.116,<2.0.0) ; extra == "batch" or extra == "feedback"
22
+ Provides-Extra: local-deployment
23
+ Requires-Dist: boto3 (>=1.24.116,<2.0.0) ; extra == "batch" or extra == "feedback" or extra == "local-deployment"
23
24
  Requires-Dist: cookiecutter
24
25
  Requires-Dist: croniter (==1.4.1)
25
26
  Requires-Dist: gitpython (>=2.1.0)
@@ -28,8 +29,9 @@ Requires-Dist: pandas (<1.4) ; (python_full_version >= "3.7.1" and python_versio
28
29
  Requires-Dist: pandas (>=1.4.3,<2.0.0) ; (python_version >= "3.8" and python_version < "3.10") and (extra == "batch" or extra == "feedback")
29
30
  Requires-Dist: pyarrow (>=6.0.0,<11.0.0) ; extra == "batch"
30
31
  Requires-Dist: python-json-logger (>=2.0.2)
31
- Requires-Dist: qwak-core (==0.3.129)
32
+ Requires-Dist: qwak-core (==0.3.163)
32
33
  Requires-Dist: qwak-inference (>=0.1,<0.2)
34
+ Requires-Dist: rich (>=13.0.0)
33
35
  Requires-Dist: tabulate (>=0.8.0)
34
36
  Requires-Dist: yaspin (>=2.0.0)
35
37
  Project-URL: Home page, https://www.qwak.com/
@@ -1,4 +1,4 @@
1
- qwak_sdk/__init__.py,sha256=IfOiNHJPhkmbzOw9hp1kPZZDP7sKoxJVXBO78h1I4Dk,135
1
+ qwak_sdk/__init__.py,sha256=lNU63Fap7GcBaP4WD3Kn5M3EOnMoAoKbJPYJRjJuWHA,135
2
2
  qwak_sdk/cli.py,sha256=FIK1dUNxR57ypb-CeD7fKSJnPJ02lrjR9G4aj2qMLPU,2458
3
3
  qwak_sdk/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  qwak_sdk/commands/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -99,8 +99,6 @@ qwak_sdk/commands/feature_store/resume/ui.py,sha256=nI87xvA30qNQVJnT67lYJgwKGBtv
99
99
  qwak_sdk/commands/feature_store/trigger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
100
  qwak_sdk/commands/feature_store/trigger/ui.py,sha256=1VJBqzxMwUWcytmZL0ymf9QlYa7oX6RDLmKsLmYVW_4,1015
101
101
  qwak_sdk/commands/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
- qwak_sdk/commands/models/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
- qwak_sdk/commands/models/_logic/variations.py,sha256=fpe6kM8HWP7zkRl0AhZa6Ffg_LE6uiSSow0Wf5HAJxA,2050
104
102
  qwak_sdk/commands/models/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
103
  qwak_sdk/commands/models/build/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
104
  qwak_sdk/commands/models/build/_logic/build_steps.py,sha256=Iqc3XDWhJ4LSlUiqfwP2Y9aUDpqab3WWVq-xXt-Uvo0,1533
@@ -123,7 +121,7 @@ qwak_sdk/commands/models/build/_logic/util/__init__.py,sha256=47DEQpj8HBSa-_TImW
123
121
  qwak_sdk/commands/models/build/_logic/util/protobuf_factory.py,sha256=ar_oY38w_x0sxgVF7EBs5h7gchNsDntvtKK5sSYxb24,1686
124
122
  qwak_sdk/commands/models/build/_logic/util/step_decorator.py,sha256=HLZyCGdqe3Ir7SaPWp1YNRHJpjXG-e-bbAvnOFysAVM,1913
125
123
  qwak_sdk/commands/models/build/_logic/util/text.py,sha256=tH-v19Mt8l90sMVxku5XRtrderT0qdRqJ-jLijqannA,188
126
- qwak_sdk/commands/models/build/ui.py,sha256=vJ5dY1RbtDY6MIWesWhNaCaWA89An7IEIaYp3nVcAMg,8851
124
+ qwak_sdk/commands/models/build/ui.py,sha256=MX4OpBHh3X83JXc0FFsx4PP_4IZVVGe2bBS1Z5VK36o,8967
127
125
  qwak_sdk/commands/models/builds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
128
126
  qwak_sdk/commands/models/builds/builds_commands_group.py,sha256=0nSfTY8TracXG61rFboQWUTXJisHO6dgtJKeijy6ru8,491
129
127
  qwak_sdk/commands/models/builds/cancel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -145,34 +143,33 @@ qwak_sdk/commands/models/deployments/deploy/__init__.py,sha256=47DEQpj8HBSa-_TIm
145
143
  qwak_sdk/commands/models/deployments/deploy/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
146
144
  qwak_sdk/commands/models/deployments/deploy/_logic/advance_deployment_options_handler.py,sha256=gCo3WuP-IDuA5Sg7WbFmp1hYT5qyXyf6pUD3tZGyZXs,1251
147
145
  qwak_sdk/commands/models/deployments/deploy/_logic/base_deploy_executor.py,sha256=HYKUG5s4ZrpFwoxJYn3EuTYU71lmM0cFOOjbHqd9ajY,2706
148
- qwak_sdk/commands/models/deployments/deploy/_logic/deploy_config.py,sha256=n7jSImcKw28xvXHoSRrtxKiGbxpqhovKv6sa7_-LYoQ,8979
146
+ qwak_sdk/commands/models/deployments/deploy/_logic/deploy_config.py,sha256=4kcjUeUqkuGf9Zab2t6Z05Spp8VgJszRpe1rjeTZ64w,8956
149
147
  qwak_sdk/commands/models/deployments/deploy/_logic/deployment.py,sha256=zBTjeeEVtTnTwUMwUfGTI3Ip9AgqKDvHAZ6_Wi22XVo,13221
150
- qwak_sdk/commands/models/deployments/deploy/_logic/deployment_message_helpers.py,sha256=sSKktrGVAwfbpKwQUW1xexhiNVBkPizaCj89OmLE-8E,4852
148
+ qwak_sdk/commands/models/deployments/deploy/_logic/deployment_message_helpers.py,sha256=wkRiQWlnQr6tjWb4wfN00Lb4Dy98QydE-Zd4j9-3zDw,4608
151
149
  qwak_sdk/commands/models/deployments/deploy/_logic/deployment_response_handler.py,sha256=cHa2iF_2A8e1wx2a4UZuTjQ5IHgPvua1xziFJwzP_6s,5837
152
- qwak_sdk/commands/models/deployments/deploy/_logic/deployment_size_mapper.py,sha256=Vm-vrNUMb2X6RLpLkX8jzA1_gl2o7k16w1UMVOzunIA,2165
150
+ qwak_sdk/commands/models/deployments/deploy/_logic/deployment_size_mapper.py,sha256=sfECrq-6Emt-qY6K3J-hf8-PYw8TP4EgpjynTPstDUE,3655
153
151
  qwak_sdk/commands/models/deployments/deploy/_logic/get_latest_successful_build.py,sha256=fsahuSvgGzVjkZGaTH9YyNSOtRWBHYlyAU3wTriyCgI,978
154
- qwak_sdk/commands/models/deployments/deploy/_logic/variations.py,sha256=n3OGHTYgjnmLYbafZbAOaudaGbeKjw8jbX4MhZX47fE,3256
152
+ qwak_sdk/commands/models/deployments/deploy/_logic/local_deployment.py,sha256=n42Hc-FnA3ODKok-_Nmnbjj1aPGW2P-DJ2dGDSZpSrE,6270
155
153
  qwak_sdk/commands/models/deployments/deploy/batch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
154
  qwak_sdk/commands/models/deployments/deploy/batch/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
157
155
  qwak_sdk/commands/models/deployments/deploy/batch/_logic/advanced_deployment_mapper.py,sha256=bwvPIoKCNaJmaNsPYL_dhK04FCRZvlj_jkwhCZM-eks,599
158
- qwak_sdk/commands/models/deployments/deploy/batch/_logic/deploy_executor.py,sha256=oOKPotp37qdLupYau5Fv2voki4SAIFitHMn_U4ZaJP8,946
159
- qwak_sdk/commands/models/deployments/deploy/batch/ui.py,sha256=LxEbrPty1CYg_ohpMFih3u3h0p65zNdbp3U3QkVXYcw,3542
156
+ qwak_sdk/commands/models/deployments/deploy/batch/_logic/deploy_executor.py,sha256=7ND4JlV0xT8v_0kEHBseS9dHtpuCs_8D1CC2L_nVJDc,914
157
+ qwak_sdk/commands/models/deployments/deploy/batch/ui.py,sha256=Sek4sZ-XiFAJ7-bCakMX1BlOB7zrOLYHF1QyRo6LETw,3542
160
158
  qwak_sdk/commands/models/deployments/deploy/deploy_commands_group.py,sha256=LnfdnfeSTCO90SrJfgXf21xiVRry6Y2sfj_JfdBQu_U,500
161
159
  qwak_sdk/commands/models/deployments/deploy/realtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
162
160
  qwak_sdk/commands/models/deployments/deploy/realtime/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
163
161
  qwak_sdk/commands/models/deployments/deploy/realtime/_logic/advanced_deployment_mapper.py,sha256=LDJR2XzSG3QkHaAY4cSeDOb6e1-Aw-p5ZbRiLy2BXsU,1006
164
- qwak_sdk/commands/models/deployments/deploy/realtime/_logic/deploy_executor.py,sha256=KClooUmWbt4Iym7_PC8jg8oHCFRWNvno-16-aFWwutA,950
165
- qwak_sdk/commands/models/deployments/deploy/realtime/_logic/serving_strategy_mapper.py,sha256=4UWpT6RMHvYCxnAxOcaSrvkEIz7v4oGCA-3Iz2KGNxs,3943
166
- qwak_sdk/commands/models/deployments/deploy/realtime/ui.py,sha256=UbuneiX9xWYYq9X7cG1MKlC6yhwZisQ_5fe63vyJcCw,5153
162
+ qwak_sdk/commands/models/deployments/deploy/realtime/_logic/deploy_executor.py,sha256=XuuwuOBS2FtT0mEhngwo1z6teKT3GyHvWvXAJXMZG9s,918
163
+ qwak_sdk/commands/models/deployments/deploy/realtime/_logic/serving_strategy_mapper.py,sha256=297a6ncG-lS7kscZRy88Fg11qn3_vgB6rTYeq7kXH24,2580
164
+ qwak_sdk/commands/models/deployments/deploy/realtime/ui.py,sha256=hiVte3GkLZp3Iio6TsOs6B1RmkGmsslCXyABGZPiT5A,5509
167
165
  qwak_sdk/commands/models/deployments/deploy/streaming/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
168
166
  qwak_sdk/commands/models/deployments/deploy/streaming/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
169
- qwak_sdk/commands/models/deployments/deploy/streaming/_logic/deploy_executor.py,sha256=RRkNzFcPhfj70pjFJE5DQDMxDi-gJOiaH5C9ofHEQUA,948
167
+ qwak_sdk/commands/models/deployments/deploy/streaming/_logic/deploy_executor.py,sha256=tfYZ3Qdk4yFBelkASYamn60JKWZRlFeFEfdXrMs8N9A,916
170
168
  qwak_sdk/commands/models/deployments/deploy/streaming/_logic/serving_strategy_mapper.py,sha256=97m93afiwmrNL9EQ3tE4JmrBYfcBnKZB9ipcvzrIWDk,1500
171
169
  qwak_sdk/commands/models/deployments/deploy/streaming/ui.py,sha256=9tPMnVnLNX3lYyBd4BAh-YThDFz99oyvrCRd_tDZufU,5796
172
170
  qwak_sdk/commands/models/deployments/undeploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
171
  qwak_sdk/commands/models/deployments/undeploy/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
174
- qwak_sdk/commands/models/deployments/undeploy/_logic/request_undeploy.py,sha256=e-9lHQ2AbTyogUHxppPVli5pugl6ml4c1zAuFoLQTpw,9481
175
- qwak_sdk/commands/models/deployments/undeploy/_logic/variations.py,sha256=qo4qUB78voUhtM1YIuTz1OSBaLRS3xw97bwxgqRuLgI,3036
172
+ qwak_sdk/commands/models/deployments/undeploy/_logic/request_undeploy.py,sha256=s9YneVS_RgBMTGODAxLxK5qqgQ9bdGsO91xoNS76CdY,8623
176
173
  qwak_sdk/commands/models/deployments/undeploy/ui.py,sha256=1XYBouPgbJWOJCNR1UU1UOVnQS10iKsUqeNkvIBhHY8,2008
177
174
  qwak_sdk/commands/models/describe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
178
175
  qwak_sdk/commands/models/describe/_logic.py,sha256=LN1r3zrXLHf83U5ate7ED7VdoZuWUqcHwLZPMMZXRfs,5589
@@ -252,12 +249,7 @@ qwak_sdk/commands/models/models_command_group.py,sha256=xrI_XwHUaahnH0P2iNkUps34
252
249
  qwak_sdk/commands/models/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
250
  qwak_sdk/commands/models/runtime/logs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
251
  qwak_sdk/commands/models/runtime/logs/ui.py,sha256=2wxk4eqwKqSr83Wsn-4wHRvug3hQLmHeoyJ4zBAsW4U,1577
255
- qwak_sdk/commands/models/runtime/runtime_commands_group.py,sha256=q8h9CNNXFdWW5ay-MFlTkG9MnwfLrkPDuI-BWsyjuos,566
256
- qwak_sdk/commands/models/runtime/traffic_update/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
257
- qwak_sdk/commands/models/runtime/traffic_update/_logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
258
- qwak_sdk/commands/models/runtime/traffic_update/_logic/execute_runtime_update_traffic.py,sha256=el3MlANMcmbfxNCVTeqfT6x9DB_F4Ns6O-M2Am9F_uI,1811
259
- qwak_sdk/commands/models/runtime/traffic_update/_logic/variations.py,sha256=UhVjyW4jeS7558ItpgffvBa7b8Pl2Jbb7SIEL0kgukU,3160
260
- qwak_sdk/commands/models/runtime/traffic_update/ui.py,sha256=gCuPpRNYtB98Glr5FExj5nLfe8iwKF7xLAUzxqJ4tb8,1212
252
+ qwak_sdk/commands/models/runtime/runtime_commands_group.py,sha256=nuyPkGSbJ7RD6HNif6ygfR4yPXtPiDEEJ7UaVn04Wm8,421
261
253
  qwak_sdk/commands/models/runtime/update/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
262
254
  qwak_sdk/commands/models/runtime/update/_logic.py,sha256=vXgx16OTwx-8iucop-fU5o-BTWo5Iqiv-8aZjyeZ3Yg,393
263
255
  qwak_sdk/commands/models/runtime/update/ui.py,sha256=24GX7V05zK0HwLdNIBMaf1zJ9CC2BcrWjQsgXAJviy8,622
@@ -326,7 +318,7 @@ qwak_sdk/tools/colors.py,sha256=7pui_GGjC4uZKYFsIyXaJjYsjLxJVHb4OrfTgr93hqo,287
326
318
  qwak_sdk/tools/files.py,sha256=AyKJTOy7NhvP3SrqwIw_lxYNCOy1CvLgMmSJpWZ0OKM,2257
327
319
  qwak_sdk/tools/log_handling.py,sha256=Aa1EmxUPCX8YWiZRutUvnqPv6K_z1zoGMwIWsEv24mM,6327
328
320
  qwak_sdk/tools/utils.py,sha256=SHmU4r_m2ABZyFYMC03P17GvltPbYbmB39hvalIZEtI,1168
329
- qwak_sdk-0.5.62.dist-info/entry_points.txt,sha256=vSl0ELYDyj640oMM57u0AjBP87wtLYxCcGOendhEx80,47
330
- qwak_sdk-0.5.62.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
331
- qwak_sdk-0.5.62.dist-info/METADATA,sha256=ZsEQFkftScEaa23YHo2LCvVGkB_1FixMFyvZPxvFob0,1889
332
- qwak_sdk-0.5.62.dist-info/RECORD,,
321
+ qwak_sdk-0.5.64.dist-info/entry_points.txt,sha256=vSl0ELYDyj640oMM57u0AjBP87wtLYxCcGOendhEx80,47
322
+ qwak_sdk-0.5.64.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
323
+ qwak_sdk-0.5.64.dist-info/METADATA,sha256=9eccdkFDg9jVxb-G3KZOWYoUwQtBJKKEp-bRBOjiSVE,1984
324
+ qwak_sdk-0.5.64.dist-info/RECORD,,
File without changes
@@ -1,55 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import List
4
-
5
- from _qwak_proto.qwak.deployment.deployment_pb2 import TrafficSpec, Variation
6
- from qwak.exceptions import QwakException
7
-
8
- from qwak_sdk.commands.models.deployments.deploy._logic.deploy_config import (
9
- DeployConfig,
10
- )
11
-
12
-
13
- def validate_percentages(variations: List[Variation]):
14
- for variation in variations:
15
- if not (0 <= variation.traffic.percentage <= 100):
16
- raise QwakException(
17
- f"The variation '{variation.name}' contains invalid value '{variation.traffic.percentage}'. "
18
- f"Value must be between 0 and 100."
19
- )
20
-
21
- non_shadow_variations = list(filter(lambda v: not v.traffic.is_shadow, variations))
22
- non_shadow_variations_percentage_sum = sum(
23
- map(lambda v: v.traffic.percentage, non_shadow_variations)
24
- )
25
- if non_shadow_variations and non_shadow_variations_percentage_sum != 100:
26
- raise QwakException(
27
- "The variations do not sum to 100 percent. Please go over the variations config file."
28
- )
29
-
30
-
31
- def validate_shadow_variation(variations: List[Variation]):
32
- shadow_variations = list(filter(lambda v: v.traffic.is_shadow, variations))
33
- if len(shadow_variations) > 1:
34
- shadow_variations_names = list(map(lambda v: v.name, shadow_variations))
35
- raise QwakException(
36
- f"The variations contain more than one shadow variation {shadow_variations_names}. "
37
- f"Please go over the variations config file."
38
- )
39
-
40
-
41
- def create_variation_from_variation_config(
42
- variation_def: DeployConfig.Realtime.VariationConfig,
43
- ) -> Variation:
44
- try:
45
- return Variation(
46
- name=variation_def.name,
47
- traffic=TrafficSpec(
48
- percentage=variation_def.traffic.percentage,
49
- is_shadow=variation_def.traffic.shadow,
50
- ),
51
- )
52
- except Exception:
53
- raise QwakException(
54
- f"Could not parse variation {variation_def}. Please check you variation configuration file."
55
- )
@@ -1,81 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import List
4
-
5
- from _qwak_proto.qwak.deployment.deployment_pb2 import TrafficSpec, Variation
6
- from qwak.exceptions import QwakException
7
-
8
- from qwak_sdk.commands.models._logic.variations import (
9
- validate_percentages,
10
- validate_shadow_variation,
11
- )
12
-
13
-
14
- def get_variations_for_deploy(
15
- variation_name: str,
16
- existing_variations: List[Variation],
17
- requested_variations: List[Variation],
18
- environment_name: str,
19
- variation_protected_state: bool,
20
- ):
21
- if not existing_variations:
22
- existing_variations: List[Variation] = [
23
- Variation(
24
- name=variation_name,
25
- traffic=TrafficSpec(
26
- percentage=100,
27
- is_shadow=False,
28
- ),
29
- protected=variation_protected_state,
30
- )
31
- ]
32
- requested_variations_names = set(
33
- map(lambda variation: variation.name, requested_variations)
34
- )
35
- existing_variations_names = set(
36
- map(lambda variation: variation.name, existing_variations)
37
- )
38
- if requested_variations_names:
39
- if variation_name not in requested_variations_names:
40
- raise QwakException(
41
- f"Selected deployment to variation '{variation_name}' in {environment_name}, but provided the "
42
- f"following variations {list(requested_variations_names)}. "
43
- f"Please modify the variation name, or adjust the requested variations definitions"
44
- )
45
- elif variation_name not in existing_variations_names:
46
- raise QwakException(
47
- f"Selected deployment to existing variation '{variation_name}' in {environment_name}, but it is not one of "
48
- f"the existing variations {list(existing_variations_names)}. "
49
- f"Please modify the variation name, or supply the requested variations definitions with"
50
- f" the necessary changes"
51
- )
52
- else:
53
- # We are deploying to an existing variations without any change
54
- return existing_variations
55
-
56
- ignored_variations = existing_variations_names.difference(
57
- requested_variations_names
58
- )
59
- if ignored_variations:
60
- raise QwakException(
61
- f"The given variation configuration does not contain the configuration for the following "
62
- f"variations {list(ignored_variations)} in {environment_name}. You must include all the existing "
63
- f"variations configuration when passing the configuration"
64
- )
65
-
66
- unexpected_variations = requested_variations_names.difference(
67
- existing_variations_names
68
- )
69
- if variation_name in unexpected_variations:
70
- unexpected_variations.remove(variation_name)
71
- if unexpected_variations:
72
- raise QwakException(
73
- f"The given variation configuration contains the configuration for the following "
74
- f"variations {list(unexpected_variations)} in {environment_name} which are not expected. When deploying, "
75
- f"the variations configuration must contain all the existing variations, and an optional new variation."
76
- )
77
-
78
- validate_percentages(requested_variations)
79
- validate_shadow_variation(requested_variations)
80
-
81
- return requested_variations
@@ -1,74 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import List, Set
4
-
5
- from _qwak_proto.qwak.deployment.deployment_pb2 import Variation
6
- from qwak.exceptions import QwakException
7
-
8
- from qwak_sdk.commands.models.deployments.deploy._logic.variations import (
9
- validate_percentages,
10
- validate_shadow_variation,
11
- )
12
-
13
-
14
- def validate_variations_for_undeploy(
15
- variation_name: str,
16
- existing_variations_names: Set[str],
17
- requested_variations: List[Variation],
18
- environment_name: str,
19
- ):
20
- if variation_name not in existing_variations_names:
21
- if not variation_name:
22
- raise QwakException(
23
- f"No selected variation. You must specify a variation since there is more than one deployed in "
24
- f"{environment_name}. Currently {list(existing_variations_names)} are deployed"
25
- )
26
- raise QwakException(
27
- f"The selected variation {variation_name} is not deployed in {environment_name}. "
28
- f"Only {list(existing_variations_names)} are deployed"
29
- )
30
-
31
- requested_variations_names = set(
32
- map(lambda variation: variation.name, requested_variations)
33
- )
34
- if variation_name in requested_variations_names:
35
- raise QwakException(
36
- f"Selected undeploy to variation '{variation_name}', but it is part of "
37
- f"the requested variations {list(requested_variations_names)}. "
38
- f"Please modify the variation name, or supply the requested variations definitions with"
39
- f" the necessary changes"
40
- )
41
- if variation_name not in existing_variations_names:
42
- raise QwakException(
43
- f"Selected undeploy to variation {variation_name} but it is not one of "
44
- f"the existing variations {list(existing_variations_names)}. "
45
- f"Please modify the variation name."
46
- )
47
-
48
- ignored_variations = existing_variations_names.difference(
49
- requested_variations_names
50
- )
51
- if variation_name in ignored_variations:
52
- ignored_variations.remove(variation_name)
53
-
54
- if len(existing_variations_names) != 2 and ignored_variations:
55
- raise QwakException(
56
- f"The given variation configuration does not contain the configuration for the following "
57
- f"variations {list(ignored_variations)}. You must include all the variations that will exist after "
58
- f"undeploy in the configuration"
59
- )
60
-
61
- unexpected_variations = requested_variations_names.difference(
62
- existing_variations_names
63
- )
64
-
65
- if unexpected_variations:
66
- raise QwakException(
67
- f"The given variation configuration contains the configuration for the following "
68
- f"variations {list(unexpected_variations)} which are not expected. When undeploying, the variations "
69
- f"configuration must contain all expected remaining variations (from the existing ones)."
70
- )
71
-
72
- if requested_variations:
73
- validate_percentages(requested_variations)
74
- validate_shadow_variation(requested_variations)
@@ -1,54 +0,0 @@
1
- from typing import Tuple
2
-
3
- from qwak.clients.administration.eco_system.client import EcosystemClient
4
- from qwak.clients.deployment.client import DeploymentManagementClient
5
- from qwak.tools.logger.logger import get_qwak_logger
6
-
7
- from qwak_sdk.commands.models._logic.variations import (
8
- create_variation_from_variation_config,
9
- )
10
- from qwak_sdk.commands.models.deployments.deploy._logic.deploy_config import (
11
- DeployConfig,
12
- )
13
- from qwak_sdk.commands.models.runtime.traffic_update._logic.variations import (
14
- validate_requested_variations_against_existing_in_apply,
15
- )
16
- from qwak_sdk.inner.tools.config_handler import config_handler
17
-
18
- logger = get_qwak_logger()
19
-
20
-
21
- def execute_runtime_traffic_update(
22
- model_id: str, from_file: str, environment_name: Tuple[str]
23
- ):
24
- deployment_client = DeploymentManagementClient()
25
- ecosystem_client = EcosystemClient()
26
- config: DeployConfig = config_handler(
27
- config=DeployConfig,
28
- from_file=from_file,
29
- out_conf=False,
30
- sections=("realtime",),
31
- model_id=model_id,
32
- )
33
- requested_variations = list(
34
- map(
35
- create_variation_from_variation_config,
36
- config.realtime.variations,
37
- )
38
- )
39
- environment_names_to_details = ecosystem_client.get_environments_names_to_details(
40
- environment_name if environment_name else config.realtime.environments
41
- )
42
- validate_requested_variations_against_existing_in_apply(
43
- model_id,
44
- requested_variations,
45
- environment_names_to_details,
46
- deployment_client,
47
- )
48
- environment_ids = [env.id for env in environment_names_to_details.values()]
49
-
50
- deployment_client.apply_model_traffic_config(
51
- model_id=model_id,
52
- requested_variations=requested_variations,
53
- environment_ids=environment_ids,
54
- )
@@ -1,84 +0,0 @@
1
- from typing import Dict, List, Optional
2
-
3
- from _qwak_proto.qwak.deployment.deployment_pb2 import (
4
- EnvironmentTrafficMessage,
5
- Variation,
6
- )
7
- from _qwak_proto.qwak.ecosystem.v0.ecosystem_pb2 import EnvironmentDetails
8
- from qwak.clients.deployment.client import DeploymentManagementClient
9
- from qwak.exceptions import QwakException
10
-
11
- from qwak_sdk.commands.models._logic.variations import (
12
- validate_percentages,
13
- validate_shadow_variation,
14
- )
15
-
16
-
17
- def validate_requested_variations_against_existing_in_apply(
18
- model_id: str,
19
- requested_variations: List[Variation],
20
- environment_names_to_details: Dict[str, EnvironmentDetails],
21
- deployment_client: DeploymentManagementClient,
22
- ):
23
- model_traffic = deployment_client.get_model_traffic_config(model_id)
24
- environments_traffic = dict(model_traffic.environment_to_model_traffic)
25
- errors = []
26
- for env_name, env_details in environment_names_to_details.items():
27
- error = validate_requested_variation_in_envs_for_apply(
28
- env_details, environments_traffic, requested_variations
29
- )
30
- if error:
31
- errors.append(error)
32
- if errors:
33
- raise QwakException("\n".join(errors))
34
-
35
-
36
- def validate_requested_variation_in_envs_for_apply(
37
- env_details: EnvironmentDetails,
38
- environments_traffic: Dict[str, EnvironmentTrafficMessage],
39
- requested_variations: List[Variation],
40
- ) -> Optional[str]:
41
- environment_traffic = environments_traffic.get(env_details.id)
42
- existing_variations = environment_traffic.variations if environment_traffic else []
43
- try:
44
- validate_variation_for_apply(
45
- existing_variations=existing_variations,
46
- requested_variations=requested_variations,
47
- )
48
- except QwakException as e:
49
- return e.message
50
-
51
-
52
- def validate_variation_for_apply(
53
- existing_variations: List[Variation],
54
- requested_variations: List[Variation],
55
- ):
56
- requested_variations_names = set(
57
- map(lambda variation: variation.name, requested_variations)
58
- )
59
- existing_variations_names = set(
60
- map(lambda variation: variation.name, existing_variations)
61
- )
62
- ignored_variations = existing_variations_names.difference(
63
- requested_variations_names
64
- )
65
- if ignored_variations:
66
- raise QwakException(
67
- f"The given variation configuration does not contain the configuration for the following "
68
- f"variations {list(ignored_variations)}. You must include all the existing variations when updating the "
69
- f"model traffic {list(existing_variations_names)}"
70
- )
71
-
72
- unexpected_variations = requested_variations_names.difference(
73
- existing_variations_names
74
- )
75
-
76
- if unexpected_variations:
77
- raise QwakException(
78
- f"The given variation configuration contains the configuration for the following "
79
- f"variations {list(unexpected_variations)} which are not expected. You must include only the existing "
80
- f"variations when updating the model traffic {list(existing_variations_names)}"
81
- )
82
-
83
- validate_percentages(requested_variations)
84
- validate_shadow_variation(requested_variations)
@@ -1,37 +0,0 @@
1
- from typing import Tuple
2
-
3
- import click
4
- from qwak.exceptions import QwakException
5
- from qwak.tools.logger.logger import get_qwak_logger
6
-
7
- from qwak_sdk.commands.models.runtime.traffic_update._logic.execute_runtime_update_traffic import (
8
- execute_runtime_traffic_update,
9
- )
10
- from qwak_sdk.inner.tools.cli_tools import QwakCommand
11
-
12
- logger = get_qwak_logger()
13
-
14
-
15
- @click.command("traffic_update", cls=QwakCommand)
16
- @click.option("-m", "--model-id", required=True, help="Model named ID")
17
- @click.option(
18
- "--environment-name",
19
- required=False,
20
- type=str,
21
- help="Environments to deploy on (if not specified uses your default environment)",
22
- multiple=True,
23
- )
24
- @click.option(
25
- "--from-file",
26
- required=True,
27
- help="The variations config file path",
28
- type=click.Path(exists=True, resolve_path=True, dir_okay=False),
29
- )
30
- def runtime_traffic_update(
31
- model_id: str, from_file: str, environment_name: Tuple[str], **kwargs
32
- ):
33
- try:
34
- execute_runtime_traffic_update(model_id, from_file, environment_name)
35
- logger.info(f"Successfully updated traffic for models {model_id}")
36
- except Exception as e:
37
- raise QwakException(f'Failed to apply traffic configurations. Error is "{e}"')