mlrun 1.10.0rc20__py3-none-any.whl → 1.10.0rc22__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/artifacts/llm_prompt.py +11 -10
- mlrun/artifacts/model.py +3 -3
- mlrun/common/schemas/auth.py +2 -0
- mlrun/db/base.py +9 -0
- mlrun/db/httpdb.py +21 -1
- mlrun/db/nopdb.py +8 -0
- mlrun/execution.py +52 -10
- mlrun/model_monitoring/applications/__init__.py +1 -1
- mlrun/model_monitoring/applications/base.py +86 -33
- mlrun/model_monitoring/db/_schedules.py +21 -0
- mlrun/model_monitoring/db/tsdb/base.py +14 -5
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +4 -5
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +53 -20
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +39 -1
- mlrun/projects/project.py +50 -7
- mlrun/run.py +38 -5
- mlrun/serving/states.py +169 -16
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.10.0rc20.dist-info → mlrun-1.10.0rc22.dist-info}/METADATA +4 -3
- {mlrun-1.10.0rc20.dist-info → mlrun-1.10.0rc22.dist-info}/RECORD +24 -24
- {mlrun-1.10.0rc20.dist-info → mlrun-1.10.0rc22.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc20.dist-info → mlrun-1.10.0rc22.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc20.dist-info → mlrun-1.10.0rc22.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc20.dist-info → mlrun-1.10.0rc22.dist-info}/top_level.txt +0 -0
mlrun/artifacts/llm_prompt.py
CHANGED
|
@@ -83,19 +83,20 @@ class LLMPromptArtifactSpec(ArtifactSpec):
|
|
|
83
83
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
84
84
|
"Expected prompt_template to be a list of dicts"
|
|
85
85
|
)
|
|
86
|
-
keys_to_pop = []
|
|
87
86
|
for message in prompt_template:
|
|
87
|
+
if set(key.lower() for key in message.keys()) != set(
|
|
88
|
+
self.PROMPT_TEMPLATE_KEYS
|
|
89
|
+
):
|
|
90
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
91
|
+
f"Expected prompt_template to contain dicts with keys "
|
|
92
|
+
f"{self.PROMPT_TEMPLATE_KEYS}, got {message.keys()}"
|
|
93
|
+
)
|
|
94
|
+
keys_to_pop = []
|
|
88
95
|
for key in message.keys():
|
|
89
96
|
if isinstance(key, str):
|
|
90
|
-
if key.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
f"only has keys from {self.PROMPT_TEMPLATE_KEYS}"
|
|
94
|
-
)
|
|
95
|
-
else:
|
|
96
|
-
if not key.islower():
|
|
97
|
-
message[key.lower()] = message[key]
|
|
98
|
-
keys_to_pop.append(key)
|
|
97
|
+
if not key.islower():
|
|
98
|
+
message[key.lower()] = message[key]
|
|
99
|
+
keys_to_pop.append(key)
|
|
99
100
|
else:
|
|
100
101
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
101
102
|
f"Expected prompt_template to contain dict that only"
|
mlrun/artifacts/model.py
CHANGED
|
@@ -190,10 +190,10 @@ class ModelArtifact(Artifact):
|
|
|
190
190
|
"""
|
|
191
191
|
super().__init__(key, body, format=format, target_path=target_path, **kwargs)
|
|
192
192
|
model_file = str(model_file or "")
|
|
193
|
-
if model_file and model_url:
|
|
193
|
+
if (model_file or model_dir or body) and model_url:
|
|
194
194
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
195
|
-
"Arguments 'model_file' and '
|
|
196
|
-
" used together with '
|
|
195
|
+
"Arguments 'model_file' and 'model_url' cannot be"
|
|
196
|
+
" used together with 'model_file', 'model_dir' or 'body'."
|
|
197
197
|
)
|
|
198
198
|
if model_file and "/" in model_file:
|
|
199
199
|
if model_dir:
|
mlrun/common/schemas/auth.py
CHANGED
|
@@ -55,6 +55,7 @@ class AuthorizationResourceTypes(mlrun.common.types.StrEnum):
|
|
|
55
55
|
secret = "secret"
|
|
56
56
|
run = "run"
|
|
57
57
|
model_endpoint = "model-endpoint"
|
|
58
|
+
model_monitoring = "model-monitoring"
|
|
58
59
|
pipeline = "pipeline"
|
|
59
60
|
hub_source = "hub-source"
|
|
60
61
|
workflow = "workflow"
|
|
@@ -96,6 +97,7 @@ class AuthorizationResourceTypes(mlrun.common.types.StrEnum):
|
|
|
96
97
|
# runtime resource doesn't have an identifier, we don't need any auth granularity behind project level
|
|
97
98
|
AuthorizationResourceTypes.runtime_resource: "/projects/{project_name}/runtime-resources",
|
|
98
99
|
AuthorizationResourceTypes.model_endpoint: "/projects/{project_name}/model-endpoints/{resource_name}",
|
|
100
|
+
AuthorizationResourceTypes.model_monitoring: "/projects/{project_name}/model-monitoring/{resource_name}",
|
|
99
101
|
AuthorizationResourceTypes.pipeline: "/projects/{project_name}/pipelines/{resource_name}",
|
|
100
102
|
AuthorizationResourceTypes.datastore_profile: "/projects/{project_name}/datastore_profiles",
|
|
101
103
|
# Hub sources are not project-scoped, and auth is globally on the sources endpoint.
|
mlrun/db/base.py
CHANGED
|
@@ -1111,6 +1111,15 @@ class RunDBInterface(ABC):
|
|
|
1111
1111
|
) -> None:
|
|
1112
1112
|
pass
|
|
1113
1113
|
|
|
1114
|
+
@abstractmethod
|
|
1115
|
+
def delete_model_monitoring_metrics(
|
|
1116
|
+
self,
|
|
1117
|
+
project: str,
|
|
1118
|
+
application_name: str,
|
|
1119
|
+
endpoint_ids: Optional[list[str]] = None,
|
|
1120
|
+
) -> None:
|
|
1121
|
+
pass
|
|
1122
|
+
|
|
1114
1123
|
@abstractmethod
|
|
1115
1124
|
def get_monitoring_function_summaries(
|
|
1116
1125
|
self,
|
mlrun/db/httpdb.py
CHANGED
|
@@ -3580,7 +3580,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3580
3580
|
intersection {"intersect_metrics":[], "intersect_results":[]}
|
|
3581
3581
|
:return: A dictionary of application metrics and/or results for the model endpoints formatted by events_format.
|
|
3582
3582
|
"""
|
|
3583
|
-
path = f"projects/{project}/model-
|
|
3583
|
+
path = f"projects/{project}/model-monitoring/metrics"
|
|
3584
3584
|
params = {
|
|
3585
3585
|
"type": type,
|
|
3586
3586
|
"endpoint-id": endpoint_ids,
|
|
@@ -4121,6 +4121,26 @@ class HTTPRunDB(RunDBInterface):
|
|
|
4121
4121
|
params={**credentials, "replace_creds": replace_creds},
|
|
4122
4122
|
)
|
|
4123
4123
|
|
|
4124
|
+
def delete_model_monitoring_metrics(
|
|
4125
|
+
self,
|
|
4126
|
+
project: str,
|
|
4127
|
+
application_name: str,
|
|
4128
|
+
endpoint_ids: Optional[list[str]] = None,
|
|
4129
|
+
) -> None:
|
|
4130
|
+
"""
|
|
4131
|
+
Delete model endpoints metrics values.
|
|
4132
|
+
|
|
4133
|
+
:param project: The name of the project.
|
|
4134
|
+
:param application_name: The name of the application.
|
|
4135
|
+
:param endpoint_ids: The unique IDs of the model endpoints to delete metrics values from. If none is
|
|
4136
|
+
provided, the metrics values will be deleted from all project's model endpoints.
|
|
4137
|
+
"""
|
|
4138
|
+
self.api_call(
|
|
4139
|
+
method=mlrun.common.types.HTTPMethod.DELETE,
|
|
4140
|
+
path=f"projects/{project}/model-monitoring/metrics",
|
|
4141
|
+
params={"endpoint-id": endpoint_ids, "application-name": application_name},
|
|
4142
|
+
)
|
|
4143
|
+
|
|
4124
4144
|
def get_monitoring_function_summaries(
|
|
4125
4145
|
self,
|
|
4126
4146
|
project: str,
|
mlrun/db/nopdb.py
CHANGED
|
@@ -885,6 +885,14 @@ class NopDB(RunDBInterface):
|
|
|
885
885
|
) -> None:
|
|
886
886
|
pass
|
|
887
887
|
|
|
888
|
+
def delete_model_monitoring_metrics(
|
|
889
|
+
self,
|
|
890
|
+
project: str,
|
|
891
|
+
application_name: str,
|
|
892
|
+
endpoint_ids: Optional[list[str]] = None,
|
|
893
|
+
) -> None:
|
|
894
|
+
pass
|
|
895
|
+
|
|
888
896
|
def get_monitoring_function_summaries(
|
|
889
897
|
self,
|
|
890
898
|
project: str,
|
mlrun/execution.py
CHANGED
|
@@ -934,14 +934,51 @@ class MLClientCtx:
|
|
|
934
934
|
|
|
935
935
|
Examples::
|
|
936
936
|
|
|
937
|
-
# Log an inline prompt
|
|
937
|
+
# Log directly with an inline prompt template
|
|
938
938
|
context.log_llm_prompt(
|
|
939
|
-
key="
|
|
940
|
-
prompt_template=[
|
|
939
|
+
key="customer_support_prompt",
|
|
940
|
+
prompt_template=[
|
|
941
|
+
{
|
|
942
|
+
"role": "system",
|
|
943
|
+
"content": "You are a helpful customer support assistant.",
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
"role": "user",
|
|
947
|
+
"content": "The customer reports: {issue_description}",
|
|
948
|
+
},
|
|
949
|
+
],
|
|
950
|
+
prompt_legend={
|
|
951
|
+
"issue_description": {
|
|
952
|
+
"field": "user_issue",
|
|
953
|
+
"description": "Detailed description of the customer's issue",
|
|
954
|
+
},
|
|
955
|
+
"solution": {
|
|
956
|
+
"field": "proposed_solution",
|
|
957
|
+
"description": "Suggested fix for the customer's issue",
|
|
958
|
+
},
|
|
959
|
+
},
|
|
941
960
|
model_artifact=model,
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
tag="
|
|
961
|
+
model_configuration={"temperature": 0.5, "max_tokens": 200},
|
|
962
|
+
description="Prompt for handling customer support queries",
|
|
963
|
+
tag="support-v1",
|
|
964
|
+
labels={"domain": "support"},
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
# Log a prompt from file
|
|
968
|
+
context.log_llm_prompt(
|
|
969
|
+
key="qa_prompt",
|
|
970
|
+
prompt_path="prompts/template.json",
|
|
971
|
+
prompt_legend={
|
|
972
|
+
"question": {
|
|
973
|
+
"field": "user_question",
|
|
974
|
+
"description": "The actual question asked by the user",
|
|
975
|
+
}
|
|
976
|
+
},
|
|
977
|
+
model_artifact=model,
|
|
978
|
+
model_configuration={"temperature": 0.7, "max_tokens": 256},
|
|
979
|
+
description="Q&A prompt template with user-provided question",
|
|
980
|
+
tag="v2",
|
|
981
|
+
labels={"task": "qa", "stage": "experiment"},
|
|
945
982
|
)
|
|
946
983
|
|
|
947
984
|
:param key: Unique name of the artifact.
|
|
@@ -950,7 +987,10 @@ class MLClientCtx:
|
|
|
950
987
|
"role": "user", "content": "I need your help with {profession}"]. only "role" and "content" keys allow in any
|
|
951
988
|
str format (upper/lower case), keys will be modified to lower case.
|
|
952
989
|
Cannot be used with `prompt_path`.
|
|
953
|
-
:param prompt_path: Path to a file containing the prompt
|
|
990
|
+
:param prompt_path: Path to a JSON file containing the prompt template.
|
|
991
|
+
Cannot be used together with `prompt_template`.
|
|
992
|
+
The file should define a list of dictionaries in the same format
|
|
993
|
+
supported by `prompt_template`.
|
|
954
994
|
:param prompt_legend: A dictionary where each key is a placeholder in the prompt (e.g., ``{user_name}``)
|
|
955
995
|
and the value is a dictionary holding two keys, "field", "description". "field" points to the field in
|
|
956
996
|
the event where the value of the place-holder inside the event, if None or not exist will be replaced
|
|
@@ -958,9 +998,11 @@ class MLClientCtx:
|
|
|
958
998
|
Useful for documenting and clarifying dynamic parts of the prompt.
|
|
959
999
|
:param model_artifact: Reference to the parent model (either `ModelArtifact` or model URI string).
|
|
960
1000
|
:param model_configuration: Dictionary of generation parameters (e.g., temperature, max_tokens).
|
|
961
|
-
:param description:
|
|
962
|
-
:param target_path:
|
|
963
|
-
:param artifact_path:
|
|
1001
|
+
:param description: Optional description of the prompt.
|
|
1002
|
+
:param target_path: Absolute target path (instead of using artifact_path + local_path)
|
|
1003
|
+
:param artifact_path: Target artifact path (when not using the default)
|
|
1004
|
+
To define a subpath under the default location use:
|
|
1005
|
+
`artifact_path=context.artifact_subpath('data')`
|
|
964
1006
|
:param tag: Tag/version to assign to the prompt artifact.
|
|
965
1007
|
:param labels: Labels to tag the artifact (e.g., list or dict of key-value pairs).
|
|
966
1008
|
:param upload: Whether to upload the artifact to the store (defaults to True).
|
|
@@ -12,6 +12,6 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
from .base import ModelMonitoringApplicationBase
|
|
15
|
+
from .base import ExistingDataHandling, ModelMonitoringApplicationBase
|
|
16
16
|
from .context import MonitoringApplicationContext
|
|
17
17
|
from .results import ModelMonitoringApplicationMetric, ModelMonitoringApplicationResult
|
|
@@ -27,6 +27,7 @@ import mlrun
|
|
|
27
27
|
import mlrun.common.constants as mlrun_constants
|
|
28
28
|
import mlrun.common.helpers
|
|
29
29
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
30
|
+
import mlrun.common.types
|
|
30
31
|
import mlrun.datastore.datastore_profile as ds_profile
|
|
31
32
|
import mlrun.errors
|
|
32
33
|
import mlrun.model_monitoring.api as mm_api
|
|
@@ -39,6 +40,12 @@ from mlrun.serving.utils import MonitoringApplicationToDict
|
|
|
39
40
|
from mlrun.utils import logger
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
class ExistingDataHandling(mlrun.common.types.StrEnum):
|
|
44
|
+
fail_on_overlap = "fail_on_overlap"
|
|
45
|
+
skip_overlap = "skip_overlap"
|
|
46
|
+
delete_all = "delete_all"
|
|
47
|
+
|
|
48
|
+
|
|
42
49
|
def _serialize_context_and_result(
|
|
43
50
|
*,
|
|
44
51
|
context: mm_context.MonitoringApplicationContext,
|
|
@@ -288,7 +295,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
288
295
|
end: Optional[str] = None,
|
|
289
296
|
base_period: Optional[int] = None,
|
|
290
297
|
write_output: bool = False,
|
|
291
|
-
|
|
298
|
+
existing_data_handling: ExistingDataHandling = ExistingDataHandling.fail_on_overlap,
|
|
292
299
|
stream_profile: Optional[ds_profile.DatastoreProfile] = None,
|
|
293
300
|
):
|
|
294
301
|
"""
|
|
@@ -350,6 +357,24 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
350
357
|
resolved_endpoints = self._handle_endpoints_type_evaluate(
|
|
351
358
|
project=project, endpoints=endpoints
|
|
352
359
|
)
|
|
360
|
+
if (
|
|
361
|
+
write_output
|
|
362
|
+
and existing_data_handling == ExistingDataHandling.delete_all
|
|
363
|
+
):
|
|
364
|
+
endpoint_ids = [
|
|
365
|
+
endpoint_id for _, endpoint_id in resolved_endpoints
|
|
366
|
+
]
|
|
367
|
+
context.logger.info(
|
|
368
|
+
"Deleting all the application data before running the application",
|
|
369
|
+
application_name=application_name,
|
|
370
|
+
endpoint_ids=endpoint_ids,
|
|
371
|
+
)
|
|
372
|
+
self._delete_application_data(
|
|
373
|
+
project_name=project.name,
|
|
374
|
+
application_name=application_name,
|
|
375
|
+
endpoint_ids=endpoint_ids,
|
|
376
|
+
application_schedules=application_schedules,
|
|
377
|
+
)
|
|
353
378
|
for endpoint_name, endpoint_id in resolved_endpoints:
|
|
354
379
|
for window_start, window_end in self._window_generator(
|
|
355
380
|
start=start,
|
|
@@ -358,7 +383,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
358
383
|
application_schedules=application_schedules,
|
|
359
384
|
endpoint_id=endpoint_id,
|
|
360
385
|
application_name=application_name,
|
|
361
|
-
|
|
386
|
+
existing_data_handling=existing_data_handling,
|
|
362
387
|
):
|
|
363
388
|
result = call_do_tracking(
|
|
364
389
|
event={
|
|
@@ -481,7 +506,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
481
506
|
end_dt: datetime,
|
|
482
507
|
base_period: Optional[int],
|
|
483
508
|
application_name: str,
|
|
484
|
-
|
|
509
|
+
existing_data_handling: ExistingDataHandling,
|
|
485
510
|
) -> datetime:
|
|
486
511
|
"""Make sure that the (app, endpoint) pair doesn't write output before the last analyzed window"""
|
|
487
512
|
if application_schedules:
|
|
@@ -490,7 +515,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
490
515
|
)
|
|
491
516
|
if last_analyzed:
|
|
492
517
|
if start_dt < last_analyzed:
|
|
493
|
-
if
|
|
518
|
+
if existing_data_handling == ExistingDataHandling.skip_overlap:
|
|
494
519
|
if last_analyzed < end_dt and base_period is None:
|
|
495
520
|
logger.warn(
|
|
496
521
|
"Setting the start time to last_analyzed since the original start time precedes "
|
|
@@ -525,6 +550,25 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
525
550
|
)
|
|
526
551
|
return start_dt
|
|
527
552
|
|
|
553
|
+
@staticmethod
|
|
554
|
+
def _delete_application_data(
|
|
555
|
+
project_name: str,
|
|
556
|
+
application_name: str,
|
|
557
|
+
endpoint_ids: list[str],
|
|
558
|
+
application_schedules: Optional[
|
|
559
|
+
mm_schedules.ModelMonitoringSchedulesFileApplication
|
|
560
|
+
],
|
|
561
|
+
) -> None:
|
|
562
|
+
mlrun.get_run_db().delete_model_monitoring_metrics(
|
|
563
|
+
project=project_name,
|
|
564
|
+
application_name=application_name,
|
|
565
|
+
endpoint_ids=endpoint_ids,
|
|
566
|
+
)
|
|
567
|
+
if application_schedules:
|
|
568
|
+
application_schedules.delete_endpoints_last_analyzed(
|
|
569
|
+
endpoint_uids=endpoint_ids
|
|
570
|
+
)
|
|
571
|
+
|
|
528
572
|
@classmethod
|
|
529
573
|
def _window_generator(
|
|
530
574
|
cls,
|
|
@@ -537,7 +581,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
537
581
|
],
|
|
538
582
|
endpoint_id: str,
|
|
539
583
|
application_name: str,
|
|
540
|
-
|
|
584
|
+
existing_data_handling: ExistingDataHandling,
|
|
541
585
|
) -> Iterator[tuple[Optional[datetime], Optional[datetime]]]:
|
|
542
586
|
if start is None or end is None:
|
|
543
587
|
# A single window based on the `sample_data` input - see `_handler`.
|
|
@@ -547,15 +591,16 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
547
591
|
start_dt = datetime.fromisoformat(start)
|
|
548
592
|
end_dt = datetime.fromisoformat(end)
|
|
549
593
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
594
|
+
if existing_data_handling != ExistingDataHandling.delete_all:
|
|
595
|
+
start_dt = cls._validate_monotonically_increasing_data(
|
|
596
|
+
application_schedules=application_schedules,
|
|
597
|
+
endpoint_id=endpoint_id,
|
|
598
|
+
start_dt=start_dt,
|
|
599
|
+
end_dt=end_dt,
|
|
600
|
+
base_period=base_period,
|
|
601
|
+
application_name=application_name,
|
|
602
|
+
existing_data_handling=existing_data_handling,
|
|
603
|
+
)
|
|
559
604
|
|
|
560
605
|
if base_period is None:
|
|
561
606
|
yield start_dt, end_dt
|
|
@@ -702,7 +747,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
702
747
|
* ``end``, ``datetime``
|
|
703
748
|
* ``base_period``, ``int``
|
|
704
749
|
* ``write_output``, ``bool``
|
|
705
|
-
* ``
|
|
750
|
+
* ``existing_data_handling``, ``str``
|
|
706
751
|
|
|
707
752
|
For Git sources, add the source archive to the returned job and change the handler:
|
|
708
753
|
|
|
@@ -788,7 +833,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
788
833
|
end: Optional[datetime] = None,
|
|
789
834
|
base_period: Optional[int] = None,
|
|
790
835
|
write_output: bool = False,
|
|
791
|
-
|
|
836
|
+
existing_data_handling: ExistingDataHandling = ExistingDataHandling.fail_on_overlap,
|
|
792
837
|
stream_profile: Optional[ds_profile.DatastoreProfile] = None,
|
|
793
838
|
) -> "mlrun.RunObject":
|
|
794
839
|
"""
|
|
@@ -856,11 +901,18 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
856
901
|
:param write_output: Whether to write the results and metrics to the time-series DB. Can be ``True`` only
|
|
857
902
|
if ``endpoints`` are passed.
|
|
858
903
|
Note: the model monitoring infrastructure must be up for the writing to work.
|
|
859
|
-
:param
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
904
|
+
:param existing_data_handling:
|
|
905
|
+
How to handle the existing application data for the model endpoints when writing the
|
|
906
|
+
new data. Relevant only when ``write_output=True``. The default is
|
|
907
|
+
``"fail_on_overlap"``. The options are:
|
|
908
|
+
|
|
909
|
+
- ``"fail_on_overlap"``: when the requested ``start`` time precedes the
|
|
910
|
+
``end`` time of a previous run that also wrote to the database - an error is raised.
|
|
911
|
+
- ``"skip_overlap"``: when the previously described situation occurs, the relevant
|
|
912
|
+
time window is cut so that it starts at the earliest possible time after ``start``.
|
|
913
|
+
- ``"delete_all"``: delete all the data that was written by the application to the
|
|
914
|
+
model endpoints, regardless of the time window, and write the new data.
|
|
915
|
+
|
|
864
916
|
:param stream_profile: The stream datastore profile. It should be provided only when running locally and
|
|
865
917
|
writing the outputs to the database (i.e., when both ``run_local`` and
|
|
866
918
|
``write_output`` are set to ``True``).
|
|
@@ -899,18 +951,6 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
899
951
|
)
|
|
900
952
|
params["end"] = end.isoformat() if isinstance(end, datetime) else end
|
|
901
953
|
params["base_period"] = base_period
|
|
902
|
-
params["write_output"] = write_output
|
|
903
|
-
params["fail_on_overlap"] = fail_on_overlap
|
|
904
|
-
if stream_profile:
|
|
905
|
-
if not run_local:
|
|
906
|
-
raise mlrun.errors.MLRunValueError(
|
|
907
|
-
"Passing a `stream_profile` is relevant only when running locally"
|
|
908
|
-
)
|
|
909
|
-
if not write_output:
|
|
910
|
-
raise mlrun.errors.MLRunValueError(
|
|
911
|
-
"Passing a `stream_profile` is relevant only when writing the outputs"
|
|
912
|
-
)
|
|
913
|
-
params["stream_profile"] = stream_profile
|
|
914
954
|
elif start or end or base_period:
|
|
915
955
|
raise mlrun.errors.MLRunValueError(
|
|
916
956
|
"Custom `start` and `end` times or base_period are supported only with endpoints data"
|
|
@@ -920,6 +960,19 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
920
960
|
"Writing the application output or passing `stream_profile` are supported only with endpoints data"
|
|
921
961
|
)
|
|
922
962
|
|
|
963
|
+
params["write_output"] = write_output
|
|
964
|
+
params["existing_data_handling"] = existing_data_handling
|
|
965
|
+
if stream_profile:
|
|
966
|
+
if not run_local:
|
|
967
|
+
raise mlrun.errors.MLRunValueError(
|
|
968
|
+
"Passing a `stream_profile` is relevant only when running locally"
|
|
969
|
+
)
|
|
970
|
+
if not write_output:
|
|
971
|
+
raise mlrun.errors.MLRunValueError(
|
|
972
|
+
"Passing a `stream_profile` is relevant only when writing the outputs"
|
|
973
|
+
)
|
|
974
|
+
params["stream_profile"] = stream_profile
|
|
975
|
+
|
|
923
976
|
inputs: dict[str, str] = {}
|
|
924
977
|
for data, identifier in [
|
|
925
978
|
(sample_data, "sample_data"),
|
|
@@ -170,6 +170,16 @@ class ModelMonitoringSchedulesFileEndpoint(ModelMonitoringSchedulesFileBase):
|
|
|
170
170
|
self._check_open_schedules()
|
|
171
171
|
self._schedules[application] = float(timestamp)
|
|
172
172
|
|
|
173
|
+
def delete_application_time(self, application: str) -> None:
|
|
174
|
+
self._check_open_schedules()
|
|
175
|
+
if application in self._schedules:
|
|
176
|
+
logger.debug(
|
|
177
|
+
"Deleting application time from schedules",
|
|
178
|
+
application=application,
|
|
179
|
+
endpoint_id=self._endpoint_id,
|
|
180
|
+
)
|
|
181
|
+
del self._schedules[application]
|
|
182
|
+
|
|
173
183
|
def get_application_list(self) -> set[str]:
|
|
174
184
|
self._check_open_schedules()
|
|
175
185
|
return set(self._schedules.keys())
|
|
@@ -275,6 +285,17 @@ class ModelMonitoringSchedulesFileApplication(ModelMonitoringSchedulesFileBase):
|
|
|
275
285
|
timezone.utc
|
|
276
286
|
).isoformat()
|
|
277
287
|
|
|
288
|
+
def delete_endpoints_last_analyzed(self, endpoint_uids: list[str]) -> None:
|
|
289
|
+
self._check_open_schedules()
|
|
290
|
+
for endpoint_uid in endpoint_uids:
|
|
291
|
+
if endpoint_uid in self._schedules:
|
|
292
|
+
logger.debug(
|
|
293
|
+
"Deleting endpoint last analyzed from schedules",
|
|
294
|
+
endpoint_uid=endpoint_uid,
|
|
295
|
+
application=self._application,
|
|
296
|
+
)
|
|
297
|
+
del self._schedules[endpoint_uid]
|
|
298
|
+
|
|
278
299
|
|
|
279
300
|
def _delete_folder(folder: str) -> None:
|
|
280
301
|
fs = mlrun.datastore.store_manager.object(folder).store.filesystem
|
|
@@ -96,14 +96,23 @@ class TSDBConnector(ABC):
|
|
|
96
96
|
"""
|
|
97
97
|
|
|
98
98
|
@abstractmethod
|
|
99
|
-
def delete_tsdb_records(
|
|
100
|
-
self,
|
|
101
|
-
endpoint_ids: list[str],
|
|
102
|
-
) -> None:
|
|
99
|
+
def delete_tsdb_records(self, endpoint_ids: list[str]) -> None:
|
|
103
100
|
"""
|
|
104
101
|
Delete model endpoint records from the TSDB connector.
|
|
102
|
+
|
|
105
103
|
:param endpoint_ids: List of model endpoint unique identifiers.
|
|
106
|
-
|
|
104
|
+
"""
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
@abstractmethod
|
|
108
|
+
def delete_application_records(
|
|
109
|
+
self, application_name: str, endpoint_ids: Optional[list[str]] = None
|
|
110
|
+
) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Delete application records from the TSDB for the given model endpoints or all if ``None``.
|
|
113
|
+
|
|
114
|
+
:param application_name: The name of the application to delete records for.
|
|
115
|
+
:param endpoint_ids: List of model endpoint unique identifiers.
|
|
107
116
|
"""
|
|
108
117
|
pass
|
|
109
118
|
|
|
@@ -122,10 +122,7 @@ class TDEngineSchema:
|
|
|
122
122
|
)
|
|
123
123
|
return f"DELETE FROM {self.database}.{subtable} WHERE {values};"
|
|
124
124
|
|
|
125
|
-
def drop_subtable_query(
|
|
126
|
-
self,
|
|
127
|
-
subtable: str,
|
|
128
|
-
) -> str:
|
|
125
|
+
def drop_subtable_query(self, subtable: str) -> str:
|
|
129
126
|
return f"DROP TABLE if EXISTS {self.database}.`{subtable}`;"
|
|
130
127
|
|
|
131
128
|
def drop_supertable_query(self) -> str:
|
|
@@ -145,8 +142,10 @@ class TDEngineSchema:
|
|
|
145
142
|
values = f" {operator} ".join(
|
|
146
143
|
f"{filter_tag} LIKE '{val}'" for val in filter_values
|
|
147
144
|
)
|
|
145
|
+
return self._get_tables_query_by_condition(values)
|
|
148
146
|
|
|
149
|
-
|
|
147
|
+
def _get_tables_query_by_condition(self, condition: str) -> str:
|
|
148
|
+
return f"SELECT DISTINCT TBNAME FROM {self.database}.{self.super_table} WHERE {condition};"
|
|
150
149
|
|
|
151
150
|
@staticmethod
|
|
152
151
|
def _get_records_query(
|