mirascope 2.0.1__py3-none-any.whl → 2.1.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 (75) hide show
  1. mirascope/_stubs.py +39 -18
  2. mirascope/_utils.py +34 -0
  3. mirascope/api/_generated/__init__.py +4 -0
  4. mirascope/api/_generated/organization_invitations/client.py +2 -2
  5. mirascope/api/_generated/organization_invitations/raw_client.py +2 -2
  6. mirascope/api/_generated/project_memberships/__init__.py +4 -0
  7. mirascope/api/_generated/project_memberships/client.py +91 -0
  8. mirascope/api/_generated/project_memberships/raw_client.py +239 -0
  9. mirascope/api/_generated/project_memberships/types/__init__.py +4 -0
  10. mirascope/api/_generated/project_memberships/types/project_memberships_get_response.py +33 -0
  11. mirascope/api/_generated/project_memberships/types/project_memberships_get_response_role.py +7 -0
  12. mirascope/api/_generated/reference.md +73 -1
  13. mirascope/llm/__init__.py +19 -0
  14. mirascope/llm/calls/calls.py +28 -21
  15. mirascope/llm/calls/decorator.py +17 -24
  16. mirascope/llm/formatting/__init__.py +2 -2
  17. mirascope/llm/formatting/format.py +2 -4
  18. mirascope/llm/formatting/types.py +19 -2
  19. mirascope/llm/models/models.py +66 -146
  20. mirascope/llm/prompts/decorator.py +5 -16
  21. mirascope/llm/prompts/prompts.py +35 -38
  22. mirascope/llm/providers/anthropic/_utils/beta_decode.py +22 -7
  23. mirascope/llm/providers/anthropic/_utils/beta_encode.py +22 -16
  24. mirascope/llm/providers/anthropic/_utils/decode.py +45 -7
  25. mirascope/llm/providers/anthropic/_utils/encode.py +28 -15
  26. mirascope/llm/providers/anthropic/beta_provider.py +33 -69
  27. mirascope/llm/providers/anthropic/provider.py +52 -91
  28. mirascope/llm/providers/base/_utils.py +4 -9
  29. mirascope/llm/providers/base/base_provider.py +89 -205
  30. mirascope/llm/providers/google/_utils/decode.py +51 -1
  31. mirascope/llm/providers/google/_utils/encode.py +38 -21
  32. mirascope/llm/providers/google/provider.py +33 -69
  33. mirascope/llm/providers/mirascope/provider.py +25 -61
  34. mirascope/llm/providers/mlx/encoding/base.py +3 -6
  35. mirascope/llm/providers/mlx/encoding/transformers.py +4 -8
  36. mirascope/llm/providers/mlx/mlx.py +9 -21
  37. mirascope/llm/providers/mlx/provider.py +33 -69
  38. mirascope/llm/providers/openai/completions/_utils/encode.py +39 -20
  39. mirascope/llm/providers/openai/completions/base_provider.py +34 -75
  40. mirascope/llm/providers/openai/provider.py +25 -61
  41. mirascope/llm/providers/openai/responses/_utils/decode.py +31 -2
  42. mirascope/llm/providers/openai/responses/_utils/encode.py +32 -17
  43. mirascope/llm/providers/openai/responses/provider.py +34 -75
  44. mirascope/llm/responses/__init__.py +2 -1
  45. mirascope/llm/responses/base_stream_response.py +4 -0
  46. mirascope/llm/responses/response.py +8 -12
  47. mirascope/llm/responses/stream_response.py +8 -12
  48. mirascope/llm/responses/usage.py +44 -0
  49. mirascope/llm/tools/__init__.py +24 -0
  50. mirascope/llm/tools/provider_tools.py +18 -0
  51. mirascope/llm/tools/tool_schema.py +11 -4
  52. mirascope/llm/tools/toolkit.py +24 -6
  53. mirascope/llm/tools/types.py +112 -0
  54. mirascope/llm/tools/web_search_tool.py +32 -0
  55. mirascope/ops/__init__.py +19 -1
  56. mirascope/ops/_internal/closure.py +4 -1
  57. mirascope/ops/_internal/exporters/exporters.py +13 -46
  58. mirascope/ops/_internal/exporters/utils.py +37 -0
  59. mirascope/ops/_internal/instrumentation/__init__.py +20 -0
  60. mirascope/ops/_internal/instrumentation/llm/common.py +19 -49
  61. mirascope/ops/_internal/instrumentation/llm/model.py +61 -82
  62. mirascope/ops/_internal/instrumentation/llm/serialize.py +36 -12
  63. mirascope/ops/_internal/instrumentation/providers/__init__.py +29 -0
  64. mirascope/ops/_internal/instrumentation/providers/anthropic.py +78 -0
  65. mirascope/ops/_internal/instrumentation/providers/base.py +179 -0
  66. mirascope/ops/_internal/instrumentation/providers/google_genai.py +85 -0
  67. mirascope/ops/_internal/instrumentation/providers/openai.py +82 -0
  68. mirascope/ops/_internal/traced_calls.py +14 -0
  69. mirascope/ops/_internal/traced_functions.py +7 -2
  70. mirascope/ops/_internal/utils.py +12 -4
  71. mirascope/ops/_internal/versioned_functions.py +1 -1
  72. {mirascope-2.0.1.dist-info → mirascope-2.1.0.dist-info}/METADATA +96 -68
  73. {mirascope-2.0.1.dist-info → mirascope-2.1.0.dist-info}/RECORD +75 -64
  74. {mirascope-2.0.1.dist-info → mirascope-2.1.0.dist-info}/WHEEL +0 -0
  75. {mirascope-2.0.1.dist-info → mirascope-2.1.0.dist-info}/licenses/LICENSE +0 -0
