validmind 2.5.19__py3-none-any.whl → 2.5.23__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.
- validmind/__init__.py +7 -46
- validmind/__version__.py +1 -1
- validmind/ai/test_result_description/context.py +2 -2
- validmind/api_client.py +131 -266
- validmind/client_config.py +1 -3
- validmind/datasets/__init__.py +1 -1
- validmind/datasets/nlp/__init__.py +1 -1
- validmind/errors.py +3 -30
- validmind/tests/load.py +4 -0
- validmind/tests/model_validation/ragas/AnswerCorrectness.py +0 -1
- validmind/tests/model_validation/sklearn/ClassifierPerformance.py +5 -2
- validmind/tests/run.py +219 -116
- validmind/vm_models/test/result_wrapper.py +4 -4
- {validmind-2.5.19.dist-info → validmind-2.5.23.dist-info}/METADATA +12 -12
- {validmind-2.5.19.dist-info → validmind-2.5.23.dist-info}/RECORD +18 -18
- {validmind-2.5.19.dist-info → validmind-2.5.23.dist-info}/LICENSE +0 -0
- {validmind-2.5.19.dist-info → validmind-2.5.23.dist-info}/WHEEL +0 -0
- {validmind-2.5.19.dist-info → validmind-2.5.23.dist-info}/entry_points.txt +0 -0
validmind/__init__.py
CHANGED
@@ -3,14 +3,14 @@
|
|
3
3
|
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
|
4
4
|
|
5
5
|
"""
|
6
|
-
ValidMind’s Python
|
6
|
+
ValidMind’s Python Library is a library of developer tools and methods designed to automate
|
7
7
|
the documentation and validation of your models.
|
8
8
|
|
9
|
-
The
|
9
|
+
The Library is designed to be model agnostic. If your model is built in Python, ValidMind's
|
10
10
|
Python library will provide all the standard functionality without requiring your developers to rewrite any functions.
|
11
11
|
|
12
|
-
The
|
13
|
-
descriptions of your dataset to testing your models for weak spots and overfit areas. The
|
12
|
+
The Library provides a rich suite of documentation tools and test suites, from documenting
|
13
|
+
descriptions of your dataset to testing your models for weak spots and overfit areas. The Library
|
14
14
|
helps you automate the generation of model documentation by feeding the ValidMind platform with documentation
|
15
15
|
artifacts and test results to the ValidMind platform.
|
16
16
|
|
@@ -35,7 +35,7 @@ vm.init(
|
|
35
35
|
```
|
36
36
|
|
37
37
|
After you have pasted the code snippet into your development source code and executed the code, the Python client
|
38
|
-
library will register with ValidMind. You can now use the
|
38
|
+
library will register with ValidMind. You can now use the Library to document and test your models,
|
39
39
|
and to upload to the ValidMind Platform.
|
40
40
|
"""
|
41
41
|
import warnings
|
@@ -47,10 +47,7 @@ warnings.simplefilter("ignore", category=NumbaDeprecationWarning)
|
|
47
47
|
warnings.simplefilter("ignore", category=NumbaPendingDeprecationWarning)
|
48
48
|
|
49
49
|
from .__version__ import __version__ # noqa: E402
|
50
|
-
from .api_client import init
|
51
|
-
from .api_client import log_figure as _log_figure_async
|
52
|
-
from .api_client import log_metrics as _log_metrics_async
|
53
|
-
from .api_client import log_test_results, reload
|
50
|
+
from .api_client import init, log_metric, reload
|
54
51
|
from .client import ( # noqa: E402
|
55
52
|
get_test_suite,
|
56
53
|
init_dataset,
|
@@ -61,39 +58,6 @@ from .client import ( # noqa: E402
|
|
61
58
|
run_test_suite,
|
62
59
|
)
|
63
60
|
from .tests.decorator import metric, tags, tasks, test
|
64
|
-
from .utils import run_async # noqa: E402
|
65
|
-
|
66
|
-
|
67
|
-
def log_metrics(metrics, inputs=None):
|
68
|
-
"""Logs metrics to ValidMind API.
|
69
|
-
|
70
|
-
Args:
|
71
|
-
metrics (list): A list of Metric objects
|
72
|
-
inputs (list): A list of input names to associate with the metrics
|
73
|
-
|
74
|
-
Raises:
|
75
|
-
Exception: If the API call fails
|
76
|
-
|
77
|
-
Returns:
|
78
|
-
dict: The response from the API
|
79
|
-
"""
|
80
|
-
run_async(_log_metrics_async, metrics, inputs)
|
81
|
-
|
82
|
-
|
83
|
-
def log_figure(figure):
|
84
|
-
"""Logs a figure
|
85
|
-
|
86
|
-
Args:
|
87
|
-
figure (Figure): The Figure object wrapper
|
88
|
-
|
89
|
-
Raises:
|
90
|
-
Exception: If the API call fails
|
91
|
-
|
92
|
-
Returns:
|
93
|
-
dict: The response from the API
|
94
|
-
"""
|
95
|
-
run_async(_log_figure_async, figure)
|
96
|
-
|
97
61
|
|
98
62
|
__all__ = [ # noqa
|
99
63
|
"__version__",
|
@@ -117,8 +81,5 @@ __all__ = [ # noqa
|
|
117
81
|
"test_suites",
|
118
82
|
"vm_models",
|
119
83
|
"unit_metrics",
|
120
|
-
|
121
|
-
"log_figure",
|
122
|
-
"log_metrics",
|
123
|
-
"log_test_results",
|
84
|
+
"log_metric",
|
124
85
|
]
|
validmind/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "2.5.
|
1
|
+
__version__ = "2.5.23"
|
@@ -37,11 +37,11 @@ class Context:
|
|
37
37
|
pass
|
38
38
|
|
39
39
|
def load(self, input_data):
|
40
|
-
# this task can accept a dict or a test result object from the
|
40
|
+
# this task can accept a dict or a test result object from the library
|
41
41
|
if isinstance(input_data, dict):
|
42
42
|
return input_data
|
43
43
|
|
44
|
-
# we are likely running outside of the
|
44
|
+
# we are likely running outside of the library and need to convert
|
45
45
|
# the test result object to a dictionary
|
46
46
|
test_result = input_data
|
47
47
|
|
validmind/api_client.py
CHANGED
@@ -12,7 +12,7 @@ import atexit
|
|
12
12
|
import json
|
13
13
|
import os
|
14
14
|
from io import BytesIO
|
15
|
-
from typing import Any,
|
15
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
16
16
|
from urllib.parse import urlencode, urljoin
|
17
17
|
|
18
18
|
import aiohttp
|
@@ -20,7 +20,7 @@ import requests
|
|
20
20
|
from aiohttp import FormData
|
21
21
|
|
22
22
|
from .client_config import client_config
|
23
|
-
from .errors import MissingAPICredentialsError,
|
23
|
+
from .errors import MissingAPICredentialsError, MissingModelIdError, raise_api_error
|
24
24
|
from .logging import get_logger, init_sentry, send_single_error
|
25
25
|
from .utils import NumpyEncoder, run_async
|
26
26
|
from .vm_models import Figure, MetricResult, ThresholdTestResults
|
@@ -33,33 +33,25 @@ _api_key = os.getenv("VM_API_KEY")
|
|
33
33
|
_api_secret = os.getenv("VM_API_SECRET")
|
34
34
|
_api_host = os.getenv("VM_API_HOST")
|
35
35
|
_model_cuid = os.getenv("VM_API_MODEL")
|
36
|
-
_run_cuid = os.getenv("VM_RUN_CUID")
|
37
36
|
_monitoring = False
|
38
37
|
|
39
|
-
__api_session: aiohttp.ClientSession = None
|
38
|
+
__api_session: Optional[aiohttp.ClientSession] = None
|
40
39
|
|
41
40
|
|
42
41
|
@atexit.register
|
43
42
|
def _close_session():
|
44
|
-
"""Closes the async client session"""
|
43
|
+
"""Closes the async client session at exit"""
|
45
44
|
global __api_session
|
46
45
|
|
47
|
-
if __api_session
|
46
|
+
if __api_session and not __api_session.closed:
|
48
47
|
try:
|
49
|
-
asyncio.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
"VM_API_KEY": _api_key,
|
57
|
-
"VM_API_SECRET": _api_secret,
|
58
|
-
"VM_API_HOST": _api_host,
|
59
|
-
"VM_API_MODEL": _model_cuid,
|
60
|
-
"VM_RUN_CUID": _run_cuid,
|
61
|
-
"X-MONITORING": _monitoring,
|
62
|
-
}
|
48
|
+
loop = asyncio.get_event_loop()
|
49
|
+
if loop.is_running():
|
50
|
+
loop.create_task(__api_session.close())
|
51
|
+
else:
|
52
|
+
loop.run_until_complete(__api_session.close())
|
53
|
+
except Exception as e:
|
54
|
+
logger.exception("Error closing aiohttp session at exit: %s", e)
|
63
55
|
|
64
56
|
|
65
57
|
def get_api_host() -> Optional[str]:
|
@@ -70,169 +62,36 @@ def get_api_model() -> Optional[str]:
|
|
70
62
|
return _model_cuid
|
71
63
|
|
72
64
|
|
73
|
-
def
|
65
|
+
def _get_api_headers() -> Dict[str, str]:
|
74
66
|
return {
|
75
67
|
"X-API-KEY": _api_key,
|
76
68
|
"X-API-SECRET": _api_secret,
|
77
|
-
"X-
|
78
|
-
"X-MONITORING": _monitoring,
|
69
|
+
"X-MODEL-CUID": _model_cuid,
|
70
|
+
"X-MONITORING": str(_monitoring),
|
79
71
|
}
|
80
72
|
|
81
73
|
|
82
|
-
def init(
|
83
|
-
project: Optional[str] = None,
|
84
|
-
api_key: Optional[str] = None,
|
85
|
-
api_secret: Optional[str] = None,
|
86
|
-
api_host: Optional[str] = None,
|
87
|
-
model: Optional[str] = None,
|
88
|
-
monitoring=False,
|
89
|
-
):
|
90
|
-
"""
|
91
|
-
Initializes the API client instances and calls the /ping endpoint to ensure
|
92
|
-
the provided credentials are valid and we can connect to the ValidMind API.
|
93
|
-
|
94
|
-
If the API key and secret are not provided, the client will attempt to
|
95
|
-
retrieve them from the environment variables `VM_API_KEY` and `VM_API_SECRET`.
|
96
|
-
|
97
|
-
Args:
|
98
|
-
project (str, optional): The project CUID. Alias for model. Defaults to None.
|
99
|
-
model (str, optional): The model CUID. Defaults to None.
|
100
|
-
api_key (str, optional): The API key. Defaults to None.
|
101
|
-
api_secret (str, optional): The API secret. Defaults to None.
|
102
|
-
api_host (str, optional): The API host. Defaults to None.
|
103
|
-
monitoring (str, optional): The ongoing monitoring flag. Defaults to False.
|
104
|
-
|
105
|
-
Raises:
|
106
|
-
ValueError: If the API key and secret are not provided
|
107
|
-
"""
|
108
|
-
global _api_key, _api_secret, _api_host, _run_cuid, _model_cuid, _monitoring
|
109
|
-
|
110
|
-
if api_key == "...":
|
111
|
-
# special case to detect when running a notebook with the standard init snippet
|
112
|
-
# will override with environment variables so we don't have to keep updating
|
113
|
-
# the notebook
|
114
|
-
api_host = api_key = api_secret = project = None
|
115
|
-
|
116
|
-
_model_cuid = project or model or os.getenv("VM_API_MODEL")
|
117
|
-
|
118
|
-
if _model_cuid is None:
|
119
|
-
raise MissingProjectIdError()
|
120
|
-
|
121
|
-
_api_key = api_key or os.getenv("VM_API_KEY")
|
122
|
-
_api_secret = api_secret or os.getenv("VM_API_SECRET")
|
123
|
-
|
124
|
-
if _api_key is None or _api_secret is None:
|
125
|
-
raise MissingAPICredentialsError()
|
126
|
-
|
127
|
-
_api_host = api_host or os.getenv(
|
128
|
-
"VM_API_HOST", "http://127.0.0.1:5000/api/v1/tracking/"
|
129
|
-
)
|
130
|
-
|
131
|
-
_run_cuid = os.getenv("VM_RUN_CUID", None)
|
132
|
-
|
133
|
-
_monitoring = monitoring
|
134
|
-
|
135
|
-
try:
|
136
|
-
__ping()
|
137
|
-
except Exception as e:
|
138
|
-
# if the api host is https, assume we're not in dev mode and send to sentry
|
139
|
-
if _api_host.startswith("https://"):
|
140
|
-
send_single_error(e)
|
141
|
-
raise e
|
142
|
-
|
143
|
-
|
144
74
|
def _get_session() -> aiohttp.ClientSession:
|
145
75
|
"""Initializes the async client session"""
|
146
76
|
global __api_session
|
147
77
|
|
148
|
-
if __api_session
|
149
|
-
__api_session = aiohttp.ClientSession(
|
150
|
-
|
151
|
-
|
152
|
-
"X-API-KEY": _api_key,
|
153
|
-
"X-API-SECRET": _api_secret,
|
154
|
-
"X-PROJECT-CUID": _model_cuid,
|
155
|
-
"X-MONITORING": str(_monitoring),
|
156
|
-
}
|
78
|
+
if not __api_session or __api_session.closed:
|
79
|
+
__api_session = aiohttp.ClientSession(
|
80
|
+
headers=_get_api_headers(),
|
81
|
+
timeout=aiohttp.ClientTimeout(total=30),
|
157
82
|
)
|
158
83
|
|
159
84
|
return __api_session
|
160
85
|
|
161
86
|
|
162
|
-
def
|
163
|
-
"""Validates that we can connect to the ValidMind API (does not use the async session)"""
|
164
|
-
r = requests.get(
|
165
|
-
__get_url("ping", should_start_run=False),
|
166
|
-
headers={
|
167
|
-
"X-API-KEY": _api_key,
|
168
|
-
"X-API-SECRET": _api_secret,
|
169
|
-
"X-PROJECT-CUID": _model_cuid,
|
170
|
-
"X-MONITORING": str(_monitoring),
|
171
|
-
},
|
172
|
-
)
|
173
|
-
if r.status_code != 200:
|
174
|
-
raise_api_error(r.text)
|
175
|
-
|
176
|
-
client_info = r.json()
|
177
|
-
|
178
|
-
init_sentry(client_info.get("sentry_config", {}))
|
179
|
-
|
180
|
-
# Only show this confirmation the first time we connect to the API
|
181
|
-
ack_connected = False
|
182
|
-
if client_config.project is None:
|
183
|
-
ack_connected = True
|
184
|
-
|
185
|
-
# TODO: use model object when backend moves away from project
|
186
|
-
client_config.project = client_info["project"]
|
187
|
-
client_config.documentation_template = client_info.get("documentation_template", {})
|
188
|
-
client_config.feature_flags = client_info.get("feature_flags", {})
|
189
|
-
client_config.model = client_info.get("model", {})
|
190
|
-
client_config.document_type = client_info.get(
|
191
|
-
"document_type", "model_documentation"
|
192
|
-
)
|
193
|
-
|
194
|
-
if ack_connected:
|
195
|
-
if client_config.model:
|
196
|
-
logger.info(
|
197
|
-
f"🎉 Connected to ValidMind!\n"
|
198
|
-
f"📊 Model: {client_config.model.get('name', 'N/A')} "
|
199
|
-
f"(ID: {client_config.model.get('cuid', 'N/A')})\n"
|
200
|
-
f"📁 Document Type: {client_config.document_type}"
|
201
|
-
)
|
202
|
-
else:
|
203
|
-
logger.info(
|
204
|
-
f"Connected to ValidMind... Current Model: {client_config.project['name']}"
|
205
|
-
f" ({client_config.project['cuid']})"
|
206
|
-
)
|
207
|
-
|
208
|
-
|
209
|
-
def reload():
|
210
|
-
"""Reconnect to the ValidMind API and reload the project configuration"""
|
211
|
-
|
212
|
-
try:
|
213
|
-
__ping()
|
214
|
-
except Exception as e:
|
215
|
-
# if the api host is https, assume we're not in dev mode and send to sentry
|
216
|
-
if _api_host.startswith("https://"):
|
217
|
-
send_single_error(e)
|
218
|
-
raise e
|
219
|
-
|
220
|
-
|
221
|
-
def __get_url(
|
87
|
+
def _get_url(
|
222
88
|
endpoint: str,
|
223
89
|
params: Optional[Dict[str, str]] = None,
|
224
|
-
should_start_run: bool = True,
|
225
90
|
) -> str:
|
226
91
|
global _api_host
|
227
92
|
|
228
93
|
params = params or {}
|
229
94
|
|
230
|
-
if not _run_cuid and should_start_run:
|
231
|
-
start_run()
|
232
|
-
|
233
|
-
if should_start_run:
|
234
|
-
params["run_cuid"] = _run_cuid
|
235
|
-
|
236
95
|
if not _api_host.endswith("/"):
|
237
96
|
_api_host += "/"
|
238
97
|
|
@@ -245,9 +104,8 @@ def __get_url(
|
|
245
104
|
async def _get(
|
246
105
|
endpoint: str, params: Optional[Dict[str, str]] = None
|
247
106
|
) -> Dict[str, Any]:
|
248
|
-
url =
|
107
|
+
url = _get_url(endpoint, params)
|
249
108
|
session = _get_session()
|
250
|
-
session.headers.update({"X-RUN-CUID": _run_cuid})
|
251
109
|
|
252
110
|
async with session.get(url) as r:
|
253
111
|
if r.status != 200:
|
@@ -262,9 +120,8 @@ async def _post(
|
|
262
120
|
data: Optional[Union[dict, FormData]] = None,
|
263
121
|
files: Optional[Dict[str, Tuple[str, BytesIO, str]]] = None,
|
264
122
|
) -> Dict[str, Any]:
|
265
|
-
url =
|
123
|
+
url = _get_url(endpoint, params)
|
266
124
|
session = _get_session()
|
267
|
-
session.headers.update({"X-RUN-CUID": _run_cuid})
|
268
125
|
|
269
126
|
if not isinstance(data, (dict)) and files is not None:
|
270
127
|
raise ValueError("Cannot pass both non-json data and file objects to _post")
|
@@ -292,20 +149,99 @@ async def _post(
|
|
292
149
|
return await r.json()
|
293
150
|
|
294
151
|
|
295
|
-
|
296
|
-
"""
|
152
|
+
def _ping() -> Dict[str, Any]:
|
153
|
+
"""Validates that we can connect to the ValidMind API (does not use the async session)"""
|
154
|
+
r = requests.get(
|
155
|
+
url=_get_url("ping"),
|
156
|
+
headers=_get_api_headers(),
|
157
|
+
)
|
158
|
+
if r.status_code != 200:
|
159
|
+
raise_api_error(r.text)
|
160
|
+
|
161
|
+
client_info = r.json()
|
162
|
+
|
163
|
+
init_sentry(client_info.get("sentry_config", {}))
|
164
|
+
|
165
|
+
# Only show this confirmation the first time we connect to the API
|
166
|
+
ack_connected = not client_config.model
|
167
|
+
|
168
|
+
client_config.documentation_template = client_info.get("documentation_template", {})
|
169
|
+
client_config.feature_flags = client_info.get("feature_flags", {})
|
170
|
+
client_config.model = client_info.get("model", {})
|
171
|
+
client_config.document_type = client_info.get(
|
172
|
+
"document_type", "model_documentation"
|
173
|
+
)
|
174
|
+
|
175
|
+
if ack_connected:
|
176
|
+
logger.info(
|
177
|
+
f"🎉 Connected to ValidMind!\n"
|
178
|
+
f"📊 Model: {client_config.model.get('name', 'N/A')} "
|
179
|
+
f"(ID: {client_config.model.get('cuid', 'N/A')})\n"
|
180
|
+
f"📁 Document Type: {client_config.document_type}"
|
181
|
+
)
|
182
|
+
|
183
|
+
|
184
|
+
def init(
|
185
|
+
project: Optional[str] = None,
|
186
|
+
api_key: Optional[str] = None,
|
187
|
+
api_secret: Optional[str] = None,
|
188
|
+
api_host: Optional[str] = None,
|
189
|
+
model: Optional[str] = None,
|
190
|
+
monitoring=False,
|
191
|
+
):
|
192
|
+
"""
|
193
|
+
Initializes the API client instances and calls the /ping endpoint to ensure
|
194
|
+
the provided credentials are valid and we can connect to the ValidMind API.
|
195
|
+
|
196
|
+
If the API key and secret are not provided, the client will attempt to
|
197
|
+
retrieve them from the environment variables `VM_API_KEY` and `VM_API_SECRET`.
|
297
198
|
|
298
199
|
Args:
|
299
|
-
|
200
|
+
project (str, optional): The project CUID. Alias for model. Defaults to None. [DEPRECATED]
|
201
|
+
model (str, optional): The model CUID. Defaults to None.
|
202
|
+
api_key (str, optional): The API key. Defaults to None.
|
203
|
+
api_secret (str, optional): The API secret. Defaults to None.
|
204
|
+
api_host (str, optional): The API host. Defaults to None.
|
205
|
+
monitoring (str, optional): The ongoing monitoring flag. Defaults to False.
|
300
206
|
|
301
207
|
Raises:
|
302
|
-
|
303
|
-
|
304
|
-
Returns:
|
305
|
-
dict: Metadata object
|
208
|
+
ValueError: If the API key and secret are not provided
|
306
209
|
"""
|
307
|
-
|
308
|
-
|
210
|
+
global _api_key, _api_secret, _api_host, _model_cuid, _monitoring
|
211
|
+
|
212
|
+
if api_key == "...":
|
213
|
+
# special case to detect when running a notebook placeholder (...)
|
214
|
+
# will override with environment variables for easier local development
|
215
|
+
api_host = api_key = api_secret = project = None
|
216
|
+
|
217
|
+
_model_cuid = project or model or os.getenv("VM_API_MODEL")
|
218
|
+
if _model_cuid is None:
|
219
|
+
raise MissingModelIdError()
|
220
|
+
|
221
|
+
_api_key = api_key or os.getenv("VM_API_KEY")
|
222
|
+
_api_secret = api_secret or os.getenv("VM_API_SECRET")
|
223
|
+
if _api_key is None or _api_secret is None:
|
224
|
+
raise MissingAPICredentialsError()
|
225
|
+
|
226
|
+
_api_host = api_host or os.getenv(
|
227
|
+
"VM_API_HOST", "http://localhost:5000/api/v1/tracking/"
|
228
|
+
)
|
229
|
+
|
230
|
+
_monitoring = monitoring
|
231
|
+
|
232
|
+
reload()
|
233
|
+
|
234
|
+
|
235
|
+
def reload():
|
236
|
+
"""Reconnect to the ValidMind API and reload the project configuration"""
|
237
|
+
|
238
|
+
try:
|
239
|
+
_ping()
|
240
|
+
except Exception as e:
|
241
|
+
# if the api host is https, assume we're not in dev mode and send to sentry
|
242
|
+
if _api_host.startswith("https://"):
|
243
|
+
send_single_error(e)
|
244
|
+
raise e
|
309
245
|
|
310
246
|
|
311
247
|
async def log_figure(figure: Figure) -> Dict[str, Any]:
|
@@ -331,19 +267,20 @@ async def log_figure(figure: Figure) -> Dict[str, Any]:
|
|
331
267
|
raise e
|
332
268
|
|
333
269
|
|
334
|
-
async def
|
335
|
-
"""
|
270
|
+
async def get_metadata(content_id: str) -> Dict[str, Any]:
|
271
|
+
"""Gets a metadata object from ValidMind API.
|
336
272
|
|
337
273
|
Args:
|
338
|
-
|
274
|
+
content_id (str): Unique content identifier for the metadata
|
339
275
|
|
340
276
|
Raises:
|
341
277
|
Exception: If the API call fails
|
342
278
|
|
343
279
|
Returns:
|
344
|
-
dict:
|
280
|
+
dict: Metadata object
|
345
281
|
"""
|
346
|
-
|
282
|
+
# TODO: add a more accurate type hint/documentation
|
283
|
+
return await _get(f"get_metadata/{content_id}")
|
347
284
|
|
348
285
|
|
349
286
|
async def log_metadata(
|
@@ -380,8 +317,8 @@ async def log_metadata(
|
|
380
317
|
raise e
|
381
318
|
|
382
319
|
|
383
|
-
async def
|
384
|
-
|
320
|
+
async def log_metric_result(
|
321
|
+
metric: MetricResult,
|
385
322
|
inputs: List[str],
|
386
323
|
output_template: str = None,
|
387
324
|
section_id: str = None,
|
@@ -390,7 +327,7 @@ async def log_metrics(
|
|
390
327
|
"""Logs metrics to ValidMind API.
|
391
328
|
|
392
329
|
Args:
|
393
|
-
|
330
|
+
metric (MetricResult): A MetricResult object
|
394
331
|
inputs (list): A list of input keys (names) that were used to run the test
|
395
332
|
output_template (str): The optional output template for the test
|
396
333
|
section_id (str): The section ID add a test driven block to the documentation
|
@@ -408,24 +345,18 @@ async def log_metrics(
|
|
408
345
|
if position is not None:
|
409
346
|
request_params["position"] = position
|
410
347
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
}
|
418
|
-
|
419
|
-
if output_template:
|
420
|
-
metric_data["output_template"] = output_template
|
421
|
-
|
422
|
-
data.append(metric_data)
|
348
|
+
metric_data = {
|
349
|
+
**metric.serialize(),
|
350
|
+
"inputs": inputs,
|
351
|
+
}
|
352
|
+
if output_template:
|
353
|
+
metric_data["output_template"] = output_template
|
423
354
|
|
424
355
|
try:
|
425
356
|
return await _post(
|
426
357
|
"log_metrics",
|
427
358
|
params=request_params,
|
428
|
-
data=json.dumps(
|
359
|
+
data=json.dumps([metric_data], cls=NumpyEncoder, allow_nan=False),
|
429
360
|
)
|
430
361
|
except Exception as e:
|
431
362
|
logger.error("Error logging metrics to ValidMind API")
|
@@ -479,35 +410,6 @@ async def log_test_result(
|
|
479
410
|
raise e
|
480
411
|
|
481
412
|
|
482
|
-
def log_test_results(
|
483
|
-
results: List[ThresholdTestResults], inputs
|
484
|
-
) -> List[Callable[..., Dict[str, Any]]]:
|
485
|
-
"""Logs test results information
|
486
|
-
|
487
|
-
This method will be called automatically be any function
|
488
|
-
running tests but can also be called directly if the user wants to run tests on their own.
|
489
|
-
|
490
|
-
Args:
|
491
|
-
results (list): A list of ThresholdTestResults objects
|
492
|
-
inputs (list): A list of input IDs that were used to run the test
|
493
|
-
|
494
|
-
Raises:
|
495
|
-
Exception: If the API call fails
|
496
|
-
|
497
|
-
Returns:
|
498
|
-
list: list of responses from the API
|
499
|
-
"""
|
500
|
-
try:
|
501
|
-
responses = [] # TODO: use asyncio.gather
|
502
|
-
for result in results:
|
503
|
-
responses.append(run_async(log_test_result, result, inputs))
|
504
|
-
except Exception as e:
|
505
|
-
logger.error("Error logging test results to ValidMind API")
|
506
|
-
raise e
|
507
|
-
|
508
|
-
return responses
|
509
|
-
|
510
|
-
|
511
413
|
def log_input(input_id: str, type: str, metadata: Dict[str, Any]) -> Dict[str, Any]:
|
512
414
|
"""Logs input information - internal use for now (don't expose via public API)
|
513
415
|
|
@@ -547,7 +449,7 @@ async def alog_metric(
|
|
547
449
|
inputs: Optional[List[str]] = None,
|
548
450
|
params: Optional[Dict[str, Any]] = None,
|
549
451
|
recorded_at: Optional[str] = None,
|
550
|
-
)
|
452
|
+
):
|
551
453
|
"""See log_metric for details"""
|
552
454
|
if not key or not isinstance(key, str):
|
553
455
|
raise ValueError("`key` must be a non-empty string")
|
@@ -581,7 +483,7 @@ def log_metric(
|
|
581
483
|
inputs: Optional[List[str]] = None,
|
582
484
|
params: Optional[Dict[str, Any]] = None,
|
583
485
|
recorded_at: Optional[str] = None,
|
584
|
-
)
|
486
|
+
):
|
585
487
|
"""Logs a unit metric
|
586
488
|
|
587
489
|
Unit metrics are key-value pairs where the key is the metric name and the value is
|
@@ -601,48 +503,11 @@ def log_metric(
|
|
601
503
|
run_async(alog_metric, key, value, inputs, params, recorded_at)
|
602
504
|
|
603
505
|
|
604
|
-
def
|
605
|
-
"""Starts a new test run
|
606
|
-
|
607
|
-
This function will take care of updating the api client with the new run CUID
|
608
|
-
and will be called automatically when logging starts if no run CUID is set.
|
609
|
-
|
610
|
-
Raises:
|
611
|
-
Exception: If the API call fails
|
612
|
-
|
613
|
-
Returns:
|
614
|
-
str: The test run CUID
|
615
|
-
"""
|
616
|
-
global _run_cuid
|
617
|
-
|
618
|
-
r = requests.post(
|
619
|
-
__get_url("start_run", should_start_run=False),
|
620
|
-
headers={
|
621
|
-
"X-API-KEY": _api_key,
|
622
|
-
"X-API-SECRET": _api_secret,
|
623
|
-
"X-PROJECT-CUID": _model_cuid,
|
624
|
-
},
|
625
|
-
)
|
626
|
-
|
627
|
-
if r.status_code != 200:
|
628
|
-
logger.error("Could not start data logging run with ValidMind API")
|
629
|
-
raise_api_error(r.text)
|
630
|
-
|
631
|
-
test_run = r.json()
|
632
|
-
_run_cuid = test_run["cuid"]
|
633
|
-
|
634
|
-
return test_run["cuid"]
|
635
|
-
|
636
|
-
|
637
|
-
def get_ai_key() -> str:
|
506
|
+
def get_ai_key() -> Dict[str, Any]:
|
638
507
|
"""Calls the api to get an api key for our LLM proxy"""
|
639
508
|
r = requests.get(
|
640
|
-
|
641
|
-
headers=
|
642
|
-
"X-API-KEY": _api_key,
|
643
|
-
"X-API-SECRET": _api_secret,
|
644
|
-
"X-PROJECT-CUID": _model_cuid,
|
645
|
-
},
|
509
|
+
url=_get_url("ai/key"),
|
510
|
+
headers=_get_api_headers(),
|
646
511
|
)
|
647
512
|
|
648
513
|
if r.status_code != 200:
|
validmind/client_config.py
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# SPDX-License-Identifier: AGPL-3.0 AND ValidMind Commercial
|
4
4
|
|
5
5
|
"""
|
6
|
-
Central class to track configuration of the
|
6
|
+
Central class to track configuration of the library
|
7
7
|
client against the ValidMind API
|
8
8
|
"""
|
9
9
|
|
@@ -17,7 +17,6 @@ class ClientConfig:
|
|
17
17
|
when initializing the API client.
|
18
18
|
"""
|
19
19
|
|
20
|
-
project: object
|
21
20
|
model: object
|
22
21
|
feature_flags: dict
|
23
22
|
document_type: str
|
@@ -42,7 +41,6 @@ class ClientConfig:
|
|
42
41
|
|
43
42
|
|
44
43
|
client_config = ClientConfig(
|
45
|
-
project=None,
|
46
44
|
model=None,
|
47
45
|
feature_flags={},
|
48
46
|
document_type="model_documentation",
|
validmind/datasets/__init__.py
CHANGED