mlrun 1.7.0rc33__py3-none-any.whl → 1.7.0rc34__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 mlrun might be problematic. Click here for more details.
- mlrun/common/schemas/__init__.py +1 -0
- mlrun/common/schemas/common.py +3 -0
- mlrun/common/schemas/function.py +7 -0
- mlrun/common/schemas/project.py +35 -3
- mlrun/db/base.py +5 -2
- mlrun/db/httpdb.py +9 -7
- mlrun/execution.py +1 -3
- mlrun/model.py +142 -22
- mlrun/model_monitoring/applications/context.py +13 -15
- mlrun/projects/project.py +7 -1
- mlrun/run.py +22 -9
- mlrun/runtimes/nuclio/api_gateway.py +22 -6
- mlrun/runtimes/nuclio/application/application.py +37 -9
- mlrun/runtimes/nuclio/function.py +3 -0
- mlrun/runtimes/nuclio/serving.py +5 -5
- mlrun/serving/server.py +12 -7
- mlrun/serving/states.py +13 -1
- mlrun/utils/db.py +3 -0
- mlrun/utils/helpers.py +3 -5
- mlrun/utils/notifications/notification/webhook.py +8 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc33.dist-info → mlrun-1.7.0rc34.dist-info}/METADATA +2 -2
- {mlrun-1.7.0rc33.dist-info → mlrun-1.7.0rc34.dist-info}/RECORD +27 -27
- {mlrun-1.7.0rc33.dist-info → mlrun-1.7.0rc34.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc33.dist-info → mlrun-1.7.0rc34.dist-info}/WHEEL +0 -0
- {mlrun-1.7.0rc33.dist-info → mlrun-1.7.0rc34.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc33.dist-info → mlrun-1.7.0rc34.dist-info}/top_level.txt +0 -0
mlrun/common/schemas/__init__.py
CHANGED
mlrun/common/schemas/common.py
CHANGED
mlrun/common/schemas/function.py
CHANGED
|
@@ -119,6 +119,13 @@ class FunctionSpec(pydantic.BaseModel):
|
|
|
119
119
|
service_account: typing.Optional[ServiceAccount]
|
|
120
120
|
state_thresholds: typing.Optional[StateThresholds]
|
|
121
121
|
|
|
122
|
+
class Config:
|
|
123
|
+
extra = pydantic.Extra.allow
|
|
124
|
+
|
|
122
125
|
|
|
123
126
|
class Function(pydantic.BaseModel):
|
|
124
127
|
spec: typing.Optional[FunctionSpec]
|
|
128
|
+
application: typing.Optional[dict[str, typing.Any]]
|
|
129
|
+
|
|
130
|
+
class Config:
|
|
131
|
+
extra = pydantic.Extra.allow
|
mlrun/common/schemas/project.py
CHANGED
|
@@ -100,6 +100,29 @@ class ProjectSpec(pydantic.BaseModel):
|
|
|
100
100
|
extra = pydantic.Extra.allow
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
class ProjectSpecOut(pydantic.BaseModel):
|
|
104
|
+
description: typing.Optional[str] = None
|
|
105
|
+
owner: typing.Optional[str] = None
|
|
106
|
+
goals: typing.Optional[str] = None
|
|
107
|
+
params: typing.Optional[dict] = {}
|
|
108
|
+
functions: typing.Optional[list] = []
|
|
109
|
+
workflows: typing.Optional[list] = []
|
|
110
|
+
artifacts: typing.Optional[list] = []
|
|
111
|
+
artifact_path: typing.Optional[str] = None
|
|
112
|
+
conda: typing.Optional[str] = None
|
|
113
|
+
source: typing.Optional[str] = None
|
|
114
|
+
subpath: typing.Optional[str] = None
|
|
115
|
+
origin_url: typing.Optional[str] = None
|
|
116
|
+
desired_state: typing.Optional[ProjectDesiredState] = ProjectDesiredState.online
|
|
117
|
+
custom_packagers: typing.Optional[list[tuple[str, bool]]] = None
|
|
118
|
+
default_image: typing.Optional[str] = None
|
|
119
|
+
build: typing.Any = None
|
|
120
|
+
default_function_node_selector: typing.Optional[dict] = {}
|
|
121
|
+
|
|
122
|
+
class Config:
|
|
123
|
+
extra = pydantic.Extra.allow
|
|
124
|
+
|
|
125
|
+
|
|
103
126
|
class Project(pydantic.BaseModel):
|
|
104
127
|
kind: ObjectKind = pydantic.Field(ObjectKind.project, const=True)
|
|
105
128
|
metadata: ProjectMetadata
|
|
@@ -107,6 +130,15 @@ class Project(pydantic.BaseModel):
|
|
|
107
130
|
status: ObjectStatus = ObjectStatus()
|
|
108
131
|
|
|
109
132
|
|
|
133
|
+
# The reason we have a different schema for the response model is that we don't want to validate project.spec.build in
|
|
134
|
+
# the response as the validation was added late and there may be corrupted values in the DB.
|
|
135
|
+
class ProjectOut(pydantic.BaseModel):
|
|
136
|
+
kind: ObjectKind = pydantic.Field(ObjectKind.project, const=True)
|
|
137
|
+
metadata: ProjectMetadata
|
|
138
|
+
spec: ProjectSpecOut = ProjectSpecOut()
|
|
139
|
+
status: ObjectStatus = ObjectStatus()
|
|
140
|
+
|
|
141
|
+
|
|
110
142
|
class ProjectOwner(pydantic.BaseModel):
|
|
111
143
|
username: str
|
|
112
144
|
access_key: str
|
|
@@ -134,16 +166,16 @@ class IguazioProject(pydantic.BaseModel):
|
|
|
134
166
|
|
|
135
167
|
|
|
136
168
|
# The format query param controls the project type used:
|
|
137
|
-
# full -
|
|
169
|
+
# full - ProjectOut
|
|
138
170
|
# name_only - str
|
|
139
171
|
# summary - ProjectSummary
|
|
140
172
|
# leader - currently only IguazioProject supported
|
|
141
173
|
# The way pydantic handles typing.Union is that it takes the object and tries to coerce it to be the types of the
|
|
142
|
-
# union by the definition order. Therefore we can't currently add generic dict for all leader formats, but we need
|
|
174
|
+
# union by the definition order. Therefore, we can't currently add generic dict for all leader formats, but we need
|
|
143
175
|
# to add a specific classes for them. it's frustrating but couldn't find other workaround, see:
|
|
144
176
|
# https://github.com/samuelcolvin/pydantic/issues/1423, https://github.com/samuelcolvin/pydantic/issues/619
|
|
145
177
|
ProjectOutput = typing.TypeVar(
|
|
146
|
-
"ProjectOutput",
|
|
178
|
+
"ProjectOutput", ProjectOut, str, ProjectSummary, IguazioProject
|
|
147
179
|
)
|
|
148
180
|
|
|
149
181
|
|
mlrun/db/base.py
CHANGED
|
@@ -690,8 +690,11 @@ class RunDBInterface(ABC):
|
|
|
690
690
|
@abstractmethod
|
|
691
691
|
def store_api_gateway(
|
|
692
692
|
self,
|
|
693
|
-
api_gateway:
|
|
694
|
-
|
|
693
|
+
api_gateway: Union[
|
|
694
|
+
mlrun.common.schemas.APIGateway,
|
|
695
|
+
"mlrun.runtimes.nuclio.api_gateway.APIGateway",
|
|
696
|
+
],
|
|
697
|
+
project: Optional[str] = None,
|
|
695
698
|
):
|
|
696
699
|
pass
|
|
697
700
|
|
mlrun/db/httpdb.py
CHANGED
|
@@ -1015,7 +1015,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
1015
1015
|
"format": format_,
|
|
1016
1016
|
"tag": tag,
|
|
1017
1017
|
"tree": tree,
|
|
1018
|
-
"
|
|
1018
|
+
"object_uid": uid,
|
|
1019
1019
|
}
|
|
1020
1020
|
if iter is not None:
|
|
1021
1021
|
params["iter"] = str(iter)
|
|
@@ -1051,7 +1051,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
1051
1051
|
"key": key,
|
|
1052
1052
|
"tag": tag,
|
|
1053
1053
|
"tree": tree,
|
|
1054
|
-
"
|
|
1054
|
+
"object_uid": uid,
|
|
1055
1055
|
"iter": iter,
|
|
1056
1056
|
"deletion_strategy": deletion_strategy,
|
|
1057
1057
|
}
|
|
@@ -1071,8 +1071,8 @@ class HTTPRunDB(RunDBInterface):
|
|
|
1071
1071
|
project=None,
|
|
1072
1072
|
tag=None,
|
|
1073
1073
|
labels: Optional[Union[dict[str, str], list[str]]] = None,
|
|
1074
|
-
since=None,
|
|
1075
|
-
until=None,
|
|
1074
|
+
since: Optional[datetime] = None,
|
|
1075
|
+
until: Optional[datetime] = None,
|
|
1076
1076
|
iter: int = None,
|
|
1077
1077
|
best_iteration: bool = False,
|
|
1078
1078
|
kind: str = None,
|
|
@@ -1102,8 +1102,8 @@ class HTTPRunDB(RunDBInterface):
|
|
|
1102
1102
|
:param tag: Return artifacts assigned this tag.
|
|
1103
1103
|
:param labels: Return artifacts that have these labels. Labels can either be a dictionary {"label": "value"} or
|
|
1104
1104
|
a list of "label=value" (match label key and value) or "label" (match just label key) strings.
|
|
1105
|
-
:param since:
|
|
1106
|
-
:param until:
|
|
1105
|
+
:param since: Return artifacts updated after this date (as datetime object).
|
|
1106
|
+
:param until: Return artifacts updated before this date (as datetime object).
|
|
1107
1107
|
:param iter: Return artifacts from a specific iteration (where ``iter=0`` means the root iteration). If
|
|
1108
1108
|
``None`` (default) return artifacts from all iterations.
|
|
1109
1109
|
:param best_iteration: Returns the artifact which belongs to the best iteration of a given run, in the case of
|
|
@@ -1137,6 +1137,8 @@ class HTTPRunDB(RunDBInterface):
|
|
|
1137
1137
|
"format": format_,
|
|
1138
1138
|
"producer_uri": producer_uri,
|
|
1139
1139
|
"limit": limit,
|
|
1140
|
+
"since": datetime_to_iso(since),
|
|
1141
|
+
"until": datetime_to_iso(until),
|
|
1140
1142
|
}
|
|
1141
1143
|
error = "list artifacts"
|
|
1142
1144
|
endpoint_path = f"projects/{project}/artifacts"
|
|
@@ -1684,7 +1686,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
1684
1686
|
last_log_timestamp = float(
|
|
1685
1687
|
resp.headers.get("x-mlrun-last-timestamp", "0.0")
|
|
1686
1688
|
)
|
|
1687
|
-
if func.kind in mlrun.runtimes.RuntimeKinds.
|
|
1689
|
+
if func.kind in mlrun.runtimes.RuntimeKinds.pure_nuclio_deployed_runtimes():
|
|
1688
1690
|
mlrun.runtimes.nuclio.function.enrich_nuclio_function_from_headers(
|
|
1689
1691
|
func, resp.headers
|
|
1690
1692
|
)
|
mlrun/execution.py
CHANGED
|
@@ -78,7 +78,6 @@ class MLClientCtx:
|
|
|
78
78
|
self._tmpfile = tmp
|
|
79
79
|
self._logger = log_stream or logger
|
|
80
80
|
self._log_level = "info"
|
|
81
|
-
self._matrics_db = None
|
|
82
81
|
self._autocommit = autocommit
|
|
83
82
|
self._notifications = []
|
|
84
83
|
self._state_thresholds = {}
|
|
@@ -103,8 +102,7 @@ class MLClientCtx:
|
|
|
103
102
|
self._error = None
|
|
104
103
|
self._commit = ""
|
|
105
104
|
self._host = None
|
|
106
|
-
self._start_time = now_date()
|
|
107
|
-
self._last_update = now_date()
|
|
105
|
+
self._start_time = self._last_update = now_date()
|
|
108
106
|
self._iteration_results = None
|
|
109
107
|
self._children = []
|
|
110
108
|
self._parent = None
|
mlrun/model.py
CHANGED
|
@@ -1490,14 +1490,37 @@ class RunObject(RunTemplate):
|
|
|
1490
1490
|
)
|
|
1491
1491
|
return ""
|
|
1492
1492
|
|
|
1493
|
-
def output(self, key):
|
|
1494
|
-
"""
|
|
1493
|
+
def output(self, key: str):
|
|
1494
|
+
"""
|
|
1495
|
+
Return the value of a specific result or artifact by key.
|
|
1496
|
+
|
|
1497
|
+
This method waits for the outputs to complete and retrieves the value corresponding to the provided key.
|
|
1498
|
+
If the key exists in the results, it returns the corresponding result value.
|
|
1499
|
+
If not found in results, it attempts to fetch the artifact by key (cached in the run status).
|
|
1500
|
+
If the artifact is not found, it tries to fetch the artifact URI by key.
|
|
1501
|
+
If no artifact or result is found for the key, returns None.
|
|
1502
|
+
|
|
1503
|
+
:param key: The key of the result or artifact to retrieve.
|
|
1504
|
+
:return: The value of the result or the artifact URI corresponding to the key, or None if not found.
|
|
1505
|
+
"""
|
|
1495
1506
|
self._outputs_wait_for_completion()
|
|
1507
|
+
|
|
1508
|
+
# Check if the key exists in results and return the result value
|
|
1496
1509
|
if self.status.results and key in self.status.results:
|
|
1497
|
-
return self.status.results
|
|
1510
|
+
return self.status.results[key]
|
|
1511
|
+
|
|
1512
|
+
# Artifacts are usually cached in the run object under `status.artifacts`. However, the artifacts are not
|
|
1513
|
+
# stored in the DB as part of the run. The server may enrich the run with the artifacts or provide
|
|
1514
|
+
# `status.artifact_uris` instead. See mlrun.common.formatters.run.RunFormat.
|
|
1515
|
+
# When running locally - `status.artifact_uri` does not exist in the run.
|
|
1516
|
+
# When listing runs - `status.artifacts` does not exist in the run.
|
|
1498
1517
|
artifact = self._artifact(key)
|
|
1499
1518
|
if artifact:
|
|
1500
1519
|
return get_artifact_target(artifact, self.metadata.project)
|
|
1520
|
+
|
|
1521
|
+
if self.status.artifact_uris and key in self.status.artifact_uris:
|
|
1522
|
+
return self.status.artifact_uris[key]
|
|
1523
|
+
|
|
1501
1524
|
return None
|
|
1502
1525
|
|
|
1503
1526
|
@property
|
|
@@ -1510,26 +1533,50 @@ class RunObject(RunTemplate):
|
|
|
1510
1533
|
|
|
1511
1534
|
@property
|
|
1512
1535
|
def outputs(self):
|
|
1513
|
-
"""
|
|
1514
|
-
outputs
|
|
1536
|
+
"""
|
|
1537
|
+
Return a dictionary of outputs, including result values and artifact URIs.
|
|
1538
|
+
|
|
1539
|
+
This method waits for the outputs to complete and combines result values
|
|
1540
|
+
and artifact URIs into a single dictionary. If there are multiple artifacts
|
|
1541
|
+
for the same key, only include the artifact that does not have the "latest" tag.
|
|
1542
|
+
If there is no other tag, include the "latest" tag as a fallback.
|
|
1543
|
+
|
|
1544
|
+
:return: Dictionary containing result values and artifact URIs.
|
|
1545
|
+
"""
|
|
1515
1546
|
self._outputs_wait_for_completion()
|
|
1547
|
+
outputs = {}
|
|
1548
|
+
|
|
1549
|
+
# Add results if available
|
|
1516
1550
|
if self.status.results:
|
|
1517
|
-
outputs
|
|
1551
|
+
outputs.update(self.status.results)
|
|
1552
|
+
|
|
1553
|
+
# Artifacts are usually cached in the run object under `status.artifacts`. However, the artifacts are not
|
|
1554
|
+
# stored in the DB as part of the run. The server may enrich the run with the artifacts or provide
|
|
1555
|
+
# `status.artifact_uris` instead. See mlrun.common.formatters.run.RunFormat.
|
|
1556
|
+
# When running locally - `status.artifact_uri` does not exist in the run.
|
|
1557
|
+
# When listing runs - `status.artifacts` does not exist in the run.
|
|
1518
1558
|
if self.status.artifacts:
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1559
|
+
outputs.update(self._process_artifacts(self.status.artifacts))
|
|
1560
|
+
elif self.status.artifact_uris:
|
|
1561
|
+
outputs.update(self.status.artifact_uris)
|
|
1562
|
+
|
|
1522
1563
|
return outputs
|
|
1523
1564
|
|
|
1524
|
-
def artifact(self, key) -> "mlrun.DataItem":
|
|
1525
|
-
"""
|
|
1565
|
+
def artifact(self, key: str) -> "mlrun.DataItem":
|
|
1566
|
+
"""Return artifact DataItem by key.
|
|
1567
|
+
|
|
1568
|
+
This method waits for the outputs to complete, searches for the artifact matching the given key,
|
|
1569
|
+
and returns a DataItem if the artifact is found.
|
|
1570
|
+
|
|
1571
|
+
:param key: The key of the artifact to find.
|
|
1572
|
+
:return: A DataItem corresponding to the artifact with the given key, or None if no such artifact is found.
|
|
1573
|
+
"""
|
|
1526
1574
|
self._outputs_wait_for_completion()
|
|
1527
1575
|
artifact = self._artifact(key)
|
|
1528
|
-
if artifact:
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
return None
|
|
1576
|
+
if not artifact:
|
|
1577
|
+
return None
|
|
1578
|
+
uri = get_artifact_target(artifact, self.metadata.project)
|
|
1579
|
+
return mlrun.get_dataitem(uri) if uri else None
|
|
1533
1580
|
|
|
1534
1581
|
def _outputs_wait_for_completion(
|
|
1535
1582
|
self,
|
|
@@ -1547,12 +1594,85 @@ class RunObject(RunTemplate):
|
|
|
1547
1594
|
)
|
|
1548
1595
|
|
|
1549
1596
|
def _artifact(self, key):
|
|
1550
|
-
"""
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1597
|
+
"""
|
|
1598
|
+
Return the last artifact DataItem that matches the given key.
|
|
1599
|
+
|
|
1600
|
+
If multiple artifacts with the same key exist, return the last one in the list.
|
|
1601
|
+
If there are artifacts with different tags, the method will return the one with a tag other than 'latest'
|
|
1602
|
+
if available.
|
|
1603
|
+
If no artifact with the given key is found, return None.
|
|
1604
|
+
|
|
1605
|
+
:param key: The key of the artifact to retrieve.
|
|
1606
|
+
:return: The last artifact DataItem with the given key, or None if no such artifact is found.
|
|
1607
|
+
"""
|
|
1608
|
+
if not self.status.artifacts:
|
|
1609
|
+
return None
|
|
1610
|
+
|
|
1611
|
+
# Collect artifacts that match the key
|
|
1612
|
+
matching_artifacts = [
|
|
1613
|
+
artifact
|
|
1614
|
+
for artifact in self.status.artifacts
|
|
1615
|
+
if artifact["metadata"].get("key") == key
|
|
1616
|
+
]
|
|
1617
|
+
|
|
1618
|
+
if not matching_artifacts:
|
|
1619
|
+
return None
|
|
1620
|
+
|
|
1621
|
+
# Sort matching artifacts by creation date in ascending order.
|
|
1622
|
+
# The last element in the list will be the one created most recently.
|
|
1623
|
+
# In case the `created` field does not exist in the artifact, that artifact will appear first in the sorted list
|
|
1624
|
+
matching_artifacts.sort(
|
|
1625
|
+
key=lambda artifact: artifact["metadata"].get("created", datetime.min)
|
|
1626
|
+
)
|
|
1627
|
+
|
|
1628
|
+
# Filter out artifacts with 'latest' tag
|
|
1629
|
+
non_latest_artifacts = [
|
|
1630
|
+
artifact
|
|
1631
|
+
for artifact in matching_artifacts
|
|
1632
|
+
if artifact["metadata"].get("tag") != "latest"
|
|
1633
|
+
]
|
|
1634
|
+
|
|
1635
|
+
# Return the last non-'latest' artifact if available, otherwise return the last artifact
|
|
1636
|
+
# In the case of only one tag, `status.artifacts` includes [v1, latest]. In that case, we want to return v1.
|
|
1637
|
+
# In the case of multiple tags, `status.artifacts` includes [v1, latest, v2, v3].
|
|
1638
|
+
# In that case, we need to return the last one (v3).
|
|
1639
|
+
return (non_latest_artifacts or matching_artifacts)[-1]
|
|
1640
|
+
|
|
1641
|
+
def _process_artifacts(self, artifacts):
|
|
1642
|
+
artifacts_by_key = {}
|
|
1643
|
+
|
|
1644
|
+
# Organize artifacts by key
|
|
1645
|
+
for artifact in artifacts:
|
|
1646
|
+
key = artifact["metadata"]["key"]
|
|
1647
|
+
if key not in artifacts_by_key:
|
|
1648
|
+
artifacts_by_key[key] = []
|
|
1649
|
+
artifacts_by_key[key].append(artifact)
|
|
1650
|
+
|
|
1651
|
+
outputs = {}
|
|
1652
|
+
for key, artifacts in artifacts_by_key.items():
|
|
1653
|
+
# Sort matching artifacts by creation date in ascending order.
|
|
1654
|
+
# The last element in the list will be the one created most recently.
|
|
1655
|
+
# In case the `created` field does not exist in the artifactthat artifact will appear
|
|
1656
|
+
# first in the sorted list
|
|
1657
|
+
artifacts.sort(
|
|
1658
|
+
key=lambda artifact: artifact["metadata"].get("created", datetime.min)
|
|
1659
|
+
)
|
|
1660
|
+
|
|
1661
|
+
# Filter out artifacts with 'latest' tag
|
|
1662
|
+
non_latest_artifacts = [
|
|
1663
|
+
artifact
|
|
1664
|
+
for artifact in artifacts
|
|
1665
|
+
if artifact["metadata"].get("tag") != "latest"
|
|
1666
|
+
]
|
|
1667
|
+
|
|
1668
|
+
# Save the last non-'latest' artifact if available, otherwise save the last artifact
|
|
1669
|
+
# In the case of only one tag, `artifacts` includes [v1, latest], in that case, we want to save v1.
|
|
1670
|
+
# In the case of multiple tags, `artifacts` includes [v1, latest, v2, v3].
|
|
1671
|
+
# In that case, we need to save the last one (v3).
|
|
1672
|
+
artifact_to_save = (non_latest_artifacts or artifacts)[-1]
|
|
1673
|
+
outputs[key] = get_artifact_target(artifact_to_save, self.metadata.project)
|
|
1674
|
+
|
|
1675
|
+
return outputs
|
|
1556
1676
|
|
|
1557
1677
|
def uid(self):
|
|
1558
1678
|
"""run unique id"""
|
|
@@ -56,7 +56,7 @@ class MonitoringApplicationContext(MLClientCtx):
|
|
|
56
56
|
def __init__(self, **kwargs):
|
|
57
57
|
super().__init__(**kwargs)
|
|
58
58
|
|
|
59
|
-
def
|
|
59
|
+
def _enrich_data(self):
|
|
60
60
|
self.application_name: typing.Optional[str] = None
|
|
61
61
|
self.start_infer_time: typing.Optional[pd.Timestamp] = None
|
|
62
62
|
self.end_infer_time: typing.Optional[pd.Timestamp] = None
|
|
@@ -87,39 +87,37 @@ class MonitoringApplicationContext(MLClientCtx):
|
|
|
87
87
|
"""
|
|
88
88
|
|
|
89
89
|
if not context:
|
|
90
|
-
|
|
90
|
+
ctx = (
|
|
91
91
|
super().from_dict(
|
|
92
92
|
attrs=attrs.get(mm_constants.ApplicationEvent.MLRUN_CONTEXT, {}),
|
|
93
93
|
**kwargs,
|
|
94
94
|
),
|
|
95
95
|
)
|
|
96
96
|
else:
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
ctx = context
|
|
98
|
+
cls._enrich_data(ctx)
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
ctx.start_infer_time = pd.Timestamp(
|
|
101
101
|
attrs.get(mm_constants.ApplicationEvent.START_INFER_TIME)
|
|
102
102
|
)
|
|
103
|
-
|
|
103
|
+
ctx.end_infer_time = pd.Timestamp(
|
|
104
104
|
attrs.get(mm_constants.ApplicationEvent.END_INFER_TIME)
|
|
105
105
|
)
|
|
106
|
-
|
|
106
|
+
ctx.latest_request = pd.Timestamp(
|
|
107
107
|
attrs.get(mm_constants.ApplicationEvent.LAST_REQUEST)
|
|
108
108
|
)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
)
|
|
112
|
-
self._feature_stats = json.loads(
|
|
109
|
+
ctx.application_name = attrs.get(mm_constants.ApplicationEvent.APPLICATION_NAME)
|
|
110
|
+
ctx._feature_stats = json.loads(
|
|
113
111
|
attrs.get(mm_constants.ApplicationEvent.FEATURE_STATS, "{}")
|
|
114
112
|
)
|
|
115
|
-
|
|
113
|
+
ctx._sample_df_stats = json.loads(
|
|
116
114
|
attrs.get(mm_constants.ApplicationEvent.CURRENT_STATS, "{}")
|
|
117
115
|
)
|
|
118
116
|
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
ctx.endpoint_id = attrs.get(mm_constants.ApplicationEvent.ENDPOINT_ID)
|
|
118
|
+
ctx._model_endpoint = model_endpoint_dict.get(ctx.endpoint_id)
|
|
121
119
|
|
|
122
|
-
return
|
|
120
|
+
return ctx
|
|
123
121
|
|
|
124
122
|
@property
|
|
125
123
|
def sample_df(self) -> pd.DataFrame:
|
mlrun/projects/project.py
CHANGED
|
@@ -725,7 +725,7 @@ def _project_instance_from_struct(struct, name, allow_cross_project):
|
|
|
725
725
|
# TODO: Remove this warning in version 1.9.0 and also fix cli to support allow_cross_project
|
|
726
726
|
warnings.warn(
|
|
727
727
|
f"Project {name=} is different than specified on the context's project yaml. "
|
|
728
|
-
"This behavior is deprecated and will not be supported
|
|
728
|
+
"This behavior is deprecated and will not be supported from version 1.9.0."
|
|
729
729
|
)
|
|
730
730
|
logger.warn(error_message)
|
|
731
731
|
elif allow_cross_project:
|
|
@@ -4063,6 +4063,12 @@ class MlrunProject(ModelObj):
|
|
|
4063
4063
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
4064
4064
|
if alert_name is None:
|
|
4065
4065
|
alert_name = alert_data.name
|
|
4066
|
+
if alert_data.project is not None and alert_data.project != self.metadata.name:
|
|
4067
|
+
logger.warn(
|
|
4068
|
+
"Project in alert does not match project in operation",
|
|
4069
|
+
project=alert_data.project,
|
|
4070
|
+
)
|
|
4071
|
+
alert_data.project = self.metadata.name
|
|
4066
4072
|
return db.store_alert_config(alert_name, alert_data, project=self.metadata.name)
|
|
4067
4073
|
|
|
4068
4074
|
def get_alert_config(self, alert_name: str) -> AlertConfig:
|
mlrun/run.py
CHANGED
|
@@ -21,6 +21,7 @@ import tempfile
|
|
|
21
21
|
import time
|
|
22
22
|
import typing
|
|
23
23
|
import uuid
|
|
24
|
+
import warnings
|
|
24
25
|
from base64 import b64decode
|
|
25
26
|
from copy import deepcopy
|
|
26
27
|
from os import environ, makedirs, path
|
|
@@ -196,18 +197,19 @@ def load_func_code(command="", workdir=None, secrets=None, name="name"):
|
|
|
196
197
|
def get_or_create_ctx(
|
|
197
198
|
name: str,
|
|
198
199
|
event=None,
|
|
199
|
-
spec=None,
|
|
200
|
+
spec: Optional[dict] = None,
|
|
200
201
|
with_env: bool = True,
|
|
201
202
|
rundb: str = "",
|
|
202
203
|
project: str = "",
|
|
203
|
-
upload_artifacts=False,
|
|
204
|
+
upload_artifacts: bool = False,
|
|
204
205
|
labels: Optional[dict] = None,
|
|
205
206
|
) -> MLClientCtx:
|
|
206
|
-
"""
|
|
207
|
+
"""
|
|
208
|
+
Called from within the user program to obtain a run context.
|
|
207
209
|
|
|
208
|
-
|
|
210
|
+
The run context is an interface for receiving parameters, data and logging
|
|
209
211
|
run results, the run context is read from the event, spec, or environment
|
|
210
|
-
(in that order), user can also work without a context (local defaults mode)
|
|
212
|
+
(in that order), user can also work without a context (local defaults mode).
|
|
211
213
|
|
|
212
214
|
all results are automatically stored in the "rundb" or artifact store,
|
|
213
215
|
the path to the rundb can be specified in the call or obtained from env.
|
|
@@ -220,7 +222,7 @@ def get_or_create_ctx(
|
|
|
220
222
|
:param project: project to initiate the context in (by default `mlrun.mlconf.default_project`)
|
|
221
223
|
:param upload_artifacts: when using local context (not as part of a job/run), upload artifacts to the
|
|
222
224
|
system default artifact path location
|
|
223
|
-
:param labels:
|
|
225
|
+
:param labels: (deprecated - use spec instead) dict of the context labels.
|
|
224
226
|
:return: execution context
|
|
225
227
|
|
|
226
228
|
Examples::
|
|
@@ -253,6 +255,20 @@ def get_or_create_ctx(
|
|
|
253
255
|
context.log_artifact("results.html", body=b"<b> Some HTML <b>", viewer="web-app")
|
|
254
256
|
|
|
255
257
|
"""
|
|
258
|
+
if labels:
|
|
259
|
+
warnings.warn(
|
|
260
|
+
"The `labels` argument is deprecated and will be removed in 1.9.0. "
|
|
261
|
+
"Please use `spec` instead, e.g.:\n"
|
|
262
|
+
"spec={'metadata': {'labels': {'key': 'value'}}}",
|
|
263
|
+
FutureWarning,
|
|
264
|
+
)
|
|
265
|
+
if spec is None:
|
|
266
|
+
spec = {}
|
|
267
|
+
if "metadata" not in spec:
|
|
268
|
+
spec["metadata"] = {}
|
|
269
|
+
if "labels" not in spec["metadata"]:
|
|
270
|
+
spec["metadata"]["labels"] = {}
|
|
271
|
+
spec["metadata"]["labels"].update(labels)
|
|
256
272
|
|
|
257
273
|
if global_context.get() and not spec and not event:
|
|
258
274
|
return global_context.get()
|
|
@@ -306,9 +322,6 @@ def get_or_create_ctx(
|
|
|
306
322
|
ctx = MLClientCtx.from_dict(
|
|
307
323
|
newspec, rundb=out, autocommit=autocommit, tmp=tmp, host=socket.gethostname()
|
|
308
324
|
)
|
|
309
|
-
labels = labels or {}
|
|
310
|
-
for key, val in labels.items():
|
|
311
|
-
ctx.set_label(key=key, value=val)
|
|
312
325
|
global_context.set(ctx)
|
|
313
326
|
return ctx
|
|
314
327
|
|
|
@@ -326,6 +326,11 @@ class APIGatewaySpec(ModelObj):
|
|
|
326
326
|
return function_names
|
|
327
327
|
|
|
328
328
|
|
|
329
|
+
class APIGatewayStatus(ModelObj):
|
|
330
|
+
def __init__(self, state: Optional[schemas.APIGatewayState] = None):
|
|
331
|
+
self.state = state or schemas.APIGatewayState.none
|
|
332
|
+
|
|
333
|
+
|
|
329
334
|
class APIGateway(ModelObj):
|
|
330
335
|
_dict_fields = [
|
|
331
336
|
"metadata",
|
|
@@ -338,16 +343,18 @@ class APIGateway(ModelObj):
|
|
|
338
343
|
self,
|
|
339
344
|
metadata: APIGatewayMetadata,
|
|
340
345
|
spec: APIGatewaySpec,
|
|
346
|
+
status: Optional[APIGatewayStatus] = None,
|
|
341
347
|
):
|
|
342
348
|
"""
|
|
343
349
|
Initialize the APIGateway instance.
|
|
344
350
|
|
|
345
351
|
:param metadata: (APIGatewayMetadata) The metadata of the API gateway.
|
|
346
352
|
:param spec: (APIGatewaySpec) The spec of the API gateway.
|
|
353
|
+
:param status: (APIGatewayStatus) The status of the API gateway.
|
|
347
354
|
"""
|
|
348
355
|
self.metadata = metadata
|
|
349
356
|
self.spec = spec
|
|
350
|
-
self.
|
|
357
|
+
self.status = status
|
|
351
358
|
|
|
352
359
|
@property
|
|
353
360
|
def metadata(self) -> APIGatewayMetadata:
|
|
@@ -365,6 +372,14 @@ class APIGateway(ModelObj):
|
|
|
365
372
|
def spec(self, spec):
|
|
366
373
|
self._spec = self._verify_dict(spec, "spec", APIGatewaySpec)
|
|
367
374
|
|
|
375
|
+
@property
|
|
376
|
+
def status(self) -> APIGatewayStatus:
|
|
377
|
+
return self._status
|
|
378
|
+
|
|
379
|
+
@status.setter
|
|
380
|
+
def status(self, status):
|
|
381
|
+
self._status = self._verify_dict(status, "status", APIGatewayStatus)
|
|
382
|
+
|
|
368
383
|
def invoke(
|
|
369
384
|
self,
|
|
370
385
|
method="POST",
|
|
@@ -394,7 +409,7 @@ class APIGateway(ModelObj):
|
|
|
394
409
|
)
|
|
395
410
|
if not self.is_ready():
|
|
396
411
|
raise mlrun.errors.MLRunPreconditionFailedError(
|
|
397
|
-
f"API gateway is not ready. " f"Current state: {self.state}"
|
|
412
|
+
f"API gateway is not ready. " f"Current state: {self.status.state}"
|
|
398
413
|
)
|
|
399
414
|
|
|
400
415
|
auth = None
|
|
@@ -459,10 +474,10 @@ class APIGateway(ModelObj):
|
|
|
459
474
|
)
|
|
460
475
|
|
|
461
476
|
def is_ready(self):
|
|
462
|
-
if self.state is not schemas.api_gateway.APIGatewayState.ready:
|
|
477
|
+
if self.status.state is not schemas.api_gateway.APIGatewayState.ready:
|
|
463
478
|
# try to sync the state
|
|
464
479
|
self.sync()
|
|
465
|
-
return self.state == schemas.api_gateway.APIGatewayState.ready
|
|
480
|
+
return self.status.state == schemas.api_gateway.APIGatewayState.ready
|
|
466
481
|
|
|
467
482
|
def sync(self):
|
|
468
483
|
"""
|
|
@@ -479,7 +494,7 @@ class APIGateway(ModelObj):
|
|
|
479
494
|
self.spec.functions = synced_gateway.spec.functions
|
|
480
495
|
self.spec.canary = synced_gateway.spec.canary
|
|
481
496
|
self.spec.description = synced_gateway.spec.description
|
|
482
|
-
self.state = synced_gateway.state
|
|
497
|
+
self.status.state = synced_gateway.status.state
|
|
483
498
|
|
|
484
499
|
def with_basic_auth(self, username: str, password: str):
|
|
485
500
|
"""
|
|
@@ -580,8 +595,8 @@ class APIGateway(ModelObj):
|
|
|
580
595
|
functions=functions,
|
|
581
596
|
canary=canary,
|
|
582
597
|
),
|
|
598
|
+
status=APIGatewayStatus(state=state),
|
|
583
599
|
)
|
|
584
|
-
new_api_gateway.state = state
|
|
585
600
|
return new_api_gateway
|
|
586
601
|
|
|
587
602
|
def to_scheme(self) -> schemas.APIGateway:
|
|
@@ -625,6 +640,7 @@ class APIGateway(ModelObj):
|
|
|
625
640
|
),
|
|
626
641
|
upstreams=upstreams,
|
|
627
642
|
),
|
|
643
|
+
status=schemas.APIGatewayStatus(state=self.status.state),
|
|
628
644
|
)
|
|
629
645
|
api_gateway.spec.authentication = self.spec.authentication.to_scheme()
|
|
630
646
|
return api_gateway
|
|
@@ -149,6 +149,7 @@ class ApplicationStatus(NuclioStatus):
|
|
|
149
149
|
build_pod=None,
|
|
150
150
|
container_image=None,
|
|
151
151
|
application_image=None,
|
|
152
|
+
application_source=None,
|
|
152
153
|
sidecar_name=None,
|
|
153
154
|
api_gateway_name=None,
|
|
154
155
|
api_gateway=None,
|
|
@@ -164,6 +165,7 @@ class ApplicationStatus(NuclioStatus):
|
|
|
164
165
|
container_image=container_image,
|
|
165
166
|
)
|
|
166
167
|
self.application_image = application_image or None
|
|
168
|
+
self.application_source = application_source or None
|
|
167
169
|
self.sidecar_name = sidecar_name or None
|
|
168
170
|
self.api_gateway_name = api_gateway_name or None
|
|
169
171
|
self.api_gateway = api_gateway or None
|
|
@@ -317,15 +319,25 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
317
319
|
)
|
|
318
320
|
|
|
319
321
|
super().deploy(
|
|
320
|
-
project,
|
|
321
|
-
tag,
|
|
322
|
-
verbose,
|
|
323
|
-
auth_info,
|
|
324
|
-
builder_env,
|
|
322
|
+
project=project,
|
|
323
|
+
tag=tag,
|
|
324
|
+
verbose=verbose,
|
|
325
|
+
auth_info=auth_info,
|
|
326
|
+
builder_env=builder_env,
|
|
327
|
+
)
|
|
328
|
+
logger.info(
|
|
329
|
+
"Successfully deployed function, creating API gateway",
|
|
330
|
+
api_gateway_name=self.status.api_gateway_name,
|
|
331
|
+
authentication_mode=authentication_mode,
|
|
325
332
|
)
|
|
326
333
|
|
|
334
|
+
# Restore the source in case it was removed to make nuclio not consider it when building
|
|
335
|
+
if not self.spec.build.source and self.status.application_source:
|
|
336
|
+
self.spec.build.source = self.status.application_source
|
|
337
|
+
self.save(versioned=False)
|
|
338
|
+
|
|
327
339
|
ports = self.spec.internal_application_port if direct_port_access else []
|
|
328
|
-
self.create_api_gateway(
|
|
340
|
+
return self.create_api_gateway(
|
|
329
341
|
name=self.status.api_gateway_name,
|
|
330
342
|
ports=ports,
|
|
331
343
|
authentication_mode=authentication_mode,
|
|
@@ -354,6 +366,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
354
366
|
target_dir=target_dir,
|
|
355
367
|
)
|
|
356
368
|
|
|
369
|
+
def from_image(self, image):
|
|
370
|
+
super().from_image(image)
|
|
371
|
+
# nuclio implementation detail - when providing the image and emptying out the source code and build source,
|
|
372
|
+
# nuclio skips rebuilding the image and simply takes the prebuilt image
|
|
373
|
+
self.spec.build.functionSourceCode = ""
|
|
374
|
+
self.status.application_source = self.spec.build.source
|
|
375
|
+
self.spec.build.source = ""
|
|
376
|
+
|
|
357
377
|
@classmethod
|
|
358
378
|
def get_filename_and_handler(cls) -> (str, str):
|
|
359
379
|
reverse_proxy_file_path = pathlib.Path(__file__).parent / "reverse_proxy.go"
|
|
@@ -409,6 +429,9 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
409
429
|
self.status.api_gateway.wait_for_readiness()
|
|
410
430
|
self.url = self.status.api_gateway.invoke_url
|
|
411
431
|
|
|
432
|
+
logger.info("Successfully created API gateway", url=self.url)
|
|
433
|
+
return self.url
|
|
434
|
+
|
|
412
435
|
def invoke(
|
|
413
436
|
self,
|
|
414
437
|
path: str,
|
|
@@ -448,6 +471,14 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
448
471
|
**http_client_kwargs,
|
|
449
472
|
)
|
|
450
473
|
|
|
474
|
+
def _run(self, runobj: "mlrun.RunObject", execution):
|
|
475
|
+
raise mlrun.runtimes.RunError(
|
|
476
|
+
"Application runtime .run() is not yet supported. Use .invoke() instead."
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
def _enrich_command_from_status(self):
|
|
480
|
+
pass
|
|
481
|
+
|
|
451
482
|
def _build_application_image(
|
|
452
483
|
self,
|
|
453
484
|
builder_env: dict = None,
|
|
@@ -506,9 +537,6 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
506
537
|
|
|
507
538
|
if self.status.container_image:
|
|
508
539
|
self.from_image(self.status.container_image)
|
|
509
|
-
# nuclio implementation detail - when providing the image and emptying out the source code,
|
|
510
|
-
# nuclio skips rebuilding the image and simply takes the prebuilt image
|
|
511
|
-
self.spec.build.functionSourceCode = ""
|
|
512
540
|
|
|
513
541
|
self.status.sidecar_name = f"{self.metadata.name}-sidecar"
|
|
514
542
|
self.with_sidecar(
|
|
@@ -568,6 +568,9 @@ class RemoteRuntime(KubeResource):
|
|
|
568
568
|
# this also means that the function object will be updated with the function status
|
|
569
569
|
self._wait_for_function_deployment(db, verbose=verbose)
|
|
570
570
|
|
|
571
|
+
return self._enrich_command_from_status()
|
|
572
|
+
|
|
573
|
+
def _enrich_command_from_status(self):
|
|
571
574
|
# NOTE: on older mlrun versions & nuclio versions, function are exposed via NodePort
|
|
572
575
|
# now, functions can be not exposed (using service type ClusterIP) and hence
|
|
573
576
|
# for BC we first try to populate the external invocation url, and then
|
mlrun/runtimes/nuclio/serving.py
CHANGED
|
@@ -325,12 +325,12 @@ class ServingRuntime(RemoteRuntime):
|
|
|
325
325
|
:param enable_tracking: Enabled/Disable model-monitoring tracking.
|
|
326
326
|
Default True (tracking enabled).
|
|
327
327
|
|
|
328
|
-
|
|
328
|
+
Example::
|
|
329
329
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
330
|
+
# initialize a new serving function
|
|
331
|
+
serving_fn = mlrun.import_function("hub://v2-model-server", new_name="serving")
|
|
332
|
+
# apply model monitoring
|
|
333
|
+
serving_fn.set_tracking()
|
|
334
334
|
|
|
335
335
|
"""
|
|
336
336
|
# Applying model monitoring configurations
|
mlrun/serving/server.py
CHANGED
|
@@ -321,9 +321,9 @@ def v2_serving_init(context, namespace=None):
|
|
|
321
321
|
server.http_trigger = getattr(context.trigger, "kind", "http") == "http"
|
|
322
322
|
context.logger.info_with(
|
|
323
323
|
"Setting current function",
|
|
324
|
-
|
|
324
|
+
current_function=os.getenv("SERVING_CURRENT_FUNCTION", ""),
|
|
325
325
|
)
|
|
326
|
-
server.set_current_function(os.
|
|
326
|
+
server.set_current_function(os.getenv("SERVING_CURRENT_FUNCTION", ""))
|
|
327
327
|
context.logger.info_with(
|
|
328
328
|
"Initializing states", namespace=namespace or get_caller_globals()
|
|
329
329
|
)
|
|
@@ -344,9 +344,14 @@ def v2_serving_init(context, namespace=None):
|
|
|
344
344
|
if server.verbose:
|
|
345
345
|
context.logger.info(server.to_yaml())
|
|
346
346
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
347
|
+
_set_callbacks(server, context)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _set_callbacks(server, context):
|
|
351
|
+
if not server.graph.supports_termination() or not hasattr(context, "platform"):
|
|
352
|
+
return
|
|
353
|
+
|
|
354
|
+
if hasattr(context.platform, "set_termination_callback"):
|
|
350
355
|
context.logger.info(
|
|
351
356
|
"Setting termination callback to terminate graph on worker shutdown"
|
|
352
357
|
)
|
|
@@ -358,7 +363,7 @@ def v2_serving_init(context, namespace=None):
|
|
|
358
363
|
|
|
359
364
|
context.platform.set_termination_callback(termination_callback)
|
|
360
365
|
|
|
361
|
-
if hasattr(context
|
|
366
|
+
if hasattr(context.platform, "set_drain_callback"):
|
|
362
367
|
context.logger.info(
|
|
363
368
|
"Setting drain callback to terminate and restart the graph on a drain event (such as rebalancing)"
|
|
364
369
|
)
|
|
@@ -417,7 +422,7 @@ def create_graph_server(
|
|
|
417
422
|
parameters = parameters or {}
|
|
418
423
|
server = GraphServer(graph, parameters, load_mode, verbose=verbose, **kwargs)
|
|
419
424
|
server.set_current_function(
|
|
420
|
-
current_function or os.
|
|
425
|
+
current_function or os.getenv("SERVING_CURRENT_FUNCTION", "")
|
|
421
426
|
)
|
|
422
427
|
return server
|
|
423
428
|
|
mlrun/serving/states.py
CHANGED
|
@@ -27,6 +27,8 @@ from copy import copy, deepcopy
|
|
|
27
27
|
from inspect import getfullargspec, signature
|
|
28
28
|
from typing import Any, Union
|
|
29
29
|
|
|
30
|
+
import storey.utils
|
|
31
|
+
|
|
30
32
|
import mlrun
|
|
31
33
|
|
|
32
34
|
from ..config import config
|
|
@@ -386,6 +388,9 @@ class BaseStep(ModelObj):
|
|
|
386
388
|
"""
|
|
387
389
|
raise NotImplementedError("set_flow() can only be called on a FlowStep")
|
|
388
390
|
|
|
391
|
+
def supports_termination(self):
|
|
392
|
+
return False
|
|
393
|
+
|
|
389
394
|
|
|
390
395
|
class TaskStep(BaseStep):
|
|
391
396
|
"""task execution step, runs a class or handler"""
|
|
@@ -867,7 +872,9 @@ class QueueStep(BaseStep):
|
|
|
867
872
|
return event
|
|
868
873
|
|
|
869
874
|
if self._stream:
|
|
870
|
-
self.
|
|
875
|
+
if self.options.get("full_event", True):
|
|
876
|
+
data = storey.utils.wrap_event_for_serialization(event, data)
|
|
877
|
+
self._stream.push(data)
|
|
871
878
|
event.terminated = True
|
|
872
879
|
event.body = None
|
|
873
880
|
return event
|
|
@@ -1273,6 +1280,8 @@ class FlowStep(BaseStep):
|
|
|
1273
1280
|
event.body = {"id": event.id}
|
|
1274
1281
|
return event
|
|
1275
1282
|
|
|
1283
|
+
event = storey.utils.unpack_event_if_wrapped(event)
|
|
1284
|
+
|
|
1276
1285
|
if len(self._start_steps) == 0:
|
|
1277
1286
|
return event
|
|
1278
1287
|
next_obj = self._start_steps[0]
|
|
@@ -1380,6 +1389,9 @@ class FlowStep(BaseStep):
|
|
|
1380
1389
|
|
|
1381
1390
|
return step
|
|
1382
1391
|
|
|
1392
|
+
def supports_termination(self):
|
|
1393
|
+
return self.engine == "async"
|
|
1394
|
+
|
|
1383
1395
|
|
|
1384
1396
|
class RootFlowStep(FlowStep):
|
|
1385
1397
|
"""root flow step"""
|
mlrun/utils/db.py
CHANGED
|
@@ -28,6 +28,9 @@ class BaseModel:
|
|
|
28
28
|
columns = [column.key for column in mapper.columns if column.key not in exclude]
|
|
29
29
|
|
|
30
30
|
def get_key_value(c):
|
|
31
|
+
# all (never say never) DB classes have "object" defined as "full_object"
|
|
32
|
+
if c == "object":
|
|
33
|
+
c = "full_object"
|
|
31
34
|
if isinstance(getattr(self, c), datetime):
|
|
32
35
|
return c, getattr(self, c).isoformat()
|
|
33
36
|
return c, getattr(self, c)
|
mlrun/utils/helpers.py
CHANGED
|
@@ -111,13 +111,11 @@ def get_artifact_target(item: dict, project=None):
|
|
|
111
111
|
tree = item["metadata"].get("tree")
|
|
112
112
|
tag = item["metadata"].get("tag")
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
if kind in ["dataset", "model", "artifact"] and db_key:
|
|
114
|
+
if item.get("kind") in {"dataset", "model", "artifact"} and db_key:
|
|
116
115
|
target = f"{DB_SCHEMA}://{StorePrefix.Artifact}/{project_str}/{db_key}"
|
|
117
|
-
if tag:
|
|
118
|
-
target = f"{target}:{tag}"
|
|
116
|
+
target += f":{tag}" if tag else ":latest"
|
|
119
117
|
if tree:
|
|
120
|
-
target
|
|
118
|
+
target += f"@{tree}"
|
|
121
119
|
return target
|
|
122
120
|
|
|
123
121
|
return item["spec"].get("target_path")
|
|
@@ -60,7 +60,14 @@ class WebhookNotification(NotificationBase):
|
|
|
60
60
|
request_body["runs"] = runs
|
|
61
61
|
|
|
62
62
|
if alert:
|
|
63
|
-
request_body["
|
|
63
|
+
request_body["name"] = alert.name
|
|
64
|
+
request_body["project"] = alert.project
|
|
65
|
+
request_body["severity"] = alert.severity
|
|
66
|
+
if alert.summary:
|
|
67
|
+
request_body["summary"] = mlrun.utils.helpers.format_alert_summary(
|
|
68
|
+
alert, event_data
|
|
69
|
+
)
|
|
70
|
+
|
|
64
71
|
if event_data:
|
|
65
72
|
request_body["value"] = event_data.value_dict
|
|
66
73
|
request_body["id"] = event_data.entity.ids[0]
|
mlrun/utils/version/version.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mlrun
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.0rc34
|
|
4
4
|
Summary: Tracking and config of machine learning runs
|
|
5
5
|
Home-page: https://github.com/mlrun/mlrun
|
|
6
6
|
Author: Yaron Haviv
|
|
@@ -43,7 +43,7 @@ Requires-Dist: semver ~=3.0
|
|
|
43
43
|
Requires-Dist: dependency-injector ~=4.41
|
|
44
44
|
Requires-Dist: fsspec <2024.4,>=2023.9.2
|
|
45
45
|
Requires-Dist: v3iofs ~=0.1.17
|
|
46
|
-
Requires-Dist: storey ~=1.7.
|
|
46
|
+
Requires-Dist: storey ~=1.7.23
|
|
47
47
|
Requires-Dist: inflection ~=0.5.0
|
|
48
48
|
Requires-Dist: python-dotenv ~=0.17.0
|
|
49
49
|
Requires-Dist: setuptools ~=71.0
|
|
@@ -2,13 +2,13 @@ mlrun/__init__.py,sha256=y08M1JcKXy5-9_5WaI9fn5aV5BxIQ5QkbduJK0OxWbA,7470
|
|
|
2
2
|
mlrun/__main__.py,sha256=iAifncsrQQx6ozXXmz7GH1OiNl8PA7KS3TnwlxnHGeo,45890
|
|
3
3
|
mlrun/config.py,sha256=5QrlkkUosFwziSFTtApeB2obpS-gW126lB8y5za7rhM,66103
|
|
4
4
|
mlrun/errors.py,sha256=VpC_imeSz2twRMZZb7u90Zj29z6aO-tCxUHD3ZA_Axw,7465
|
|
5
|
-
mlrun/execution.py,sha256=
|
|
5
|
+
mlrun/execution.py,sha256=Gv7mzzaf5y8fIEF0VVu8dSJYQp2uCezXDUiE60cGxWU,41970
|
|
6
6
|
mlrun/features.py,sha256=m17K_3l9Jktwb9dOwlHLTAPTlemsWrRF7dJhXUX0iJU,15429
|
|
7
7
|
mlrun/k8s_utils.py,sha256=WdUajadvAhTR7sAMQdwFqKeJMimuTyqm02VdwK1A4xU,7023
|
|
8
8
|
mlrun/lists.py,sha256=3PqBdcajdwhTe1XuFsAaHTuFVM2kjwepf31qqE82apg,8384
|
|
9
|
-
mlrun/model.py,sha256=
|
|
9
|
+
mlrun/model.py,sha256=QA-Wi3Po5OB9Y7aR-vEFjLT4dkU-597QM4qAEmv-UvI,79092
|
|
10
10
|
mlrun/render.py,sha256=n8SeY3ogVrsV02-7-H0lt1RmpkxGpbI-11RQx61Vq9E,13267
|
|
11
|
-
mlrun/run.py,sha256=
|
|
11
|
+
mlrun/run.py,sha256=uIHNQ-OSrviNwkZ54cQy27SIO62WIaFHdwz08K5NwK0,43410
|
|
12
12
|
mlrun/secrets.py,sha256=ibtCK79u7JVBZF6F0SP1-xXXF5MyrLEUs_TCWiJAnlc,7798
|
|
13
13
|
mlrun/alerts/__init__.py,sha256=0gtG1BG0DXxFrXegIkjbM1XEN4sP9ODo0ucXrNld1hU,601
|
|
14
14
|
mlrun/alerts/alert.py,sha256=JJfMFF-o0j8oTAIkyXAQG0YbU-kZlIDl0A8ILQi8vfA,6510
|
|
@@ -36,7 +36,7 @@ mlrun/common/formatters/run.py,sha256=eEBy1NEwGT9b98TWS2OetEbDnDrnHBIBVMrlXsxveo
|
|
|
36
36
|
mlrun/common/model_monitoring/__init__.py,sha256=x0EMEvxVjHsm858J1t6IEA9dtKTdFpJ9sKhss10ld8A,721
|
|
37
37
|
mlrun/common/model_monitoring/helpers.py,sha256=1CpxIDQPumFnpUB1eqcvCpLlyPFVeW2sL6prM-N5A1A,4405
|
|
38
38
|
mlrun/common/runtimes/constants.py,sha256=Rl0Sd8n_L7Imo-uF1LL9CJ5Szi0W1gUm36yrF8PXfSc,10989
|
|
39
|
-
mlrun/common/schemas/__init__.py,sha256=
|
|
39
|
+
mlrun/common/schemas/__init__.py,sha256=txPKk2yW6vpyIc6ri48JD0vBwFpZfSdW3CFjVp7XDl0,5252
|
|
40
40
|
mlrun/common/schemas/alert.py,sha256=Gb2eSjZLTkm-lGy_rQ_D4crEjCTdyf1N90bnIJmQ1H8,6574
|
|
41
41
|
mlrun/common/schemas/api_gateway.py,sha256=_1JyCFNP89dAcFxQE-C8Tj8o6TvPgV13aBzU3wRcM4g,6908
|
|
42
42
|
mlrun/common/schemas/artifact.py,sha256=V3ngobnzI1v2eoOroWBEedjAZu0ntCSIQ-LzsOK1Z9k,3570
|
|
@@ -44,13 +44,13 @@ mlrun/common/schemas/auth.py,sha256=5c4WSn3KdX1v04ttSQblkF_gyjdjuJSHG7BTCx4_LWM,
|
|
|
44
44
|
mlrun/common/schemas/background_task.py,sha256=2qZxib2qrF_nPZj0ncitCG-2jxz2hg1qj0hFc8eswWQ,1707
|
|
45
45
|
mlrun/common/schemas/client_spec.py,sha256=xQ_9S5i5q7vJmkp2_3IYD0FSYnWoAr1k-W9MU2ClgEU,2955
|
|
46
46
|
mlrun/common/schemas/clusterization_spec.py,sha256=aeaFJZms7r7h2HDv6ML_GDAT6gboW-PxBbc3GKPalGk,888
|
|
47
|
-
mlrun/common/schemas/common.py,sha256=
|
|
47
|
+
mlrun/common/schemas/common.py,sha256=nxtZDzs92-p0wygIhQ_SEU3J9QsJRKE4RsY18olLXyo,1613
|
|
48
48
|
mlrun/common/schemas/constants.py,sha256=sTNCimttd7ytSZ3jxbftItw_HDGxPwY96Ub86OvcT9w,6660
|
|
49
49
|
mlrun/common/schemas/datastore_profile.py,sha256=hJ8q54A8VZKsnOvSIjcllj4MZ1bBhb_EmBgsqpwSF_Y,750
|
|
50
50
|
mlrun/common/schemas/events.py,sha256=ROHJLo_fqYjc96pek7yhAUPpPRIuAR76lwxvNz8LIr8,1026
|
|
51
51
|
mlrun/common/schemas/feature_store.py,sha256=T0yKYcv6cb3ZwgY5Jh9kWp94zLv2ImxAQUy6x68Imd0,4776
|
|
52
52
|
mlrun/common/schemas/frontend_spec.py,sha256=CNPq3YV0U1jCbCMbb84_Oid2Snow5EXYt1F5wsuhgD8,2454
|
|
53
|
-
mlrun/common/schemas/function.py,sha256=
|
|
53
|
+
mlrun/common/schemas/function.py,sha256=fZZBZroj6Ok0giRn2pYSzR40bx037v9pIWvSagPA2fE,4820
|
|
54
54
|
mlrun/common/schemas/http.py,sha256=1PtYFhF6sqLSBRcuPMtYcUGmroBhaleqLmYidSdL9LM,705
|
|
55
55
|
mlrun/common/schemas/hub.py,sha256=cuv_vpkO27XNCZzfytnUyi0k0ZA4wf_QRn5B0ZPoK-Y,4116
|
|
56
56
|
mlrun/common/schemas/k8s.py,sha256=nmMnhgjVMLem5jyumoG2eQKioGK9eUVhQnOSb3hG7yw,1395
|
|
@@ -59,7 +59,7 @@ mlrun/common/schemas/notification.py,sha256=Ge7eWNGf_XUFkjOnUkyUOubdEbmXh9z_OSGc
|
|
|
59
59
|
mlrun/common/schemas/object.py,sha256=VleJSUmDJMl92knLgaDE8SWCi3ky0UaHcwcwOIapPQ8,1980
|
|
60
60
|
mlrun/common/schemas/pagination.py,sha256=q7nk6bipkDiE7HExIVqhy5ANl-zv0x8QC9Kg6AkLtDA,887
|
|
61
61
|
mlrun/common/schemas/pipeline.py,sha256=MhH07_fAQXNAnmf5j6oXZp8qh9cxGcZlReMdt-ZJf40,1429
|
|
62
|
-
mlrun/common/schemas/project.py,sha256=
|
|
62
|
+
mlrun/common/schemas/project.py,sha256=WSkiuZx3IBfYmur9DE3qSZGrY8z_BQWdRhAo7gdnuMU,6313
|
|
63
63
|
mlrun/common/schemas/regex.py,sha256=8_vbDeAE0SODJDj7yUFg1FbaB9CNydYQTJ29JxE74Kc,776
|
|
64
64
|
mlrun/common/schemas/runs.py,sha256=yGGJxSHT_Mq4RLjlfuxW4pm9i-Py9eOsGUAofs_VqVM,1268
|
|
65
65
|
mlrun/common/schemas/runtime_resource.py,sha256=2rSuYL-9JkESSomlnU91mYDbfV-IkqZeXx6OHuMmDxs,1554
|
|
@@ -101,9 +101,9 @@ mlrun/datastore/wasbfs/__init__.py,sha256=s5Ul-0kAhYqFjKDR2X0O2vDGDbLQQduElb32Ev
|
|
|
101
101
|
mlrun/datastore/wasbfs/fs.py,sha256=MnSj7Q4OKA2L55ihCmUnj2t3GA3B77oLMdAw-yxvN9w,6151
|
|
102
102
|
mlrun/db/__init__.py,sha256=WqJ4x8lqJ7ZoKbhEyFqkYADd9P6E3citckx9e9ZLcIU,1163
|
|
103
103
|
mlrun/db/auth_utils.py,sha256=hpg8D2r82oN0BWabuWN04BTNZ7jYMAF242YSUpK7LFM,5211
|
|
104
|
-
mlrun/db/base.py,sha256=
|
|
104
|
+
mlrun/db/base.py,sha256=cHMkIJW1cnhIH8dxVjZTusUDsFirtKaEjDuF7he0VWM,24185
|
|
105
105
|
mlrun/db/factory.py,sha256=ibIrE5QkIIyzDU1FXKrfbc31cZiRLYKDZb8dqCpQwyU,2397
|
|
106
|
-
mlrun/db/httpdb.py,sha256=
|
|
106
|
+
mlrun/db/httpdb.py,sha256=ivOg9Sy5HVT62nhIis365KhNrsWS5sS-rgLZ33Tc-7I,183980
|
|
107
107
|
mlrun/db/nopdb.py,sha256=d7vSk_2sfwZGY24w7ucSkoq88fLPDLF137IXabongXU,20791
|
|
108
108
|
mlrun/feature_store/__init__.py,sha256=FhHRc8NdqL_HWpCs7A8dKruxJS5wEm55Gs3dcgBiRUg,1522
|
|
109
109
|
mlrun/feature_store/api.py,sha256=uYheyPkJOVCrz1jivvpGatgy_JBAq0It0XZqPpNVQkE,48699
|
|
@@ -224,7 +224,7 @@ mlrun/model_monitoring/writer.py,sha256=aQ1DAi5XUi1WXXfcSgBQGKiTANT6E61I74abiu_5
|
|
|
224
224
|
mlrun/model_monitoring/applications/__init__.py,sha256=i793GqYee01mRh_KD6GShvX7UbPBgdJDO4qf9Z3BXEQ,970
|
|
225
225
|
mlrun/model_monitoring/applications/_application_steps.py,sha256=-g9jxIAFM5f22iJaUAQVlM8QRSv6KFT92I4WHmZe_f0,6028
|
|
226
226
|
mlrun/model_monitoring/applications/base.py,sha256=buVKyghH4AB3chZ5py1vyMIFnTF-deY8YDf_fPC9BnQ,11307
|
|
227
|
-
mlrun/model_monitoring/applications/context.py,sha256=
|
|
227
|
+
mlrun/model_monitoring/applications/context.py,sha256=LGRJdI1eyyssFzjE4W_rk2VAUV8KpOkUZUX3xCmnC9g,8537
|
|
228
228
|
mlrun/model_monitoring/applications/evidently_base.py,sha256=AE_eIz-GEYm3AZTrMCiqF9bcSMlvYk08LJb6bKWAQLg,8057
|
|
229
229
|
mlrun/model_monitoring/applications/histogram_data_drift.py,sha256=TE6995h2PyO4lytVngH2HidhXFY7reLupWi4cHmdZdw,13163
|
|
230
230
|
mlrun/model_monitoring/applications/results.py,sha256=VVlu9Si7Tj2LNJzPQrp4_Qeyh9mxOVMu1Jwb5K2LfvY,3577
|
|
@@ -274,7 +274,7 @@ mlrun/platforms/iguazio.py,sha256=1h5BpdAEQJBg2vIt7ySjUADU0ip5OkaMYr0_VREi9ys,13
|
|
|
274
274
|
mlrun/projects/__init__.py,sha256=Lv5rfxyXJrw6WGOWJKhBz66M6t3_zsNMCfUD6waPwx4,1153
|
|
275
275
|
mlrun/projects/operations.py,sha256=Y-NwrIFXpltUXcDLDQ9b33NY_r4TOPvJgO4F-xSuzoM,19252
|
|
276
276
|
mlrun/projects/pipelines.py,sha256=Xc9tQSBBPEg1Yxn-b4RseFdfO7SvrYC-ekdw_hAcPH8,40006
|
|
277
|
-
mlrun/projects/project.py,sha256=
|
|
277
|
+
mlrun/projects/project.py,sha256=ROcuNkjV6kA8oGrnk4-MFj_0h5D04af9oWmXqVq7-lg,184688
|
|
278
278
|
mlrun/runtimes/__init__.py,sha256=0-tYDkew-Cr4DM-wztvMbzDA5xq385Jjo-GrtO_84Sc,8741
|
|
279
279
|
mlrun/runtimes/base.py,sha256=g716uF0BpL6vLe75bNqpJ2SjtYW_tQqICl46d_4ljHs,37633
|
|
280
280
|
mlrun/runtimes/daskjob.py,sha256=JfK8rSPY-0SYnLJdtp_ts3oKyad0pA98th-2VntYzK0,19387
|
|
@@ -294,12 +294,12 @@ mlrun/runtimes/mpijob/__init__.py,sha256=V_1gQD1VHa0Qvjqgyv8RLouH27Sy9YTwj2ZG62o
|
|
|
294
294
|
mlrun/runtimes/mpijob/abstract.py,sha256=kDWo-IY1FKLZhI30j38Xx9HMhlUvHezfd1DT2ShoxZY,9161
|
|
295
295
|
mlrun/runtimes/mpijob/v1.py,sha256=1XQZC7AIMGX_AQCbApcwpH8I7y39-v0v2O35MvxjXoo,3213
|
|
296
296
|
mlrun/runtimes/nuclio/__init__.py,sha256=gx1kizzKv8pGT5TNloN1js1hdbxqDw3rM90sLVYVffY,794
|
|
297
|
-
mlrun/runtimes/nuclio/api_gateway.py,sha256=
|
|
298
|
-
mlrun/runtimes/nuclio/function.py,sha256=
|
|
297
|
+
mlrun/runtimes/nuclio/api_gateway.py,sha256=TsEZFv-Ene2WZCS9jKReVmuMil_kIbcr9eFmmLxgsYU,25781
|
|
298
|
+
mlrun/runtimes/nuclio/function.py,sha256=Y-lNSQJXzsYXu4wCVOB69fv_X9ZvB-uwS1UZhbQPucE,50411
|
|
299
299
|
mlrun/runtimes/nuclio/nuclio.py,sha256=sLK8KdGO1LbftlL3HqPZlFOFTAAuxJACZCVl1c0Ha6E,2942
|
|
300
|
-
mlrun/runtimes/nuclio/serving.py,sha256=
|
|
300
|
+
mlrun/runtimes/nuclio/serving.py,sha256=qefcNtvAETt5otI4NymZnY_5Su6MTHmI35WHZsrCqww,29746
|
|
301
301
|
mlrun/runtimes/nuclio/application/__init__.py,sha256=rRs5vasy_G9IyoTpYIjYDafGoL6ifFBKgBtsXn31Atw,614
|
|
302
|
-
mlrun/runtimes/nuclio/application/application.py,sha256=
|
|
302
|
+
mlrun/runtimes/nuclio/application/application.py,sha256=EO0hY86ZX4XWf7F5mnEX-VTbii1Q_UtVyeoCSEJa0XE,20817
|
|
303
303
|
mlrun/runtimes/nuclio/application/reverse_proxy.go,sha256=JIIYae6bXzCLf3jXuu49KWPQYoXr_FDQ2Rbo1OWKAd0,3150
|
|
304
304
|
mlrun/runtimes/sparkjob/__init__.py,sha256=_KPvk0qefeLtHO6lxQE_AMOGiMTG_OT48eRCE4Z2ldw,709
|
|
305
305
|
mlrun/runtimes/sparkjob/spark3job.py,sha256=1bNRy72Migrh_ZASQOx7UlSZTbB-xpNc76sz4kfc9UM,41191
|
|
@@ -307,9 +307,9 @@ mlrun/serving/__init__.py,sha256=-SMRV3q_5cGVPDxRslXPU0zGYZIygs0cSj7WKlOJJUc,116
|
|
|
307
307
|
mlrun/serving/merger.py,sha256=PXLn3A21FiLteJHaDSLm5xKNT-80eTTjfHUJnBX1gKY,6116
|
|
308
308
|
mlrun/serving/remote.py,sha256=MrFByphQWmIsKXqw-MOwl2Q1hbtWReYVRKvlcKj9pfw,17980
|
|
309
309
|
mlrun/serving/routers.py,sha256=tjTAiLkV-BcRnUfbTqfrKB0j2LMTMygG_2oV_eQ26bs,55470
|
|
310
|
-
mlrun/serving/server.py,sha256=
|
|
310
|
+
mlrun/serving/server.py,sha256=LUf38ArOvs1cUbqxr3ZT015DTr4G5GIlToRaKqbhamc,21512
|
|
311
311
|
mlrun/serving/serving_wrapper.py,sha256=R670-S6PX_d5ER6jiHtRvacuPyFzQH0mEf2K0sBIIOM,836
|
|
312
|
-
mlrun/serving/states.py,sha256=
|
|
312
|
+
mlrun/serving/states.py,sha256=VuRqrZG-Vhhy8Nfi0IiYG2Adn4kAIDZsWgRDfcOKibo,59641
|
|
313
313
|
mlrun/serving/utils.py,sha256=lej7XcUPX1MmHkEOi_0KZRGSpfbmpnE0GK_Sn4zLkHY,4025
|
|
314
314
|
mlrun/serving/v1_serving.py,sha256=by4myxlnwyZ0ijQ5fURilGCK1sUpdQL2Il1VR3Xqpxg,11805
|
|
315
315
|
mlrun/serving/v2_serving.py,sha256=ARsAU0xaQqZoYWdtTLauMPlIX33Nus-BFQOTPZBYda8,24496
|
|
@@ -323,8 +323,8 @@ mlrun/utils/async_http.py,sha256=CZY8hNBMQaWrT6PLplyocCFbzaKrJnknFUP0e6kcDBw,117
|
|
|
323
323
|
mlrun/utils/azure_vault.py,sha256=IEFizrDGDbAaoWwDr1WoA88S_EZ0T--vjYtY-i0cvYQ,3450
|
|
324
324
|
mlrun/utils/clones.py,sha256=mJpx4nyFiY6jlBCvFABsNuyi_mr1mvfPWn81vlafpOU,7361
|
|
325
325
|
mlrun/utils/condition_evaluator.py,sha256=-nGfRmZzivn01rHTroiGY4rqEv8T1irMyhzxEei-sKc,1897
|
|
326
|
-
mlrun/utils/db.py,sha256=
|
|
327
|
-
mlrun/utils/helpers.py,sha256=
|
|
326
|
+
mlrun/utils/db.py,sha256=2TydIZzJJs9Rf8Qid6ze-Odb1HIzSPAT-Jr-HuHAris,1863
|
|
327
|
+
mlrun/utils/helpers.py,sha256=TUADKuE5d9jDpjiykNbQ9fV2G3e0StaZIsAcIX12VoI,56948
|
|
328
328
|
mlrun/utils/http.py,sha256=l_JCPrCq8bfYUcUcAFWUPvb9Xu-93bLGIhV-H-XCU9s,8707
|
|
329
329
|
mlrun/utils/logger.py,sha256=cag2J30-jynIHmHZ2J8RYmVMNhYBGgAoimc5sbk-A1U,10016
|
|
330
330
|
mlrun/utils/regex.py,sha256=b0AUa2THS-ELzJj0grl5b8Stq609F2XomTZkD9SB1fQ,4900
|
|
@@ -340,13 +340,13 @@ mlrun/utils/notifications/notification/console.py,sha256=MAVk7v5PJ52vdGRv76YcEPi
|
|
|
340
340
|
mlrun/utils/notifications/notification/git.py,sha256=g_8RksjCboGrKKjyhkePk5nSWrfdT61JkhMeg9EeGcY,6119
|
|
341
341
|
mlrun/utils/notifications/notification/ipython.py,sha256=ZtVL30B_Ha0VGoo4LxO-voT1U41IYwyytovv5X_LsI4,2066
|
|
342
342
|
mlrun/utils/notifications/notification/slack.py,sha256=wqpFGr5BTvFO5KuUSzFfxsgmyU1Ohq7fbrGeNe9TXOk,7006
|
|
343
|
-
mlrun/utils/notifications/notification/webhook.py,sha256=
|
|
343
|
+
mlrun/utils/notifications/notification/webhook.py,sha256=cb9w1Mc8ENfJBdgan7iiVHK9eVls4-R3tUxmXM-P-8I,4746
|
|
344
344
|
mlrun/utils/version/__init__.py,sha256=7kkrB7hEZ3cLXoWj1kPoDwo4MaswsI2JVOBpbKgPAgc,614
|
|
345
|
-
mlrun/utils/version/version.json,sha256=
|
|
345
|
+
mlrun/utils/version/version.json,sha256=zEtEwyoNa009X90xhQ4jFOfgdeXH8KwRfsHjXy_i7-I,89
|
|
346
346
|
mlrun/utils/version/version.py,sha256=eEW0tqIAkU9Xifxv8Z9_qsYnNhn3YH7NRAfM-pPLt1g,1878
|
|
347
|
-
mlrun-1.7.
|
|
348
|
-
mlrun-1.7.
|
|
349
|
-
mlrun-1.7.
|
|
350
|
-
mlrun-1.7.
|
|
351
|
-
mlrun-1.7.
|
|
352
|
-
mlrun-1.7.
|
|
347
|
+
mlrun-1.7.0rc34.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
348
|
+
mlrun-1.7.0rc34.dist-info/METADATA,sha256=nAG6_6ocnS0VmLAyKlqb_EiEAGlCqylo-aMGixGAB8o,19534
|
|
349
|
+
mlrun-1.7.0rc34.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
|
|
350
|
+
mlrun-1.7.0rc34.dist-info/entry_points.txt,sha256=1Owd16eAclD5pfRCoJpYC2ZJSyGNTtUr0nCELMioMmU,46
|
|
351
|
+
mlrun-1.7.0rc34.dist-info/top_level.txt,sha256=NObLzw3maSF9wVrgSeYBv-fgnHkAJ1kEkh12DLdd5KM,6
|
|
352
|
+
mlrun-1.7.0rc34.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|