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 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(self._api_client)
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."
@@ -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"
@@ -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.scan_job_inventory import ScanJobInventory
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
- ) -> None:
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': None,
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[None]:
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': None,
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': None,
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='/scans/v3/jobs',
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
- ) -> List[ScanJob]:
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': "List[ScanJob]",
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[List[ScanJob]]:
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': "List[ScanJob]",
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': "List[ScanJob]",
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='/scans/v3/jobs',
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.scan_job_inventory import ScanJobInventory
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.scan_job_inventory import ScanJobInventory
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
- inventory: Optional[ScanJobInventory] = None
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 MultiFileUploadRequestV3
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__(self, api_client: ApiClient) -> None:
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
- try:
291
- scan_report = self._model_supply_chain_api.get_scan_results(scan_id)
292
- except NotFoundException:
293
- return EmptyScanResults()
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}")
@@ -1 +1 @@
1
- VERSION = "2.0.5"
1
+ VERSION = "2.0.7"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: hiddenlayer-sdk
3
- Version: 2.0.5
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=iRisM3FnxlTbMjuRy72BQcCTZcp2_9iWck4kY-S2yV8,3206
1
+ hiddenlayer/__init__.py,sha256=TJLQ2sXIrJTzv04Cl7N1iHSEF5hycMaM1PsU4WnNocU,3421
2
2
  hiddenlayer/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- hiddenlayer/sdk/constants.py,sha256=QwBZAQdF7FW9q4C-O8LyxrpqfJFBBlqRUDMjnJ-ouZQ,320
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=CF5-GE6YzGyHRbD2JDoMG91u33vJQfZJLi32ju9NWWw,18
8
- hiddenlayer/sdk/rest/__init__.py,sha256=dSLESB6qPKLmF4FseZH_XRnHOSuXaeYQHPDrlgTFHW4,8422
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=JxDl0EulcJgrdcmKXv5YkOBjXRoUxFWfxvtAKaGLmE8,159229
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=oAOggd_25EY0c2qajVPQCiiO1X5fzQh7r1xGRa7KXew,7433
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=36Izh6LR8KTu0GllhKMqDmXMVVIqLOfGIZ930awtltA,3568
89
- hiddenlayer/sdk/rest/models/scan_job_inventory.py,sha256=5ROvus3liVCnzuNmheeNy6nmI-LB0uPkHWzBS3zqJys,5328
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=uwHyQNWKwmUmAyGCT-u4Hpc-HXBf_-_v8GPg5JefFuA,15928
121
- hiddenlayer_sdk-2.0.5.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
122
- hiddenlayer_sdk-2.0.5.dist-info/METADATA,sha256=ZW4WvrUY88_sjyQnPx0KQ55N_X2kRPEv8OeR4afRZ_A,18131
123
- hiddenlayer_sdk-2.0.5.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
124
- hiddenlayer_sdk-2.0.5.dist-info/top_level.txt,sha256=8BxcmGvEN1RqsMvTWg9Q0vDmtBGcTmatnme6nzyPj2U,12
125
- hiddenlayer_sdk-2.0.5.dist-info/RECORD,,
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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.0.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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
-