ob-metaflow-extensions 1.1.168rc5__py2.py3-none-any.whl → 1.1.170rc0__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.
- metaflow_extensions/outerbounds/plugins/__init__.py +25 -32
- metaflow_extensions/outerbounds/plugins/apps/app_cli.py +3 -0
- metaflow_extensions/outerbounds/plugins/kubernetes/pod_killer.py +106 -30
- metaflow_extensions/outerbounds/toplevel/ob_internal.py +1 -0
- {ob_metaflow_extensions-1.1.168rc5.dist-info → ob_metaflow_extensions-1.1.170rc0.dist-info}/METADATA +1 -1
- {ob_metaflow_extensions-1.1.168rc5.dist-info → ob_metaflow_extensions-1.1.170rc0.dist-info}/RECORD +8 -8
- {ob_metaflow_extensions-1.1.168rc5.dist-info → ob_metaflow_extensions-1.1.170rc0.dist-info}/WHEEL +0 -0
- {ob_metaflow_extensions-1.1.168rc5.dist-info → ob_metaflow_extensions-1.1.170rc0.dist-info}/top_level.txt +0 -0
|
@@ -52,10 +52,6 @@ def get_boto3_session(role_arn=None, session_vars=None):
|
|
|
52
52
|
|
|
53
53
|
token_info = get_token("/generate/aws")
|
|
54
54
|
|
|
55
|
-
# Check if the assume_role decorator has set a role ARN via environment variable
|
|
56
|
-
# This takes precedence over CSPR role
|
|
57
|
-
decorator_role_arn = os.environ.get(OBP_ASSUME_ROLE_ARN_ENV_VAR)
|
|
58
|
-
|
|
59
55
|
# Write token to a file. The file name is derived from the user name
|
|
60
56
|
# so it works with multiple users on the same machine.
|
|
61
57
|
#
|
|
@@ -76,13 +72,18 @@ def get_boto3_session(role_arn=None, session_vars=None):
|
|
|
76
72
|
if token_info.get("cspr_role_arn"):
|
|
77
73
|
cspr_role = token_info["cspr_role_arn"]
|
|
78
74
|
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
# Check if the assume_role decorator has set a CSPR ARN via environment variable
|
|
76
|
+
# This takes precedence over CSPR role that comes from the token_info response
|
|
77
|
+
decorator_role_arn = os.environ.get(OBP_ASSUME_ROLE_ARN_ENV_VAR)
|
|
78
|
+
if decorator_role_arn:
|
|
79
|
+
cspr_role = decorator_role_arn
|
|
80
|
+
|
|
81
|
+
if cspr_role:
|
|
82
|
+
# If CSPR role is set, we set it as the default role to assume
|
|
83
|
+
# for the AWS SDK. We do this by writing an AWS config file
|
|
83
84
|
# with two profiles. One to get credentials for the task role
|
|
84
85
|
# in exchange for the OIDC token, and second to assume the
|
|
85
|
-
#
|
|
86
|
+
# CSPR role using the task role credentials.
|
|
86
87
|
import configparser
|
|
87
88
|
from io import StringIO
|
|
88
89
|
|
|
@@ -94,9 +95,9 @@ def get_boto3_session(role_arn=None, session_vars=None):
|
|
|
94
95
|
"web_identity_token_file": token_file,
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
#
|
|
98
|
-
aws_config["profile
|
|
99
|
-
"role_arn":
|
|
98
|
+
# CSPR role profile (default)
|
|
99
|
+
aws_config["profile cspr"] = {
|
|
100
|
+
"role_arn": cspr_role,
|
|
100
101
|
"source_profile": "task",
|
|
101
102
|
}
|
|
102
103
|
|
|
@@ -112,7 +113,7 @@ def get_boto3_session(role_arn=None, session_vars=None):
|
|
|
112
113
|
tmp_aws_config_file = f.name
|
|
113
114
|
os.rename(tmp_aws_config_file, aws_config_file)
|
|
114
115
|
os.environ["AWS_CONFIG_FILE"] = aws_config_file
|
|
115
|
-
os.environ["AWS_PROFILE"] = "
|
|
116
|
+
os.environ["AWS_PROFILE"] = "cspr"
|
|
116
117
|
else:
|
|
117
118
|
os.environ["AWS_WEB_IDENTITY_TOKEN_FILE"] = token_file
|
|
118
119
|
os.environ["AWS_ROLE_ARN"] = token_info["role_arn"]
|
|
@@ -124,29 +125,21 @@ def get_boto3_session(role_arn=None, session_vars=None):
|
|
|
124
125
|
if token_info.get("region"):
|
|
125
126
|
os.environ["AWS_DEFAULT_REGION"] = token_info["region"]
|
|
126
127
|
|
|
127
|
-
if
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
#
|
|
132
|
-
session = boto3.session.Session(profile_name="
|
|
128
|
+
if cspr_role:
|
|
129
|
+
# The generated AWS config will be used here since we set the
|
|
130
|
+
# AWS_CONFIG_FILE environment variable above.
|
|
131
|
+
if role_arn == USE_CSPR_ROLE_ARN_IF_SET:
|
|
132
|
+
# Otherwise start from the default profile, assuming CSPR role
|
|
133
|
+
session = boto3.session.Session(profile_name="cspr")
|
|
133
134
|
else:
|
|
134
135
|
session = boto3.session.Session(profile_name="task")
|
|
135
136
|
else:
|
|
136
|
-
# No decorator role or CSPR role, use default session
|
|
137
137
|
# Not using AWS config, just AWS_WEB_IDENTITY_TOKEN_FILE + AWS_ROLE_ARN
|
|
138
138
|
session = boto3.session.Session()
|
|
139
139
|
|
|
140
|
-
if
|
|
141
|
-
role_arn
|
|
142
|
-
|
|
143
|
-
and role_arn != decorator_role_arn
|
|
144
|
-
):
|
|
145
|
-
# If the user provided a role_arn that's different from the decorator role,
|
|
146
|
-
# we assume that role using the current session credentials.
|
|
147
|
-
# This works for both cases:
|
|
148
|
-
# 1. No decorator role: Task role -> Secrets role
|
|
149
|
-
# 2. With decorator role: Decorator role -> Secrets role
|
|
140
|
+
if role_arn and role_arn != USE_CSPR_ROLE_ARN_IF_SET:
|
|
141
|
+
# If the user provided a role_arn, we assume that role
|
|
142
|
+
# using the task role credentials. CSPR role is not used.
|
|
150
143
|
fetcher = botocore.credentials.AssumeRoleCredentialFetcher(
|
|
151
144
|
client_creator=session._session.create_client,
|
|
152
145
|
source_credentials=session._session.get_credentials(),
|
|
@@ -162,8 +155,8 @@ def get_boto3_session(role_arn=None, session_vars=None):
|
|
|
162
155
|
else:
|
|
163
156
|
# If the user didn't provide a role_arn, or if the role_arn
|
|
164
157
|
# is set to USE_CSPR_ROLE_ARN_IF_SET, we return the default
|
|
165
|
-
# session which would use the
|
|
166
|
-
#
|
|
158
|
+
# session which would use the CSPR role if it is set on the
|
|
159
|
+
# server, and the task role otherwise.
|
|
167
160
|
return session
|
|
168
161
|
|
|
169
162
|
|
|
@@ -39,7 +39,7 @@ def derive_job_outcome(job_status: "V1JobStatus"):
|
|
|
39
39
|
|
|
40
40
|
# This means that the job has neither finished or succedded.
|
|
41
41
|
if job_status.active:
|
|
42
|
-
return JobOutcomes.
|
|
42
|
+
return JobOutcomes.DELETE
|
|
43
43
|
|
|
44
44
|
# This means that the job is not active. Had started. There is not succedded/fail.
|
|
45
45
|
# This is a weird state. Better to just kill the job
|
|
@@ -47,7 +47,7 @@ def derive_job_outcome(job_status: "V1JobStatus"):
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class PodKiller:
|
|
50
|
-
def __init__(self, kubernetes_client, echo_func, namespace):
|
|
50
|
+
def __init__(self, kubernetes_client, echo_func, namespace, progress_bar=None):
|
|
51
51
|
self.client = kubernetes_client
|
|
52
52
|
self.echo = echo_func
|
|
53
53
|
self.api_instance = self.client.CoreV1Api()
|
|
@@ -55,6 +55,7 @@ class PodKiller:
|
|
|
55
55
|
self._namespace = namespace
|
|
56
56
|
self.jobset_api = None
|
|
57
57
|
self.jobset_api = self.client.CustomObjectsApi()
|
|
58
|
+
self.progress_bar = progress_bar
|
|
58
59
|
|
|
59
60
|
def _delete_jobset(self, owner_ref, namespace):
|
|
60
61
|
"""Delete a JobSet if it's the owner of a job."""
|
|
@@ -147,20 +148,31 @@ class PodKiller:
|
|
|
147
148
|
|
|
148
149
|
def _find_matching_jobs(self, flow_name, run_id=None, user=None):
|
|
149
150
|
"""Find jobs that match the flow_name, run_id, and user criteria using similar logic to _find_active_pods"""
|
|
151
|
+
|
|
152
|
+
def paginated_job_finder(namespace):
|
|
153
|
+
continue_token = None
|
|
154
|
+
while True:
|
|
155
|
+
response = self.job_api.list_namespaced_job(
|
|
156
|
+
namespace=namespace, limit=100, _continue=continue_token
|
|
157
|
+
)
|
|
158
|
+
yield response.items
|
|
159
|
+
continue_token = response.metadata._continue
|
|
160
|
+
if not continue_token:
|
|
161
|
+
break
|
|
162
|
+
|
|
150
163
|
try:
|
|
151
|
-
jobs = self.job_api.list_namespaced_job(namespace=self._namespace)
|
|
152
164
|
matching_jobs = []
|
|
153
|
-
for
|
|
154
|
-
job
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
for _jobs in paginated_job_finder(self._namespace):
|
|
166
|
+
for job in _jobs:
|
|
167
|
+
_match = self._metaflow_matching_spec(
|
|
168
|
+
run_id=run_id,
|
|
169
|
+
user=user,
|
|
170
|
+
flow_name=flow_name,
|
|
171
|
+
annotations=job.metadata.annotations,
|
|
172
|
+
labels=job.metadata.labels,
|
|
173
|
+
)
|
|
174
|
+
if _match:
|
|
175
|
+
matching_jobs.append(job)
|
|
164
176
|
return matching_jobs
|
|
165
177
|
except Exception as e:
|
|
166
178
|
self.echo(f"Error finding jobs: {str(e)}\n")
|
|
@@ -171,25 +183,38 @@ class PodKiller:
|
|
|
171
183
|
if not self.jobset_api:
|
|
172
184
|
return []
|
|
173
185
|
|
|
186
|
+
def paginated_jobset_finder(namespace):
|
|
187
|
+
continue_token = None
|
|
188
|
+
responses = []
|
|
189
|
+
while True:
|
|
190
|
+
response = self.jobset_api.list_namespaced_custom_object(
|
|
191
|
+
group="jobset.x-k8s.io",
|
|
192
|
+
version="v1alpha2",
|
|
193
|
+
namespace=namespace,
|
|
194
|
+
plural="jobsets",
|
|
195
|
+
limit=100,
|
|
196
|
+
**({"_continue": continue_token} if continue_token else {}),
|
|
197
|
+
)
|
|
198
|
+
continue_token = response.get("metadata", {}).get("continue", None)
|
|
199
|
+
responses.append(response)
|
|
200
|
+
if not continue_token:
|
|
201
|
+
break
|
|
202
|
+
return responses
|
|
203
|
+
|
|
174
204
|
try:
|
|
175
|
-
jobsets = self.jobset_api.list_namespaced_custom_object(
|
|
176
|
-
group="jobset.x-k8s.io",
|
|
177
|
-
version="v1alpha2",
|
|
178
|
-
namespace=self._namespace,
|
|
179
|
-
plural="jobsets",
|
|
180
|
-
)
|
|
181
205
|
matching_jobsets = []
|
|
182
206
|
|
|
183
|
-
for
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
207
|
+
for jobset_response in paginated_jobset_finder(self._namespace):
|
|
208
|
+
for jobset in jobset_response.get("items", []):
|
|
209
|
+
_match = self._metaflow_matching_spec(
|
|
210
|
+
run_id=run_id,
|
|
211
|
+
user=user,
|
|
212
|
+
flow_name=flow_name,
|
|
213
|
+
annotations=jobset.get("metadata", {}).get("annotations", {}),
|
|
214
|
+
labels=jobset.get("metadata", {}).get("labels", {}),
|
|
215
|
+
)
|
|
216
|
+
if _match:
|
|
217
|
+
matching_jobsets.append(jobset)
|
|
193
218
|
|
|
194
219
|
return matching_jobsets
|
|
195
220
|
except Exception as e:
|
|
@@ -270,9 +295,20 @@ class PodKiller:
|
|
|
270
295
|
self.echo(f"Unknown outcome {outcome} for JobSet {jobset_name}")
|
|
271
296
|
return False
|
|
272
297
|
|
|
298
|
+
def extract_matching_jobs_and_jobsets(self, flow_name, run_id, user):
|
|
299
|
+
"""Extract matching jobs and jobsets based on the flow_name, run_id, and user criteria"""
|
|
300
|
+
jobs = self._find_matching_jobs(flow_name, run_id, user)
|
|
301
|
+
jobsets = self._find_matching_jobsets(flow_name, run_id, user)
|
|
302
|
+
return [(j, derive_job_outcome(j.status)) for j in jobs], [
|
|
303
|
+
(j, derive_jobset_outcome(j.get("status", {}))) for j in jobsets
|
|
304
|
+
]
|
|
305
|
+
|
|
273
306
|
def process_matching_jobs_and_jobsets(self, flow_name, run_id, user):
|
|
274
307
|
"""Process all matching jobs and jobsets based on their derived outcomes"""
|
|
275
308
|
results = []
|
|
309
|
+
progress_update = lambda x: x
|
|
310
|
+
if self.progress_bar:
|
|
311
|
+
progress_update = lambda x: self.progress_bar.update(1, x)
|
|
276
312
|
|
|
277
313
|
# Process matching jobs
|
|
278
314
|
_jobs, _jobsets = [], []
|
|
@@ -282,6 +318,7 @@ class PodKiller:
|
|
|
282
318
|
result = self._handle_job_outcome(job, outcome)
|
|
283
319
|
# results.append(result)
|
|
284
320
|
if result is not None:
|
|
321
|
+
progress_update("💀 Killing Job %s" % job.metadata.name)
|
|
285
322
|
results.append(result)
|
|
286
323
|
_jobs.append(result)
|
|
287
324
|
|
|
@@ -292,7 +329,46 @@ class PodKiller:
|
|
|
292
329
|
outcome = derive_jobset_outcome(jobset_status)
|
|
293
330
|
result = self._handle_jobset_outcome(jobset, outcome)
|
|
294
331
|
if result is not None:
|
|
332
|
+
progress_update(
|
|
333
|
+
"💀 Deleting JobSet %s"
|
|
334
|
+
% jobset.get("metadata", {}).get("name", "unknown")
|
|
335
|
+
)
|
|
295
336
|
results.append(result)
|
|
296
337
|
_jobsets.append(result)
|
|
297
338
|
|
|
298
339
|
return results, len(_jobs), len(_jobsets)
|
|
340
|
+
|
|
341
|
+
def process_matching_jobs_and_jobsets_force_all(self, flow_name, run_id, user):
|
|
342
|
+
"""Force process ALL matching jobs and jobsets regardless of their status/outcome"""
|
|
343
|
+
results = []
|
|
344
|
+
progress_update = lambda x: x
|
|
345
|
+
if self.progress_bar:
|
|
346
|
+
progress_update = lambda x: self.progress_bar.update(1, x)
|
|
347
|
+
|
|
348
|
+
# Process matching jobs - FORCE DELETE ALL
|
|
349
|
+
_jobs, _jobsets = [], []
|
|
350
|
+
jobs = self._find_matching_jobs(flow_name, run_id, user)
|
|
351
|
+
for job in jobs:
|
|
352
|
+
# Force DELETE outcome regardless of actual status
|
|
353
|
+
result = self._handle_job_outcome(job, JobOutcomes.DELETE)
|
|
354
|
+
progress_update("🔥 FORCE Deleting Job %s" % job.metadata.name)
|
|
355
|
+
results.append(
|
|
356
|
+
result if result is not None else True
|
|
357
|
+
) # Treat None as success for force mode
|
|
358
|
+
_jobs.append(result if result is not None else True)
|
|
359
|
+
|
|
360
|
+
# Process matching jobsets - FORCE DELETE ALL
|
|
361
|
+
jobsets = self._find_matching_jobsets(flow_name, run_id, user)
|
|
362
|
+
for jobset in jobsets:
|
|
363
|
+
# Force DELETE outcome regardless of actual status
|
|
364
|
+
result = self._handle_jobset_outcome(jobset, JobOutcomes.DELETE)
|
|
365
|
+
progress_update(
|
|
366
|
+
"🔥 FORCE Deleting JobSet %s"
|
|
367
|
+
% jobset.get("metadata", {}).get("name", "unknown")
|
|
368
|
+
)
|
|
369
|
+
results.append(
|
|
370
|
+
result if result is not None else True
|
|
371
|
+
) # Treat None as success for force mode
|
|
372
|
+
_jobsets.append(result if result is not None else True)
|
|
373
|
+
|
|
374
|
+
return results, len(_jobs), len(_jobsets)
|
{ob_metaflow_extensions-1.1.168rc5.dist-info → ob_metaflow_extensions-1.1.170rc0.dist-info}/RECORD
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
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=
|
|
4
|
+
metaflow_extensions/outerbounds/plugins/__init__.py,sha256=rWDE4k-KbsFsCoqct1Rw7w3bSV0jLunx6LMBKD2SYEA,13747
|
|
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=
|
|
8
|
+
metaflow_extensions/outerbounds/plugins/apps/app_cli.py,sha256=Uv9viEmQ0_6ogVHO8O_FGQyXR6rV5HR5agsP3gGnKm0,638
|
|
9
9
|
metaflow_extensions/outerbounds/plugins/apps/app_utils.py,sha256=sw9whU17lAzlD2K2kEDNjlk1Ib-2xE2UNhJkmzD8Qv8,8543
|
|
10
10
|
metaflow_extensions/outerbounds/plugins/apps/consts.py,sha256=iHsyqbUg9k-rgswCs1Jxf5QZIxR1V-peCDRjgr9kdBM,177
|
|
11
11
|
metaflow_extensions/outerbounds/plugins/apps/deploy_decorator.py,sha256=VkmiMdNYHhNdt-Qm9AVv7aE2LWFsIFEc16YcOYjwF6Q,8568
|
|
@@ -28,7 +28,7 @@ metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_cli.py,sha256=kq
|
|
|
28
28
|
metaflow_extensions/outerbounds/plugins/fast_bakery/fast_bakery_decorator.py,sha256=MXSIp05-jvt8Q2uGaLKjtuM_ToLeRLxhtMbfHc9Kcko,1515
|
|
29
29
|
metaflow_extensions/outerbounds/plugins/kubernetes/__init__.py,sha256=5zG8gShSj8m7rgF4xgWBZFuY3GDP5n1T0ktjRpGJLHA,69
|
|
30
30
|
metaflow_extensions/outerbounds/plugins/kubernetes/kubernetes_client.py,sha256=sjBhQ4aa-i1UkKsJyTswdDLYOBAFIvHRco4r7wfs9Tc,5003
|
|
31
|
-
metaflow_extensions/outerbounds/plugins/kubernetes/pod_killer.py,sha256=
|
|
31
|
+
metaflow_extensions/outerbounds/plugins/kubernetes/pod_killer.py,sha256=3b9y6gYEqWkN-_-GOqcAV2pOJOeCjpVmfW-nOMCJ4Z0,14924
|
|
32
32
|
metaflow_extensions/outerbounds/plugins/nim/card.py,sha256=dXOJvsZed5NyYyxYLPDvtwg9z_X4azL9HTJGYaiNriY,4690
|
|
33
33
|
metaflow_extensions/outerbounds/plugins/nim/nim_decorator.py,sha256=50YVvC7mcZYlPluM0Wq1UtufhzlQb-RxzZkTOJJ3LkM,3439
|
|
34
34
|
metaflow_extensions/outerbounds/plugins/nim/nim_manager.py,sha256=y8U71106KJtrC6nlhsNnzX9Xkv3RnyZ1KEpRFwqZZFk,13686
|
|
@@ -78,7 +78,7 @@ metaflow_extensions/outerbounds/profilers/__init__.py,sha256=wa_jhnCBr82TBxoS0e8
|
|
|
78
78
|
metaflow_extensions/outerbounds/profilers/gpu.py,sha256=3Er8uKQzfm_082uadg4yn_D4Y-iSCgzUfFmguYxZsz4,27485
|
|
79
79
|
metaflow_extensions/outerbounds/toplevel/__init__.py,sha256=qWUJSv_r5hXJ7jV_On4nEasKIfUCm6_UjkjXWA_A1Ts,90
|
|
80
80
|
metaflow_extensions/outerbounds/toplevel/global_aliases_for_metaflow_package.py,sha256=_fKWv_-O1k5Nk5A1q05Ioh-PSsFXGL-jiAt7zfl8pIE,2999
|
|
81
|
-
metaflow_extensions/outerbounds/toplevel/ob_internal.py,sha256=
|
|
81
|
+
metaflow_extensions/outerbounds/toplevel/ob_internal.py,sha256=VdoPHis20NUkasUiM8d6JEq-X8QVO4eMDaMBAJgKKLg,105
|
|
82
82
|
metaflow_extensions/outerbounds/toplevel/plugins/azure/__init__.py,sha256=WUuhz2YQfI4fz7nIcipwwWq781eaoHEk7n4GAn1npDg,63
|
|
83
83
|
metaflow_extensions/outerbounds/toplevel/plugins/gcp/__init__.py,sha256=BbZiaH3uILlEZ6ntBLKeNyqn3If8nIXZFq_Apd7Dhco,70
|
|
84
84
|
metaflow_extensions/outerbounds/toplevel/plugins/kubernetes/__init__.py,sha256=5zG8gShSj8m7rgF4xgWBZFuY3GDP5n1T0ktjRpGJLHA,69
|
|
@@ -86,7 +86,7 @@ metaflow_extensions/outerbounds/toplevel/plugins/ollama/__init__.py,sha256=GRSz2
|
|
|
86
86
|
metaflow_extensions/outerbounds/toplevel/plugins/snowflake/__init__.py,sha256=LptpH-ziXHrednMYUjIaosS1SXD3sOtF_9_eRqd8SJw,50
|
|
87
87
|
metaflow_extensions/outerbounds/toplevel/plugins/torchtune/__init__.py,sha256=uTVkdSk3xZ7hEKYfdlyVteWj5KeDwaM1hU9WT-_YKfI,50
|
|
88
88
|
metaflow_extensions/outerbounds/toplevel/plugins/vllm/__init__.py,sha256=ekcgD3KVydf-a0xMI60P4uy6ePkSEoFHiGnDq1JM940,45
|
|
89
|
-
ob_metaflow_extensions-1.1.
|
|
90
|
-
ob_metaflow_extensions-1.1.
|
|
91
|
-
ob_metaflow_extensions-1.1.
|
|
92
|
-
ob_metaflow_extensions-1.1.
|
|
89
|
+
ob_metaflow_extensions-1.1.170rc0.dist-info/METADATA,sha256=B5xv_U5U4uooT7aqY74T1AT5sVkxF7oZ36j56HLkllI,524
|
|
90
|
+
ob_metaflow_extensions-1.1.170rc0.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110
|
|
91
|
+
ob_metaflow_extensions-1.1.170rc0.dist-info/top_level.txt,sha256=NwG0ukwjygtanDETyp_BUdtYtqIA_lOjzFFh1TsnxvI,20
|
|
92
|
+
ob_metaflow_extensions-1.1.170rc0.dist-info/RECORD,,
|
{ob_metaflow_extensions-1.1.168rc5.dist-info → ob_metaflow_extensions-1.1.170rc0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|