ob-metaflow-extensions 1.1.175rc3__py2.py3-none-any.whl → 1.1.175rc4__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.

@@ -324,7 +324,6 @@ CLIS_DESC = [
324
324
  ("nvct", ".nvct.nvct_cli.cli"),
325
325
  ("fast-bakery", ".fast_bakery.fast_bakery_cli.cli"),
326
326
  ("snowpark", ".snowpark.snowpark_cli.cli"),
327
- ("app", ".apps.app_cli.cli"),
328
327
  ]
329
328
  STEP_DECORATORS_DESC = [
330
329
  ("nvidia", ".nvcf.nvcf_decorator.NvcfDecorator"),
@@ -1,3 +0,0 @@
1
- from .core import app_cli as ob_apps_cli
2
-
3
- cli = ob_apps_cli.cli
@@ -1,4 +1,24 @@
1
- from typing import List, Tuple, Dict, Union
1
+ import sys
2
+ from typing import TYPE_CHECKING, Dict, List, Tuple, Union
3
+
4
+
5
+ # on 3.8+ use the stdlib TypedDict;
6
+ # in TYPE_CHECKING blocks mypy/pyright still pick it up on older Pythons
7
+ if sys.version_info >= (3, 8):
8
+ from typing import TypedDict
9
+ else:
10
+ if TYPE_CHECKING:
11
+ # for the benefit of type-checkers
12
+ from typing import TypedDict # noqa: F401
13
+ # runtime no-op TypedDict shim
14
+ class _TypedDictMeta(type):
15
+ def __new__(cls, name, bases, namespace, total=True):
16
+ # ignore total at runtime
17
+ return super().__new__(cls, name, bases, namespace)
18
+
19
+ class TypedDict(dict, metaclass=_TypedDictMeta):
20
+ # Runtime stand-in for typing.TypedDict on <3.8.
21
+ pass
2
22
 
3
23
 
4
24
  class _dagNode:
@@ -134,9 +154,6 @@ class _capsuleDeployerStateMachine:
134
154
  dot.render("state_machine", view=False)
135
155
 
136
156
 
137
- from typing import TypedDict
138
-
139
-
140
157
  class AccessInfo(TypedDict):
141
158
  outOfClusterURL: str
142
159
  inClusterURL: str
@@ -158,9 +175,6 @@ class WorkerStatus(TypedDict):
158
175
  version: str
159
176
 
160
177
 
161
- from typing import Dict, List, TypedDict
162
-
163
-
164
178
  class WorkerInfoDict(TypedDict):
165
179
  # TODO : Check if we need to account for the `Terminating` state
166
180
  pending: Dict[str, List[WorkerStatus]]
@@ -191,7 +205,7 @@ class DEPLOYMENT_READY_CONDITIONS:
191
205
  2) [all_running] Atleast min_replicas number of workers are running for the deployment to be considered ready.
192
206
  - Usecase: Operators may require that all replicas are available before traffic is routed. Needed when inference endpoints maybe under some SLA or require a larger load
193
207
  3) [fully_finished] Atleast min_replicas number of workers are running for the deployment and there are no pending or crashlooping workers from previous versions lying around.
194
- - Usecase: Ensuring endpoint is fully available and no other versions are running.
208
+ - Usecase: Ensuring endpoint is fully available and no other versions are running or endpoint has been fully scaled down.
195
209
  4) [async] The deployment will be assumed ready as soon as the server responds with a 200.
196
210
  - Usecase: Operators may only care that the URL is minted for the deployment or the deployment eventually scales down to 0.
