hiddenlayer-sdk 2.0.10__py3-none-any.whl → 3.0.0__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.0.dist-info/METADATA +431 -0
  79. hiddenlayer_sdk-3.0.0.dist-info/RECORD +82 -0
  80. {hiddenlayer_sdk-2.0.10.dist-info → hiddenlayer_sdk-3.0.0.dist-info}/WHEEL +1 -2
  81. {hiddenlayer_sdk-2.0.10.dist-info → hiddenlayer_sdk-3.0.0.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
hiddenlayer/_client.py ADDED
@@ -0,0 +1,761 @@
1
+ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from typing import TYPE_CHECKING, Any, Dict, Mapping, cast
7
+ from typing_extensions import Self, Literal, override
8
+
9
+ import httpx
10
+
11
+ from . import _exceptions
12
+ from ._qs import Querystring
13
+ from ._types import (
14
+ Omit,
15
+ Timeout,
16
+ NotGiven,
17
+ Transport,
18
+ ProxiesTypes,
19
+ RequestOptions,
20
+ not_given,
21
+ )
22
+ from ._utils import is_given, get_async_library
23
+ from ._compat import cached_property
24
+ from ._oauth2 import OAuth2ClientCredentials, make_oauth2
25
+ from ._version import __version__
26
+ from ._streaming import Stream as Stream, AsyncStream as AsyncStream
27
+ from ._exceptions import APIStatusError
28
+ from ._base_client import (
29
+ DEFAULT_MAX_RETRIES,
30
+ SyncAPIClient,
31
+ AsyncAPIClient,
32
+ )
33
+
34
+ if TYPE_CHECKING:
35
+ from .resources import scans, models, sensors, interactions, prompt_analyzer
36
+ from .lib.model_scan import ModelScanner, AsyncModelScanner
37
+ from .resources.sensors import SensorsResource, AsyncSensorsResource
38
+ from .lib.community_scan import CommunityScanner, AsyncCommunityScanner
39
+ from .resources.scans.scans import ScansResource, AsyncScansResource
40
+ from .resources.interactions import InteractionsResource, AsyncInteractionsResource
41
+ from .resources.models.models import ModelsResource, AsyncModelsResource
42
+ from .resources.prompt_analyzer import PromptAnalyzerResource, AsyncPromptAnalyzerResource
43
+
44
+ __all__ = [
45
+ "ENVIRONMENTS",
46
+ "Timeout",
47
+ "Transport",
48
+ "ProxiesTypes",
49
+ "RequestOptions",
50
+ "HiddenLayer",
51
+ "AsyncHiddenLayer",
52
+ "Client",
53
+ "AsyncClient",
54
+ ]
55
+
56
+ ENVIRONMENTS: Dict[str, str] = {
57
+ "prod-us": "https://api.hiddenlayer.ai",
58
+ "prod-eu": "https://api.eu.hiddenlayer.ai",
59
+ }
60
+
61
+
62
+ class HiddenLayer(SyncAPIClient):
63
+ # client options
64
+ bearer_token: str | None
65
+ client_id: str | None
66
+ client_secret: str | None
67
+
68
+ _environment: Literal["prod-us", "prod-eu"] | NotGiven
69
+
70
+ def __init__(
71
+ self,
72
+ *,
73
+ bearer_token: str | None = None,
74
+ client_id: str | None = None,
75
+ client_secret: str | None = None,
76
+ environment: Literal["prod-us", "prod-eu"] | NotGiven = not_given,
77
+ base_url: str | httpx.URL | None | NotGiven = not_given,
78
+ timeout: float | Timeout | None | NotGiven = not_given,
79
+ max_retries: int = DEFAULT_MAX_RETRIES,
80
+ default_headers: Mapping[str, str] | None = None,
81
+ default_query: Mapping[str, object] | None = None,
82
+ # Configure a custom httpx client.
83
+ # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`.
84
+ # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details.
85
+ http_client: httpx.Client | None = None,
86
+ # Enable or disable schema validation for data returned by the API.
87
+ # When enabled an error APIResponseValidationError is raised
88
+ # if the API responds with invalid data for the expected schema.
89
+ #
90
+ # This parameter may be removed or changed in the future.
91
+ # If you rely on this feature, please open a GitHub issue
92
+ # outlining your use-case to help us decide if it should be
93
+ # part of our public interface in the future.
94
+ _strict_response_validation: bool = False,
95
+ ) -> None:
96
+ """Construct a new synchronous HiddenLayer client instance.
97
+
98
+ This automatically infers the following arguments from their corresponding environment variables if they are not provided:
99
+ - `bearer_token` from `HIDDENLAYER_TOKEN`
100
+ - `client_id` from `HIDDENLAYER_CLIENT_ID`
101
+ - `client_secret` from `HIDDENLAYER_CLIENT_SECRET`
102
+ """
103
+ if bearer_token is None:
104
+ bearer_token = os.environ.get("HIDDENLAYER_TOKEN")
105
+ self.bearer_token = bearer_token
106
+
107
+ if client_id is None:
108
+ client_id = os.environ.get("HIDDENLAYER_CLIENT_ID")
109
+ self.client_id = client_id
110
+
111
+ if client_secret is None:
112
+ client_secret = os.environ.get("HIDDENLAYER_CLIENT_SECRET")
113
+ self.client_secret = client_secret
114
+
115
+ self._environment = environment
116
+
117
+ base_url_env = os.environ.get("HIDDENLAYER_BASE_URL")
118
+ if is_given(base_url) and base_url is not None:
119
+ # cast required because mypy doesn't understand the type narrowing
120
+ base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
121
+ elif is_given(environment):
122
+ if base_url_env and base_url is not None:
123
+ raise ValueError(
124
+ "Ambiguous URL; The `HIDDENLAYER_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
125
+ )
126
+
127
+ try:
128
+ base_url = ENVIRONMENTS[environment]
129
+ except KeyError as exc:
130
+ raise ValueError(f"Unknown environment: {environment}") from exc
131
+ elif base_url_env is not None:
132
+ base_url = base_url_env
133
+ else:
134
+ self._environment = environment = "prod-us"
135
+
136
+ try:
137
+ base_url = ENVIRONMENTS[environment]
138
+ except KeyError as exc:
139
+ raise ValueError(f"Unknown environment: {environment}") from exc
140
+
141
+ super().__init__(
142
+ version=__version__,
143
+ base_url=base_url,
144
+ max_retries=max_retries,
145
+ timeout=timeout,
146
+ http_client=http_client,
147
+ custom_headers=default_headers,
148
+ custom_query=default_query,
149
+ _strict_response_validation=_strict_response_validation,
150
+ )
151
+
152
+ @cached_property
153
+ def models(self) -> ModelsResource:
154
+ from .resources.models import ModelsResource
155
+
156
+ return ModelsResource(self)
157
+
158
+ @cached_property
159
+ def prompt_analyzer(self) -> PromptAnalyzerResource:
160
+ from .resources.prompt_analyzer import PromptAnalyzerResource
161
+
162
+ return PromptAnalyzerResource(self)
163
+
164
+ @cached_property
165
+ def interactions(self) -> InteractionsResource:
166
+ from .resources.interactions import InteractionsResource
167
+
168
+ return InteractionsResource(self)
169
+
170
+ @cached_property
171
+ def sensors(self) -> SensorsResource:
172
+ from .resources.sensors import SensorsResource
173
+
174
+ return SensorsResource(self)
175
+
176
+ @cached_property
177
+ def scans(self) -> ScansResource:
178
+ from .resources.scans import ScansResource
179
+
180
+ return ScansResource(self)
181
+
182
+ @cached_property
183
+ def community_scanner(self) -> "CommunityScanner":
184
+ from .lib.community_scan import CommunityScanner
185
+
186
+ return CommunityScanner(self)
187
+
188
+ @cached_property
189
+ def model_scanner(self) -> "ModelScanner":
190
+ from .lib.model_scan import ModelScanner
191
+
192
+ return ModelScanner(self)
193
+
194
+ @cached_property
195
+ def with_raw_response(self) -> HiddenLayerWithRawResponse:
196
+ return HiddenLayerWithRawResponse(self)
197
+
198
+ @cached_property
199
+ def with_streaming_response(self) -> HiddenLayerWithStreamedResponse:
200
+ return HiddenLayerWithStreamedResponse(self)
201
+
202
+ @property
203
+ @override
204
+ def qs(self) -> Querystring:
205
+ return Querystring(array_format="comma")
206
+
207
+ @property
208
+ @override
209
+ def auth_headers(self) -> dict[str, str]:
210
+ bearer_token = self.bearer_token
211
+ if bearer_token is None:
212
+ return {}
213
+ return {"Authorization": f"Bearer {bearer_token}"}
214
+
215
+ @property
216
+ @override
217
+ def custom_auth(self) -> httpx.Auth | None:
218
+ if self.client_id and self.client_secret:
219
+ return make_oauth2(
220
+ client_id=self.client_id,
221
+ client_secret=self.client_secret,
222
+ token_url=self._prepare_url("/oauth2/token?grant_type=client_credentials"),
223
+ header="Authorization",
224
+ )
225
+ return None
226
+
227
+ @property
228
+ @override
229
+ def default_headers(self) -> dict[str, str | Omit]:
230
+ return {
231
+ **super().default_headers,
232
+ "X-Stainless-Async": "false",
233
+ **self._custom_headers,
234
+ }
235
+
236
+ @override
237
+ def _should_retry(self, response: httpx.Response) -> bool:
238
+ # Retry on 401 if we are using OAuth2 and the token might be expired
239
+ if response.status_code == 401 and isinstance(self.custom_auth, OAuth2ClientCredentials):
240
+ if self.custom_auth.token_is_expired():
241
+ self.custom_auth.invalidate_token()
242
+ return True
243
+ return super()._should_retry(response)
244
+
245
+
246
+ def copy(
247
+ self,
248
+ *,
249
+ bearer_token: str | None = None,
250
+ client_id: str | None = None,
251
+ client_secret: str | None = None,
252
+ environment: Literal["prod-us", "prod-eu"] | None = None,
253
+ base_url: str | httpx.URL | None = None,
254
+ timeout: float | Timeout | None | NotGiven = not_given,
255
+ http_client: httpx.Client | None = None,
256
+ max_retries: int | NotGiven = not_given,
257
+ default_headers: Mapping[str, str] | None = None,
258
+ set_default_headers: Mapping[str, str] | None = None,
259
+ default_query: Mapping[str, object] | None = None,
260
+ set_default_query: Mapping[str, object] | None = None,
261
+ _extra_kwargs: Mapping[str, Any] = {},
262
+ ) -> Self:
263
+ """
264
+ Create a new client instance re-using the same options given to the current client with optional overriding.
265
+ """
266
+ if default_headers is not None and set_default_headers is not None:
267
+ raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
268
+
269
+ if default_query is not None and set_default_query is not None:
270
+ raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive")
271
+
272
+ headers = self._custom_headers
273
+ if default_headers is not None:
274
+ headers = {**headers, **default_headers}
275
+ elif set_default_headers is not None:
276
+ headers = set_default_headers
277
+
278
+ params = self._custom_query
279
+ if default_query is not None:
280
+ params = {**params, **default_query}
281
+ elif set_default_query is not None:
282
+ params = set_default_query
283
+
284
+ http_client = http_client or self._client
285
+ return self.__class__(
286
+ bearer_token=bearer_token or self.bearer_token,
287
+ client_id=client_id or self.client_id,
288
+ client_secret=client_secret or self.client_secret,
289
+ base_url=base_url or self.base_url,
290
+ environment=environment or self._environment,
291
+ timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
292
+ http_client=http_client,
293
+ max_retries=max_retries if is_given(max_retries) else self.max_retries,
294
+ default_headers=headers,
295
+ default_query=params,
296
+ **_extra_kwargs,
297
+ )
298
+
299
+ # Alias for `copy` for nicer inline usage, e.g.
300
+ # client.with_options(timeout=10).foo.create(...)
301
+ with_options = copy
302
+
303
+ @override
304
+ def _make_status_error(
305
+ self,
306
+ err_msg: str,
307
+ *,
308
+ body: object,
309
+ response: httpx.Response,
310
+ ) -> APIStatusError:
311
+ if response.status_code == 400:
312
+ return _exceptions.BadRequestError(err_msg, response=response, body=body)
313
+
314
+ if response.status_code == 401:
315
+ return _exceptions.AuthenticationError(err_msg, response=response, body=body)
316
+
317
+ if response.status_code == 403:
318
+ return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)
319
+
320
+ if response.status_code == 404:
321
+ return _exceptions.NotFoundError(err_msg, response=response, body=body)
322
+
323
+ if response.status_code == 409:
324
+ return _exceptions.ConflictError(err_msg, response=response, body=body)
325
+
326
+ if response.status_code == 422:
327
+ return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)
328
+
329
+ if response.status_code == 429:
330
+ return _exceptions.RateLimitError(err_msg, response=response, body=body)
331
+
332
+ if response.status_code >= 500:
333
+ return _exceptions.InternalServerError(err_msg, response=response, body=body)
334
+ return APIStatusError(err_msg, response=response, body=body)
335
+
336
+
337
+ class AsyncHiddenLayer(AsyncAPIClient):
338
+ # client options
339
+ bearer_token: str | None
340
+ client_id: str | None
341
+ client_secret: str | None
342
+
343
+ _environment: Literal["prod-us", "prod-eu"] | NotGiven
344
+
345
+ def __init__(
346
+ self,
347
+ *,
348
+ bearer_token: str | None = None,
349
+ client_id: str | None = None,
350
+ client_secret: str | None = None,
351
+ environment: Literal["prod-us", "prod-eu"] | NotGiven = not_given,
352
+ base_url: str | httpx.URL | None | NotGiven = not_given,
353
+ timeout: float | Timeout | None | NotGiven = not_given,
354
+ max_retries: int = DEFAULT_MAX_RETRIES,
355
+ default_headers: Mapping[str, str] | None = None,
356
+ default_query: Mapping[str, object] | None = None,
357
+ # Configure a custom httpx client.
358
+ # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`.
359
+ # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details.
360
+ http_client: httpx.AsyncClient | None = None,
361
+ # Enable or disable schema validation for data returned by the API.
362
+ # When enabled an error APIResponseValidationError is raised
363
+ # if the API responds with invalid data for the expected schema.
364
+ #
365
+ # This parameter may be removed or changed in the future.
366
+ # If you rely on this feature, please open a GitHub issue
367
+ # outlining your use-case to help us decide if it should be
368
+ # part of our public interface in the future.
369
+ _strict_response_validation: bool = False,
370
+ ) -> None:
371
+ """Construct a new async AsyncHiddenLayer client instance.
372
+
373
+ This automatically infers the following arguments from their corresponding environment variables if they are not provided:
374
+ - `bearer_token` from `HIDDENLAYER_TOKEN`
375
+ - `client_id` from `HIDDENLAYER_CLIENT_ID`
376
+ - `client_secret` from `HIDDENLAYER_CLIENT_SECRET`
377
+ """
378
+ if bearer_token is None:
379
+ bearer_token = os.environ.get("HIDDENLAYER_TOKEN")
380
+ self.bearer_token = bearer_token
381
+
382
+ if client_id is None:
383
+ client_id = os.environ.get("HIDDENLAYER_CLIENT_ID")
384
+ self.client_id = client_id
385
+
386
+ if client_secret is None:
387
+ client_secret = os.environ.get("HIDDENLAYER_CLIENT_SECRET")
388
+ self.client_secret = client_secret
389
+
390
+ self._environment = environment
391
+
392
+ base_url_env = os.environ.get("HIDDENLAYER_BASE_URL")
393
+ if is_given(base_url) and base_url is not None:
394
+ # cast required because mypy doesn't understand the type narrowing
395
+ base_url = cast("str | httpx.URL", base_url) # pyright: ignore[reportUnnecessaryCast]
396
+ elif is_given(environment):
397
+ if base_url_env and base_url is not None:
398
+ raise ValueError(
399
+ "Ambiguous URL; The `HIDDENLAYER_BASE_URL` env var and the `environment` argument are given. If you want to use the environment, you must pass base_url=None",
400
+ )
401
+
402
+ try:
403
+ base_url = ENVIRONMENTS[environment]
404
+ except KeyError as exc:
405
+ raise ValueError(f"Unknown environment: {environment}") from exc
406
+ elif base_url_env is not None:
407
+ base_url = base_url_env
408
+ else:
409
+ self._environment = environment = "prod-us"
410
+
411
+ try:
412
+ base_url = ENVIRONMENTS[environment]
413
+ except KeyError as exc:
414
+ raise ValueError(f"Unknown environment: {environment}") from exc
415
+
416
+ super().__init__(
417
+ version=__version__,
418
+ base_url=base_url,
419
+ max_retries=max_retries,
420
+ timeout=timeout,
421
+ http_client=http_client,
422
+ custom_headers=default_headers,
423
+ custom_query=default_query,
424
+ _strict_response_validation=_strict_response_validation,
425
+ )
426
+
427
+ @cached_property
428
+ def models(self) -> AsyncModelsResource:
429
+ from .resources.models import AsyncModelsResource
430
+
431
+ return AsyncModelsResource(self)
432
+
433
+ @cached_property
434
+ def prompt_analyzer(self) -> AsyncPromptAnalyzerResource:
435
+ from .resources.prompt_analyzer import AsyncPromptAnalyzerResource
436
+
437
+ return AsyncPromptAnalyzerResource(self)
438
+
439
+ @cached_property
440
+ def interactions(self) -> AsyncInteractionsResource:
441
+ from .resources.interactions import AsyncInteractionsResource
442
+
443
+ return AsyncInteractionsResource(self)
444
+
445
+ @cached_property
446
+ def sensors(self) -> AsyncSensorsResource:
447
+ from .resources.sensors import AsyncSensorsResource
448
+
449
+ return AsyncSensorsResource(self)
450
+
451
+ @cached_property
452
+ def scans(self) -> AsyncScansResource:
453
+ from .resources.scans import AsyncScansResource
454
+
455
+ return AsyncScansResource(self)
456
+
457
+ @cached_property
458
+ def community_scanner(self) -> "AsyncCommunityScanner":
459
+ from .lib.community_scan import AsyncCommunityScanner
460
+
461
+ return AsyncCommunityScanner(self)
462
+
463
+ @cached_property
464
+ def model_scanner(self) -> "AsyncModelScanner":
465
+ from .lib.model_scan import AsyncModelScanner
466
+
467
+ return AsyncModelScanner(self)
468
+
469
+ @cached_property
470
+ def with_raw_response(self) -> AsyncHiddenLayerWithRawResponse:
471
+ return AsyncHiddenLayerWithRawResponse(self)
472
+
473
+ @cached_property
474
+ def with_streaming_response(self) -> AsyncHiddenLayerWithStreamedResponse:
475
+ return AsyncHiddenLayerWithStreamedResponse(self)
476
+
477
+ @property
478
+ @override
479
+ def qs(self) -> Querystring:
480
+ return Querystring(array_format="comma")
481
+
482
+ @property
483
+ @override
484
+ def auth_headers(self) -> dict[str, str]:
485
+ bearer_token = self.bearer_token
486
+ if bearer_token is None:
487
+ return {}
488
+ return {"Authorization": f"Bearer {bearer_token}"}
489
+
490
+ @property
491
+ @override
492
+ def custom_auth(self) -> httpx.Auth | None:
493
+ if self.client_id and self.client_secret:
494
+ return make_oauth2(
495
+ client_id=self.client_id,
496
+ client_secret=self.client_secret,
497
+ token_url=self._prepare_url("/oauth2/token?grant_type=client_credentials"),
498
+ header="Authorization",
499
+ )
500
+ return None
501
+
502
+ @property
503
+ @override
504
+ def default_headers(self) -> dict[str, str | Omit]:
505
+ return {
506
+ **super().default_headers,
507
+ "X-Stainless-Async": f"async:{get_async_library()}",
508
+ **self._custom_headers,
509
+ }
510
+
511
+ @override
512
+ def _should_retry(self, response: httpx.Response) -> bool:
513
+ # Retry on 401 if we are using OAuth2 and the token might be expired
514
+ if response.status_code == 401 and isinstance(self.custom_auth, OAuth2ClientCredentials):
515
+ if self.custom_auth.token_is_expired():
516
+ self.custom_auth.invalidate_token()
517
+ return True
518
+ return super()._should_retry(response)
519
+
520
+ def copy(
521
+ self,
522
+ *,
523
+ bearer_token: str | None = None,
524
+ client_id: str | None = None,
525
+ client_secret: str | None = None,
526
+ environment: Literal["prod-us", "prod-eu"] | None = None,
527
+ base_url: str | httpx.URL | None = None,
528
+ timeout: float | Timeout | None | NotGiven = not_given,
529
+ http_client: httpx.AsyncClient | None = None,
530
+ max_retries: int | NotGiven = not_given,
531
+ default_headers: Mapping[str, str] | None = None,
532
+ set_default_headers: Mapping[str, str] | None = None,
533
+ default_query: Mapping[str, object] | None = None,
534
+ set_default_query: Mapping[str, object] | None = None,
535
+ _extra_kwargs: Mapping[str, Any] = {},
536
+ ) -> Self:
537
+ """
538
+ Create a new client instance re-using the same options given to the current client with optional overriding.
539
+ """
540
+ if default_headers is not None and set_default_headers is not None:
541
+ raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive")
542
+
543
+ if default_query is not None and set_default_query is not None:
544
+ raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive")
545
+
546
+ headers = self._custom_headers
547
+ if default_headers is not None:
548
+ headers = {**headers, **default_headers}
549
+ elif set_default_headers is not None:
550
+ headers = set_default_headers
551
+
552
+ params = self._custom_query
553
+ if default_query is not None:
554
+ params = {**params, **default_query}
555
+ elif set_default_query is not None:
556
+ params = set_default_query
557
+
558
+ http_client = http_client or self._client
559
+ return self.__class__(
560
+ bearer_token=bearer_token or self.bearer_token,
561
+ client_id=client_id or self.client_id,
562
+ client_secret=client_secret or self.client_secret,
563
+ base_url=base_url or self.base_url,
564
+ environment=environment or self._environment,
565
+ timeout=self.timeout if isinstance(timeout, NotGiven) else timeout,
566
+ http_client=http_client,
567
+ max_retries=max_retries if is_given(max_retries) else self.max_retries,
568
+ default_headers=headers,
569
+ default_query=params,
570
+ **_extra_kwargs,
571
+ )
572
+
573
+ # Alias for `copy` for nicer inline usage, e.g.
574
+ # client.with_options(timeout=10).foo.create(...)
575
+ with_options = copy
576
+
577
+ @override
578
+ def _make_status_error(
579
+ self,
580
+ err_msg: str,
581
+ *,
582
+ body: object,
583
+ response: httpx.Response,
584
+ ) -> APIStatusError:
585
+ if response.status_code == 400:
586
+ return _exceptions.BadRequestError(err_msg, response=response, body=body)
587
+
588
+ if response.status_code == 401:
589
+ return _exceptions.AuthenticationError(err_msg, response=response, body=body)
590
+
591
+ if response.status_code == 403:
592
+ return _exceptions.PermissionDeniedError(err_msg, response=response, body=body)
593
+
594
+ if response.status_code == 404:
595
+ return _exceptions.NotFoundError(err_msg, response=response, body=body)
596
+
597
+ if response.status_code == 409:
598
+ return _exceptions.ConflictError(err_msg, response=response, body=body)
599
+
600
+ if response.status_code == 422:
601
+ return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body)
602
+
603
+ if response.status_code == 429:
604
+ return _exceptions.RateLimitError(err_msg, response=response, body=body)
605
+
606
+ if response.status_code >= 500:
607
+ return _exceptions.InternalServerError(err_msg, response=response, body=body)
608
+ return APIStatusError(err_msg, response=response, body=body)
609
+
610
+
611
+ class HiddenLayerWithRawResponse:
612
+ _client: HiddenLayer
613
+
614
+ def __init__(self, client: HiddenLayer) -> None:
615
+ self._client = client
616
+
617
+ @cached_property
618
+ def models(self) -> models.ModelsResourceWithRawResponse:
619
+ from .resources.models import ModelsResourceWithRawResponse
620
+
621
+ return ModelsResourceWithRawResponse(self._client.models)
622
+
623
+ @cached_property
624
+ def prompt_analyzer(self) -> prompt_analyzer.PromptAnalyzerResourceWithRawResponse:
625
+ from .resources.prompt_analyzer import PromptAnalyzerResourceWithRawResponse
626
+
627
+ return PromptAnalyzerResourceWithRawResponse(self._client.prompt_analyzer)
628
+
629
+ @cached_property
630
+ def interactions(self) -> interactions.InteractionsResourceWithRawResponse:
631
+ from .resources.interactions import InteractionsResourceWithRawResponse
632
+
633
+ return InteractionsResourceWithRawResponse(self._client.interactions)
634
+
635
+ @cached_property
636
+ def sensors(self) -> sensors.SensorsResourceWithRawResponse:
637
+ from .resources.sensors import SensorsResourceWithRawResponse
638
+
639
+ return SensorsResourceWithRawResponse(self._client.sensors)
640
+
641
+ @cached_property
642
+ def scans(self) -> scans.ScansResourceWithRawResponse:
643
+ from .resources.scans import ScansResourceWithRawResponse
644
+
645
+ return ScansResourceWithRawResponse(self._client.scans)
646
+
647
+
648
+ class AsyncHiddenLayerWithRawResponse:
649
+ _client: AsyncHiddenLayer
650
+
651
+ def __init__(self, client: AsyncHiddenLayer) -> None:
652
+ self._client = client
653
+
654
+ @cached_property
655
+ def models(self) -> models.AsyncModelsResourceWithRawResponse:
656
+ from .resources.models import AsyncModelsResourceWithRawResponse
657
+
658
+ return AsyncModelsResourceWithRawResponse(self._client.models)
659
+
660
+ @cached_property
661
+ def prompt_analyzer(self) -> prompt_analyzer.AsyncPromptAnalyzerResourceWithRawResponse:
662
+ from .resources.prompt_analyzer import AsyncPromptAnalyzerResourceWithRawResponse
663
+
664
+ return AsyncPromptAnalyzerResourceWithRawResponse(self._client.prompt_analyzer)
665
+
666
+ @cached_property
667
+ def interactions(self) -> interactions.AsyncInteractionsResourceWithRawResponse:
668
+ from .resources.interactions import AsyncInteractionsResourceWithRawResponse
669
+
670
+ return AsyncInteractionsResourceWithRawResponse(self._client.interactions)
671
+
672
+ @cached_property
673
+ def sensors(self) -> sensors.AsyncSensorsResourceWithRawResponse:
674
+ from .resources.sensors import AsyncSensorsResourceWithRawResponse
675
+
676
+ return AsyncSensorsResourceWithRawResponse(self._client.sensors)
677
+
678
+ @cached_property
679
+ def scans(self) -> scans.AsyncScansResourceWithRawResponse:
680
+ from .resources.scans import AsyncScansResourceWithRawResponse
681
+
682
+ return AsyncScansResourceWithRawResponse(self._client.scans)
683
+
684
+
685
+ class HiddenLayerWithStreamedResponse:
686
+ _client: HiddenLayer
687
+
688
+ def __init__(self, client: HiddenLayer) -> None:
689
+ self._client = client
690
+
691
+ @cached_property
692
+ def models(self) -> models.ModelsResourceWithStreamingResponse:
693
+ from .resources.models import ModelsResourceWithStreamingResponse
694
+
695
+ return ModelsResourceWithStreamingResponse(self._client.models)
696
+
697
+ @cached_property
698
+ def prompt_analyzer(self) -> prompt_analyzer.PromptAnalyzerResourceWithStreamingResponse:
699
+ from .resources.prompt_analyzer import PromptAnalyzerResourceWithStreamingResponse
700
+
701
+ return PromptAnalyzerResourceWithStreamingResponse(self._client.prompt_analyzer)
702
+
703
+ @cached_property
704
+ def interactions(self) -> interactions.InteractionsResourceWithStreamingResponse:
705
+ from .resources.interactions import InteractionsResourceWithStreamingResponse
706
+
707
+ return InteractionsResourceWithStreamingResponse(self._client.interactions)
708
+
709
+ @cached_property
710
+ def sensors(self) -> sensors.SensorsResourceWithStreamingResponse:
711
+ from .resources.sensors import SensorsResourceWithStreamingResponse
712
+
713
+ return SensorsResourceWithStreamingResponse(self._client.sensors)
714
+
715
+ @cached_property
716
+ def scans(self) -> scans.ScansResourceWithStreamingResponse:
717
+ from .resources.scans import ScansResourceWithStreamingResponse
718
+
719
+ return ScansResourceWithStreamingResponse(self._client.scans)
720
+
721
+
722
+ class AsyncHiddenLayerWithStreamedResponse:
723
+ _client: AsyncHiddenLayer
724
+
725
+ def __init__(self, client: AsyncHiddenLayer) -> None:
726
+ self._client = client
727
+
728
+ @cached_property
729
+ def models(self) -> models.AsyncModelsResourceWithStreamingResponse:
730
+ from .resources.models import AsyncModelsResourceWithStreamingResponse
731
+
732
+ return AsyncModelsResourceWithStreamingResponse(self._client.models)
733
+
734
+ @cached_property
735
+ def prompt_analyzer(self) -> prompt_analyzer.AsyncPromptAnalyzerResourceWithStreamingResponse:
736
+ from .resources.prompt_analyzer import AsyncPromptAnalyzerResourceWithStreamingResponse
737
+
738
+ return AsyncPromptAnalyzerResourceWithStreamingResponse(self._client.prompt_analyzer)
739
+
740
+ @cached_property
741
+ def interactions(self) -> interactions.AsyncInteractionsResourceWithStreamingResponse:
742
+ from .resources.interactions import AsyncInteractionsResourceWithStreamingResponse
743
+
744
+ return AsyncInteractionsResourceWithStreamingResponse(self._client.interactions)
745
+
746
+ @cached_property
747
+ def sensors(self) -> sensors.AsyncSensorsResourceWithStreamingResponse:
748
+ from .resources.sensors import AsyncSensorsResourceWithStreamingResponse
749
+
750
+ return AsyncSensorsResourceWithStreamingResponse(self._client.sensors)
751
+
752
+ @cached_property
753
+ def scans(self) -> scans.AsyncScansResourceWithStreamingResponse:
754
+ from .resources.scans import AsyncScansResourceWithStreamingResponse
755
+
756
+ return AsyncScansResourceWithStreamingResponse(self._client.scans)
757
+
758
+
759
+ Client = HiddenLayer
760
+
761
+ AsyncClient = AsyncHiddenLayer