mirascope/_stubs.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """Utilities for stubbing modules with missing optional dependencies."""
2
2
 
3
3
  import sys
4
- from collections.abc import MutableSequence
4
+ from collections.abc import MutableSequence, Sequence
5
5
  from importlib.abc import Loader, MetaPathFinder
6
6
  from importlib.machinery import ModuleSpec
7
7
  from types import ModuleType
@@ -33,6 +33,9 @@ EXTRA_IMPORTS: dict[str, list[str]] = {
33
33
  "opentelemetry.propagators.jaeger",
34
34
  "libcst",
35
35
  "packaging",
36
+ "opentelemetry.instrumentation.openai_v2",
37
+ "opentelemetry.instrumentation.anthropic",
38
+ "opentelemetry.instrumentation.google_genai",
36
39
  ],
37
40
  "mlx": ["mlx_lm"],
38
41
  }
@@ -314,44 +317,62 @@ class _StubModule(ModuleType):
314
317
 
315
318
  def stub_module_if_missing(
316
319
  module_path: str,
317
- package_name: str,
320
+ package_name: str | Sequence[str],
318
321
  ) -> bool:
319
- """Check if all packages for an extra are installed; if not, install a stub module.
322
+ """Check if all packages for one or more extras are installed; if not, stub a module.
320
323
 
321
324
  This must be called BEFORE importing from the module.
322
325
 
323
326
  Args:
324
327
  module_path: Full module path to stub (e.g., 'mirascope.ops._internal.tracing')
325
- package_name: The extra name (e.g., 'ops'). Must exist in EXTRA_IMPORTS.
328
+ package_name: The extra name (e.g., "ops") or a sequence of extra names.
329
+ If multiple extras are provided, the module is available only when
330
+ all of the extras have all imports available.
331
+ An empty sequence is a no-op and returns True (vacuously satisfied).
326
332
 
327
333
  Returns:
328
- True if all packages for the extra are available, False if stubbed.
334
+ True if all packages for all extras are available (including empty sequence),
335
+ False if stubbed.
329
336
 
330
337
  Raises:
331
338
  KeyError: If package_name is not found in EXTRA_IMPORTS mapping.