197
211
  """
@@ -203,7 +217,7 @@ class DEPLOYMENT_READY_CONDITIONS:
203
217
  # It doesn't imply that all the workers relating to other deployments have been torn down.
204
218
  ALL_RUNNING = "all_running"
205
219
 
206
- # `FULLY_FINISHED` implies that the deployment has the minimum number of replicas and all the workers are related to the current deployment instance's version.
220
+ # `FULLY_FINISHED` implies Atleast min_replicas number of workers are running for the deployment and there are no pending or crashlooping workers from previous versions lying around.
207
221
  FULLY_FINISHED = "fully_finished"
208
222
 
209
223
  # `ASYNC` implies that the deployment will be assumed ready after the URL is minted and the worker statuses are not checked.
@@ -273,10 +287,13 @@ class DEPLOYMENT_READY_CONDITIONS:
273
287
  and not capsule_status["updateInProgress"]
274
288
  )
275
289
  elif readiness_condition == cls.FULLY_FINISHED:
276
- _readiness_condition_satisfied = (
277
- worker_semantic_status["status"]["fully_finished"]
278
- and not capsule_status["updateInProgress"]
279
- )
290
+ # We dont wait for updateInProgress in this condition since
291
+ # UpdateInProgress can switch to false when users scale all replicas down to 0.
292
+ # So for this condition to satisfy we will only rely on the worker semantic status.
293
+ # ie. the thing actually tracking what is running and what is not.
294
+ _readiness_condition_satisfied = worker_semantic_status["status"][
295
+ "fully_finished"
296
+ ]
280
297
  elif readiness_condition == cls.ASYNC:
281
298
  # The async readiness condition is satisfied immediately after the server responds
282
299
  # with the URL.
@@ -402,6 +419,11 @@ def _capsule_worker_status_diff(
402
419
  def _capsule_worker_semantic_status(
403
420
  workers: List[WorkerStatus], version: str, min_replicas: int
404
421
  ) -> CapsuleWorkerSemanticStatus:
422
+ def _filter_workers_by_phase(
423
+ workers: List[WorkerStatus], phase: str
424
+ ) -> List[WorkerStatus]:
425
+ return [w for w in workers if w.get("phase") == phase]
426
+
405
427
  def _make_version_dict(
406
428
  _workers: List[WorkerStatus], phase: str
407
429
  ) -> Dict[str, List[WorkerStatus]]:
@@ -447,8 +469,12 @@ def _capsule_worker_semantic_status(
447
469
  "all_running": count_for_version(running_workers) >= min_replicas,
448
470
  "fully_finished": (
449
471
  count_for_version(running_workers) >= min_replicas
450
- and len(pending_workers) == 0
451
- and len(crashlooping_workers) == 0
472
+ # count the workers of different versions that are runnning
473
+ # and ensure that only the current version's workers are running.
474
+ and count_for_version(running_workers)
475
+ == len(_filter_workers_by_phase(workers, "Running"))
476
+ and len(_filter_workers_by_phase(workers, "Pending")) == 0
477
+ and len(_filter_workers_by_phase(workers, "CrashLoopBackOff")) == 0
452
478
  ),
453
479
  "current_info": {
454
480
  "pending": count_for_version(pending_workers),
@@ -206,7 +206,7 @@ class CapsuleWorkersStateMachine:
206
206
 
207
207
  class CapsuleInput:
208
208
  @classmethod
209
- def construct_exec_command(cls, commands: list[str]):
209
+ def construct_exec_command(cls, commands: List[str]):
210
210
  commands = ["set -eEuo pipefail"] + commands
211
211
  command_string = "\n".join(commands)
212
212
  # First constuct a base64 encoded string of the quoted command
@@ -254,7 +254,7 @@ class CapsuleInput:
254
254
  replicas.get("min"),
255
255
  replicas.get("max"),
256
256
  )
257
- if fixed:
257
+ if fixed is not None:
258
258
  _min, _max = fixed, fixed
259
259
  gpu_resource = app_config.get_state("resources").get("gpu")
260
260
  resources = {}
@@ -711,7 +711,7 @@ class CapsuleDeployer:
711
711
  def _get_min_replicas(self):
712
712
  replicas = self._app_config.get_state("replicas", {})
713
713
  fixed, _min, _ = replicas.get("fixed"), replicas.get("min"), replicas.get("max")
714
- if fixed:
714
+ if fixed is not None:
715
715
  return fixed
716
716
  return _min
717
717
 
@@ -740,9 +740,9 @@ class CapsuleDeployer:
740
740
  # We first need to check if someone has not upgraded the capsule under the hood and
741
741
  # the current deployment instance is invalid.
742
742
  self._backend_version_mismatch_check(
743
- capsule_response, self.current_deployment_instance_version
743
+ capsule_response, self.current_deployment_instance_version # type: ignore
744
744
  )
745
- state_machine.add_status(capsule_response.get("status", {}))
745
+ state_machine.add_status(capsule_response.get("status", {})) # type: ignore
746
746
  workers_state_machine.add_status(workers_response)
747
747
  state_machine.report_current_status(logger)
748
748
 
@@ -2,11 +2,38 @@
2
2
  Auto-generated typed classes for ConfigMeta classes.
3
3
 
4
4
  This module provides IDE-friendly typed interfaces for all configuration classes.
5
+ The reason we auto-generate this file is because we want to provide a bridge between what is the ConfigMeta classes and the typed programmatic interface.
6
+ The CoreConfig class is setup in a way that if any additionally params are missed out from being auto-generated then it will not affect the core functionality of the programmatic API.
7
+ The new parameters will just not show up in IDE autocompletions.
8
+ It is fine if this file is not regularly updated by running the script in the .pre-commit-config.app-changes.yaml
9
+ but it is recommended that this file not be deleted or manually edited.
10
+
5
11
  """
