dyff-schema 0.7.1__py3-none-any.whl → 0.8.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.
dyff/schema/v0/r1/base.py CHANGED
@@ -1,8 +1,11 @@
1
1
  # SPDX-FileCopyrightText: 2024 UL Research Institutes
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
- from datetime import datetime
5
- from typing import Any, Generic, NamedTuple, Optional, Type, TypeVar
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ from datetime import datetime, timezone
8
+ from typing import Any, Generic, Literal, NamedTuple, Optional, Type, TypeVar
6
9
 
7
10
  import pydantic
8
11
 
@@ -555,8 +558,12 @@ def list_(
555
558
  return pydantic.conlist(item_type, min_items=list_size, max_items=list_size)
556
559
 
557
560
 
558
- class DyffSchemaBaseModel(pydantic.BaseModel):
559
- """Base class for pydantic models that used for defining data schemas.
561
+ # mypy gets confused because 'dict' is the name of a method in DyffDefaultSerializers
562
+ _ModelAsDict = dict[str, Any]
563
+
564
+
565
+ class DyffDefaultSerializers(pydantic.BaseModel):
566
+ """This must be the base class for *all pydantic models* in the Dyff schema.
560
567
 
561
568
  Overrides serialization functions to serialize by alias, so that "round-trip"
562
569
  serialization is the default for fields with aliases. We prefer aliases because we
@@ -564,24 +571,66 @@ class DyffSchemaBaseModel(pydantic.BaseModel):
564
571
  Python reserved words like 'bytes' as field names.
565
572
  """
566
573
 
574
+ def dict(self, *, by_alias: bool = True, **kwargs) -> _ModelAsDict:
575
+ return super().dict(by_alias=by_alias, **kwargs)
576
+
577
+ def json(self, *, by_alias: bool = True, **kwargs) -> str:
578
+ return super().json(by_alias=by_alias, **kwargs)
579
+
580
+ def model_dump(
581
+ self,
582
+ *,
583
+ mode: Literal["python", "json"] = "python",
584
+ by_alias: bool = True,
585
+ **kwargs,
586
+ ) -> _ModelAsDict:
587
+ """Encode the object as a dict containing only JSON datatypes.
588
+
589
+ .. deprecated::
590
+
591
+ FIXME: This emulates a Pydantic 2 feature, but the mode="json"
592
+ option can only be implemented in an inefficient way. Remove when
593
+ we convert to Pydantic 2. See: DYFF-223
594
+ """
595
+ if mode == "python":
596
+ return self.dict(by_alias=by_alias, **kwargs)
597
+ else:
598
+ return json.loads(self.json(by_alias=by_alias, **kwargs))
599
+
600
+
601
+ # Note: I *really* wanted to require datetimes to have timezones, like in
602
+ # DyffRequestDefaultValidators, but some existing objects in the Auth database
603
+ # don't have timezones set currently for historical reasons. It's actually
604
+ # better if all datetimes in the system are UTC, so that their JSON
605
+ # representations (i.e., isoformat strings) are well-ordered.
606
+ class DyffSchemaBaseModel(DyffDefaultSerializers):
607
+ """This should be the base class for *almost all* non-request models in the Dyff
608
+ schema. Models that do not inherit from this class *must* still inherit from
609
+ DyffDefaultSerializers.
610
+
611
+ Adds a root validator to ensure that all datetime fields are represented in the UTC
612
+ timezone. This is necessary to avoid errors when comparing "naive" and "aware"
613
+ datetimes. Using the UTC timezone everywhere ensures that JSON representations of
614
+ datetimes are well-ordered.
615
+ """
616
+
567
617
  @pydantic.root_validator
568
- def _require_datetime_timezone_aware(cls, values):
618
+ def _ensure_datetime_timezone_utc(cls, values):
619
+ update = {}
569
620
  for k, v in values.items():
570
621
  if isinstance(v, datetime):
571
622
  if v.tzinfo is None:
572
- raise ValueError(f"{cls.__qualname__}.{k}: timezone not set")
623
+ update[k] = v.replace(tzinfo=timezone.utc)
624
+ elif v.tzinfo != timezone.utc:
625
+ update[k] = v.astimezone(timezone.utc)
626
+ values.update(update)
573
627
  return values
574
628
 
575
- def dict(self, *, by_alias: bool = True, **kwargs) -> dict[str, Any]:
576
- return super().dict(by_alias=by_alias, **kwargs)
577
-
578
- def json(self, *, by_alias: bool = True, **kwargs) -> str:
579
- return super().json(by_alias=by_alias, **kwargs)
580
-
581
629
 
582
630
  __all__ = [
583
631
  "DTYPE",
584
632
  "DType",
633
+ "DyffDefaultSerializers",
585
634
  "DyffSchemaBaseModel",
586
635
  "FixedWidthFloat",
587
636
  "FixedWidthInt",
@@ -562,6 +562,27 @@ class APIKey(DyffSchemaBaseModel):
562
562
  )
563
563
 
564
564
 
565
+ class Identity(DyffSchemaBaseModel):
566
+ """The identity of an Account according to one or more external identity
567
+ providers."""
568
+
569
+ google: Optional[str] = pydantic.Field(default=None)
570
+
571
+
572
+ class Account(DyffSchemaBaseModel):
573
+ """An Account in the system.
574
+
575
+ All entities are owned by an Account.
576
+ """
577
+
578
+ name: str
579
+ identity: Identity = pydantic.Field(default_factory=Identity)
580
+ apiKeys: list[APIKey] = pydantic.Field(default_factory=list)
581
+ # --- Added by system
582
+ id: Optional[str] = None
583
+ creationTime: Optional[datetime] = None
584
+
585
+
565
586
  # ----------------------------------------------------------------------------
566
587
 
567
588
 
@@ -1841,6 +1862,7 @@ __all__ = [
1841
1862
  "Accelerator",
1842
1863
  "AcceleratorGPU",
1843
1864
  "AccessGrant",
1865
+ "Account",
1844
1866
  "Analysis",
1845
1867
  "AnalysisArgument",
1846
1868
  "AnalysisBase",
@@ -1881,6 +1903,7 @@ __all__ = [
1881
1903
  "ForeignMethod",
1882
1904
  "ForeignModel",
1883
1905
  "Frameworks",
1906
+ "Identity",
1884
1907
  "InferenceInterface",
1885
1908
  "InferenceService",
1886
1909
  "InferenceServiceBase",
@@ -18,7 +18,7 @@ from typing import Any, Optional, Union
18
18
 
19
19
  import pydantic
20
20
 
21
- from .base import DyffSchemaBaseModel
21
+ from .base import DyffDefaultSerializers
22
22
  from .platform import (
23
23
  AnalysisBase,
24
24
  DatasetBase,
@@ -37,7 +37,28 @@ from .platform import (
37
37
  from .version import SchemaVersion
38
38
 
39
39
 
40
- class DyffEntityCreateRequest(SchemaVersion, DyffSchemaBaseModel):
40
+ class DyffRequestDefaultValidators(DyffDefaultSerializers):
41
+ """This must be the base class for *all* request models in the Dyff schema.
42
+
43
+ Adds a root validator to ensure that all user-provided datetime fields have a
44
+ timezone set. Timezones will be converted to UTC once the data enters the platform,
45
+ but we allow requests to have non-UTC timezones for user convenience.
46
+ """
47
+
48
+ @pydantic.root_validator
49
+ def _require_datetime_timezone_aware(cls, values):
50
+ for k, v in values.items():
51
+ if isinstance(v, datetime):
52
+ if v.tzinfo is None:
53
+ raise ValueError(f"{cls.__qualname__}.{k}: timezone not set")
54
+ return values
55
+
56
+
57
+ class DyffRequestBase(SchemaVersion, DyffRequestDefaultValidators):
58
+ pass
59
+
60
+
61
+ class DyffEntityCreateRequest(DyffRequestBase):
41
62
  account: str = pydantic.Field(description="Account that owns the entity")
42
63
 
43
64
 
@@ -52,7 +73,7 @@ class DatasetCreateRequest(DyffEntityCreateRequest, DatasetBase):
52
73
  pass
53
74
 
54
75
 
55
- class DocumentationEditRequest(SchemaVersion, DocumentationBase):
76
+ class DocumentationEditRequest(DyffRequestBase, DocumentationBase):
56
77
  pass
57
78
 
58
79
 
@@ -66,7 +87,7 @@ class InferenceSessionCreateRequest(DyffEntityCreateRequest, InferenceSessionBas
66
87
  inferenceService: str = pydantic.Field(description="InferenceService ID")
67
88
 
68
89
 
69
- class InferenceSessionTokenCreateRequest(SchemaVersion, DyffSchemaBaseModel):
90
+ class InferenceSessionTokenCreateRequest(DyffRequestBase):
70
91
  expires: Optional[datetime] = pydantic.Field(
71
92
  default=None,
72
93
  description="Expiration time of the token. Must be <= expiration time"
@@ -137,11 +158,11 @@ class ReportCreateRequest(DyffEntityCreateRequest, ReportBase):
137
158
  )
138
159
 
139
160
 
140
- class TagCreateRequest(SchemaVersion, TagBase):
161
+ class TagCreateRequest(DyffRequestBase, TagBase):
141
162
  pass
142
163
 
143
164
 
144
- class LabelUpdateRequest(SchemaVersion, Labeled):
165
+ class LabelUpdateRequest(DyffRequestBase, Labeled):
145
166
  pass
146
167
 
147
168
 
@@ -150,7 +171,7 @@ class LabelUpdateRequest(SchemaVersion, Labeled):
150
171
  # specify. I think it's not that important, because all of the query parameters
151
172
  # will always be optional. There could be a problem if the semantics of a
152
173
  # name change, but let's just not do that!
153
- class DyffEntityQueryRequest(DyffSchemaBaseModel):
174
+ class DyffEntityQueryRequest(DyffRequestDefaultValidators):
154
175
  id: Optional[str] = pydantic.Field(default=None)
155
176
  account: Optional[str] = pydantic.Field(default=None)
156
177
  status: Optional[str] = pydantic.Field(default=None)
@@ -240,6 +261,8 @@ __all__ = [
240
261
  "AuditQueryRequest",
241
262
  "DyffEntityCreateRequest",
242
263
  "DyffEntityQueryRequest",
264
+ "DyffRequestBase",
265
+ "DyffRequestDefaultValidators",
243
266
  "DatasetCreateRequest",
244
267
  "DatasetQueryRequest",
245
268
  "DocumentationEditRequest",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dyff-schema
3
- Version: 0.7.1
3
+ Version: 0.8.0
4
4
  Summary: Data models for the Dyff AI auditing platform.
5
5
  Author-email: Digital Safety Research Institute <contact@dsri.org>
6
6
  License: Apache-2.0
@@ -20,9 +20,9 @@ dyff/schema/io/vllm.py,sha256=2q05M_-lTzq9oywKXHPPpCFCSDVCSsRQqtmERzWTtio,123
20
20
  dyff/schema/v0/__init__.py,sha256=L5y8UhRnojerPYHumsxQJRcHCNz8Hj9NM8b47mewMNs,92
21
21
  dyff/schema/v0/r1/__init__.py,sha256=L5y8UhRnojerPYHumsxQJRcHCNz8Hj9NM8b47mewMNs,92
22
22
  dyff/schema/v0/r1/adapters.py,sha256=2t2oxsnGfSEDKKDIEYw4qqLXMH7qlFIwPVuLyUmbsHs,23552
23
- dyff/schema/v0/r1/base.py,sha256=X4QVwOzNw5xPCF_f14w2JhARj1CHmGMjUmTcNzb-X0c,17471
24
- dyff/schema/v0/r1/platform.py,sha256=CJV_KrreahTyNUjswiie7rwYFAGaKYWwP7PgFXmbfSo,60823
25
- dyff/schema/v0/r1/requests.py,sha256=bTLtQUK80_fFeEWRG6QUJoLwy1_PyXf8ua9Ei4av0ug,8913
23
+ dyff/schema/v0/r1/base.py,sha256=QX1TfqX3jBafxpBnf2bUTcgP0sMyqZFFNJZQHhM48BI,19385
24
+ dyff/schema/v0/r1/platform.py,sha256=yvIvYaXvKT2Sk1y9zzqwUpyguPZi08WFeU2AI6LhPPM,61413
25
+ dyff/schema/v0/r1/requests.py,sha256=ADZ1FXn_lfgcI8hjNHnB6gfJK8fqrCm-oIADcDFWhpo,9728
26
26
  dyff/schema/v0/r1/test.py,sha256=X6dUyVd5svcPCI-PBMOAqEfK9jv3bRDvkQTJzwS96c0,10720
27
27
  dyff/schema/v0/r1/version.py,sha256=isKAGuGxsdru8vDaYmI4YiZdJOu_wNxXK7u6QzD6FE4,392
28
28
  dyff/schema/v0/r1/dataset/__init__.py,sha256=LbVlkO2asyGYBKk2z49xjJYTM-pu9y9e4eQDXgTDLnM,2553
@@ -33,9 +33,9 @@ dyff/schema/v0/r1/dataset/text.py,sha256=nLIn91Zlt0tNdXUklSgjJ-kEDxoPX32ISLkiv2D
33
33
  dyff/schema/v0/r1/dataset/vision.py,sha256=aIe0fbfM_g3DsrDTdg2K803YKLjZBpurM_VJcJFuZLc,369
34
34
  dyff/schema/v0/r1/io/__init__.py,sha256=L5y8UhRnojerPYHumsxQJRcHCNz8Hj9NM8b47mewMNs,92
35
35
  dyff/schema/v0/r1/io/vllm.py,sha256=CUE9y8KthtUI7sD49S875rDmPvKotSXVIRaBS79aBZs,5320
36
- dyff_schema-0.7.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
37
- dyff_schema-0.7.1.dist-info/METADATA,sha256=z_E0Af_XfeI-56ee6cFPPHkrTmhBd3UX_xabrnjbwZQ,3459
38
- dyff_schema-0.7.1.dist-info/NOTICE,sha256=YONACu0s_Ui6jNi-wtEsVQbTU1JIkh8wvLH6d1-Ni_w,43
39
- dyff_schema-0.7.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
40
- dyff_schema-0.7.1.dist-info/top_level.txt,sha256=9e3VVdeX73t_sUJOPQPCcGtYO1JhoErhHIi3WoWGcFI,5
41
- dyff_schema-0.7.1.dist-info/RECORD,,
36
+ dyff_schema-0.8.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
37
+ dyff_schema-0.8.0.dist-info/METADATA,sha256=SAykB7teIygjExhNJabNE8zp2NVNAMwudB2VdtgbPx8,3459
38
+ dyff_schema-0.8.0.dist-info/NOTICE,sha256=YONACu0s_Ui6jNi-wtEsVQbTU1JIkh8wvLH6d1-Ni_w,43
39
+ dyff_schema-0.8.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
40
+ dyff_schema-0.8.0.dist-info/top_level.txt,sha256=9e3VVdeX73t_sUJOPQPCcGtYO1JhoErhHIi3WoWGcFI,5
41
+ dyff_schema-0.8.0.dist-info/RECORD,,