ob-metaflow-extensions 1.2.2rc0__py2.py3-none-any.whl → 1.2.4__py2.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 ob-metaflow-extensions might be problematic. Click here for more details.

@@ -26,6 +26,9 @@ class AppDeployDecorator(StepDecorator):
26
26
  package_url = None
27
27
  package_sha = None
28
28
 
29
+ MAX_ENTROPY = 6
30
+ MAX_NAME_LENGTH = 15 - MAX_ENTROPY - 1 # -1 for the hyphen
31
+
29
32
  def step_init(self, flow, graph, step, decos, environment, flow_datastore, logger):
30
33
  self.logger = logger
31
34
  self.environment = environment
@@ -37,6 +40,23 @@ class AppDeployDecorator(StepDecorator):
37
40
  "METAFLOW_CODE_SHA", self.package_sha
38
41
  )
39
42
 
43
+ def _extract_project_info(self):
44
+ project = current.get("project_name")
45
+ branch = current.get("branch_name")
46
+ is_production = current.get("is_production")
47
+ return project, branch, is_production
48
+
49
+ def _resolve_default_image(self, flow):
50
+ # TODO : Resolve the default image over here.
51
+ pass
52
+
53
+ def _resolve_default_name_prefix(self, flow, step_name):
54
+ # TODO: Only tweek MAX_NAME_LENGTH as backend support allows longer names.
55
+ base_prefix = (flow.name + "-" + step_name).lower()
56
+ if len(base_prefix) > self.MAX_NAME_LENGTH:
57
+ base_prefix = "mf-app"
58
+ return base_prefix
59
+
40
60
  def task_pre_step(
41
61
  self,
42
62
  step_name,
@@ -58,22 +78,36 @@ class AppDeployDecorator(StepDecorator):
58
78
  "METAFLOW_CODE_URL or METAFLOW_CODE_SHA is not set. "
59
79
  "Please set METAFLOW_CODE_URL and METAFLOW_CODE_SHA in your environment."
60
80
  )
61
- default_name = "-".join(current.pathspec.split("/")).lower()
62
81
  image = os.environ.get("FASTBAKERY_IMAGE", None)
63
82
 
64
- hash_key = hashlib.sha256(package_url.encode()).hexdigest()[:6]
65
-
66
- default_name = (
67
- (current.flow_name + "-" + current.step_name)[:12] + "-" + hash_key
68
- ).lower()
83
+ # TODO [Apps] - This is temporary. Backend will support longer names in the future.
84
+ default_name = self._resolve_default_name_prefix(flow, step_name)
85
+ project, branch, is_production = self._extract_project_info()
86
+ project_info = {}
87
+ if project is not None:
88
+ project_info["metaflow/project"] = project
89
+ project_info["metaflow/branch"] = branch
90
+ project_info["metaflow/is_production"] = is_production
91
+
92
+ default_tags = {
93
+ "metaflow/flow_name": flow.name,
94
+ "metaflow/step_name": step_name,
95
+ "metaflow/run_id": run_id,
96
+ "metaflow/task_id": task_id,
97
+ "metaflow/retry_count": retry_count,
98
+ "metaflow/pathspec": current.pathspec,
99
+ **project_info,
100
+ }
69
101
 
70
102
  AppDeployer._set_state(
71
103
  perimeter,
72
104
  api_server,
73
105
  code_package_url=package_url,
74
106
  code_package_key=package_sha,
75
- name=default_name,
107
+ name_prefix=default_name,
76
108
  image=image,
109
+ max_entropy=self.MAX_ENTROPY,
110
+ default_tags=[{k: str(v)} for k, v in default_tags.items()],
77
111
  )