6
12
 
7
- from typing import Optional, List, Dict, Any, TypedDict
13
+ from typing import Optional, List, Dict, Any
8
14
  from .unified_config import CoreConfig
9
15
 
16
+ import sys
17
+ from typing import TYPE_CHECKING
18
+
19
+ # on 3.8+ use the stdlib TypedDict;
20
+ # in TYPE_CHECKING blocks mypy/pyright still pick it up on older Pythons
21
+ if sys.version_info >= (3, 8):
22
+ from typing import TypedDict
23
+ else:
24
+ if TYPE_CHECKING:
25
+ # for the benefit of type-checkers
26
+ from typing import TypedDict # noqa: F401
27
+ # runtime no-op TypedDict shim
28
+ class _TypedDictMeta(type):
29
+ def __new__(cls, name, bases, namespace, total=True):
30
+ # ignore total at runtime
31
+ return super().__new__(cls, name, bases, namespace)
32
+
33
+ class TypedDict(dict, metaclass=_TypedDictMeta):
34
+ # Runtime stand-in for typing.TypedDict on <3.8.
35
+ pass
36
+
10
37
 
11
38
  class ResourceConfigDict(TypedDict, total=False):
12
39
  cpu: Optional[str]
@@ -13,6 +13,29 @@ import os
13
13
 
14
14
  current_dir = os.path.dirname(__file__)
15
15
 
16
+ TYPED_DICT_IMPORT = """
17
+ import sys
18
+ from typing import TYPE_CHECKING
19
+
20
+ # on 3.8+ use the stdlib TypedDict;
21
+ # in TYPE_CHECKING blocks mypy/pyright still pick it up on older Pythons
22
+ if sys.version_info >= (3, 8):
23
+ from typing import TypedDict
24
+ else:
25
+ if TYPE_CHECKING:
26
+ # for the benefit of type-checkers
27
+ from typing import TypedDict # noqa: F401
28
+ # runtime no-op TypedDict shim
29
+ class _TypedDictMeta(type):
30
+ def __new__(cls, name, bases, namespace, total=True):
31
+ # ignore total at runtime
32
+ return super().__new__(cls, name, bases, namespace)
33
+
34
+ class TypedDict(dict, metaclass=_TypedDictMeta):
35
+ # Runtime stand-in for typing.TypedDict on <3.8.
36
+ pass
37
+ """
38
+
16
39
 
17
40
  def generate_typed_class_code(config_class: Type) -> str:
