hiddenlayer-sdk 2.0.10__py3-none-any.whl → 3.0.1__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.
Files changed (204) hide show
  1. hiddenlayer/__init__.py +109 -114
  2. hiddenlayer/_base_client.py +1995 -0
  3. hiddenlayer/_client.py +761 -0
  4. hiddenlayer/_compat.py +219 -0
  5. hiddenlayer/_constants.py +14 -0
  6. hiddenlayer/_exceptions.py +108 -0
  7. hiddenlayer/_files.py +123 -0
  8. hiddenlayer/_models.py +835 -0
  9. hiddenlayer/_oauth2.py +118 -0
  10. hiddenlayer/_qs.py +150 -0
  11. hiddenlayer/_resource.py +43 -0
  12. hiddenlayer/_response.py +832 -0
  13. hiddenlayer/_streaming.py +333 -0
  14. hiddenlayer/_types.py +260 -0
  15. hiddenlayer/_utils/__init__.py +64 -0
  16. hiddenlayer/_utils/_compat.py +45 -0
  17. hiddenlayer/_utils/_datetime_parse.py +136 -0
  18. hiddenlayer/_utils/_logs.py +25 -0
  19. hiddenlayer/_utils/_proxy.py +65 -0
  20. hiddenlayer/_utils/_reflection.py +42 -0
  21. hiddenlayer/_utils/_resources_proxy.py +24 -0
  22. hiddenlayer/_utils/_streams.py +12 -0
  23. hiddenlayer/_utils/_sync.py +86 -0
  24. hiddenlayer/_utils/_transform.py +457 -0
  25. hiddenlayer/_utils/_typing.py +156 -0
  26. hiddenlayer/_utils/_utils.py +421 -0
  27. hiddenlayer/_version.py +4 -0
  28. hiddenlayer/lib/.keep +4 -0
  29. hiddenlayer/lib/__init__.py +6 -0
  30. hiddenlayer/lib/community_scan.py +174 -0
  31. hiddenlayer/lib/model_scan.py +752 -0
  32. hiddenlayer/lib/scan_utils.py +142 -0
  33. hiddenlayer/pagination.py +127 -0
  34. hiddenlayer/resources/__init__.py +75 -0
  35. hiddenlayer/resources/interactions.py +205 -0
  36. hiddenlayer/resources/models/__init__.py +33 -0
  37. hiddenlayer/resources/models/cards.py +259 -0
  38. hiddenlayer/resources/models/models.py +284 -0
  39. hiddenlayer/resources/prompt_analyzer.py +207 -0
  40. hiddenlayer/resources/scans/__init__.py +61 -0
  41. hiddenlayer/resources/scans/jobs.py +499 -0
  42. hiddenlayer/resources/scans/results.py +169 -0
  43. hiddenlayer/resources/scans/scans.py +166 -0
  44. hiddenlayer/resources/scans/upload/__init__.py +33 -0
  45. hiddenlayer/resources/scans/upload/file.py +279 -0
  46. hiddenlayer/resources/scans/upload/upload.py +340 -0
  47. hiddenlayer/resources/sensors.py +575 -0
  48. hiddenlayer/types/__init__.py +16 -0
  49. hiddenlayer/types/interaction_analyze_params.py +62 -0
  50. hiddenlayer/types/interaction_analyze_response.py +199 -0
  51. hiddenlayer/types/model_retrieve_response.py +50 -0
  52. hiddenlayer/types/models/__init__.py +6 -0
  53. hiddenlayer/types/models/card_list_params.py +65 -0
  54. hiddenlayer/types/models/card_list_response.py +50 -0
  55. hiddenlayer/types/prompt_analyzer_create_params.py +23 -0
  56. hiddenlayer/types/prompt_analyzer_create_response.py +381 -0
  57. hiddenlayer/types/scans/__init__.py +14 -0
  58. hiddenlayer/types/scans/job_list_params.py +75 -0
  59. hiddenlayer/types/scans/job_list_response.py +22 -0
  60. hiddenlayer/types/scans/job_request_params.py +49 -0
  61. hiddenlayer/types/scans/job_retrieve_params.py +16 -0
  62. hiddenlayer/types/scans/result_sarif_response.py +7 -0
  63. hiddenlayer/types/scans/scan_job.py +46 -0
  64. hiddenlayer/types/scans/scan_report.py +367 -0
  65. hiddenlayer/types/scans/upload/__init__.py +6 -0
  66. hiddenlayer/types/scans/upload/file_add_response.py +24 -0
  67. hiddenlayer/types/scans/upload/file_complete_response.py +12 -0
  68. hiddenlayer/types/scans/upload_complete_all_response.py +12 -0
  69. hiddenlayer/types/scans/upload_start_params.py +34 -0
  70. hiddenlayer/types/scans/upload_start_response.py +12 -0
  71. hiddenlayer/types/sensor_create_params.py +24 -0
  72. hiddenlayer/types/sensor_create_response.py +33 -0
  73. hiddenlayer/types/sensor_query_params.py +39 -0
  74. hiddenlayer/types/sensor_query_response.py +43 -0
  75. hiddenlayer/types/sensor_retrieve_response.py +33 -0
  76. hiddenlayer/types/sensor_update_params.py +20 -0
  77. hiddenlayer/types/sensor_update_response.py +9 -0
  78. hiddenlayer_sdk-3.0.1.dist-info/METADATA +521 -0
  79. hiddenlayer_sdk-3.0.1.dist-info/RECORD +82 -0
  80. {hiddenlayer_sdk-2.0.10.dist-info → hiddenlayer_sdk-3.0.1.dist-info}/WHEEL +1 -2
  81. {hiddenlayer_sdk-2.0.10.dist-info → hiddenlayer_sdk-3.0.1.dist-info}/licenses/LICENSE +1 -1
  82. hiddenlayer/sdk/constants.py +0 -26
  83. hiddenlayer/sdk/exceptions.py +0 -12
  84. hiddenlayer/sdk/models.py +0 -58
  85. hiddenlayer/sdk/rest/__init__.py +0 -135
  86. hiddenlayer/sdk/rest/api/__init__.py +0 -10
  87. hiddenlayer/sdk/rest/api/aidr_predictive_api.py +0 -308
  88. hiddenlayer/sdk/rest/api/health_api.py +0 -272
  89. hiddenlayer/sdk/rest/api/model_api.py +0 -559
  90. hiddenlayer/sdk/rest/api/model_supply_chain_api.py +0 -4063
  91. hiddenlayer/sdk/rest/api/readiness_api.py +0 -272
  92. hiddenlayer/sdk/rest/api/sensor_api.py +0 -1432
  93. hiddenlayer/sdk/rest/api_client.py +0 -770
  94. hiddenlayer/sdk/rest/api_response.py +0 -21
  95. hiddenlayer/sdk/rest/configuration.py +0 -445
  96. hiddenlayer/sdk/rest/exceptions.py +0 -199
  97. hiddenlayer/sdk/rest/models/__init__.py +0 -113
  98. hiddenlayer/sdk/rest/models/address.py +0 -110
  99. hiddenlayer/sdk/rest/models/artifact.py +0 -155
  100. hiddenlayer/sdk/rest/models/artifact_change.py +0 -108
  101. hiddenlayer/sdk/rest/models/artifact_content.py +0 -101
  102. hiddenlayer/sdk/rest/models/artifact_location.py +0 -109
  103. hiddenlayer/sdk/rest/models/attachment.py +0 -129
  104. hiddenlayer/sdk/rest/models/begin_multi_file_upload200_response.py +0 -87
  105. hiddenlayer/sdk/rest/models/begin_multipart_file_upload200_response.py +0 -97
  106. hiddenlayer/sdk/rest/models/begin_multipart_file_upload200_response_parts_inner.py +0 -94
  107. hiddenlayer/sdk/rest/models/code_flow.py +0 -113
  108. hiddenlayer/sdk/rest/models/configuration_override.py +0 -108
  109. hiddenlayer/sdk/rest/models/conversion.py +0 -114
  110. hiddenlayer/sdk/rest/models/create_sensor_request.py +0 -95
  111. hiddenlayer/sdk/rest/models/edge.py +0 -108
  112. hiddenlayer/sdk/rest/models/edge_traversal.py +0 -122
  113. hiddenlayer/sdk/rest/models/errors_inner.py +0 -91
  114. hiddenlayer/sdk/rest/models/exception.py +0 -113
  115. hiddenlayer/sdk/rest/models/external_properties.py +0 -273
  116. hiddenlayer/sdk/rest/models/external_property_file_reference.py +0 -102
  117. hiddenlayer/sdk/rest/models/external_property_file_references.py +0 -240
  118. hiddenlayer/sdk/rest/models/file_details_v3.py +0 -139
  119. hiddenlayer/sdk/rest/models/file_result_v3.py +0 -117
  120. hiddenlayer/sdk/rest/models/file_scan_report_v3.py +0 -132
  121. hiddenlayer/sdk/rest/models/file_scan_reports_v3.py +0 -95
  122. hiddenlayer/sdk/rest/models/fix.py +0 -113
  123. hiddenlayer/sdk/rest/models/get_condensed_model_scan_reports200_response.py +0 -102
  124. hiddenlayer/sdk/rest/models/graph.py +0 -123
  125. hiddenlayer/sdk/rest/models/graph_traversal.py +0 -97
  126. hiddenlayer/sdk/rest/models/inventory_v3.py +0 -101
  127. hiddenlayer/sdk/rest/models/invocation.py +0 -199
  128. hiddenlayer/sdk/rest/models/location.py +0 -146
  129. hiddenlayer/sdk/rest/models/location_inner.py +0 -138
  130. hiddenlayer/sdk/rest/models/location_relationship.py +0 -107
  131. hiddenlayer/sdk/rest/models/logical_location.py +0 -104
  132. hiddenlayer/sdk/rest/models/message.py +0 -92
  133. hiddenlayer/sdk/rest/models/mitre_atlas_inner.py +0 -110
  134. hiddenlayer/sdk/rest/models/model.py +0 -103
  135. hiddenlayer/sdk/rest/models/model_inventory_info.py +0 -103
  136. hiddenlayer/sdk/rest/models/model_version.py +0 -97
  137. hiddenlayer/sdk/rest/models/multi_file_upload_request_v3.py +0 -97
  138. hiddenlayer/sdk/rest/models/multiformat_message_string.py +0 -95
  139. hiddenlayer/sdk/rest/models/node.py +0 -122
  140. hiddenlayer/sdk/rest/models/notification.py +0 -157
  141. hiddenlayer/sdk/rest/models/notify_model_scan_completed200_response.py +0 -87
  142. hiddenlayer/sdk/rest/models/paged_response_with_total.py +0 -94
  143. hiddenlayer/sdk/rest/models/pagination_v3.py +0 -95
  144. hiddenlayer/sdk/rest/models/physical_location.py +0 -94
  145. hiddenlayer/sdk/rest/models/problem_details.py +0 -103
  146. hiddenlayer/sdk/rest/models/property_bag.py +0 -101
  147. hiddenlayer/sdk/rest/models/rectangle.py +0 -110
  148. hiddenlayer/sdk/rest/models/region.py +0 -127
  149. hiddenlayer/sdk/rest/models/replacement.py +0 -103
  150. hiddenlayer/sdk/rest/models/reporting_configuration.py +0 -113
  151. hiddenlayer/sdk/rest/models/reporting_descriptor.py +0 -162
  152. hiddenlayer/sdk/rest/models/reporting_descriptor_reference.py +0 -103
  153. hiddenlayer/sdk/rest/models/reporting_descriptor_relationship.py +0 -115
  154. hiddenlayer/sdk/rest/models/result.py +0 -312
  155. hiddenlayer/sdk/rest/models/result_provenance.py +0 -133
  156. hiddenlayer/sdk/rest/models/rule_details_inner.py +0 -102
  157. hiddenlayer/sdk/rest/models/run.py +0 -318
  158. hiddenlayer/sdk/rest/models/run_automation_details.py +0 -129
  159. hiddenlayer/sdk/rest/models/sarif210.py +0 -123
  160. hiddenlayer/sdk/rest/models/scan_create_request.py +0 -87
  161. hiddenlayer/sdk/rest/models/scan_detection_v3.py +0 -159
  162. hiddenlayer/sdk/rest/models/scan_detection_v31.py +0 -158
  163. hiddenlayer/sdk/rest/models/scan_header_v3.py +0 -129
  164. hiddenlayer/sdk/rest/models/scan_job.py +0 -115
  165. hiddenlayer/sdk/rest/models/scan_job_access.py +0 -97
  166. hiddenlayer/sdk/rest/models/scan_model_details_v3.py +0 -99
  167. hiddenlayer/sdk/rest/models/scan_model_details_v31.py +0 -97
  168. hiddenlayer/sdk/rest/models/scan_model_ids_v3.py +0 -89
  169. hiddenlayer/sdk/rest/models/scan_report_v3.py +0 -139
  170. hiddenlayer/sdk/rest/models/scan_results_map_v3.py +0 -105
  171. hiddenlayer/sdk/rest/models/scan_results_v3.py +0 -120
  172. hiddenlayer/sdk/rest/models/security_posture.py +0 -89
  173. hiddenlayer/sdk/rest/models/sensor.py +0 -100
  174. hiddenlayer/sdk/rest/models/sensor_query_response.py +0 -101
  175. hiddenlayer/sdk/rest/models/sensor_sor_model_card_query_response.py +0 -101
  176. hiddenlayer/sdk/rest/models/sensor_sor_model_card_response.py +0 -127
  177. hiddenlayer/sdk/rest/models/sensor_sor_query_filter.py +0 -108
  178. hiddenlayer/sdk/rest/models/sensor_sor_query_request.py +0 -109
  179. hiddenlayer/sdk/rest/models/special_locations.py +0 -97
  180. hiddenlayer/sdk/rest/models/stack.py +0 -113
  181. hiddenlayer/sdk/rest/models/stack_frame.py +0 -104
  182. hiddenlayer/sdk/rest/models/submission_response.py +0 -95
  183. hiddenlayer/sdk/rest/models/submission_v2.py +0 -109
  184. hiddenlayer/sdk/rest/models/suppression.py +0 -133
  185. hiddenlayer/sdk/rest/models/thread_flow.py +0 -144
  186. hiddenlayer/sdk/rest/models/thread_flow_location.py +0 -166
  187. hiddenlayer/sdk/rest/models/tool.py +0 -107
  188. hiddenlayer/sdk/rest/models/tool_component.py +0 -251
  189. hiddenlayer/sdk/rest/models/tool_component_reference.py +0 -108
  190. hiddenlayer/sdk/rest/models/translation_metadata.py +0 -110
  191. hiddenlayer/sdk/rest/models/validation_error_model.py +0 -99
  192. hiddenlayer/sdk/rest/models/version_control_details.py +0 -108
  193. hiddenlayer/sdk/rest/models/web_request.py +0 -112
  194. hiddenlayer/sdk/rest/models/web_response.py +0 -112
  195. hiddenlayer/sdk/rest/rest.py +0 -257
  196. hiddenlayer/sdk/services/__init__.py +0 -0
  197. hiddenlayer/sdk/services/aidr_predictive.py +0 -130
  198. hiddenlayer/sdk/services/model_scan.py +0 -505
  199. hiddenlayer/sdk/utils.py +0 -92
  200. hiddenlayer/sdk/version.py +0 -1
  201. hiddenlayer_sdk-2.0.10.dist-info/METADATA +0 -368
  202. hiddenlayer_sdk-2.0.10.dist-info/RECORD +0 -126
  203. hiddenlayer_sdk-2.0.10.dist-info/top_level.txt +0 -1
  204. /hiddenlayer/{sdk/__init__.py → py.typed} +0 -0