78
112
  current._update_env(
79
113
  {
@@ -189,6 +189,10 @@ class CurrentWorkerInfo(TypedDict):
189
189
  crashlooping: int
190
190
 
191
191
 
192
+ class LogLine(TypedDict):
193
+ message: str
194
+
195
+
192
196
  class DEPLOYMENT_READY_CONDITIONS:
193
197
  """
194
198
  Deployment ready conditions define what is considered a successful completion of the current deployment instance.
@@ -18,6 +18,7 @@ from ._state_machine import (
18
18
  WorkerStatus,
19
19
  CapsuleStatus,
20
20
  DEPLOYMENT_READY_CONDITIONS,
21
+ LogLine,
21
22
  )
22
23
 
23
24
 
@@ -508,7 +509,7 @@ class CapsuleApi:
508
509
 
509
510
  def logs(
510
511
  self, capsule_id: str, worker_id: str, previous: bool = False
511
- ) -> List[str]:
512
+ ) -> List[LogLine]:
512
513
  _url = os.path.join(self._base_url, capsule_id, "workers", worker_id, "logs")
513
514
  options = None
514
515
  if previous:
@@ -530,6 +531,20 @@ class CapsuleApi:
530
531
  message="Capsule JSON decode failed",
531
532
  )
532
533
 
534
+ def patch(self, capsule_id: str, patch_input: dict):
535
+ capsule_response = self.get(capsule_id)
536
+ if "spec" not in capsule_response or len(capsule_response.get("spec", {})) == 0:
537
+ raise CapsuleApiException(
538
+ self._base_url,
539
+ "patch",
540
+ 403,
541
+ "Capsule response of incorrect format",
542
+ )
543
+
544
+ spec = capsule_response.get("spec")
545
+ spec.update(patch_input)
546
+ return self.create(spec)
547
+
533
548
 
534
549
  def list_and_filter_capsules(
535
550
  capsule_api: CapsuleApi, project, branch, name, tags, auth_type, capsule_id
@@ -9,4 +9,4 @@ from .config_utils import (
9
9
  RequiredFieldMissingException,
10
10
  )
11
11
  from . import schema_export
12
- from .typed_configs import TypedCoreConfig
12
+ from .typed_configs import TypedCoreConfig, TypedDict
@@ -527,6 +527,10 @@ class PackageConfig(metaclass=ConfigMeta):
527
527
  )
528
528
 
529
529
 
530
+ def everything_is_string(*args):
531
+ return all(isinstance(arg, str) for arg in args)
532
+
533
+
530
534
  class BasicAppValidations:
531
535
  @staticmethod
532
536
  def name(name):
@@ -534,7 +538,7 @@ class BasicAppValidations:
534
538
  return True
535
539
  regex = r"^[a-z0-9-]+$" # Only allow lowercase letters, numbers, and hyphens
536
540
  validator = BasicValidations(CoreConfig, "name")
537
- return validator.length_validation(20, name) and validator.regex_validation(
541
+ return validator.length_validation(15, name) and validator.regex_validation(
538
542
  regex, name
539
543
  )
540
544
 
@@ -548,12 +552,19 @@ class BasicAppValidations:
548
552
  def tags(tags):
549
553
  if tags is None:
550
554
  return True
551
- if not all(isinstance(tag, dict) and len(tag) == 1 for tag in tags):
555
+ if not all(
556
+ isinstance(tag, dict)
557
+ and len(tag) == 1
558
+ and all(
559
+ [everything_is_string(*tag.keys()), everything_is_string(*tag.values())]
560
+ )
561
+ for tag in tags
562
+ ):
552
563
  raise ConfigValidationFailedException(
553
564
  field_name="tags",
554
565
  field_info=CoreConfig._get_field(CoreConfig, "tags"), # type: ignore
555
566
  current_value=tags,
556
- message="Tags must be a list of dictionaries with one key. Currently they are set to %s "
567
+ message="Tags must be a list of dictionaries with one key and the value must be a string. Currently they are set to %s "
557
568
  % (str(tags)),
558
569
  )
559
570
  return True
@@ -1,11 +1,15 @@
1
- from .config import TypedCoreConfig
2
-
3
- from ._state_machine import DEPLOYMENT_READY_CONDITIONS
1
+ from .config import TypedCoreConfig, TypedDict
2
+ from .perimeters import PerimeterExtractor
3
+ from .capsule import CapsuleApi
4
+ import json
5
+ from ._state_machine import DEPLOYMENT_READY_CONDITIONS, LogLine
4
6
  from .app_config import AppConfig, AppConfigError
5
7
  from .capsule import CapsuleDeployer, list_and_filter_capsules
6
8
  from functools import partial
7
9
  import sys
8
- from typing import Type
10
+ import uuid
11
+ from typing import Type, Dict, List
12
+ from datetime import datetime
9
13
 
10
14
 
11
15
  class AppDeployer(TypedCoreConfig):
@@ -17,8 +21,18 @@ class AppDeployer(TypedCoreConfig):
17
21
 
18
22
  _state = {}
19
23
 
24
+ __state_items = [
25
+ "perimeter",
26
+ "api_url",
27
+ "code_package_url",
28
+ "code_package_key",
29
+ "image",
30
+ "project",
31
+ "branch",
32
+ ]
33
+
20
34
  @property
21
- def app_config(self) -> AppConfig:
35
+ def _deploy_config(self) -> AppConfig:
22
36
  if not hasattr(self, "_app_config"):
23
37
  self._app_config = AppConfig(self._config)
24
38
  return self._app_config
@@ -31,15 +45,27 @@ class AppDeployer(TypedCoreConfig):
31
45
  api_url: str,
32
46
  code_package_url: str = None,
33
47
  code_package_key: str = None,
34
- name: str = None,
48
+ name_prefix: str = None,
35
49
  image: str = None,
50
+ max_entropy: int = 4,
51
+ default_tags: List[Dict[str, str]] = None,
52
+ project: str = None,
53
+ branch: str = None,
36
54
  ):
37
55
  cls._state["perimeter"] = perimeter
38
56
  cls._state["api_url"] = api_url
39
57
  cls._state["code_package_url"] = code_package_url
40
58
  cls._state["code_package_key"] = code_package_key
41
- cls._state["name"] = name
59
+ cls._state["name_prefix"] = name_prefix
42
60
  cls._state["image"] = image
61
+ cls._state["max_entropy"] = max_entropy
62
+ cls._state["default_tags"] = default_tags
63
+ cls._state["project"] = project
64
+ cls._state["branch"] = branch
65
+
66
+ assert (
67
+ max_entropy > 0
68
+ ), "max_entropy must be greater than 0. Since AppDeployer's deploy fn can be called many time inside a step itself."
43
69
 
44
70
  def deploy(
45
71
  self,
@@ -50,20 +76,29 @@ class AppDeployer(TypedCoreConfig):
50
76
  status_file=None,
51
77
  no_loader=False,
52
78
  **kwargs,
53
- ):
79
+ ) -> "DeployedApp":
80
+
54
81
  # Name setting from top level if none is set in the code
55
- if self.app_config._core_config.name is None:
56
- self.app_config._core_config.name = self._state["name"]
82
+ if self._deploy_config._core_config.name is None:
83
+ name = self._state[
84
+ "name_prefix"
85
+ ] # for now the name-prefix cannot be very large.
86
+ entropy = uuid.uuid4().hex[: self._state["max_entropy"]]
87
+ self._deploy_config._core_config.name = f"{name}-{entropy}"
57
88
 
58
- self.app_config.commit()
89
+ if len(self._state["default_tags"]) > 0:
90
+ self._deploy_config._core_config.tags = (
91
+ self._deploy_config._core_config.tags or []
92
+ ) + self._state["default_tags"]
59
93
 
94
+ self._deploy_config.commit()
60
95
  # Set any state that might have been passed down from the top level
61
- for k, v in self._state.items():
62
- if self.app_config.get_state(k) is None:
63
- self.app_config.set_state(k, v)
96
+ for k in self.__state_items:
97
+ if self._deploy_config.get_state(k) is None:
98
+ self._deploy_config.set_state(k, self._state[k])
64
99
 
65
100
  capsule = CapsuleDeployer(
66
- self.app_config,
101
+ self._deploy_config,
67
102
  self._state["api_url"],
68
103
  create_timeout=max_wait_time,
69
104
  debug_dir=None,
@@ -82,7 +117,7 @@ class AppDeployer(TypedCoreConfig):
82
117
  None,
83
118
  )
84
119
 
85
- force_upgrade = self.app_config.get_state("force_upgrade", False)
120
+ force_upgrade = self._deploy_config.get_state("force_upgrade", False)
86
121
 
87
122
  if len(currently_present_capsules) > 0:
88
123
  # Only update the capsule if there is no upgrade in progress
@@ -112,7 +147,149 @@ class AppDeployer(TypedCoreConfig):
112
147
 
113
148
  capsule.create()
114
149
  final_status = capsule.wait_for_terminal_state()
115
- return final_status
150
+ return DeployedApp(
151
+ final_status["id"],
152
+ final_status["auth_type"],
153
+ final_status["public_url"],
154
+ final_status["available_replicas"],
155
+ final_status["name"],
156
+ final_status["deployed_version"],
157
+ final_status["deployed_at"],
158
+ )
159
+
160
+
161
+ class DeployedApp:
162
+ def __init__(
163
+ self,
164
+ _id: str,
165
+ capsule_type: str,
166
+ public_url: str,
167
+ available_replicas: int,
168
+ name: str,
169
+ deployed_version: str,
170
+ deployed_at: str,
171
+ ):
172
+ self._id = _id
173
+ self._capsule_type = capsule_type
174
+ self._public_url = public_url
175
+ self._available_replicas = available_replicas
176
+ self._name = name
177
+ self._deployed_version = deployed_version
178
+ self._deployed_at = deployed_at
179
+
180
+ def _get_capsule_api(self) -> CapsuleApi:
181
+ perimeter, api_server = PerimeterExtractor.during_metaflow_execution()
182
+ return CapsuleApi(api_server, perimeter)
183
+
184
+ def logs(self, previous=False) -> Dict[str, List[LogLine]]:
185
+ """
186
+ Returns a dictionary of worker_id to logs.
187
+ If `previous` is True, it will return the logs from the previous execution of the workers. Useful when debugging a crashlooping worker.
188
+ """
189
+ capsule_api = self._get_capsule_api()
190
+ # extract workers from capsule
191
+ workers = capsule_api.get_workers(self._id)
192
+ # get logs from workers
193
+ logs = {
194
+ # worker_id: logs
195
+ }
196
+ for worker in workers:
197
+ # TODO: Handle exceptions better over here.
198
+ logs[worker["workerId"]] = capsule_api.logs(
199
+ self._id, worker["workerId"], previous=previous
200
+ )
201
+ return logs
202
+
203
+ def info(self) -> dict:
204
+ """
205
+ Returns a dictionary representing the deployed app.
206
+ """
207
+ capsule_api = self._get_capsule_api()
208
+ capsule = capsule_api.get(self._id)
209
+ return capsule
210
+
211
+ def scale_to_zero(self):
212
+ """
213
+ Scales the DeployedApp to 0 replicas.
214
+ """
215
+ capsule_api = self._get_capsule_api()
216
+ return capsule_api.patch(
217
+ self._id,
218
+ {
219
+ "autoscalingConfig": {
220
+ "minReplicas": 0,
221
+ "maxReplicas": 0,
222
+ }
223
+ },
224
+ )
225
+
226
+ def delete(self):
227
+ """
228
+ Deletes the DeployedApp.
229
+ """
230
+ capsule_api = self._get_capsule_api()
231
+ return capsule_api.delete(self._id)
232
+
233
+ @property
234
+ def id(self) -> str:
235
+ return self._id
236
+
237
+ @property
238
+ def auth_style(self) -> str:
239
+ # TODO : Fix naming here.
240
+ return self._capsule_type
241
+
242
+ @property
243
+ def public_url(self) -> str:
244
+ return self._public_url
245
+
246
+ @property
247
+ def available_replicas(self) -> int:
248
+ return self._available_replicas
249
+
250
+ @property
251
+ def name(self) -> str:
252
+ return self._name
253
+
254
+ @property
255
+ def deployed_version(self) -> str:
256
+ return self._deployed_version
257
+
258
+ def to_dict(self) -> dict:
259
+ return {
260
+ "id": self._id,
261
+ "auth_style": self.auth_style, # TODO : Fix naming here.
262
+ "public_url": self._public_url,
263
+ "available_replicas": self._available_replicas,
264
+ "name": self._name,
265
+ "deployed_version": self._deployed_version,
266
+ "deployed_at": self._deployed_at,
267
+ }
268
+
269
+ @classmethod
270
+ def from_dict(cls, data: dict):
271
+ return cls(
272
+ _id=data["id"],
273
+ capsule_type=data["capsule_type"],
274
+ public_url=data["public_url"],
275
+ available_replicas=data["available_replicas"],
276
+ name=data["name"],
277
+ deployed_version=data["deployed_version"],
278
+ deployed_at=data["deployed_at"],
279
+ )
280
+
281
+ @property
282
+ def deployed_at(self) -> datetime:
283
+ return datetime.fromisoformat(self._deployed_at)
284
+
285
+ def __repr__(self) -> str:
286
+ return (
287
+ f"DeployedApp(id='{self._id}', "
288
+ f"name='{self._name}', "
289
+ f"public_url='{self._public_url}', "
290
+ f"available_replicas={self._available_replicas}, "
291
+ f"deployed_version='{self._deployed_version}')"
292
+ )
116
293
 
117
294
 
118
295
  class apps:
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ob-metaflow-extensions
3
- Version: 1.2.2rc0
3
+ Version: 1.2.4
4
4
  Summary: Outerbounds Platform Extensions for Metaflow
5
5
  Author: Outerbounds, Inc.
6
6
  License: Commercial
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: boto3
9
9
  Requires-Dist: kubernetes
10
- Requires-Dist: ob-metaflow (==2.16.0.1)
10
+ Requires-Dist: ob-metaflow (==2.16.1.1)
11
11
 
12
12
  # Outerbounds platform package
13
13
 
@@ -6,21 +6,21 @@ metaflow_extensions/outerbounds/plugins/auth_server.py,sha256=_Q9_2EL0Xy77bCRphk
6
6
  metaflow_extensions/outerbounds/plugins/perimeters.py,sha256=QXh3SFP7GQbS-RAIxUOPbhPzQ7KDFVxZkTdKqFKgXjI,2697
7
7
  metaflow_extensions/outerbounds/plugins/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  metaflow_extensions/outerbounds/plugins/apps/app_cli.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- metaflow_extensions/outerbounds/plugins/apps/app_deploy_decorator.py,sha256=aN2B7nf0IwnOg_9kpqVSkGyZPhr4D9YI1WuCSlNP92s,3498
9
+ metaflow_extensions/outerbounds/plugins/apps/app_deploy_decorator.py,sha256=WWFvyl0EDUbp_UwmWjXBqlLfPEZ-lq98NJ5F5skBDKU,4901
10
10
  metaflow_extensions/outerbounds/plugins/apps/app_utils.py,sha256=sw9whU17lAzlD2K2kEDNjlk1Ib-2xE2UNhJkmzD8Qv8,8543
11
11
  metaflow_extensions/outerbounds/plugins/apps/consts.py,sha256=iHsyqbUg9k-rgswCs1Jxf5QZIxR1V-peCDRjgr9kdBM,177
12
12
  metaflow_extensions/outerbounds/plugins/apps/deploy_decorator.py,sha256=VkmiMdNYHhNdt-Qm9AVv7aE2LWFsIFEc16YcOYjwF6Q,8568
13
13
  metaflow_extensions/outerbounds/plugins/apps/supervisord_utils.py,sha256=GQoN2gyPClcpR9cLldJmbCfqXnoAHxp8xUnY7vzaYtY,9026
14
14
  metaflow_extensions/outerbounds/plugins/apps/core/__init__.py,sha256=c6uCgKlgEkTmM9BVdAO-m3vZvUpK2KW_AZZ2236now4,237
15
- metaflow_extensions/outerbounds/plugins/apps/core/_state_machine.py,sha256=b0WI7jVIReWo52AtWXFlaoET2u3nOVH9oITnVlWFIBk,19881
15
+ metaflow_extensions/outerbounds/plugins/apps/core/_state_machine.py,sha256=8z4MnPeat3Vm8ekLIMJj8vVsvUAQ__i8daf-9UItUIQ,19926
16
16
  metaflow_extensions/outerbounds/plugins/apps/core/app_cli.py,sha256=9YyvOQzPNlpxA2K9AZ4jYpfDWpLSp66u_NotGGE5DHg,42155
17
17
  metaflow_extensions/outerbounds/plugins/apps/core/app_config.py,sha256=PHt-HdNfTHIuhY-eB5vkRMp1RKQNWJ4DKdgZWyYgUuc,4167
18
18
  metaflow_extensions/outerbounds/plugins/apps/core/artifacts.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- metaflow_extensions/outerbounds/plugins/apps/core/capsule.py,sha256=fRcJ_GgC4FxGscRS4M4mcMNpjoHA6Zdc1LIgdfDkJkI,33935
19
+ metaflow_extensions/outerbounds/plugins/apps/core/capsule.py,sha256=9_LrpZcDg3w0FZvs9h_6Mmiy5YTYLCLnEu8wnNau24E,34445
20
20
  metaflow_extensions/outerbounds/plugins/apps/core/click_importer.py,sha256=kgoPQmK_-8PSSTc3QMSaynCLQ5VWTkKFOC69FPURyXA,998
21
21
  metaflow_extensions/outerbounds/plugins/apps/core/config_schema.yaml,sha256=iTThO2vNQrFWe9nYfjiOcMf6FOQ6vU_1ZhXhUAr0L24,8142
22
22
  metaflow_extensions/outerbounds/plugins/apps/core/dependencies.py,sha256=HDPj7rDARcsKeex5GwH0IP8rOXMH6YdOufgXDknP1S8,4006
23
- metaflow_extensions/outerbounds/plugins/apps/core/deployer.py,sha256=oARCVpHSNb7MnCKnIvzNB6fGDfF7BvAB19hDobETuZc,4234
23
+ metaflow_extensions/outerbounds/plugins/apps/core/deployer.py,sha256=VkYe8mK_VOr-bAiR2RohhKeLf8Z3gHZw7RoRBSCu2FA,9765
24
24
  metaflow_extensions/outerbounds/plugins/apps/core/perimeters.py,sha256=jeFGAUnFQkBFiOMp_Ls7Ofb80Qogh509suam5sMucYU,3030
25
25
  metaflow_extensions/outerbounds/plugins/apps/core/secrets.py,sha256=aWzcAayQEJghQgFP_qp9w6jyvan_hoL4_ceqZ0ZjLd4,6126
26
26
  metaflow_extensions/outerbounds/plugins/apps/core/utils.py,sha256=RLO6p25Fzq4olLFtQmfSl9LT0NPDfUosxPrsjO9sczo,7897
@@ -31,13 +31,13 @@ metaflow_extensions/outerbounds/plugins/apps/core/_vendor/spinner/spinners.py,sh
31
31
  metaflow_extensions/outerbounds/plugins/apps/core/code_package/__init__.py,sha256=8McF7pgx8ghvjRnazp2Qktlxi9yYwNiwESSQrk-2oW8,68
32
32
  metaflow_extensions/outerbounds/plugins/apps/core/code_package/code_packager.py,sha256=wrGKpsg9pBU2rvhIveJ3kzc5ODUTtzciybcGHyi7pR8,22815
33
33
  metaflow_extensions/outerbounds/plugins/apps/core/code_package/examples.py,sha256=aF8qKIJxCVv_ugcShQjqUsXKKKMsm1oMkQIl8w3QKuw,4016
34
- metaflow_extensions/outerbounds/plugins/apps/core/config/__init__.py,sha256=fSFBjC5ujTpBlNJGyVsaoWl5VZ_8mXbEIPiFvzTrgKA,382
34
+ metaflow_extensions/outerbounds/plugins/apps/core/config/__init__.py,sha256=ZgC9U4NFu7TNngUuUMzt69PmjMENXb6eUK2HCSroIDo,393
35
35
  metaflow_extensions/outerbounds/plugins/apps/core/config/cli_generator.py,sha256=0R0-wy7RxAMR9doVRvuluRYxAYgyjZXlTIkOeYGyz7M,5350
36
36
  metaflow_extensions/outerbounds/plugins/apps/core/config/config_utils.py,sha256=bozzUR8rbfOnb5M532RZxB5QNvVgEC1gnVjfCvQ82Yk,34053
37
37
  metaflow_extensions/outerbounds/plugins/apps/core/config/schema_export.py,sha256=tigPtb0we-urwbmctG1GbaQ9NKRKZn4KBbJKmaEntCg,9501
38
38
  metaflow_extensions/outerbounds/plugins/apps/core/config/typed_configs.py,sha256=bAC2lV1xWtcw0r2LPlqDrggeXPLOyrtZha2KDpm_Vx0,4454
39
39
  metaflow_extensions/outerbounds/plugins/apps/core/config/typed_init_generator.py,sha256=KiJ1eiwtBR5eWdBzWqvO6KlqJ2qzjJvl3w4c1uJ3g0Y,13419
40
- metaflow_extensions/outerbounds/plugins/apps/core/config/unified_config.py,sha256=HzLFqMHuo-2o3KR4hJlTtMkl978yvCnGu2lm_PnhEp0,35548
40
+ metaflow_extensions/outerbounds/plugins/apps/core/config/unified_config.py,sha256=f_Qxy-pJoyGq_tUYr_gHmS7INEivLqv4QbOJljIwECA,35837
41
41
  metaflow_extensions/outerbounds/plugins/apps/core/experimental/__init__.py,sha256=rd4qGTkHndKYfJmoAKZWiY0KK4j5BK6RBrtle-it1Mg,2746
42
42
  metaflow_extensions/outerbounds/plugins/aws/__init__.py,sha256=VBGdjNKeFLXGZuqh4jVk8cFtO1AWof73a6k_cnbAOYA,145
43
43
  metaflow_extensions/outerbounds/plugins/aws/assume_role.py,sha256=mBewNlnSYsR2rFXFkX-DUH6ku01h2yOcMcLHoCL7eyI,161
@@ -115,7 +115,7 @@ metaflow_extensions/outerbounds/toplevel/plugins/ollama/__init__.py,sha256=GRSz2
115
115
  metaflow_extensions/outerbounds/toplevel/plugins/snowflake/__init__.py,sha256=LptpH-ziXHrednMYUjIaosS1SXD3sOtF_9_eRqd8SJw,50
116
116
  metaflow_extensions/outerbounds/toplevel/plugins/torchtune/__init__.py,sha256=uTVkdSk3xZ7hEKYfdlyVteWj5KeDwaM1hU9WT-_YKfI,50
117
117
  metaflow_extensions/outerbounds/toplevel/plugins/vllm/__init__.py,sha256=ekcgD3KVydf-a0xMI60P4uy6ePkSEoFHiGnDq1JM940,45
118
- ob_metaflow_extensions-1.2.2rc0.dist-info/METADATA,sha256=J9PVJBF533qri25fk5ikgHvKBAgN3XNW55QmOxh1GD8,521
119
- ob_metaflow_extensions-1.2.2rc0.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
120
- ob_metaflow_extensions-1.2.2rc0.dist-info/top_level.txt,sha256=NwG0ukwjygtanDETyp_BUdtYtqIA_lOjzFFh1TsnxvI,20
121
- ob_metaflow_extensions-1.2.2rc0.dist-info/RECORD,,
118
+ ob_metaflow_extensions-1.2.4.dist-info/METADATA,sha256=zLo5imOFamNDf-Dd5Uql7yobbwMilB9p5AWXNfrYpUQ,518
119
+ ob_metaflow_extensions-1.2.4.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
120
+ ob_metaflow_extensions-1.2.4.dist-info/top_level.txt,sha256=NwG0ukwjygtanDETyp_BUdtYtqIA_lOjzFFh1TsnxvI,20
121
+ ob_metaflow_extensions-1.2.4.dist-info/RECORD,,