18
41
  """
@@ -167,9 +190,10 @@ def generate_typed_classes_module(
167
190
  Complete Python module code
168
191
  """
169
192
  imports = [
170
- "from typing import Optional, List, Dict, Any, TypedDict",
193
+ "from typing import Optional, List, Dict, Any",
171
194
  "from .unified_config import "
172
195
  + ", ".join(cls.__name__ for cls in config_classes),
196
+ TYPED_DICT_IMPORT,
173
197
  ]
174
198
 
175
199
  class_codes = []
@@ -186,6 +210,18 @@ def generate_typed_classes_module(
186
210
  + newline
187
211
  + "This module provides IDE-friendly typed interfaces for all configuration classes."
188
212
  + newline
213
+ + "The reason we auto-generate this file is because we want to provide a bridge between what is the ConfigMeta classes and the typed programmatic interface."
214
+ + newline
215
+ + "The CoreConfig class is setup in a way that if any additionally params are missed out from being auto-generated "
216
+ + "then it will not affect the core functionality of the programmatic API."
217
+ + newline
218
+ + "The new parameters will just not show up in IDE autocompletions."
219
+ + newline
220
+ + "It is fine if this file is not regularly updated by running the script in the .pre-commit-config.app-changes.yaml"
221
+ + newline
222
+ + "but it is recommended that this file not be deleted or manually edited."
223
+ + newline
224
+ + newline
189
225
  + '"""'
190
226
  + newline
191
227
  + newline
@@ -345,12 +345,14 @@ class ReplicaConfig(metaclass=ConfigMeta):
345
345
  replica_config.fixed is None,
346
346
  ]
347
347
  ):
348
+ # if nothing is set then set
348
349
  replica_config.fixed = 1
350
+ elif replica_config.min is not None and replica_config.max is None:
351
+ replica_config.max = replica_config.min
349
352
  return
350
353
 
351
354
  @staticmethod
352
355
  def validate(replica_config: "ReplicaConfig"):