332
339
  """
333
- if package_name not in EXTRA_IMPORTS:
340
+ package_names: tuple[str, ...]
341
+ if isinstance(package_name, str):
342
+ package_names = (package_name,)
343
+ else:
344
+ package_names = tuple(package_name)
345
+
346
+ unknown = [name for name in package_names if name not in EXTRA_IMPORTS]
347
+ if unknown:
348
+ unknown_names = ", ".join(unknown)
334
349
  raise KeyError(
335
- f"Unknown extra '{package_name}'. "
350
+ f"Unknown extra '{unknown_names}'. "
336
351
  f"Available extras: {', '.join(EXTRA_IMPORTS.keys())}"
337
352
  )
338
353
 
339
- imports_to_check = EXTRA_IMPORTS[package_name]
354
+ def _extra_available(extra_name: str) -> bool:
355
+ imports_to_check = EXTRA_IMPORTS[extra_name]
356
+ for import_name in imports_to_check:
357
+ try:
358
+ __import__(import_name)
359
+ except ImportError:
360
+ return False
361
+ return True
340
362
 
341
- # Check if ALL imports for this extra are available
342
- all_available = True
343
- for import_name in imports_to_check:
344
- try:
345
- __import__(import_name)
346
- except ImportError:
347
- all_available = False
363
+ # Find the first missing extra (short-circuit to avoid unnecessary imports)
364
+ first_missing_extra: str | None = None
365
+ for extra_name in package_names:
366
+ if not _extra_available(extra_name):
367
+ first_missing_extra = extra_name
348
368
  break
349
369
 
350
- if all_available:
370
+ # If no extras are missing, all packages are available
371
+ if first_missing_extra is None:
351
372
  return True
352
373
 
353
- # At least one import is missing, so stub the module
354
- sys.modules[module_path] = _StubModule(module_path, package_name)
374
+ # Use the first missing extra for the error message
375
+ sys.modules[module_path] = _StubModule(module_path, first_missing_extra)
355
376
 
356
377
  # Register with the finder to handle nested imports
357
378
  _finder.register_stub(module_path)
mirascope/_utils.py ADDED
@@ -0,0 +1,34 @@
1
+ """Shared internal utilities for mirascope."""
2
+
3
+ from typing import Any
4
+
5
+ # Attributes to copy from wrapped functions (matches functools.WRAPPER_ASSIGNMENTS)
6
+ WRAPPER_ASSIGNMENTS = (
7
+ "__module__",
8
+ "__name__",
9
+ "__qualname__",
10
+ "__annotations__",
11
+ "__doc__",
12
+ )
13
+
14
+
15
+ def copy_function_metadata(target: Any, source: Any) -> None: # noqa: ANN401
16
+ """Copy standard function metadata from source to target.
17
+
18
+ Copies __module__, __name__, __qualname__, __annotations__, __doc__
19
+ from source to target, and sets __wrapped__ to source.
20
+
21
+ This enables decorator stacking by preserving the original function's
22
+ metadata on wrapper objects.
23
+
24
+ Args:
25
+ target: The wrapper object to copy metadata to
26
+ source: The original function to copy metadata from
27
+ """
28
+ for attr in WRAPPER_ASSIGNMENTS:
29
+ try:
30
+ value = getattr(source, attr)
31
+ object.__setattr__(target, attr, value)
32
+ except AttributeError:
33
+ pass
34
+ object.__setattr__(target, "__wrapped__", source)
@@ -165,6 +165,8 @@ from .project_memberships import (
165
165
  ProjectMembershipsCreateRequestRole,
166
166
  ProjectMembershipsCreateResponse,
167
167
  ProjectMembershipsCreateResponseRole,
168
+ ProjectMembershipsGetResponse,
169
+ ProjectMembershipsGetResponseRole,
168
170
  ProjectMembershipsListResponseItem,
169
171
  ProjectMembershipsListResponseItemRole,
170
172
  ProjectMembershipsUpdateRequestRole,
@@ -348,6 +350,8 @@ __all__ = [
348
350
  "ProjectMembershipsCreateRequestRole",
349
351
  "ProjectMembershipsCreateResponse",
350
352
  "ProjectMembershipsCreateResponseRole",
353
+ "ProjectMembershipsGetResponse",
354
+ "ProjectMembershipsGetResponseRole",
351
355
  "ProjectMembershipsListResponseItem",
352
356
  "ProjectMembershipsListResponseItemRole",
353
357
  "ProjectMembershipsUpdateRequestRole",
@@ -92,7 +92,7 @@ class OrganizationInvitationsClient:
92
92
  organization_id : str
93
93
 
94
94
  recipient_email : str
95
- a string matching the pattern ^[^\s@]+@[^\s@]+\.[^\s@]+$
95
+ a string matching the pattern ^[^ \t\n\r\f\v@]+@[^ \t\n\r\f\v@]+[.][^ \t\n\r\f\v@]+$
96
96
 
97
97
  role : OrganizationInvitationsCreateRequestRole
98
98
 
@@ -335,7 +335,7 @@ class AsyncOrganizationInvitationsClient:
335
335
  organization_id : str
336
336
 
337
337
  recipient_email : str
338
- a string matching the pattern ^[^\s@]+@[^\s@]+\.[^\s@]+$
338
+ a string matching the pattern ^[^ \t\n\r\f\v@]+@[^ \t\n\r\f\v@]+[.][^ \t\n\r\f\v@]+$
339
339
 
340
340
  role : OrganizationInvitationsCreateRequestRole
341
341
 
@@ -172,7 +172,7 @@ class RawOrganizationInvitationsClient:
172
172
  organization_id : str
173
173
 
174
174
  recipient_email : str
175
- a string matching the pattern ^[^\s@]+@[^\s@]+\.[^\s@]+$
175
+ a string matching the pattern ^[^ \t\n\r\f\v@]+@[^ \t\n\r\f\v@]+[.][^ \t\n\r\f\v@]+$
176
176
 
177
177
  role : OrganizationInvitationsCreateRequestRole
178
178
 
@@ -911,7 +911,7 @@ class AsyncRawOrganizationInvitationsClient:
911
911
  organization_id : str
912
912
 
913
913
  recipient_email : str
914
- a string matching the pattern ^[^\s@]+@[^\s@]+\.[^\s@]+$
914
+ a string matching the pattern ^[^ \t\n\r\f\v@]+@[^ \t\n\r\f\v@]+[.][^ \t\n\r\f\v@]+$
915
915
 
916
916
  role : OrganizationInvitationsCreateRequestRole
917
917
 
@@ -6,6 +6,8 @@ from .types import (
6
6
  ProjectMembershipsCreateRequestRole,
7
7
  ProjectMembershipsCreateResponse,
8
8
  ProjectMembershipsCreateResponseRole,
9
+ ProjectMembershipsGetResponse,
10
+ ProjectMembershipsGetResponseRole,
9
11
  ProjectMembershipsListResponseItem,
10
12
  ProjectMembershipsListResponseItemRole,
11
13
  ProjectMembershipsUpdateRequestRole,
@@ -17,6 +19,8 @@ __all__ = [
17
19
  "ProjectMembershipsCreateRequestRole",
18
20
  "ProjectMembershipsCreateResponse",
19
21
  "ProjectMembershipsCreateResponseRole",
22
+ "ProjectMembershipsGetResponse",
23
+ "ProjectMembershipsGetResponseRole",
20
24
  "ProjectMembershipsListResponseItem",
21
25
  "ProjectMembershipsListResponseItemRole",
22
26
  "ProjectMembershipsUpdateRequestRole",
@@ -9,6 +9,7 @@ from .types.project_memberships_create_request_role import (
9
9
  ProjectMembershipsCreateRequestRole,
10
10
  )
11
11
  from .types.project_memberships_create_response import ProjectMembershipsCreateResponse
12
+ from .types.project_memberships_get_response import ProjectMembershipsGetResponse
12
13
  from .types.project_memberships_list_response_item import (
13
14
  ProjectMembershipsListResponseItem,
14
15
  )
@@ -122,6 +123,47 @@ class ProjectMembershipsClient:
122
123
  )
123
124
  return _response.data
124
125
 
126
+ def get(
127
+ self,
128
+ organization_id: str,
129
+ project_id: str,
130
+ member_id: str,
131
+ *,
132
+ request_options: typing.Optional[RequestOptions] = None,
133
+ ) -> ProjectMembershipsGetResponse:
134
+ """
135
+ Parameters
136
+ ----------
137
+ organization_id : str
138
+
139
+ project_id : str
140
+
141
+ member_id : str
142
+
143
+ request_options : typing.Optional[RequestOptions]
144
+ Request-specific configuration.
145
+
146
+ Returns
147
+ -------
148
+ ProjectMembershipsGetResponse
149
+ Success
150
+
151
+ Examples
152
+ --------
153
+ from mirascope.api._generated import Mirascope
154
+
155
+ client = Mirascope()
156
+ client.project_memberships.get(
157
+ organization_id="organizationId",
158
+ project_id="projectId",
159
+ member_id="memberId",
160
+ )
161
+ """
162
+ _response = self._raw_client.get(
163
+ organization_id, project_id, member_id, request_options=request_options
164
+ )
165
+ return _response.data
166
+
125
167
  def delete(
126
168
  self,
127
169
  organization_id: str,
@@ -331,6 +373,55 @@ class AsyncProjectMembershipsClient:
331
373
  )
332
374
  return _response.data
333
375
 
376
+ async def get(
377
+ self,
378
+ organization_id: str,
379
+ project_id: str,
380
+ member_id: str,
381
+ *,
382
+ request_options: typing.Optional[RequestOptions] = None,
383
+ ) -> ProjectMembershipsGetResponse:
384
+ """
385
+ Parameters
386
+ ----------
387
+ organization_id : str
388
+
389
+ project_id : str
390
+
391
+ member_id : str
392
+
393
+ request_options : typing.Optional[RequestOptions]
394
+ Request-specific configuration.
395
+
396
+ Returns
397
+ -------
398
+ ProjectMembershipsGetResponse
399
+ Success
400
+
401
+ Examples
402
+ --------
403
+ import asyncio
404
+
405
+ from mirascope.api._generated import AsyncMirascope
406
+
407
+ client = AsyncMirascope()
408
+
409
+
410
+ async def main() -> None:
411
+ await client.project_memberships.get(
412
+ organization_id="organizationId",
413
+ project_id="projectId",
414
+ member_id="memberId",
415
+ )
416
+
417
+
418
+ asyncio.run(main())
419
+ """
420
+ _response = await self._raw_client.get(
421
+ organization_id, project_id, member_id, request_options=request_options
422
+ )
423
+ return _response.data
424
+
334
425
  async def delete(
335
426
  self,
336
427
  organization_id: str,
@@ -23,6 +23,7 @@ from .types.project_memberships_create_request_role import (
23
23
  ProjectMembershipsCreateRequestRole,
24
24
  )
25
25
  from .types.project_memberships_create_response import ProjectMembershipsCreateResponse
26
+ from .types.project_memberships_get_response import ProjectMembershipsGetResponse
26
27
  from .types.project_memberships_list_response_item import (
27
28
  ProjectMembershipsListResponseItem,
28
29
  )
@@ -296,6 +297,125 @@ class RawProjectMembershipsClient:
296
297
  body=_response_json,
297
298
  )
298
299
 
300
+ def get(
301
+ self,
302
+ organization_id: str,
303
+ project_id: str,
304
+ member_id: str,
305
+ *,
306
+ request_options: typing.Optional[RequestOptions] = None,
307
+ ) -> HttpResponse[ProjectMembershipsGetResponse]:
308
+ """
309
+ Parameters
310
+ ----------
311
+ organization_id : str
312
+
313
+ project_id : str
314
+
315
+ member_id : str
316
+
317
+ request_options : typing.Optional[RequestOptions]
318
+ Request-specific configuration.
319
+
320
+ Returns
321
+ -------
322
+ HttpResponse[ProjectMembershipsGetResponse]
323
+ Success
324
+ """
325
+ _response = self._client_wrapper.httpx_client.request(
326
+ f"organizations/{jsonable_encoder(organization_id)}/projects/{jsonable_encoder(project_id)}/members/{jsonable_encoder(member_id)}",
327
+ method="GET",
328
+ request_options=request_options,
329
+ )
330
+ try:
331
+ if 200 <= _response.status_code < 300:
332
+ _data = typing.cast(
333
+ ProjectMembershipsGetResponse,
334
+ parse_obj_as(
335
+ type_=ProjectMembershipsGetResponse, # type: ignore
336
+ object_=_response.json(),
337
+ ),
338
+ )
339
+ return HttpResponse(response=_response, data=_data)
340
+ if _response.status_code == 400:
341
+ raise BadRequestError(
342
+ headers=dict(_response.headers),
343
+ body=typing.cast(
344
+ typing.Optional[typing.Any],
345
+ parse_obj_as(
346
+ type_=typing.Optional[typing.Any], # type: ignore
347
+ object_=_response.json(),
348
+ ),
349
+ ),
350
+ )
351
+ if _response.status_code == 403:
352
+ raise ForbiddenError(
353
+ headers=dict(_response.headers),
354
+ body=typing.cast(
355
+ PermissionDeniedError,
356
+ parse_obj_as(
357
+ type_=PermissionDeniedError, # type: ignore
358
+ object_=_response.json(),
359
+ ),
360
+ ),
361
+ )
362
+ if _response.status_code == 404:
363
+ raise NotFoundError(
364
+ headers=dict(_response.headers),
365
+ body=typing.cast(
366
+ NotFoundErrorBody,
367
+ parse_obj_as(
368
+ type_=NotFoundErrorBody, # type: ignore
369
+ object_=_response.json(),
370
+ ),
371
+ ),
372
+ )
373
+ if _response.status_code == 429:
374
+ raise TooManyRequestsError(
375
+ headers=dict(_response.headers),
376
+ body=typing.cast(
377
+ RateLimitError,
378
+ parse_obj_as(
379
+ type_=RateLimitError, # type: ignore
380
+ object_=_response.json(),
381
+ ),
382
+ ),
383
+ )
384
+ if _response.status_code == 500:
385
+ raise InternalServerError(
386
+ headers=dict(_response.headers),
387
+ body=typing.cast(
388
+ typing.Optional[typing.Any],
389
+ parse_obj_as(
390
+ type_=typing.Optional[typing.Any], # type: ignore
391
+ object_=_response.json(),
392
+ ),
393
+ ),
394
+ )
395
+ if _response.status_code == 503:
396
+ raise ServiceUnavailableError(
397
+ headers=dict(_response.headers),
398
+ body=typing.cast(
399
+ typing.Optional[typing.Any],
400
+ parse_obj_as(
401
+ type_=typing.Optional[typing.Any], # type: ignore
402
+ object_=_response.json(),
403
+ ),
404
+ ),
405
+ )
406
+ _response_json = _response.json()
407
+ except JSONDecodeError:
408
+ raise ApiError(
409
+ status_code=_response.status_code,
410
+ headers=dict(_response.headers),
411
+ body=_response.text,
412
+ )
413
+ raise ApiError(
414
+ status_code=_response.status_code,
415
+ headers=dict(_response.headers),
416
+ body=_response_json,
417
+ )
418
+
299
419
  def delete(
300
420
  self,
301
421
  organization_id: str,
@@ -798,6 +918,125 @@ class AsyncRawProjectMembershipsClient:
798
918
  body=_response_json,
799
919
  )
800
920
 
921
+ async def get(
922
+ self,
923
+ organization_id: str,
924
+ project_id: str,
925
+ member_id: str,
926
+ *,
927
+ request_options: typing.Optional[RequestOptions] = None,
928
+ ) -> AsyncHttpResponse[ProjectMembershipsGetResponse]:
929
+ """
930
+ Parameters
931
+ ----------
932
+ organization_id : str
933
+
934
+ project_id : str
935
+
936
+ member_id : str
937
+
938
+ request_options : typing.Optional[RequestOptions]
939
+ Request-specific configuration.
940
+
941
+ Returns
942
+ -------
943
+ AsyncHttpResponse[ProjectMembershipsGetResponse]
944
+ Success
945
+ """
946
+ _response = await self._client_wrapper.httpx_client.request(
947
+ f"organizations/{jsonable_encoder(organization_id)}/projects/{jsonable_encoder(project_id)}/members/{jsonable_encoder(member_id)}",
948
+ method="GET",
949
+ request_options=request_options,
950
+ )
951
+ try:
952
+ if 200 <= _response.status_code < 300:
953
+ _data = typing.cast(
954
+ ProjectMembershipsGetResponse,
955
+ parse_obj_as(
956
+ type_=ProjectMembershipsGetResponse, # type: ignore
957
+ object_=_response.json(),
958
+ ),
959
+ )
960
+ return AsyncHttpResponse(response=_response, data=_data)
961
+ if _response.status_code == 400:
962
+ raise BadRequestError(
963
+ headers=dict(_response.headers),
964
+ body=typing.cast(
965
+ typing.Optional[typing.Any],
966
+ parse_obj_as(
967
+ type_=typing.Optional[typing.Any], # type: ignore
968
+ object_=_response.json(),
969
+ ),
970
+ ),
971
+ )
972
+ if _response.status_code == 403:
973
+ raise ForbiddenError(
974
+ headers=dict(_response.headers),
975
+ body=typing.cast(
976
+ PermissionDeniedError,
977
+ parse_obj_as(
978
+ type_=PermissionDeniedError, # type: ignore
979
+ object_=_response.json(),
980
+ ),
981
+ ),
982
+ )
983
+ if _response.status_code == 404:
984
+ raise NotFoundError(
985
+ headers=dict(_response.headers),
986
+ body=typing.cast(
987
+ NotFoundErrorBody,
988
+ parse_obj_as(
989
+ type_=NotFoundErrorBody, # type: ignore
990
+ object_=_response.json(),
991
+ ),
992
+ ),
993
+ )
994
+ if _response.status_code == 429:
995
+ raise TooManyRequestsError(
996
+ headers=dict(_response.headers),
997
+ body=typing.cast(
998
+ RateLimitError,
999
+ parse_obj_as(
1000
+ type_=RateLimitError, # type: ignore
1001
+ object_=_response.json(),
1002
+ ),
1003
+ ),
1004
+ )
1005
+ if _response.status_code == 500:
1006
+ raise InternalServerError(
1007
+ headers=dict(_response.headers),
1008
+ body=typing.cast(
1009
+ typing.Optional[typing.Any],
1010
+ parse_obj_as(
1011
+ type_=typing.Optional[typing.Any], # type: ignore
1012
+ object_=_response.json(),
1013
+ ),
1014
+ ),
1015
+ )
1016
+ if _response.status_code == 503:
1017
+ raise ServiceUnavailableError(
1018
+ headers=dict(_response.headers),
1019
+ body=typing.cast(
1020
+ typing.Optional[typing.Any],
1021
+ parse_obj_as(
1022
+ type_=typing.Optional[typing.Any], # type: ignore
1023
+ object_=_response.json(),
1024
+ ),
1025
+ ),
1026
+ )
1027
+ _response_json = _response.json()
1028
+ except JSONDecodeError:
1029
+ raise ApiError(
1030
+ status_code=_response.status_code,
1031
+ headers=dict(_response.headers),
1032
+ body=_response.text,
1033
+ )
1034
+ raise ApiError(
1035
+ status_code=_response.status_code,
1036
+ headers=dict(_response.headers),
1037
+ body=_response_json,
1038
+ )
1039
+
801
1040
  async def delete(
802
1041
  self,
803
1042
  organization_id: str,
@@ -7,6 +7,8 @@ from .project_memberships_create_response import ProjectMembershipsCreateRespons
7
7
  from .project_memberships_create_response_role import (
8
8
  ProjectMembershipsCreateResponseRole,
9
9
  )
10
+ from .project_memberships_get_response import ProjectMembershipsGetResponse
11
+ from .project_memberships_get_response_role import ProjectMembershipsGetResponseRole
10
12
  from .project_memberships_list_response_item import ProjectMembershipsListResponseItem
11
13
  from .project_memberships_list_response_item_role import (
12
14
  ProjectMembershipsListResponseItemRole,
@@ -21,6 +23,8 @@ __all__ = [
21
23
  "ProjectMembershipsCreateRequestRole",
22
24
  "ProjectMembershipsCreateResponse",
23
25
  "ProjectMembershipsCreateResponseRole",
26
+ "ProjectMembershipsGetResponse",
27
+ "ProjectMembershipsGetResponseRole",
24
28
  "ProjectMembershipsListResponseItem",
25
29
  "ProjectMembershipsListResponseItemRole",
26
30
  "ProjectMembershipsUpdateRequestRole",
@@ -0,0 +1,33 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ import pydantic
6
+ import typing_extensions
7
+ from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
8
+ from ...core.serialization import FieldMetadata
9
+ from ...types.date import Date
10
+ from .project_memberships_get_response_role import ProjectMembershipsGetResponseRole
11
+
12
+
13
+ class ProjectMembershipsGetResponse(UniversalBaseModel):
14
+ member_id: typing_extensions.Annotated[str, FieldMetadata(alias="memberId")]
15
+ organization_id: typing_extensions.Annotated[
16
+ str, FieldMetadata(alias="organizationId")
17
+ ]
18
+ project_id: typing_extensions.Annotated[str, FieldMetadata(alias="projectId")]
19
+ role: ProjectMembershipsGetResponseRole
20
+ created_at: typing_extensions.Annotated[
21
+ typing.Optional[Date], FieldMetadata(alias="createdAt")
22
+ ] = None
23
+
24
+ if IS_PYDANTIC_V2:
25
+ model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
26
+ extra="allow", frozen=True
27
+ ) # type: ignore # Pydantic v2
28
+ else:
29
+
30
+ class Config:
31
+ frozen = True
32
+ smart_union = True
33
+ extra = pydantic.Extra.allow
@@ -0,0 +1,7 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ ProjectMembershipsGetResponseRole = typing.Union[
6
+ typing.Literal["ADMIN", "DEVELOPER", "VIEWER", "ANNOTATOR"], typing.Any
7
+ ]