hiddenlayer-sdk 2.0.5__py3-none-any.whl → 2.0.7__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.
- hiddenlayer/__init__.py +6 -2
- hiddenlayer/sdk/constants.py +11 -0
- hiddenlayer/sdk/rest/__init__.py +2 -1
- hiddenlayer/sdk/rest/api/model_supply_chain_api.py +13 -12
- hiddenlayer/sdk/rest/models/__init__.py +2 -1
- hiddenlayer/sdk/rest/models/scan_job.py +11 -5
- hiddenlayer/sdk/rest/models/scan_job_access.py +97 -0
- hiddenlayer/sdk/rest/models/scan_model_details_v31.py +93 -0
- hiddenlayer/sdk/services/model_scan.py +68 -10
- hiddenlayer/sdk/version.py +1 -1
- {hiddenlayer_sdk-2.0.5.dist-info → hiddenlayer_sdk-2.0.7.dist-info}/METADATA +3 -2
- {hiddenlayer_sdk-2.0.5.dist-info → hiddenlayer_sdk-2.0.7.dist-info}/RECORD +15 -14
- {hiddenlayer_sdk-2.0.5.dist-info → hiddenlayer_sdk-2.0.7.dist-info}/WHEEL +1 -1
- hiddenlayer/sdk/rest/models/scan_job_inventory.py +0 -137
- {hiddenlayer_sdk-2.0.5.dist-info → hiddenlayer_sdk-2.0.7.dist-info/licenses}/LICENSE +0 -0
- {hiddenlayer_sdk-2.0.5.dist-info → hiddenlayer_sdk-2.0.7.dist-info}/top_level.txt +0 -0
hiddenlayer/__init__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import os
|
2
|
-
from typing import Optional
|
2
|
+
from typing import Callable, Optional
|
3
3
|
|
4
4
|
import requests
|
5
5
|
from requests.auth import HTTPBasicAuth
|
@@ -36,6 +36,7 @@ class HiddenlayerServiceClient:
|
|
36
36
|
):
|
37
37
|
self.host = host.strip()
|
38
38
|
self.is_saas = is_saas(host)
|
39
|
+
refresh_jwt_func: Optional[Callable[[], str]] = None
|
39
40
|
|
40
41
|
if self.is_saas:
|
41
42
|
if not api_id:
|
@@ -50,13 +51,16 @@ class HiddenlayerServiceClient:
|
|
50
51
|
|
51
52
|
jwt_token = self._get_jwt(api_id=api_id, api_key=api_key)
|
52
53
|
self._config = Configuration(host=self.host, access_token=jwt_token)
|
54
|
+
refresh_jwt_func = lambda: self._get_jwt(api_id=api_id, api_key=api_key)
|
53
55
|
|
54
56
|
else:
|
55
57
|
self._config = Configuration(host=self.host)
|
56
58
|
|
57
59
|
self._api_client = ApiClient(configuration=self._config)
|
58
60
|
self._aidr_predictive = AIDRPredictive(self._api_client)
|
59
|
-
self._model_scan = ModelScanAPI(
|
61
|
+
self._model_scan = ModelScanAPI(
|
62
|
+
self._api_client, refresh_token_func=refresh_jwt_func
|
63
|
+
)
|
60
64
|
|
61
65
|
def _get_jwt(self, *, api_id: str, api_key: str) -> str:
|
62
66
|
"Get the JWT token to auth to the Hiddenlayer API."
|
hiddenlayer/sdk/constants.py
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
|
3
3
|
|
4
|
+
class CommunityScanSource(str, Enum):
|
5
|
+
LOCAL = "LOCAL"
|
6
|
+
AWS_PRESIGNED = "AWS_PRESIGNED"
|
7
|
+
AWS_IAM_ROLE = "AWS_IAM_ROLE"
|
8
|
+
AZURE_BLOB_SAS = "AZURE_BLOB_SAS"
|
9
|
+
AZURE_BLOB_AD = "AZURE_BLOB_AD"
|
10
|
+
GOOGLE_SIGNED = "GOOGLE_SIGNED"
|
11
|
+
GOOGLE_OAUTH = "GOOGLE_OAUTH"
|
12
|
+
HUGGING_FACE = "HUGGING_FACE"
|
13
|
+
|
14
|
+
|
4
15
|
class ScanStatus(str, Enum):
|
5
16
|
DONE = "done"
|
6
17
|
ACCEPTED = "accepted"
|
hiddenlayer/sdk/rest/__init__.py
CHANGED
@@ -103,8 +103,9 @@ from hiddenlayer.sdk.rest.models.scan_detection_v3 import ScanDetectionV3
|
|
103
103
|
from hiddenlayer.sdk.rest.models.scan_detection_v31 import ScanDetectionV31
|
104
104
|
from hiddenlayer.sdk.rest.models.scan_header_v3 import ScanHeaderV3
|
105
105
|
from hiddenlayer.sdk.rest.models.scan_job import ScanJob
|
106
|
-
from hiddenlayer.sdk.rest.models.
|
106
|
+
from hiddenlayer.sdk.rest.models.scan_job_access import ScanJobAccess
|
107
107
|
from hiddenlayer.sdk.rest.models.scan_model_details_v3 import ScanModelDetailsV3
|
108
|
+
from hiddenlayer.sdk.rest.models.scan_model_details_v31 import ScanModelDetailsV31
|
108
109
|
from hiddenlayer.sdk.rest.models.scan_model_ids_v3 import ScanModelIdsV3
|
109
110
|
from hiddenlayer.sdk.rest.models.scan_report_v3 import ScanReportV3
|
110
111
|
from hiddenlayer.sdk.rest.models.scan_results_map_v3 import ScanResultsMapV3
|
@@ -1166,7 +1166,7 @@ class ModelSupplyChainApi:
|
|
1166
1166
|
_content_type: Optional[StrictStr] = None,
|
1167
1167
|
_headers: Optional[Dict[StrictStr, Any]] = None,
|
1168
1168
|
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
1169
|
-
) ->
|
1169
|
+
) -> ScanReportV3:
|
1170
1170
|
"""Request a Model Scan Job
|
1171
1171
|
|
1172
1172
|
|
@@ -1203,7 +1203,7 @@ class ModelSupplyChainApi:
|
|
1203
1203
|
)
|
1204
1204
|
|
1205
1205
|
_response_types_map: Dict[str, Optional[str]] = {
|
1206
|
-
'201':
|
1206
|
+
'201': "ScanReportV3",
|
1207
1207
|
'400': None,
|
1208
1208
|
'422': "ProblemDetails",
|
1209
1209
|
}
|
@@ -1234,7 +1234,7 @@ class ModelSupplyChainApi:
|
|
1234
1234
|
_content_type: Optional[StrictStr] = None,
|
1235
1235
|
_headers: Optional[Dict[StrictStr, Any]] = None,
|
1236
1236
|
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
1237
|
-
) -> ApiResponse[
|
1237
|
+
) -> ApiResponse[ScanReportV3]:
|
1238
1238
|
"""Request a Model Scan Job
|
1239
1239
|
|
1240
1240
|
|
@@ -1271,7 +1271,7 @@ class ModelSupplyChainApi:
|
|
1271
1271
|
)
|
1272
1272
|
|
1273
1273
|
_response_types_map: Dict[str, Optional[str]] = {
|
1274
|
-
'201':
|
1274
|
+
'201': "ScanReportV3",
|
1275
1275
|
'400': None,
|
1276
1276
|
'422': "ProblemDetails",
|
1277
1277
|
}
|
@@ -1339,7 +1339,7 @@ class ModelSupplyChainApi:
|
|
1339
1339
|
)
|
1340
1340
|
|
1341
1341
|
_response_types_map: Dict[str, Optional[str]] = {
|
1342
|
-
'201':
|
1342
|
+
'201': "ScanReportV3",
|
1343
1343
|
'400': None,
|
1344
1344
|
'422': "ProblemDetails",
|
1345
1345
|
}
|
@@ -1383,6 +1383,7 @@ class ModelSupplyChainApi:
|
|
1383
1383
|
# set the HTTP header `Accept`
|
1384
1384
|
_header_params['Accept'] = self.api_client.select_header_accept(
|
1385
1385
|
[
|
1386
|
+
'application/json; charset=utf-8',
|
1386
1387
|
'application/json'
|
1387
1388
|
]
|
1388
1389
|
)
|
@@ -1409,7 +1410,7 @@ class ModelSupplyChainApi:
|
|
1409
1410
|
|
1410
1411
|
return self.api_client.param_serialize(
|
1411
1412
|
method='POST',
|
1412
|
-
resource_path='/
|
1413
|
+
resource_path='/scan/v3/jobs',
|
1413
1414
|
path_params=_path_params,
|
1414
1415
|
query_params=_query_params,
|
1415
1416
|
header_params=_header_params,
|
@@ -1878,7 +1879,7 @@ class ModelSupplyChainApi:
|
|
1878
1879
|
_content_type: Optional[StrictStr] = None,
|
1879
1880
|
_headers: Optional[Dict[StrictStr, Any]] = None,
|
1880
1881
|
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
1881
|
-
) ->
|
1882
|
+
) -> ScanJob:
|
1882
1883
|
"""List all Model Scan Jobs
|
1883
1884
|
|
1884
1885
|
|
@@ -1912,7 +1913,7 @@ class ModelSupplyChainApi:
|
|
1912
1913
|
)
|
1913
1914
|
|
1914
1915
|
_response_types_map: Dict[str, Optional[str]] = {
|
1915
|
-
'200': "
|
1916
|
+
'200': "ScanJob",
|
1916
1917
|
'400': None,
|
1917
1918
|
'404': None,
|
1918
1919
|
}
|
@@ -1942,7 +1943,7 @@ class ModelSupplyChainApi:
|
|
1942
1943
|
_content_type: Optional[StrictStr] = None,
|
1943
1944
|
_headers: Optional[Dict[StrictStr, Any]] = None,
|
1944
1945
|
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
1945
|
-
) -> ApiResponse[
|
1946
|
+
) -> ApiResponse[ScanJob]:
|
1946
1947
|
"""List all Model Scan Jobs
|
1947
1948
|
|
1948
1949
|
|
@@ -1976,7 +1977,7 @@ class ModelSupplyChainApi:
|
|
1976
1977
|
)
|
1977
1978
|
|
1978
1979
|
_response_types_map: Dict[str, Optional[str]] = {
|
1979
|
-
'200': "
|
1980
|
+
'200': "ScanJob",
|
1980
1981
|
'400': None,
|
1981
1982
|
'404': None,
|
1982
1983
|
}
|
@@ -2040,7 +2041,7 @@ class ModelSupplyChainApi:
|
|
2040
2041
|
)
|
2041
2042
|
|
2042
2043
|
_response_types_map: Dict[str, Optional[str]] = {
|
2043
|
-
'200': "
|
2044
|
+
'200': "ScanJob",
|
2044
2045
|
'400': None,
|
2045
2046
|
'404': None,
|
2046
2047
|
}
|
@@ -2093,7 +2094,7 @@ class ModelSupplyChainApi:
|
|
2093
2094
|
|
2094
2095
|
return self.api_client.param_serialize(
|
2095
2096
|
method='GET',
|
2096
|
-
resource_path='/
|
2097
|
+
resource_path='/scan/v3/jobs',
|
2097
2098
|
path_params=_path_params,
|
2098
2099
|
query_params=_query_params,
|
2099
2100
|
header_params=_header_params,
|
@@ -81,8 +81,9 @@ from hiddenlayer.sdk.rest.models.scan_detection_v3 import ScanDetectionV3
|
|
81
81
|
from hiddenlayer.sdk.rest.models.scan_detection_v31 import ScanDetectionV31
|
82
82
|
from hiddenlayer.sdk.rest.models.scan_header_v3 import ScanHeaderV3
|
83
83
|
from hiddenlayer.sdk.rest.models.scan_job import ScanJob
|
84
|
-
from hiddenlayer.sdk.rest.models.
|
84
|
+
from hiddenlayer.sdk.rest.models.scan_job_access import ScanJobAccess
|
85
85
|
from hiddenlayer.sdk.rest.models.scan_model_details_v3 import ScanModelDetailsV3
|
86
|
+
from hiddenlayer.sdk.rest.models.scan_model_details_v31 import ScanModelDetailsV31
|
86
87
|
from hiddenlayer.sdk.rest.models.scan_model_ids_v3 import ScanModelIdsV3
|
87
88
|
from hiddenlayer.sdk.rest.models.scan_report_v3 import ScanReportV3
|
88
89
|
from hiddenlayer.sdk.rest.models.scan_results_map_v3 import ScanResultsMapV3
|
@@ -19,7 +19,8 @@ import json
|
|
19
19
|
|
20
20
|
from pydantic import BaseModel, ConfigDict, Field, StrictStr, field_validator
|
21
21
|
from typing import Any, ClassVar, Dict, List, Optional
|
22
|
-
from hiddenlayer.sdk.rest.models.
|
22
|
+
from hiddenlayer.sdk.rest.models.scan_job_access import ScanJobAccess
|
23
|
+
from hiddenlayer.sdk.rest.models.scan_model_details_v31 import ScanModelDetailsV31
|
23
24
|
from typing import Optional, Set
|
24
25
|
from typing_extensions import Self
|
25
26
|
|
@@ -27,10 +28,11 @@ class ScanJob(BaseModel):
|
|
27
28
|
"""
|
28
29
|
ScanJob
|
29
30
|
""" # noqa: E501
|
31
|
+
access: Optional[ScanJobAccess] = None
|
32
|
+
inventory: Optional[ScanModelDetailsV31] = None
|
30
33
|
scan_id: Optional[StrictStr] = Field(default=None, description="unique identifier for the scan")
|
31
34
|
status: Optional[StrictStr] = Field(default=None, description="Status of the scan")
|
32
|
-
|
33
|
-
__properties: ClassVar[List[str]] = ["scan_id", "status", "inventory"]
|
35
|
+
__properties: ClassVar[List[str]] = ["access", "inventory", "scan_id", "status"]
|
34
36
|
|
35
37
|
@field_validator('status')
|
36
38
|
def status_validate_enum(cls, value):
|
@@ -85,6 +87,9 @@ class ScanJob(BaseModel):
|
|
85
87
|
exclude=excluded_fields,
|
86
88
|
exclude_none=True,
|
87
89
|
)
|
90
|
+
# override the default output from pydantic by calling `to_dict()` of access
|
91
|
+
if self.access:
|
92
|
+
_dict['access'] = self.access.to_dict()
|
88
93
|
# override the default output from pydantic by calling `to_dict()` of inventory
|
89
94
|
if self.inventory:
|
90
95
|
_dict['inventory'] = self.inventory.to_dict()
|
@@ -100,9 +105,10 @@ class ScanJob(BaseModel):
|
|
100
105
|
return cls.model_validate(obj)
|
101
106
|
|
102
107
|
_obj = cls.model_validate({
|
108
|
+
"access": ScanJobAccess.from_dict(obj["access"]) if obj.get("access") is not None else None,
|
109
|
+
"inventory": ScanModelDetailsV31.from_dict(obj["inventory"]) if obj.get("inventory") is not None else None,
|
103
110
|
"scan_id": obj.get("scan_id"),
|
104
|
-
"status": obj.get("status")
|
105
|
-
"inventory": ScanJobInventory.from_dict(obj["inventory"]) if obj.get("inventory") is not None else None
|
111
|
+
"status": obj.get("status")
|
106
112
|
})
|
107
113
|
return _obj
|
108
114
|
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
"""
|
4
|
+
HiddenLayer-API
|
5
|
+
|
6
|
+
HiddenLayer-API
|
7
|
+
|
8
|
+
The version of the OpenAPI document: 1
|
9
|
+
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
10
|
+
|
11
|
+
Do not edit the class manually.
|
12
|
+
""" # noqa: E501
|
13
|
+
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
import pprint
|
17
|
+
import re # noqa: F401
|
18
|
+
import json
|
19
|
+
|
20
|
+
from pydantic import BaseModel, ConfigDict, StrictStr, field_validator
|
21
|
+
from typing import Any, ClassVar, Dict, List, Optional
|
22
|
+
from typing import Optional, Set
|
23
|
+
from typing_extensions import Self
|
24
|
+
|
25
|
+
class ScanJobAccess(BaseModel):
|
26
|
+
"""
|
27
|
+
ScanJobAccess
|
28
|
+
""" # noqa: E501
|
29
|
+
source: Optional[StrictStr] = None
|
30
|
+
__properties: ClassVar[List[str]] = ["source"]
|
31
|
+
|
32
|
+
@field_validator('source')
|
33
|
+
def source_validate_enum(cls, value):
|
34
|
+
"""Validates the enum"""
|
35
|
+
if value is None:
|
36
|
+
return value
|
37
|
+
|
38
|
+
if value not in set(['LOCAL', 'AWS_PRESIGNED', 'AWS_IAM_ROLE', 'AZURE_BLOB_SAS', 'AZURE_BLOB_AD', 'GOOGLE_SIGNED', 'GOOGLE_OAUTH', 'HUGGING_FACE']):
|
39
|
+
raise ValueError("must be one of enum values ('LOCAL', 'AWS_PRESIGNED', 'AWS_IAM_ROLE', 'AZURE_BLOB_SAS', 'AZURE_BLOB_AD', 'GOOGLE_SIGNED', 'GOOGLE_OAUTH', 'HUGGING_FACE')")
|
40
|
+
return value
|
41
|
+
|
42
|
+
model_config = ConfigDict(
|
43
|
+
populate_by_name=True,
|
44
|
+
validate_assignment=True,
|
45
|
+
protected_namespaces=(),
|
46
|
+
)
|
47
|
+
|
48
|
+
|
49
|
+
def to_str(self) -> str:
|
50
|
+
"""Returns the string representation of the model using alias"""
|
51
|
+
return pprint.pformat(self.model_dump(by_alias=True))
|
52
|
+
|
53
|
+
def to_json(self) -> str:
|
54
|
+
"""Returns the JSON representation of the model using alias"""
|
55
|
+
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
|
56
|
+
return json.dumps(self.to_dict())
|
57
|
+
|
58
|
+
@classmethod
|
59
|
+
def from_json(cls, json_str: str) -> Optional[Self]:
|
60
|
+
"""Create an instance of ScanJobAccess from a JSON string"""
|
61
|
+
return cls.from_dict(json.loads(json_str))
|
62
|
+
|
63
|
+
def to_dict(self) -> Dict[str, Any]:
|
64
|
+
"""Return the dictionary representation of the model using alias.
|
65
|
+
|
66
|
+
This has the following differences from calling pydantic's
|
67
|
+
`self.model_dump(by_alias=True)`:
|
68
|
+
|
69
|
+
* `None` is only added to the output dict for nullable fields that
|
70
|
+
were set at model initialization. Other fields with value `None`
|
71
|
+
are ignored.
|
72
|
+
"""
|
73
|
+
excluded_fields: Set[str] = set([
|
74
|
+
])
|
75
|
+
|
76
|
+
_dict = self.model_dump(
|
77
|
+
by_alias=True,
|
78
|
+
exclude=excluded_fields,
|
79
|
+
exclude_none=True,
|
80
|
+
)
|
81
|
+
return _dict
|
82
|
+
|
83
|
+
@classmethod
|
84
|
+
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
85
|
+
"""Create an instance of ScanJobAccess from a dict"""
|
86
|
+
if obj is None:
|
87
|
+
return None
|
88
|
+
|
89
|
+
if not isinstance(obj, dict):
|
90
|
+
return cls.model_validate(obj)
|
91
|
+
|
92
|
+
_obj = cls.model_validate({
|
93
|
+
"source": obj.get("source")
|
94
|
+
})
|
95
|
+
return _obj
|
96
|
+
|
97
|
+
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
"""
|
4
|
+
HiddenLayer-API
|
5
|
+
|
6
|
+
HiddenLayer-API
|
7
|
+
|
8
|
+
The version of the OpenAPI document: 1
|
9
|
+
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
10
|
+
|
11
|
+
Do not edit the class manually.
|
12
|
+
""" # noqa: E501
|
13
|
+
|
14
|
+
|
15
|
+
from __future__ import annotations
|
16
|
+
import pprint
|
17
|
+
import re # noqa: F401
|
18
|
+
import json
|
19
|
+
|
20
|
+
from pydantic import BaseModel, ConfigDict, Field, StrictStr
|
21
|
+
from typing import Any, ClassVar, Dict, List
|
22
|
+
from typing import Optional, Set
|
23
|
+
from typing_extensions import Self
|
24
|
+
|
25
|
+
class ScanModelDetailsV31(BaseModel):
|
26
|
+
"""
|
27
|
+
ScanModelDetailsV31
|
28
|
+
""" # noqa: E501
|
29
|
+
model_name: StrictStr = Field(description="Name of the model")
|
30
|
+
model_version: StrictStr = Field(description="If you do not provide a version, one will be generated for you.")
|
31
|
+
requested_scan_location: StrictStr = Field(description="Location to be scanned")
|
32
|
+
requesting_entity: StrictStr = Field(description="Entity that requested the scan")
|
33
|
+
__properties: ClassVar[List[str]] = ["model_name", "model_version", "requested_scan_location", "requesting_entity"]
|
34
|
+
|
35
|
+
model_config = ConfigDict(
|
36
|
+
populate_by_name=True,
|
37
|
+
validate_assignment=True,
|
38
|
+
protected_namespaces=(),
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
def to_str(self) -> str:
|
43
|
+
"""Returns the string representation of the model using alias"""
|
44
|
+
return pprint.pformat(self.model_dump(by_alias=True))
|
45
|
+
|
46
|
+
def to_json(self) -> str:
|
47
|
+
"""Returns the JSON representation of the model using alias"""
|
48
|
+
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
|
49
|
+
return json.dumps(self.to_dict())
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def from_json(cls, json_str: str) -> Optional[Self]:
|
53
|
+
"""Create an instance of ScanModelDetailsV31 from a JSON string"""
|
54
|
+
return cls.from_dict(json.loads(json_str))
|
55
|
+
|
56
|
+
def to_dict(self) -> Dict[str, Any]:
|
57
|
+
"""Return the dictionary representation of the model using alias.
|
58
|
+
|
59
|
+
This has the following differences from calling pydantic's
|
60
|
+
`self.model_dump(by_alias=True)`:
|
61
|
+
|
62
|
+
* `None` is only added to the output dict for nullable fields that
|
63
|
+
were set at model initialization. Other fields with value `None`
|
64
|
+
are ignored.
|
65
|
+
"""
|
66
|
+
excluded_fields: Set[str] = set([
|
67
|
+
])
|
68
|
+
|
69
|
+
_dict = self.model_dump(
|
70
|
+
by_alias=True,
|
71
|
+
exclude=excluded_fields,
|
72
|
+
exclude_none=True,
|
73
|
+
)
|
74
|
+
return _dict
|
75
|
+
|
76
|
+
@classmethod
|
77
|
+
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
78
|
+
"""Create an instance of ScanModelDetailsV31 from a dict"""
|
79
|
+
if obj is None:
|
80
|
+
return None
|
81
|
+
|
82
|
+
if not isinstance(obj, dict):
|
83
|
+
return cls.model_validate(obj)
|
84
|
+
|
85
|
+
_obj = cls.model_validate({
|
86
|
+
"model_name": obj.get("model_name"),
|
87
|
+
"model_version": obj.get("model_version"),
|
88
|
+
"requested_scan_location": obj.get("requested_scan_location"),
|
89
|
+
"requesting_entity": obj.get("requesting_entity")
|
90
|
+
})
|
91
|
+
return _obj
|
92
|
+
|
93
|
+
|
@@ -4,14 +4,19 @@ import tempfile
|
|
4
4
|
import time
|
5
5
|
import zipfile
|
6
6
|
from pathlib import Path
|
7
|
-
from typing import List, Optional, Union
|
7
|
+
from typing import Callable, List, Optional, Union
|
8
8
|
|
9
|
-
from hiddenlayer.sdk.constants import ScanStatus
|
9
|
+
from hiddenlayer.sdk.constants import CommunityScanSource, ScanStatus
|
10
10
|
from hiddenlayer.sdk.models import EmptyScanResults, ScanResults
|
11
11
|
from hiddenlayer.sdk.rest.api import ModelSupplyChainApi
|
12
12
|
from hiddenlayer.sdk.rest.api_client import ApiClient
|
13
|
-
from hiddenlayer.sdk.rest.exceptions import NotFoundException
|
14
|
-
from hiddenlayer.sdk.rest.models import
|
13
|
+
from hiddenlayer.sdk.rest.exceptions import NotFoundException, UnauthorizedException
|
14
|
+
from hiddenlayer.sdk.rest.models import (
|
15
|
+
MultiFileUploadRequestV3,
|
16
|
+
ScanJob,
|
17
|
+
ScanJobAccess,
|
18
|
+
ScanModelDetailsV31,
|
19
|
+
)
|
15
20
|
from hiddenlayer.sdk.utils import filter_path_objects
|
16
21
|
|
17
22
|
EXCLUDE_FILE_TYPES = [
|
@@ -27,9 +32,51 @@ EXCLUDE_FILE_TYPES = [
|
|
27
32
|
|
28
33
|
|
29
34
|
class ModelScanAPI:
|
30
|
-
def __init__(
|
35
|
+
def __init__(
|
36
|
+
self,
|
37
|
+
api_client: ApiClient,
|
38
|
+
refresh_token_func: Optional[Callable[[], str]] = None,
|
39
|
+
) -> None:
|
31
40
|
self._api_client = api_client
|
32
41
|
self._model_supply_chain_api = ModelSupplyChainApi(api_client=api_client)
|
42
|
+
self._refresh_token_func = refresh_token_func
|
43
|
+
|
44
|
+
def community_scan(
|
45
|
+
self,
|
46
|
+
model_name: str,
|
47
|
+
model_path: str,
|
48
|
+
model_source: CommunityScanSource,
|
49
|
+
model_version: str = "main",
|
50
|
+
wait_for_results: bool = True,
|
51
|
+
) -> ScanResults:
|
52
|
+
"""
|
53
|
+
Scan a model available at a remote location using the HiddenLayer Model Scanner.
|
54
|
+
|
55
|
+
:param model_name: Name of the model to be shown on the HiddenLayer UI.
|
56
|
+
:param model_path: Path to the model file in the remote location, e.g. a presigned S3 URL
|
57
|
+
:param model_source: type of remote location where the model is stored.
|
58
|
+
:param wait_for_results: True whether to wait for the scan to finish, defaults to True.
|
59
|
+
:param model_version: Version of the model to be shown on the HiddenLayer UI.
|
60
|
+
|
61
|
+
:returns: Scan Results
|
62
|
+
"""
|
63
|
+
scan_job = ScanJob(
|
64
|
+
access=ScanJobAccess(source=model_source),
|
65
|
+
inventory=ScanModelDetailsV31(
|
66
|
+
model_name=model_name,
|
67
|
+
model_version=model_version,
|
68
|
+
requested_scan_location=model_path,
|
69
|
+
requesting_entity="hiddenlayer-python-sdk",
|
70
|
+
),
|
71
|
+
)
|
72
|
+
result = self._model_supply_chain_api.create_scan_job(scan_job)
|
73
|
+
scan_id = result.scan_id
|
74
|
+
if scan_id is None:
|
75
|
+
raise Exception("scan_id must have a value")
|
76
|
+
if wait_for_results:
|
77
|
+
return self._wait_for_scan_results(scan_id=scan_id)
|
78
|
+
else:
|
79
|
+
return ScanResults.from_scanreportv3(scan_report_v3=result)
|
33
80
|
|
34
81
|
def scan_file(
|
35
82
|
self,
|
@@ -286,11 +333,21 @@ class ModelScanAPI:
|
|
286
333
|
|
287
334
|
:returns: Scan results.
|
288
335
|
"""
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
336
|
+
retry = False
|
337
|
+
while True:
|
338
|
+
try:
|
339
|
+
scan_report = self._model_supply_chain_api.get_scan_results(scan_id)
|
340
|
+
break
|
341
|
+
except NotFoundException:
|
342
|
+
return EmptyScanResults()
|
343
|
+
except UnauthorizedException as e:
|
344
|
+
if not retry and self._refresh_token_func:
|
345
|
+
new_token = self._refresh_token_func()
|
346
|
+
self._api_client.configuration.access_token = new_token
|
347
|
+
self._api_client = ApiClient(self._api_client.configuration)
|
348
|
+
retry = True
|
349
|
+
else:
|
350
|
+
raise e
|
294
351
|
|
295
352
|
return ScanResults.from_scanreportv3(scan_report_v3=scan_report)
|
296
353
|
|
@@ -419,6 +476,7 @@ class ModelScanAPI:
|
|
419
476
|
delay = base_delay * 2**retries + random.uniform(
|
420
477
|
0, 1
|
421
478
|
) # exponential back off retry
|
479
|
+
delay = min(delay, 30)
|
422
480
|
time.sleep(delay)
|
423
481
|
scan_results = self.get_scan_results(scan_id=scan_id)
|
424
482
|
print(f"scan status: {scan_results.status}")
|
hiddenlayer/sdk/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
VERSION = "2.0.
|
1
|
+
VERSION = "2.0.7"
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: hiddenlayer-sdk
|
3
|
-
Version: 2.0.
|
3
|
+
Version: 2.0.7
|
4
4
|
Summary: Official HiddenLayer Python SDK
|
5
5
|
Author-email: HiddenLayer Integrations Team <integrations@hiddenlayer.com>
|
6
6
|
Maintainer-email: HiddenLayer Integrations Team <integrations@hiddenlayer.com>
|
@@ -236,6 +236,7 @@ Requires-Dist: huggingface_hub; extra == "hf"
|
|
236
236
|
Provides-Extra: azure
|
237
237
|
Requires-Dist: azure-storage-blob; extra == "azure"
|
238
238
|
Requires-Dist: azure-identity; extra == "azure"
|
239
|
+
Dynamic: license-file
|
239
240
|
|
240
241
|
# HiddenLayer SDK Python (Beta)
|
241
242
|
|
@@ -1,11 +1,11 @@
|
|
1
|
-
hiddenlayer/__init__.py,sha256=
|
1
|
+
hiddenlayer/__init__.py,sha256=TJLQ2sXIrJTzv04Cl7N1iHSEF5hycMaM1PsU4WnNocU,3421
|
2
2
|
hiddenlayer/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
hiddenlayer/sdk/constants.py,sha256=
|
3
|
+
hiddenlayer/sdk/constants.py,sha256=BytOHlVWk6lhWB4bpT7kDj3ctozZA9ftaB2D6lSGtGI,628
|
4
4
|
hiddenlayer/sdk/exceptions.py,sha256=_Lelwk32LWxWuUPglfw8R8RuiX_Uxm_N7TP3BLhDLz0,226
|
5
5
|
hiddenlayer/sdk/models.py,sha256=t92fK7xxuPm1XzazNsRg5AOAD17qnvNZiGaN1gM6lHk,1776
|
6
6
|
hiddenlayer/sdk/utils.py,sha256=Ntao8hfsYuB7obZOanayIlrgJ98IWcPhs3sRi39IDkg,2997
|
7
|
-
hiddenlayer/sdk/version.py,sha256=
|
8
|
-
hiddenlayer/sdk/rest/__init__.py,sha256=
|
7
|
+
hiddenlayer/sdk/version.py,sha256=P3xkxiU2SjdsjCR_fMhXDUtoWheuX3suVT8TGRznCoY,18
|
8
|
+
hiddenlayer/sdk/rest/__init__.py,sha256=fL2dURfsADiHHxdxxbUyjrqQi5BPRVdd9p51XPrJhks,8499
|
9
9
|
hiddenlayer/sdk/rest/api_client.py,sha256=Yd71Up1SHj5pPp4jB_gIsm5Y77jHph7T4iU9NiIhUak,26291
|
10
10
|
hiddenlayer/sdk/rest/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9sKxGv7s,652
|
11
11
|
hiddenlayer/sdk/rest/configuration.py,sha256=yWESM8qffwcficDVjX6SDiJISOWRJYLD3BX9RRgdyhM,14705
|
@@ -15,10 +15,10 @@ hiddenlayer/sdk/rest/api/__init__.py,sha256=dKrlNcnraOo_qcSNtyz4GYkS79YGkEcg8__T
|
|
15
15
|
hiddenlayer/sdk/rest/api/aidr_predictive_api.py,sha256=_XBrwv2SjK9rsNyFuUQARMs4iplVigmRyDEZAo3Fm5k,11316
|
16
16
|
hiddenlayer/sdk/rest/api/health_api.py,sha256=OO-snu3VvpBJWseRi4wdjk6poh5U1_t7WZf5V116wzE,10024
|
17
17
|
hiddenlayer/sdk/rest/api/model_api.py,sha256=yZ3Rn-Sv5StPDDtrGPGtab8YqDxkWLph6bHuZEwUAs4,20107
|
18
|
-
hiddenlayer/sdk/rest/api/model_supply_chain_api.py,sha256=
|
18
|
+
hiddenlayer/sdk/rest/api/model_supply_chain_api.py,sha256=WmxcEGZ6IBacNsmkj0Yf47D74k6HhHnKapO4tPhubRI,159295
|
19
19
|
hiddenlayer/sdk/rest/api/readiness_api.py,sha256=PL3iDhS25SDmUmreLLlcEy1r6_iGrY8ejrZqPDqXwEc,10060
|
20
20
|
hiddenlayer/sdk/rest/api/sensor_api.py,sha256=L5ZaEfvNublZbcUsL_LHsj2IV4DGr4lVfnHl6wsESkQ,55425
|
21
|
-
hiddenlayer/sdk/rest/models/__init__.py,sha256=
|
21
|
+
hiddenlayer/sdk/rest/models/__init__.py,sha256=Df9k4U_TOqWPBvCp2rjpYIehk44YFjjWPXvPz-tHfH4,7510
|
22
22
|
hiddenlayer/sdk/rest/models/address.py,sha256=_KjrEeyq_1hvkKJnH-d4d7ocqPLQyS3t48MG9Sb_rMU,5243
|
23
23
|
hiddenlayer/sdk/rest/models/artifact.py,sha256=QiuwqQj0ROPalIH_LvqB6Z2b3_IdXdlmvmRJ6DSlxwE,8096
|
24
24
|
hiddenlayer/sdk/rest/models/artifact_change.py,sha256=Sc2Z24g7m3Rrht83-2w235WZPpnsfEFF-ff5HfrPmno,4094
|
@@ -85,9 +85,10 @@ hiddenlayer/sdk/rest/models/scan_create_request.py,sha256=s8xEol9DIH1MpTklL6fjWt
|
|
85
85
|
hiddenlayer/sdk/rest/models/scan_detection_v3.py,sha256=sk65pt5rkvxOBgs5OZ090fZI-GNTY_SdHyibi4bSZ5Y,6426
|
86
86
|
hiddenlayer/sdk/rest/models/scan_detection_v31.py,sha256=tTNRlggUmL-cY4ZRiC-2au6xwEbUqZlbjxSukY72nG0,6484
|
87
87
|
hiddenlayer/sdk/rest/models/scan_header_v3.py,sha256=sgpiAE6NflJtLJhfUA4NmveUCHTPQN2JSsk7cEBMjVQ,5035
|
88
|
-
hiddenlayer/sdk/rest/models/scan_job.py,sha256=
|
89
|
-
hiddenlayer/sdk/rest/models/
|
88
|
+
hiddenlayer/sdk/rest/models/scan_job.py,sha256=IADBv0WCXCZ8m2rUJ8Do7IPSc3aczICCdkLnz2g8FXs,3970
|
89
|
+
hiddenlayer/sdk/rest/models/scan_job_access.py,sha256=Q9SWDj7u9ec5H03zlzPUeO_si1rxS_c7iL_YPc-FDxo,2956
|
90
90
|
hiddenlayer/sdk/rest/models/scan_model_details_v3.py,sha256=KItM8K8oZASWFh1b8_5zWKuqmbX1UHdELCY7_MjrcTU,3177
|
91
|
+
hiddenlayer/sdk/rest/models/scan_model_details_v31.py,sha256=7ykIhQz8OsgdIRmUkVs5RlgpBOuy_3Ztzs3Vi-puN0M,3023
|
91
92
|
hiddenlayer/sdk/rest/models/scan_model_ids_v3.py,sha256=MzU_KNr5CXylDbakDOLv7qGz4naAJ2_56cuW9MATJpw,2639
|
92
93
|
hiddenlayer/sdk/rest/models/scan_report_v3.py,sha256=n9oi5wXwdBNIKdPzg1-2wEOSilDARHalTMjTF6THtfg,5658
|
93
94
|
hiddenlayer/sdk/rest/models/scan_results_map_v3.py,sha256=BQowU2WqGi0wlNqr9Oi3n_F-NEbfA4aPA-2QfdENwGQ,3363
|
@@ -117,9 +118,9 @@ hiddenlayer/sdk/rest/models/web_request.py,sha256=u9pmXP-NPDBKcW3ivNDa1I0i0vxQki
|
|
117
118
|
hiddenlayer/sdk/rest/models/web_response.py,sha256=gSKHygOE25yvLKMk__Aga90cHfpFxWvmib1SwWGFTik,4704
|
118
119
|
hiddenlayer/sdk/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
119
120
|
hiddenlayer/sdk/services/aidr_predictive.py,sha256=S2MvN5qPyYhYXbYM3lvDmylhY71p4d07L36m-a1ZMyM,4773
|
120
|
-
hiddenlayer/sdk/services/model_scan.py,sha256=
|
121
|
-
hiddenlayer_sdk-2.0.
|
122
|
-
hiddenlayer_sdk-2.0.
|
123
|
-
hiddenlayer_sdk-2.0.
|
124
|
-
hiddenlayer_sdk-2.0.
|
125
|
-
hiddenlayer_sdk-2.0.
|
121
|
+
hiddenlayer/sdk/services/model_scan.py,sha256=tFqtMKaDl-oq2nSaqqOicK5sh3TqCk6hYP2-Zd5yNNY,18240
|
122
|
+
hiddenlayer_sdk-2.0.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
123
|
+
hiddenlayer_sdk-2.0.7.dist-info/METADATA,sha256=JzWg5V3dbxtToBp4GzVuEciOgEPPru7GFGw26dDDKhg,18153
|
124
|
+
hiddenlayer_sdk-2.0.7.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
125
|
+
hiddenlayer_sdk-2.0.7.dist-info/top_level.txt,sha256=8BxcmGvEN1RqsMvTWg9Q0vDmtBGcTmatnme6nzyPj2U,12
|
126
|
+
hiddenlayer_sdk-2.0.7.dist-info/RECORD,,
|
@@ -1,137 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
"""
|
4
|
-
HiddenLayer-API
|
5
|
-
|
6
|
-
HiddenLayer-API
|
7
|
-
|
8
|
-
The version of the OpenAPI document: 1
|
9
|
-
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
10
|
-
|
11
|
-
Do not edit the class manually.
|
12
|
-
""" # noqa: E501
|
13
|
-
|
14
|
-
|
15
|
-
from __future__ import annotations
|
16
|
-
import json
|
17
|
-
import pprint
|
18
|
-
from pydantic import BaseModel, ConfigDict, Field, StrictStr, ValidationError, field_validator
|
19
|
-
from typing import Any, List, Optional
|
20
|
-
from hiddenlayer.sdk.rest.models.scan_model_details_v3 import ScanModelDetailsV3
|
21
|
-
from hiddenlayer.sdk.rest.models.scan_model_ids_v3 import ScanModelIdsV3
|
22
|
-
from pydantic import StrictStr, Field
|
23
|
-
from typing import Union, List, Set, Optional, Dict
|
24
|
-
from typing_extensions import Literal, Self
|
25
|
-
|
26
|
-
SCANJOBINVENTORY_ONE_OF_SCHEMAS = ["ScanModelDetailsV3", "ScanModelIdsV3"]
|
27
|
-
|
28
|
-
class ScanJobInventory(BaseModel):
|
29
|
-
"""
|
30
|
-
ScanJobInventory
|
31
|
-
"""
|
32
|
-
# data type: ScanModelDetailsV3
|
33
|
-
oneof_schema_1_validator: Optional[ScanModelDetailsV3] = None
|
34
|
-
# data type: ScanModelIdsV3
|
35
|
-
oneof_schema_2_validator: Optional[ScanModelIdsV3] = None
|
36
|
-
actual_instance: Optional[Union[ScanModelDetailsV3, ScanModelIdsV3]] = None
|
37
|
-
one_of_schemas: Set[str] = { "ScanModelDetailsV3", "ScanModelIdsV3" }
|
38
|
-
|
39
|
-
model_config = ConfigDict(
|
40
|
-
validate_assignment=True,
|
41
|
-
protected_namespaces=(),
|
42
|
-
)
|
43
|
-
|
44
|
-
|
45
|
-
def __init__(self, *args, **kwargs) -> None:
|
46
|
-
if args:
|
47
|
-
if len(args) > 1:
|
48
|
-
raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`")
|
49
|
-
if kwargs:
|
50
|
-
raise ValueError("If a position argument is used, keyword arguments cannot be used.")
|
51
|
-
super().__init__(actual_instance=args[0])
|
52
|
-
else:
|
53
|
-
super().__init__(**kwargs)
|
54
|
-
|
55
|
-
@field_validator('actual_instance')
|
56
|
-
def actual_instance_must_validate_oneof(cls, v):
|
57
|
-
instance = ScanJobInventory.model_construct()
|
58
|
-
error_messages = []
|
59
|
-
match = 0
|
60
|
-
# validate data type: ScanModelDetailsV3
|
61
|
-
if not isinstance(v, ScanModelDetailsV3):
|
62
|
-
error_messages.append(f"Error! Input type `{type(v)}` is not `ScanModelDetailsV3`")
|
63
|
-
else:
|
64
|
-
match += 1
|
65
|
-
# validate data type: ScanModelIdsV3
|
66
|
-
if not isinstance(v, ScanModelIdsV3):
|
67
|
-
error_messages.append(f"Error! Input type `{type(v)}` is not `ScanModelIdsV3`")
|
68
|
-
else:
|
69
|
-
match += 1
|
70
|
-
if match > 1:
|
71
|
-
# more than 1 match
|
72
|
-
raise ValueError("Multiple matches found when setting `actual_instance` in ScanJobInventory with oneOf schemas: ScanModelDetailsV3, ScanModelIdsV3. Details: " + ", ".join(error_messages))
|
73
|
-
elif match == 0:
|
74
|
-
# no match
|
75
|
-
raise ValueError("No match found when setting `actual_instance` in ScanJobInventory with oneOf schemas: ScanModelDetailsV3, ScanModelIdsV3. Details: " + ", ".join(error_messages))
|
76
|
-
else:
|
77
|
-
return v
|
78
|
-
|
79
|
-
@classmethod
|
80
|
-
def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self:
|
81
|
-
return cls.from_json(json.dumps(obj))
|
82
|
-
|
83
|
-
@classmethod
|
84
|
-
def from_json(cls, json_str: str) -> Self:
|
85
|
-
"""Returns the object represented by the json string"""
|
86
|
-
instance = cls.model_construct()
|
87
|
-
error_messages = []
|
88
|
-
match = 0
|
89
|
-
|
90
|
-
# deserialize data into ScanModelDetailsV3
|
91
|
-
try:
|
92
|
-
instance.actual_instance = ScanModelDetailsV3.from_json(json_str)
|
93
|
-
match += 1
|
94
|
-
except (ValidationError, ValueError) as e:
|
95
|
-
error_messages.append(str(e))
|
96
|
-
# deserialize data into ScanModelIdsV3
|
97
|
-
try:
|
98
|
-
instance.actual_instance = ScanModelIdsV3.from_json(json_str)
|
99
|
-
match += 1
|
100
|
-
except (ValidationError, ValueError) as e:
|
101
|
-
error_messages.append(str(e))
|
102
|
-
|
103
|
-
if match > 1:
|
104
|
-
# more than 1 match
|
105
|
-
raise ValueError("Multiple matches found when deserializing the JSON string into ScanJobInventory with oneOf schemas: ScanModelDetailsV3, ScanModelIdsV3. Details: " + ", ".join(error_messages))
|
106
|
-
elif match == 0:
|
107
|
-
# no match
|
108
|
-
raise ValueError("No match found when deserializing the JSON string into ScanJobInventory with oneOf schemas: ScanModelDetailsV3, ScanModelIdsV3. Details: " + ", ".join(error_messages))
|
109
|
-
else:
|
110
|
-
return instance
|
111
|
-
|
112
|
-
def to_json(self) -> str:
|
113
|
-
"""Returns the JSON representation of the actual instance"""
|
114
|
-
if self.actual_instance is None:
|
115
|
-
return "null"
|
116
|
-
|
117
|
-
if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json):
|
118
|
-
return self.actual_instance.to_json()
|
119
|
-
else:
|
120
|
-
return json.dumps(self.actual_instance)
|
121
|
-
|
122
|
-
def to_dict(self) -> Optional[Union[Dict[str, Any], ScanModelDetailsV3, ScanModelIdsV3]]:
|
123
|
-
"""Returns the dict representation of the actual instance"""
|
124
|
-
if self.actual_instance is None:
|
125
|
-
return None
|
126
|
-
|
127
|
-
if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict):
|
128
|
-
return self.actual_instance.to_dict()
|
129
|
-
else:
|
130
|
-
# primitive type
|
131
|
-
return self.actual_instance
|
132
|
-
|
133
|
-
def to_str(self) -> str:
|
134
|
-
"""Returns the string representation of the actual instance"""
|
135
|
-
return pprint.pformat(self.model_dump())
|
136
|
-
|
137
|
-
|
File without changes
|
File without changes
|