353
- # TODO: Have a better validation story.
354
356
  both_min_max_set = (
355
357
  replica_config.min is not None and replica_config.max is not None
356
358
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ob-metaflow-extensions
3
- Version: 1.1.175rc3
3
+ Version: 1.1.175rc4
4
4
  Summary: Outerbounds Platform Extensions for Metaflow
5
5
  Author: Outerbounds, Inc.
6
6
  License: Commercial
@@ -1,22 +1,22 @@
1
1
  metaflow_extensions/outerbounds/__init__.py,sha256=Gb8u06s9ClQsA_vzxmkCzuMnigPy7kKcDnLfb7eB-64,514
2
2
  metaflow_extensions/outerbounds/remote_config.py,sha256=pEFJuKDYs98eoB_-ryPjVi9b_c4gpHMdBHE14ltoxIU,4672
3
3
  metaflow_extensions/outerbounds/config/__init__.py,sha256=JsQGRuGFz28fQWjUvxUgR8EKBLGRdLUIk_buPLJplJY,1225
4
- metaflow_extensions/outerbounds/plugins/__init__.py,sha256=8DmLGwlr6UZWe39rbJdhO5ANemDp2ZwxY6MlqVPCppc,13740
4
+ metaflow_extensions/outerbounds/plugins/__init__.py,sha256=SgqRTG7FFk-jrPBNPYQ-D0W26Yr_dgg7ZL-YJInTQGw,13706
5
5
  metaflow_extensions/outerbounds/plugins/auth_server.py,sha256=_Q9_2EL0Xy77bCRphkwT1aSu8gQXRDOH-Z-RxTUO8N4,2202
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
- metaflow_extensions/outerbounds/plugins/apps/app_cli.py,sha256=aVgKe2CBcOgXQL2TYgO-sdIFlTntvbjHZFphR1w1CAM,64
8
+ metaflow_extensions/outerbounds/plugins/apps/app_cli.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  metaflow_extensions/outerbounds/plugins/apps/app_deploy_decorator.py,sha256=aN2B7nf0IwnOg_9kpqVSkGyZPhr4D9YI1WuCSlNP92s,3498
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=8RvEqRI9WaUiFu2LBKIqWRbbe9bSM2EptTR56wPit-0,18434
15
+ metaflow_extensions/outerbounds/plugins/apps/core/_state_machine.py,sha256=b0WI7jVIReWo52AtWXFlaoET2u3nOVH9oITnVlWFIBk,19881
16
16
  metaflow_extensions/outerbounds/plugins/apps/core/app_cli.py,sha256=HR4nQXyxHSe6NbUF0mutsOQR_rIT-_U6C-mdSQ1_8Os,42326
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=kOyHX8mzt44N4x4MKXCcBTZxS9ZcmRcUgbVuTgAMPpA,33879
19
+ metaflow_extensions/outerbounds/plugins/apps/core/capsule.py,sha256=fRcJ_GgC4FxGscRS4M4mcMNpjoHA6Zdc1LIgdfDkJkI,33935
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
@@ -35,9 +35,9 @@ metaflow_extensions/outerbounds/plugins/apps/core/config/__init__.py,sha256=fSFB
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=zBZSe-1CsHj5MxrKNuAGLy11yXD4qF_IJJkdORfCW6A,32089
37
37
  metaflow_extensions/outerbounds/plugins/apps/core/config/schema_export.py,sha256=tigPtb0we-urwbmctG1GbaQ9NKRKZn4KBbJKmaEntCg,9501
38
- metaflow_extensions/outerbounds/plugins/apps/core/config/typed_configs.py,sha256=RufXI8BQn6G5kixY6P27VYJ0d36ZG-JNFW15bA1bnk4,3179
39
- metaflow_extensions/outerbounds/plugins/apps/core/config/typed_init_generator.py,sha256=1iszHsXjxAWeGI4XhwoTT8lVtzQwstF3ZOiVYBykO6w,10256
40
- metaflow_extensions/outerbounds/plugins/apps/core/config/unified_config.py,sha256=58PsC1ZK3p5wNvavVRzb4Qq764wOUjPuSvXDsrrsSw4,35474
38
+ metaflow_extensions/outerbounds/plugins/apps/core/config/typed_configs.py,sha256=bAC2lV1xWtcw0r2LPlqDrggeXPLOyrtZha2KDpm_Vx0,4454
39
+ metaflow_extensions/outerbounds/plugins/apps/core/config/typed_init_generator.py,sha256=bdWS5THMeae08wHvTrMq-cJP52r4sTMP1dsABHX0cK0,11766
40
+ metaflow_extensions/outerbounds/plugins/apps/core/config/unified_config.py,sha256=bUL-W91BvJ5O94wegoxPpBGOdyZYMzPEMeFF96UpYPI,35595
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.1.175rc3.dist-info/METADATA,sha256=wnDlhzELSLX2xxzL6U87sqIbb1tWYehFruJ783o-8Gg,524
119
- ob_metaflow_extensions-1.1.175rc3.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
120
- ob_metaflow_extensions-1.1.175rc3.dist-info/top_level.txt,sha256=NwG0ukwjygtanDETyp_BUdtYtqIA_lOjzFFh1TsnxvI,20
121
- ob_metaflow_extensions-1.1.175rc3.dist-info/RECORD,,
118
+ ob_metaflow_extensions-1.1.175rc4.dist-info/METADATA,sha256=QKrSlPcSdpTnsJIJS6c4xl5B04HoNnpVeBldTegc3BQ,524
119
+ ob_metaflow_extensions-1.1.175rc4.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
120
+ ob_metaflow_extensions-1.1.175rc4.dist-info/top_level.txt,sha256=NwG0ukwjygtanDETyp_BUdtYtqIA_lOjzFFh1TsnxvI,20
121
+ ob_metaflow_extensions-1.1.175rc4.dist-info/RECORD,,