@@ -0,0 +1,421 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import re
5
+ import inspect
6
+ import functools
7
+ from typing import (
8
+ Any,
9
+ Tuple,
10
+ Mapping,
11
+ TypeVar,
12
+ Callable,
13
+ Iterable,
14
+ Sequence,
15
+ cast,
16
+ overload,
17
+ )
18
+ from pathlib import Path
19
+ from datetime import date, datetime
20
+ from typing_extensions import TypeGuard
21
+
22
+ import sniffio
23
+
24
+ from .._types import Omit, NotGiven, FileTypes, HeadersLike
25
+
26
+ _T = TypeVar("_T")
27
+ _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
28
+ _MappingT = TypeVar("_MappingT", bound=Mapping[str, object])
29
+ _SequenceT = TypeVar("_SequenceT", bound=Sequence[object])
30
+ CallableT = TypeVar("CallableT", bound=Callable[..., Any])
31
+
32
+
33
+ def flatten(t: Iterable[Iterable[_T]]) -> list[_T]:
34
+ return [item for sublist in t for item in sublist]
35
+
36
+
37
+ def extract_files(
38
+ # TODO: this needs to take Dict but variance issues.....
39
+ # create protocol type ?
40
+ query: Mapping[str, object],
41
+ *,
42
+ paths: Sequence[Sequence[str]],
43
+ ) -> list[tuple[str, FileTypes]]:
44
+ """Recursively extract files from the given dictionary based on specified paths.
45
+
46
+ A path may look like this ['foo', 'files', '<array>', 'data'].
47
+
48
+ Note: this mutates the given dictionary.
49
+ """
50
+ files: list[tuple[str, FileTypes]] = []
51
+ for path in paths:
52
+ files.extend(_extract_items(query, path, index=0, flattened_key=None))
53
+ return files
54
+
55
+
56
+ def _extract_items(
57
+ obj: object,
58
+ path: Sequence[str],
59
+ *,
60
+ index: int,
61
+ flattened_key: str | None,
62
+ ) -> list[tuple[str, FileTypes]]:
63
+ try:
64
+ key = path[index]
65
+ except IndexError:
66
+ if not is_given(obj):
67
+ # no value was provided - we can safely ignore
68
+ return []
69
+
70
+ # cyclical import
71
+ from .._files import assert_is_file_content
72
+
73
+ # We have exhausted the path, return the entry we found.
74
+ assert flattened_key is not None
75
+
76
+ if is_list(obj):
77
+ files: list[tuple[str, FileTypes]] = []
78
+ for entry in obj:
79
+ assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "")
80
+ files.append((flattened_key + "[]", cast(FileTypes, entry)))
81
+ return files
82
+
83
+ assert_is_file_content(obj, key=flattened_key)
84
+ return [(flattened_key, cast(FileTypes, obj))]
85
+
86
+ index += 1
87
+ if is_dict(obj):
88
+ try:
89
+ # We are at the last entry in the path so we must remove the field
90
+ if (len(path)) == index:
91
+ item = obj.pop(key)
92
+ else:
93
+ item = obj[key]
94
+ except KeyError:
95
+ # Key was not present in the dictionary, this is not indicative of an error
96
+ # as the given path may not point to a required field. We also do not want
97
+ # to enforce required fields as the API may differ from the spec in some cases.
98
+ return []
99
+ if flattened_key is None:
100
+ flattened_key = key
101
+ else:
102
+ flattened_key += f"[{key}]"
103
+ return _extract_items(
104
+ item,
105
+ path,
106
+ index=index,
107
+ flattened_key=flattened_key,
108
+ )
109
+ elif is_list(obj):
110
+ if key != "<array>":
111
+ return []
112
+
113
+ return flatten(
114
+ [
115
+ _extract_items(
116
+ item,
117
+ path,
118
+ index=index,
119
+ flattened_key=flattened_key + "[]" if flattened_key is not None else "[]",
120
+ )
121
+ for item in obj
122
+ ]
123
+ )
124
+
125
+ # Something unexpected was passed, just ignore it.
126
+ return []
127
+
128
+
129
+ def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]:
130
+ return not isinstance(obj, NotGiven) and not isinstance(obj, Omit)
131
+
132
+
133
+ # Type safe methods for narrowing types with TypeVars.
134
+ # The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],
135
+ # however this cause Pyright to rightfully report errors. As we know we don't
136
+ # care about the contained types we can safely use `object` in it's place.
137
+ #
138
+ # There are two separate functions defined, `is_*` and `is_*_t` for different use cases.
139
+ # `is_*` is for when you're dealing with an unknown input
140
+ # `is_*_t` is for when you're narrowing a known union type to a specific subset
141
+
142
+
143
+ def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]:
144
+ return isinstance(obj, tuple)
145
+
146
+
147
+ def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]:
148
+ return isinstance(obj, tuple)
149
+
150
+
151
+ def is_sequence(obj: object) -> TypeGuard[Sequence[object]]:
152
+ return isinstance(obj, Sequence)
153
+
154
+
155
+ def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]:
156
+ return isinstance(obj, Sequence)
157
+
158
+
159
+ def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]:
160
+ return isinstance(obj, Mapping)
161
+
162
+
163
+ def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]:
164
+ return isinstance(obj, Mapping)
165
+
166
+
167
+ def is_dict(obj: object) -> TypeGuard[dict[object, object]]:
168
+ return isinstance(obj, dict)
169
+
170
+
171
+ def is_list(obj: object) -> TypeGuard[list[object]]:
172
+ return isinstance(obj, list)
173
+
174
+
175
+ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]:
176
+ return isinstance(obj, Iterable)
177
+
178
+
179
+ def deepcopy_minimal(item: _T) -> _T:
180
+ """Minimal reimplementation of copy.deepcopy() that will only copy certain object types:
181
+
182
+ - mappings, e.g. `dict`
183
+ - list
184
+
185
+ This is done for performance reasons.
186
+ """
187
+ if is_mapping(item):
188
+ return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()})
189
+ if is_list(item):
190
+ return cast(_T, [deepcopy_minimal(entry) for entry in item])
191
+ return item
192
+
193
+
194
+ # copied from https://github.com/Rapptz/RoboDanny
195
+ def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str:
196
+ size = len(seq)
197
+ if size == 0:
198
+ return ""
199
+
200
+ if size == 1:
201
+ return seq[0]
202
+
203
+ if size == 2:
204
+ return f"{seq[0]} {final} {seq[1]}"
205
+
206
+ return delim.join(seq[:-1]) + f" {final} {seq[-1]}"
207
+
208
+
209
+ def quote(string: str) -> str:
210
+ """Add single quotation marks around the given string. Does *not* do any escaping."""
211
+ return f"'{string}'"
212
+
213
+
214
+ def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]:
215
+ """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function.
216
+
217
+ Useful for enforcing runtime validation of overloaded functions.
218
+
219
+ Example usage:
220
+ ```py
221
+ @overload
222
+ def foo(*, a: str) -> str: ...
223
+
224
+
225
+ @overload
226
+ def foo(*, b: bool) -> str: ...
227
+
228
+
229
+ # This enforces the same constraints that a static type checker would
230
+ # i.e. that either a or b must be passed to the function
231
+ @required_args(["a"], ["b"])
232
+ def foo(*, a: str | None = None, b: bool | None = None) -> str: ...
233
+ ```
234
+ """
235
+
236
+ def inner(func: CallableT) -> CallableT:
237
+ params = inspect.signature(func).parameters
238
+ positional = [
239
+ name
240
+ for name, param in params.items()
241
+ if param.kind
242
+ in {
243
+ param.POSITIONAL_ONLY,
244
+ param.POSITIONAL_OR_KEYWORD,
245
+ }
246
+ ]
247
+
248
+ @functools.wraps(func)
249
+ def wrapper(*args: object, **kwargs: object) -> object:
250
+ given_params: set[str] = set()
251
+ for i, _ in enumerate(args):
252
+ try:
253
+ given_params.add(positional[i])
254
+ except IndexError:
255
+ raise TypeError(
256
+ f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given"
257
+ ) from None
258
+
259
+ for key in kwargs.keys():
260
+ given_params.add(key)
261
+
262
+ for variant in variants:
263
+ matches = all((param in given_params for param in variant))
264
+ if matches:
265
+ break
266
+ else: # no break
267
+ if len(variants) > 1:
268
+ variations = human_join(
269
+ ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants]
270
+ )
271
+ msg = f"Missing required arguments; Expected either {variations} arguments to be given"
272
+ else:
273
+ assert len(variants) > 0
274
+
275
+ # TODO: this error message is not deterministic
276
+ missing = list(set(variants[0]) - given_params)
277
+ if len(missing) > 1:
278
+ msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}"
279
+ else:
280
+ msg = f"Missing required argument: {quote(missing[0])}"
281
+ raise TypeError(msg)
282
+ return func(*args, **kwargs)
283
+
284
+ return wrapper # type: ignore
285
+
286
+ return inner
287
+
288
+
289
+ _K = TypeVar("_K")
290
+ _V = TypeVar("_V")
291
+
292
+
293
+ @overload
294
+ def strip_not_given(obj: None) -> None: ...
295
+
296
+
297
+ @overload
298
+ def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ...
299
+
300
+
301
+ @overload
302
+ def strip_not_given(obj: object) -> object: ...
303
+
304
+
305
+ def strip_not_given(obj: object | None) -> object:
306
+ """Remove all top-level keys where their values are instances of `NotGiven`"""
307
+ if obj is None:
308
+ return None
309
+
310
+ if not is_mapping(obj):
311
+ return obj
312
+
313
+ return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)}
314
+
315
+
316
+ def coerce_integer(val: str) -> int:
317
+ return int(val, base=10)
318
+
319
+
320
+ def coerce_float(val: str) -> float:
321
+ return float(val)
322
+
323
+
324
+ def coerce_boolean(val: str) -> bool:
325
+ return val == "true" or val == "1" or val == "on"
326
+
327
+
328
+ def maybe_coerce_integer(val: str | None) -> int | None:
329
+ if val is None:
330
+ return None
331
+ return coerce_integer(val)
332
+
333
+
334
+ def maybe_coerce_float(val: str | None) -> float | None:
335
+ if val is None:
336
+ return None
337
+ return coerce_float(val)
338
+
339
+
340
+ def maybe_coerce_boolean(val: str | None) -> bool | None:
341
+ if val is None:
342
+ return None
343
+ return coerce_boolean(val)
344
+
345
+
346
+ def removeprefix(string: str, prefix: str) -> str:
347
+ """Remove a prefix from a string.
348
+
349
+ Backport of `str.removeprefix` for Python < 3.9
350
+ """
351
+ if string.startswith(prefix):
352
+ return string[len(prefix) :]
353
+ return string
354
+
355
+
356
+ def removesuffix(string: str, suffix: str) -> str:
357
+ """Remove a suffix from a string.
358
+
359
+ Backport of `str.removesuffix` for Python < 3.9
360
+ """
361
+ if string.endswith(suffix):
362
+ return string[: -len(suffix)]
363
+ return string
364
+
365
+
366
+ def file_from_path(path: str) -> FileTypes:
367
+ contents = Path(path).read_bytes()
368
+ file_name = os.path.basename(path)
369
+ return (file_name, contents)
370
+
371
+
372
+ def get_required_header(headers: HeadersLike, header: str) -> str:
373
+ lower_header = header.lower()
374
+ if is_mapping_t(headers):
375
+ # mypy doesn't understand the type narrowing here
376
+ for k, v in headers.items(): # type: ignore
377
+ if k.lower() == lower_header and isinstance(v, str):
378
+ return v
379
+
380
+ # to deal with the case where the header looks like Stainless-Event-Id
381
+ intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize())
382
+
383
+ for normalized_header in [header, lower_header, header.upper(), intercaps_header]:
384
+ value = headers.get(normalized_header)
385
+ if value:
386
+ return value
387
+
388
+ raise ValueError(f"Could not find {header} header")
389
+
390
+
391
+ def get_async_library() -> str:
392
+ try:
393
+ return sniffio.current_async_library()
394
+ except Exception:
395
+ return "false"
396
+
397
+
398
+ def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]:
399
+ """A version of functools.lru_cache that retains the type signature
400
+ for the wrapped function arguments.
401
+ """
402
+ wrapper = functools.lru_cache( # noqa: TID251
403
+ maxsize=maxsize,
404
+ )
405
+ return cast(Any, wrapper) # type: ignore[no-any-return]
406
+
407
+
408
+ def json_safe(data: object) -> object:
409
+ """Translates a mapping / sequence recursively in the same fashion
410
+ as `pydantic` v2's `model_dump(mode="json")`.
411
+ """
412
+ if is_mapping(data):
413
+ return {json_safe(key): json_safe(value) for key, value in data.items()}
414
+
415
+ if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)):
416
+ return [json_safe(item) for item in data]
417
+
418
+ if isinstance(data, (datetime, date)):
419
+ return data.isoformat()
420
+
421
+ return data
@@ -0,0 +1,4 @@
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ __title__ = "hiddenlayer"
4
+ __version__ = "3.0.1" # x-release-please-version
hiddenlayer/lib/.keep ADDED
@@ -0,0 +1,4 @@
1
+ File generated from our OpenAPI spec by Stainless.
2
+
3
+ This directory can be used to store custom files to expand the SDK.
4
+ It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
@@ -0,0 +1,6 @@
1
+ # Custom extensions for HiddenLayer SDK
2
+
3
+ from .model_scan import ModelScanner, AsyncModelScanner
4
+ from .community_scan import CommunityScanner, AsyncCommunityScanner
5
+
6
+ __all__ = ["CommunityScanner", "AsyncCommunityScanner", "ModelScanner", "AsyncModelScanner"]
@@ -0,0 +1,174 @@
1
+ """
2
+ Community scan functionality for Hidden Layer SDK.
3
+
4
+ This module provides the community_scan method that was available in the old SDK,
5
+ which combines scan request + polling + results retrieval in a single convenient method.
6
+ """
7
+
8
+ import logging
9
+ from typing import Union, Literal, cast
10
+ from typing_extensions import TYPE_CHECKING
11
+
12
+ from .scan_utils import get_scan_results, wait_for_scan_results, get_scan_results_async, wait_for_scan_results_async
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ if TYPE_CHECKING:
17
+ from .. import HiddenLayer, AsyncHiddenLayer
18
+ from ..types.scans import ScanReport
19
+
20
+
21
+ class CommunityScanSource:
22
+ """Community scan source constants matching the old SDK."""
23
+
24
+ LOCAL = "LOCAL"
25
+ AWS_PRESIGNED = "AWS_PRESIGNED"
26
+ AWS_IAM_ROLE = "AWS_IAM_ROLE"
27
+ AZURE_BLOB_SAS = "AZURE_BLOB_SAS"
28
+ AZURE_BLOB_AD = "AZURE_BLOB_AD"
29
+ GOOGLE_SIGNED = "GOOGLE_SIGNED"
30
+ GOOGLE_OAUTH = "GOOGLE_OAUTH"
31
+ HUGGING_FACE = "HUGGING_FACE"
32
+
33
+
34
+ class CommunityScanner:
35
+ """
36
+ Community scanner that provides the community_scan method with polling functionality.
37
+
38
+ This class extends the generated SDK to provide the same functionality as the old SDK's
39
+ community_scan method, which initiates a scan and optionally waits for results.
40
+ """
41
+
42
+ def __init__(self, client: "HiddenLayer") -> None:
43
+ self._client = client
44
+
45
+ def community_scan(
46
+ self,
47
+ model_name: str,
48
+ model_path: str,
49
+ model_source: Union[
50
+ str,
51
+ Literal[
52
+ "LOCAL",
53
+ "AWS_PRESIGNED",
54
+ "AWS_IAM_ROLE",
55
+ "AZURE_BLOB_SAS",
56
+ "AZURE_BLOB_AD",
57
+ "GOOGLE_SIGNED",
58
+ "GOOGLE_OAUTH",
59
+ "HUGGING_FACE",
60
+ ],
61
+ ],
62
+ model_version: str = "main",
63
+ wait_for_results: bool = True,
64
+ request_source: str = "API Upload",
65
+ origin: str = "",
66
+ ) -> "ScanReport":
67
+ """
68
+ Scan a model available at a remote location using the HiddenLayer Model Scanner.
69
+
70
+ :param model_name: Name of the model to be shown on the HiddenLayer UI.
71
+ :param model_path: Path to the model file in the remote location, e.g. a presigned S3 URL
72
+ :param model_source: type of remote location where the model is stored.
73
+ :param model_version: Version of the model to be shown on the HiddenLayer UI.
74
+ :param wait_for_results: True whether to wait for the scan to finish, defaults to True.
75
+ :param request_source: Source that requested the scan.
76
+ :param origin: Origin platform where the model came from.
77
+
78
+ :returns: Scan Results
79
+ """
80
+ # Create the scan job
81
+ scan_job = self._client.scans.jobs.request(
82
+ access={
83
+ "source": cast(
84
+ "Literal['LOCAL', 'AWS_PRESIGNED', 'AWS_IAM_ROLE', 'AZURE_BLOB_SAS', 'AZURE_BLOB_AD', 'GOOGLE_SIGNED', 'GOOGLE_OAUTH', 'HUGGING_FACE']",
85
+ model_source,
86
+ )
87
+ },
88
+ inventory={
89
+ "model_name": model_name,
90
+ "model_version": model_version,
91
+ "requested_scan_location": model_path,
92
+ "requesting_entity": "hiddenlayer-python-sdk",
93
+ "request_source": cast(
94
+ "Literal['Hybrid Upload', 'API Upload', 'Integration', 'UI Upload']", request_source
95
+ ),
96
+ "origin": origin,
97
+ },
98
+ )
99
+
100
+ scan_id = scan_job.scan_id
101
+ if scan_id is None:
102
+ raise ValueError("scan_id must have a value")
103
+
104
+ if wait_for_results:
105
+ return wait_for_scan_results(self._client, scan_id=scan_id)
106
+ else:
107
+ # Return current scan status without waiting
108
+ return get_scan_results(self._client, scan_id=scan_id)
109
+
110
+
111
+ class AsyncCommunityScanner:
112
+ """
113
+ Async version of CommunityScanner for use with AsyncHiddenLayer client.
114
+ """
115
+
116
+ def __init__(self, client: "AsyncHiddenLayer") -> None:
117
+ self._client = client
118
+
119
+ async def community_scan(
120
+ self,
121
+ model_name: str,
122
+ model_path: str,
123
+ model_source: Union[
124
+ str,
125
+ Literal[
126
+ "LOCAL",
127
+ "AWS_PRESIGNED",
128
+ "AWS_IAM_ROLE",
129
+ "AZURE_BLOB_SAS",
130
+ "AZURE_BLOB_AD",
131
+ "GOOGLE_SIGNED",
132
+ "GOOGLE_OAUTH",
133
+ "HUGGING_FACE",
134
+ ],
135
+ ],
136
+ model_version: str = "main",
137
+ wait_for_results: bool = True,
138
+ request_source: str = "API Upload",
139
+ origin: str = "",
140
+ ) -> "ScanReport":
141
+ """
142
+ Async version of community_scan.
143
+
144
+ See CommunityScanner.community_scan for parameter documentation.
145
+ """
146
+ # Create the scan job
147
+ scan_job = await self._client.scans.jobs.request(
148
+ access={
149
+ "source": cast(
150
+ "Literal['LOCAL', 'AWS_PRESIGNED', 'AWS_IAM_ROLE', 'AZURE_BLOB_SAS', 'AZURE_BLOB_AD', 'GOOGLE_SIGNED', 'GOOGLE_OAUTH', 'HUGGING_FACE']",
151
+ model_source,
152
+ )
153
+ },
154
+ inventory={
155
+ "model_name": model_name,
156
+ "model_version": model_version,
157
+ "requested_scan_location": model_path,
158
+ "requesting_entity": "hiddenlayer-python-sdk",
159
+ "request_source": cast(
160
+ "Literal['Hybrid Upload', 'API Upload', 'Integration', 'UI Upload']", request_source
161
+ ),
162
+ "origin": origin,
163
+ },
164
+ )
165
+
166
+ scan_id = scan_job.scan_id
167
+ if scan_id is None:
168
+ raise ValueError("scan_id must have a value")
169
+
170
+ if wait_for_results:
171
+ return await wait_for_scan_results_async(self._client, scan_id=scan_id)
172
+ else:
173
+ # Return current scan status without waiting
174
+ return await get_scan_results_async(self._client, scan_id=scan_id)