dyff-schema 0.35.5__tar.gz → 0.36.0__tar.gz

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.

Potentially problematic release.


This version of dyff-schema might be problematic. Click here for more details.

Files changed (68) hide show
  1. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/.pre-commit-config.yaml +8 -8
  2. {dyff_schema-0.35.5/dyff_schema.egg-info → dyff_schema-0.36.0}/PKG-INFO +3 -1
  3. dyff_schema-0.36.0/dyff/schema/_version.py +2 -0
  4. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/commands.py +228 -1
  5. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/platform.py +401 -1
  6. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/requests.py +20 -0
  7. {dyff_schema-0.35.5 → dyff_schema-0.36.0/dyff_schema.egg-info}/PKG-INFO +3 -1
  8. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff_schema.egg-info/requires.txt +2 -0
  9. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/pyproject.toml +2 -0
  10. dyff_schema-0.35.5/dyff/schema/_version.py +0 -2
  11. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/.gitignore +0 -0
  12. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/.gitlab-ci.yml +0 -0
  13. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/.idea/dyff-schema.iml +0 -0
  14. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/.licenserc.yaml +0 -0
  15. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/.prettierignore +0 -0
  16. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/.secrets.baseline +0 -0
  17. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/CODE_OF_CONDUCT.md +0 -0
  18. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/LICENSE +0 -0
  19. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/NOTICE +0 -0
  20. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/README.md +0 -0
  21. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/__init__.py +0 -0
  22. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/adapters.py +0 -0
  23. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/annotations.py +0 -0
  24. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/base.py +0 -0
  25. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/commands.py +0 -0
  26. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/copydoc.py +0 -0
  27. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/dataset/__init__.py +0 -0
  28. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/dataset/arrow.py +0 -0
  29. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/dataset/binary.py +0 -0
  30. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/dataset/classification.py +0 -0
  31. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/dataset/embedding.py +0 -0
  32. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/dataset/text.py +0 -0
  33. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/dataset/vision.py +0 -0
  34. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/errors.py +0 -0
  35. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/ids.py +0 -0
  36. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/io/__init__.py +0 -0
  37. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/io/vllm.py +0 -0
  38. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/platform.py +0 -0
  39. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/py.typed +0 -0
  40. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/quantity.py +0 -0
  41. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/requests.py +0 -0
  42. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/responses.py +0 -0
  43. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/test.py +0 -0
  44. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/__init__.py +0 -0
  45. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/__init__.py +0 -0
  46. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/adapters.py +0 -0
  47. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/base.py +0 -0
  48. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/dataset/__init__.py +0 -0
  49. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/dataset/arrow.py +0 -0
  50. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/dataset/binary.py +0 -0
  51. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/dataset/classification.py +0 -0
  52. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/dataset/embedding.py +0 -0
  53. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/dataset/text.py +0 -0
  54. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/dataset/vision.py +0 -0
  55. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/io/__init__.py +0 -0
  56. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/io/vllm.py +0 -0
  57. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/oci.py +0 -0
  58. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/responses.py +0 -0
  59. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/test.py +0 -0
  60. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/v0/r1/version.py +0 -0
  61. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff/schema/version.py +0 -0
  62. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff_schema.egg-info/SOURCES.txt +0 -0
  63. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff_schema.egg-info/dependency_links.txt +0 -0
  64. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/dyff_schema.egg-info/top_level.txt +0 -0
  65. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/makefile +0 -0
  66. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/setup.cfg +0 -0
  67. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/tests/test_adapters.py +0 -0
  68. {dyff_schema-0.35.5 → dyff_schema-0.36.0}/tests/test_import.py +0 -0
@@ -3,7 +3,7 @@
3
3
 
4
4
  repos:
5
5
  - repo: https://github.com/pre-commit/pre-commit-hooks
6
- rev: v5.0.0
6
+ rev: v6.0.0
7
7
  hooks:
8
8
  - id: check-added-large-files
9
9
  - id: check-merge-conflict
@@ -23,12 +23,12 @@ repos:
23
23
  # - "--remove-all-unused-imports"
24
24
 
25
25
  - repo: https://github.com/psf/black
26
- rev: 25.1.0
26
+ rev: 25.9.0
27
27
  hooks:
28
28
  - id: black
29
29
 
30
30
  - repo: https://github.com/PyCQA/isort
31
- rev: "6.0.1"
31
+ rev: "6.1.0"
32
32
  hooks:
33
33
  - id: isort
34
34
 
@@ -40,25 +40,25 @@ repos:
40
40
  args: ["--in-place", "--black", "--config", "./pyproject.toml"]
41
41
 
42
42
  - repo: https://gitlab.com/saferatday0/library/skywalking-eyes
43
- rev: "0.6.0"
43
+ rev: "0.8.3"
44
44
  hooks:
45
45
  - id: license-eye-header-fix
46
46
 
47
47
  - repo: https://gitlab.com/saferatday0/library/prettier
48
- rev: 0.6.0
48
+ rev: 0.7.1
49
49
  hooks:
50
50
  - id: prettier
51
51
  - repo: https://gitlab.com/saferatday0/badgie
52
- rev: "0.14.0"
52
+ rev: "0.14.2"
53
53
  hooks:
54
54
  - id: badgie
55
55
 
56
56
  - repo: https://gitlab.com/saferatday0/cici
57
- rev: "0.9.3"
57
+ rev: "0.14.1"
58
58
  hooks:
59
59
  - id: cici-update
60
60
 
61
61
  - repo: https://gitlab.com/saferatday0/library/detect-secrets
62
- rev: 0.6.0
62
+ rev: 0.7.2
63
63
  hooks:
64
64
  - id: detect-secrets
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dyff-schema
3
- Version: 0.35.5
3
+ Version: 0.36.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
@@ -21,6 +21,8 @@ Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  License-File: NOTICE
23
23
  Requires-Dist: canonicaljson==2.0.0
24
+ Requires-Dist: email-validator
25
+ Requires-Dist: google-i18n-address
24
26
  Requires-Dist: hypothesis
25
27
  Requires-Dist: hypothesis-jsonschema
26
28
  Requires-Dist: jsonpath-ng
@@ -0,0 +1,2 @@
1
+ __version__ = version = "0.36.0"
2
+ __version_tuple__ = version_tuple = (0, 36, 0)
@@ -7,6 +7,7 @@ These are used internally by the platform and users typically won't encounter th
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ from datetime import datetime, timedelta, timezone
10
11
  from typing import Literal, Optional, Union
11
12
 
12
13
  import pydantic
@@ -15,6 +16,9 @@ from typing_extensions import Annotated
15
16
 
16
17
  from .base import DyffSchemaBaseModel, JsonMergePatchSemantics
17
18
  from .platform import (
19
+ ChallengeTask,
20
+ ChallengeTaskExecutionEnvironment,
21
+ ChallengeTaskSchedule,
18
22
  DyffEntityType,
19
23
  EntityIdentifier,
20
24
  FamilyMember,
@@ -24,6 +28,7 @@ from .platform import (
24
28
  SchemaVersion,
25
29
  Status,
26
30
  TagNameType,
31
+ body_maxlen,
27
32
  summary_maxlen,
28
33
  title_maxlen,
29
34
  )
@@ -42,7 +47,10 @@ class Command(SchemaVersion, DyffSchemaBaseModel):
42
47
  """
43
48
 
44
49
  command: Literal[
50
+ "CreateChallengeTask",
45
51
  "CreateEntity",
52
+ "EditChallengeContent",
53
+ "EditChallengeTaskRules",
46
54
  "EditEntityDocumentation",
47
55
  "EditEntityLabels",
48
56
  "EditFamilyMembers",
@@ -68,11 +76,220 @@ class CreateEntity(Command):
68
76
  # ----------------------------------------------------------------------------
69
77
 
70
78
 
79
+ class CreateChallengeTaskAttributes(DyffSchemaBaseModel):
80
+ """Attributes for the CreateChallengeTask command."""
81
+
82
+ task: ChallengeTask = pydantic.Field(description="The task to create.")
83
+
84
+
85
+ class CreateChallengeTaskData(EntityIdentifier):
86
+ """Payload data for the CreateChallengeTask command."""
87
+
88
+ attributes: CreateChallengeTaskAttributes = pydantic.Field(
89
+ description="The command attributes"
90
+ )
91
+
92
+
93
+ class CreateChallengeTask(Command):
94
+ """Create a new challenge task within an existing challenge."""
95
+
96
+ command: Literal["CreateChallengeTask"] = "CreateChallengeTask"
97
+
98
+ data: CreateChallengeTaskData = pydantic.Field(description="The command data.")
99
+
100
+
101
+ # ----------------------------------------------------------------------------
102
+
103
+
104
+ class EditChallengeContentPagePatch(JsonMergePatchSemantics):
105
+ """Same properties as ChallengeContentPage.
106
+
107
+ Fields that are not assigned explicitly remain unchanged.
108
+ """
109
+
110
+ title: Annotated[str, StringConstraints(max_length=title_maxlen())] = ( # type: ignore
111
+ pydantic.Field(
112
+ default="",
113
+ description='A short plain string suitable as a title or "headline".',
114
+ )
115
+ )
116
+
117
+ summary: Annotated[str, StringConstraints(max_length=summary_maxlen())] = ( # type: ignore
118
+ pydantic.Field(
119
+ default="",
120
+ description="A brief summary, suitable for display in"
121
+ " small UI elements.",
122
+ )
123
+ )
124
+
125
+ body: Annotated[str, StringConstraints(max_length=body_maxlen())] = pydantic.Field( # type: ignore
126
+ default="",
127
+ description="Long-form documentation. Interpreted as"
128
+ " Markdown. There are no length constraints, but be reasonable.",
129
+ )
130
+
131
+
132
+ class EditChallengeContentPatch(DyffSchemaBaseModel):
133
+ page: EditChallengeContentPagePatch
134
+
135
+ @field_serializer("page")
136
+ def _serialize_page(self, page: EditChallengeContentPagePatch, _info):
137
+ return page.model_dump(mode=_info.mode)
138
+
139
+
140
+ class EditChallengeContentAttributes(DyffSchemaBaseModel):
141
+ """Attributes for the EditChallengePageContent command."""
142
+
143
+ challenge: Optional[EditChallengeContentPatch] = pydantic.Field(
144
+ default=None,
145
+ description="Edits to make to the content of the main challenge page.",
146
+ )
147
+
148
+ tasks: Optional[dict[str, EditChallengeContentPatch]] = pydantic.Field(
149
+ default=None, description="Edits to make to the content of the challenge tasks."
150
+ )
151
+
152
+
153
+ class EditChallengeContentData(EntityIdentifier):
154
+ """Payload data for the EditChallengePageContent command."""
155
+
156
+ attributes: EditChallengeContentAttributes = pydantic.Field(
157
+ description="The command attributes"
158
+ )
159
+
160
+
161
+ class EditChallengeContent(Command):
162
+ """Edit the page content associated with a challenge-related entity.
163
+
164
+ Setting a documentation field to null/None deletes the corresponding value. To
165
+ preserve the existing value, leave the field *unset*.
166
+ """
167
+
168
+ command: Literal["EditChallengeContent"] = "EditChallengeContent"
169
+
170
+ data: EditChallengeContentData = pydantic.Field(description="The edit data.")
171
+
172
+
173
+ # ----------------------------------------------------------------------------
174
+
175
+
176
+ class EditChallengeTaskRulesExecutionEnvironmentChoices(JsonMergePatchSemantics):
177
+ """Same properties as ChallengeTaskExecutionEnvironmentChocies, but assigning None
178
+ to a field is interpreted as a command to delete that field.
179
+
180
+ Fields that are not assigned explicitly remain unchanged.
181
+ """
182
+
183
+ choices: dict[str, Optional[ChallengeTaskExecutionEnvironment]] = pydantic.Field(
184
+ default_factory=dict, description="Execution environment choices."
185
+ )
186
+ default: Optional[str] = pydantic.Field(
187
+ default=None, description="The default execution environment."
188
+ )
189
+
190
+
191
+ class EditChallengeTaskRulesSchedule(JsonMergePatchSemantics):
192
+ """Same properties as ChallengeTaskSchedule, but assigning None to a field is
193
+ interpreted as a command to delete that field.
194
+
195
+ Fields that are not assigned explicitly remain unchanged.
196
+ """
197
+
198
+ openingTime: Optional[datetime] = pydantic.Field(
199
+ default=None, description="The announced opening time for task submissions."
200
+ )
201
+ closingTime: Optional[datetime] = pydantic.Field(
202
+ default=None, description="The announced closing time for task submissions."
203
+ )
204
+
205
+ submissionCycleDuration: timedelta = pydantic.Field(
206
+ default=timedelta(days=1),
207
+ description="The duration of a submission cycle."
208
+ " Teams are limited to a maximum number of submissions per cycle.",
209
+ )
210
+ submissionCycleEpoch: datetime = pydantic.Field(
211
+ default=datetime.fromtimestamp(0, timezone.utc),
212
+ description="The epoch of a submission cycle."
213
+ " For example, any given cycle lasts from"
214
+ " [epoch + N*duration, epoch + (N+1)*duration)."
215
+ " Teams are limited to a maximum number of submissions per cycle.",
216
+ )
217
+ submissionLimitPerCycle: int = pydantic.Field(
218
+ default=1,
219
+ ge=1,
220
+ description="Teams are limited to this many submissions per cycle.",
221
+ )
222
+
223
+
224
+ class EditChallengeTaskRulesPatch(DyffSchemaBaseModel):
225
+ """Same properties as ChallengeTaskRules, but assigning None to a field is
226
+ interpreted as a command to delete that field.
227
+
228
+ Fields that are not assigned explicitly remain unchanged.
229
+ """
230
+
231
+ executionEnvironment: Optional[
232
+ EditChallengeTaskRulesExecutionEnvironmentChoices
233
+ ] = pydantic.Field(
234
+ default=None, description="Patch for the .rules.executionEnvironments field."
235
+ )
236
+ schedule: Optional[EditChallengeTaskRulesSchedule] = pydantic.Field(
237
+ default=None, description="Patch for the .rules.schedule field."
238
+ )
239
+
240
+ @field_serializer("executionEnvironment")
241
+ def _serialize_executionEnvironment(
242
+ self,
243
+ executionEnvironment: EditChallengeTaskRulesExecutionEnvironmentChoices,
244
+ _info,
245
+ ):
246
+ return executionEnvironment.model_dump(mode=_info.mode)
247
+
248
+ @field_serializer("schedule")
249
+ def _serialize_schedule(
250
+ self,
251
+ schedule: EditChallengeTaskRulesSchedule,
252
+ _info,
253
+ ):
254
+ return schedule.model_dump(mode=_info.mode)
255
+
256
+
257
+ class EditChallengeTaskRulesAttributes(DyffSchemaBaseModel):
258
+ """Attributes for the EditChallengeTaskRules command."""
259
+
260
+ rules: EditChallengeTaskRulesPatch = pydantic.Field(
261
+ description="Edits to make to the task rules."
262
+ )
263
+
264
+
265
+ class EditChallengeTaskRulesData(EntityIdentifier):
266
+ """Payload data for the EditChallengeTaskRules command."""
267
+
268
+ attributes: EditChallengeTaskRulesAttributes = pydantic.Field(
269
+ description="The command attributes"
270
+ )
271
+
272
+
273
+ class EditChallengeTaskRules(Command):
274
+ """Edit the rules of a challenge task.
275
+
276
+ Setting a field to null/None deletes the corresponding value. To preserve the
277
+ existing value, leave the field *unset*.
278
+ """
279
+
280
+ command: Literal["EditChallengeTaskRules"] = "EditChallengeTaskRules"
281
+
282
+ data: EditChallengeTaskRulesData = pydantic.Field(description="The edit data.")
283
+
284
+
285
+ # ----------------------------------------------------------------------------
286
+
287
+
71
288
  class EditEntityDocumentationPatch(JsonMergePatchSemantics):
72
289
  """Same properties as DocumentationBase, but assigning None to a field is
73
290
  interpreted as a command to delete that field.
74
291
 
75
- Fields that are assigned explicitly remain unchanged.
292
+ Fields that are not assigned explicitly remain unchanged.
76
293
  """
77
294
 
78
295
  title: Optional[Annotated[str, StringConstraints(max_length=title_maxlen())]] = ( # type: ignore
@@ -292,7 +509,9 @@ class UpdateEntityStatus(Command):
292
509
 
293
510
 
294
511
  DyffCommandType = Union[
512
+ CreateChallengeTask,
295
513
  CreateEntity,
514
+ EditChallengeContent,
296
515
  EditEntityDocumentation,
297
516
  EditEntityLabels,
298
517
  EditFamilyMembers,
@@ -304,8 +523,16 @@ DyffCommandType = Union[
304
523
 
305
524
  __all__ = [
306
525
  "Command",
526
+ "CreateChallengeTask",
527
+ "CreateChallengeTaskAttributes",
528
+ "CreateChallengeTaskData",
307
529
  "CreateEntity",
308
530
  "DyffCommandType",
531
+ "EditChallengeContent",
532
+ "EditChallengeContentAttributes",
533
+ "EditChallengeContentData",
534
+ "EditChallengeContentPagePatch",
535
+ "EditChallengeContentPatch",
309
536
  "EditEntityDocumentation",
310
537
  "EditEntityDocumentationAttributes",
311
538
  "EditEntityDocumentationData",
@@ -22,11 +22,12 @@ We use the following naming convention:
22
22
  import abc
23
23
  import enum
24
24
  import urllib.parse
25
- from datetime import datetime
25
+ from datetime import datetime, timedelta, timezone
26
26
  from enum import Enum
27
27
  from pathlib import Path
28
28
  from typing import Any, Literal, NamedTuple, Optional, Type, Union
29
29
 
30
+ import i18naddress
30
31
  import pyarrow
31
32
  import pydantic
32
33
  from pydantic import StringConstraints
@@ -182,6 +183,10 @@ def summary_maxlen() -> int:
182
183
  return 280
183
184
 
184
185
 
186
+ def body_maxlen() -> int:
187
+ return 1_000_000
188
+
189
+
185
190
  def entity_id_regex() -> str:
186
191
  """An entity ID is a 32-character HEX string.
187
192
 
@@ -199,6 +204,7 @@ class Entities(str, enum.Enum):
199
204
  Artifact = "OCIArtifact"
200
205
  Audit = "Audit"
201
206
  AuditProcedure = "AuditProcedure"
207
+ Challenge = "Challenge"
202
208
  Concern = "Concern"
203
209
  DataSource = "DataSource"
204
210
  Dataset = "Dataset"
@@ -217,6 +223,8 @@ class Entities(str, enum.Enum):
217
223
  Revision = "Revision"
218
224
  SafetyCase = "SafetyCase"
219
225
  Score = "Score"
226
+ Submission = "Submission"
227
+ Team = "Team"
220
228
  UseCase = "UseCase"
221
229
 
222
230
 
@@ -227,6 +235,7 @@ class Resources(str, enum.Enum):
227
235
  Artifact = "artifacts"
228
236
  Audit = "audits"
229
237
  AuditProcedure = "auditprocedures"
238
+ Challenge = "challenges"
230
239
  Concern = "concerns"
231
240
  Dataset = "datasets"
232
241
  DataSource = "datasources"
@@ -246,6 +255,8 @@ class Resources(str, enum.Enum):
246
255
  Revision = "revisions"
247
256
  SafetyCase = "safetycases"
248
257
  Score = "scores"
258
+ Submission = "submissions"
259
+ Team = "teams"
249
260
  UseCase = "usecases"
250
261
 
251
262
  Task = "tasks"
@@ -276,6 +287,7 @@ EntityKindLiteral = Literal[
276
287
  "Analysis",
277
288
  "Audit",
278
289
  "AuditProcedure",
290
+ "Challenge",
279
291
  "DataSource",
280
292
  "Dataset",
281
293
  "Evaluation",
@@ -293,6 +305,8 @@ EntityKindLiteral = Literal[
293
305
  "Report",
294
306
  "Revision",
295
307
  "SafetyCase",
308
+ "Submission",
309
+ "Team",
296
310
  "UseCase",
297
311
  ]
298
312
 
@@ -503,6 +517,7 @@ class DyffEntity(Status, Labeled, SchemaVersion, DyffModelWithID):
503
517
  "Analysis",
504
518
  "Audit",
505
519
  "AuditProcedure",
520
+ "Challenge",
506
521
  "DataSource",
507
522
  "Dataset",
508
523
  "Evaluation",
@@ -520,6 +535,8 @@ class DyffEntity(Status, Labeled, SchemaVersion, DyffModelWithID):
520
535
  "Report",
521
536
  "Revision",
522
537
  "SafetyCase",
538
+ "Submission",
539
+ "Team",
523
540
  "UseCase",
524
541
  ]
525
542
 
@@ -2294,6 +2311,372 @@ class OCIArtifact(DyffEntity):
2294
2311
  return None
2295
2312
 
2296
2313
 
2314
+ # ----------------------------------------------------------------------------
2315
+ # Challenges
2316
+
2317
+
2318
+ class ChallengeContentPage(DyffSchemaBaseModel):
2319
+ """The content for the Web page corresponding to a challenge or challenge-related
2320
+ object."""
2321
+
2322
+ title: str = pydantic.Field(
2323
+ default="Untitled Challenge",
2324
+ description='A short plain string suitable as a title or "headline".',
2325
+ max_length=title_maxlen(),
2326
+ )
2327
+
2328
+ summary: str = pydantic.Field(
2329
+ default="",
2330
+ description="A brief summary, suitable for display in small UI elements.",
2331
+ max_length=summary_maxlen(),
2332
+ )
2333
+
2334
+ body: str = pydantic.Field(
2335
+ default="",
2336
+ description="Long-form documentation. Interpreted as"
2337
+ " Markdown and rendered as a single page. There are no length"
2338
+ " constraints, but be reasonable.",
2339
+ max_length=body_maxlen(),
2340
+ )
2341
+
2342
+
2343
+ class ChallengeNewsItem(DyffSchemaBaseModel):
2344
+ """A news item to display in the challenge news feed."""
2345
+
2346
+ id: str = pydantic.Field(description="Unique ID of the news item.")
2347
+ title: str = pydantic.Field(
2348
+ description='A short plain string suitable as a title or "headline".',
2349
+ max_length=title_maxlen(),
2350
+ )
2351
+ summary: str = pydantic.Field(
2352
+ description="A brief summary, suitable for display in small UI elements.",
2353
+ max_length=summary_maxlen(),
2354
+ )
2355
+ postingTime: datetime = pydantic.Field(
2356
+ description="The timestamp when the news item was posted."
2357
+ )
2358
+
2359
+
2360
+ class TeamMember(DyffSchemaBaseModel):
2361
+ """A member of a team."""
2362
+
2363
+ name: str = pydantic.Field(
2364
+ description="The member's full name as it will be displayed in the UI."
2365
+ " Include all desired titles and honorifics.",
2366
+ max_length=title_maxlen(),
2367
+ )
2368
+ isCorrespondingMember: bool = pydantic.Field(
2369
+ description="Indicates that this member will receive and respond to"
2370
+ " correspondence pertaining to the team's participation in the challenge."
2371
+ " At least one member must be a corresponding member."
2372
+ )
2373
+ email: Optional[pydantic.EmailStr] = pydantic.Field(
2374
+ default=None, description="The member's email address."
2375
+ )
2376
+ orcid: Optional[str] = pydantic.Field(
2377
+ default=None, description="The member's ORCID."
2378
+ )
2379
+ url: Optional[pydantic.HttpUrl] = pydantic.Field(
2380
+ default=None,
2381
+ description="The URL of the member's personal Web page or similar.",
2382
+ )
2383
+ note: Optional[str] = pydantic.Field(
2384
+ default=None,
2385
+ description="A brief note giving additional information about the member.",
2386
+ max_length=summary_maxlen(),
2387
+ )
2388
+ affiliations: Optional[list[str]] = pydantic.Field(
2389
+ default=None,
2390
+ description="The member's affiliations, specified as keys in the"
2391
+ " .affiliations dict in the corresonding Team object.",
2392
+ )
2393
+
2394
+
2395
+ class GoogleI18nMailingAddress(DyffSchemaBaseModel):
2396
+ """An international mailing address, in the schema used by Google's
2397
+ i18n address metadata repository
2398
+ (https://chromium-i18n.appspot.com/ssl-address) and as interpreted by the
2399
+ Python ``google-i18n-address`` package.
2400
+
2401
+ The field names depart from our camelCase convention to match the
2402
+ existing google-i18n-address format. We also use the empty string rather
2403
+ than
2404
+ """
2405
+
2406
+ street_address: str = pydantic.Field(
2407
+ description="The (possibly multiline) street address.",
2408
+ )
2409
+ country_code: str = pydantic.Field(
2410
+ description="Two-letter ISO 3166-1 country code.",
2411
+ )
2412
+ city: Optional[str] = pydantic.Field(
2413
+ default=None,
2414
+ description="A city or town name.",
2415
+ )
2416
+ country_area: Optional[str] = pydantic.Field(
2417
+ default=None,
2418
+ description="A designation of a region, province, or state.",
2419
+ )
2420
+ postal_code: Optional[str] = pydantic.Field(
2421
+ default=None,
2422
+ description="A postal code or zip code.",
2423
+ )
2424
+ sorting_code: Optional[str] = pydantic.Field(
2425
+ default=None,
2426
+ description="A sorting code.",
2427
+ )
2428
+ name: Optional[str] = pydantic.Field(
2429
+ default=None,
2430
+ description="A person's name.",
2431
+ )
2432
+ company_name: Optional[str] = pydantic.Field(
2433
+ default=None,
2434
+ description="A name of a company or organization.",
2435
+ )
2436
+
2437
+ formatted: str = pydantic.Field(
2438
+ default="",
2439
+ description="The address formatted as a multi-line string (auto-generated).",
2440
+ )
2441
+
2442
+ @pydantic.model_validator(mode="after")
2443
+ def normalize_address(self):
2444
+ """Normalizes the address and populates .formatted."""
2445
+ unnormalized = self.model_dump()
2446
+ normalized = i18naddress.normalize_address(unnormalized)
2447
+ for k, v in normalized.items():
2448
+ if v != "":
2449
+ setattr(self, k, v)
2450
+ self.formatted = i18naddress.format_address(normalized, latin=True)
2451
+ return self
2452
+
2453
+
2454
+ class TeamAffiliation(DyffSchemaBaseModel):
2455
+ """An organization with which one or more team members are affiliated, such as a
2456
+ university or company."""
2457
+
2458
+ name: str = pydantic.Field(
2459
+ description="The name of the organization as it will be displayed in the UI.",
2460
+ max_length=title_maxlen(),
2461
+ )
2462
+ department: Optional[str] = pydantic.Field(
2463
+ default=None,
2464
+ description="A department within the organization.",
2465
+ max_length=title_maxlen(),
2466
+ )
2467
+ group: Optional[str] = pydantic.Field(
2468
+ default=None,
2469
+ description="A group within the organization or department.",
2470
+ max_length=title_maxlen(),
2471
+ )
2472
+
2473
+ address: Optional[GoogleI18nMailingAddress] = pydantic.Field(
2474
+ default=None,
2475
+ description="The mailing address of the organization.",
2476
+ )
2477
+
2478
+ url: Optional[pydantic.HttpUrl] = pydantic.Field(
2479
+ default=None, description="The organization's Web page or similar."
2480
+ )
2481
+
2482
+
2483
+ class Team(DyffEntity):
2484
+ """The members and affiliations of a team that has entered a challenge."""
2485
+
2486
+ kind: Literal["Team"] = Entities.Team.value
2487
+
2488
+ challenge: str = pydantic.Field(
2489
+ description="ID of the Challenge that this Team is participating in"
2490
+ )
2491
+ members: dict[str, TeamMember] = pydantic.Field(
2492
+ description="The members of this team"
2493
+ )
2494
+ affiliations: dict[str, TeamAffiliation] = pydantic.Field(
2495
+ description="The affiliations of the team. Team members state their"
2496
+ " affiliations by referencing these entries by their keys."
2497
+ )
2498
+
2499
+ def dependencies(self) -> list[str]:
2500
+ return []
2501
+
2502
+ def resource_allocation(self) -> Optional[ResourceAllocation]:
2503
+ return None
2504
+
2505
+
2506
+ class ChallengeTaskExecutionEnvironment(DyffSchemaBaseModel):
2507
+ """Description of an execution environment that is available to run challenge
2508
+ entries.
2509
+
2510
+ The specified computational resources are maximums; entries are free to request and
2511
+ use fewer resources.
2512
+ """
2513
+
2514
+ cpu: Quantity
2515
+ memory: Quantity
2516
+ accelerators: dict[str, int]
2517
+
2518
+
2519
+ class ChallengeTaskExecutionEnvironmentChoices(DyffSchemaBaseModel):
2520
+ """The execution environment(s) available to run challenge entries.
2521
+
2522
+ For an InferenceService to be a valid submission for a Challenge, there must be at
2523
+ least one execution environment defined for the challenge such that all of the
2524
+ resource requests of the service are less than or equal to the corresponding limits
2525
+ defined in the execution environment.
2526
+ """
2527
+
2528
+ choices: dict[str, ChallengeTaskExecutionEnvironment]
2529
+ default: str
2530
+
2531
+
2532
+ class ChallengeTaskSchedule(DyffSchemaBaseModel):
2533
+ """The schedule of a challenge task."""
2534
+
2535
+ openingTime: Optional[datetime] = pydantic.Field(
2536
+ default=None, description="The announced opening time for task submissions."
2537
+ )
2538
+ closingTime: Optional[datetime] = pydantic.Field(
2539
+ default=None, description="The announced closing time for task submissions."
2540
+ )
2541
+ submissionCycleDuration: timedelta = pydantic.Field(
2542
+ default=timedelta(days=1),
2543
+ description="The duration of a submission cycle."
2544
+ " Teams are limited to a maximum number of submissions per cycle.",
2545
+ )
2546
+ submissionCycleEpoch: datetime = pydantic.Field(
2547
+ default=datetime.fromtimestamp(0, timezone.utc),
2548
+ description="The epoch of a submission cycle."
2549
+ " For example, any given cycle lasts from"
2550
+ " [epoch + N*duration, epoch + (N+1)*duration)."
2551
+ " Teams are limited to a maximum number of submissions per cycle.",
2552
+ )
2553
+ submissionLimitPerCycle: int = pydantic.Field(
2554
+ default=1,
2555
+ ge=1,
2556
+ description="Teams are limited to this many submissions per cycle.",
2557
+ )
2558
+
2559
+
2560
+ class ChallengeTaskRules(DyffSchemaBaseModel):
2561
+ """The rules of the challenge."""
2562
+
2563
+ executionEnvironment: ChallengeTaskExecutionEnvironmentChoices = pydantic.Field(
2564
+ description="The available choices for the execution environment."
2565
+ )
2566
+ schedule: ChallengeTaskSchedule = pydantic.Field(
2567
+ description="The challenge schedule."
2568
+ )
2569
+
2570
+
2571
+ class ChallengeTaskContent(DyffSchemaBaseModel):
2572
+ """The content of a ChallengeTask UI view."."""
2573
+
2574
+ page: ChallengeContentPage = pydantic.Field(
2575
+ description="The content of the challenge task Web page."
2576
+ )
2577
+
2578
+
2579
+ class ChallengeTaskBase(DyffSchemaBaseModel):
2580
+ """The part of the ChallengeTask spec that is not system-populated."""
2581
+
2582
+ challenge: str = pydantic.Field(
2583
+ description="The ID of the Challenge of which this task is a member."
2584
+ )
2585
+ name: str = pydantic.Field(
2586
+ description="Unique name for the task in the context of the challenge."
2587
+ " This may appear in URLs and must follow naming restrictions.",
2588
+ max_length=title_maxlen(),
2589
+ pattern=r"[a-z0-9-]*",
2590
+ )
2591
+ assessment: str = pydantic.Field(
2592
+ description="ID of the Assessment that the task runs."
2593
+ )
2594
+ rules: ChallengeTaskRules = pydantic.Field(description="The rules for submissions.")
2595
+ content: ChallengeTaskContent = pydantic.Field(
2596
+ description="Content of the task view in the Dyff App."
2597
+ )
2598
+
2599
+
2600
+ class ChallengeTask(Status, ChallengeTaskBase, DyffModelWithID):
2601
+ """A task that is part of a challenge.
2602
+
2603
+ Teams make submissions to individual tasks, rather than the overall challenge. A
2604
+ task combines an assessment pipeline that actually implements the computations along
2605
+ with rules for submissions and descriptive content for the Web app.
2606
+ """
2607
+
2608
+ creationTime: datetime = pydantic.Field(
2609
+ description="Resource creation time (assigned by system)"
2610
+ )
2611
+
2612
+ lastTransitionTime: Optional[datetime] = pydantic.Field(
2613
+ default=None, description="Time of last (status, reason) change."
2614
+ )
2615
+
2616
+
2617
+ class ChallengeContent(DyffSchemaBaseModel):
2618
+ """The content of a Challenge UI view."."""
2619
+
2620
+ page: ChallengeContentPage = pydantic.Field(
2621
+ default_factory=ChallengeContentPage,
2622
+ description="The content of the challenge Web page.",
2623
+ )
2624
+ news: dict[str, ChallengeNewsItem] = pydantic.Field(
2625
+ default_factory=dict,
2626
+ description="News items to display in the challenge news feed.",
2627
+ )
2628
+
2629
+
2630
+ class Challenge(DyffEntity):
2631
+ """A Challenge is a collection of assessments on which participating teams compete
2632
+ to achieve the best performance."""
2633
+
2634
+ kind: Literal["Challenge"] = Entities.Challenge.value
2635
+
2636
+ content: ChallengeContent = pydantic.Field(
2637
+ description="Content of the challenge view in the Dyff App."
2638
+ )
2639
+
2640
+ tasks: dict[str, ChallengeTask] = pydantic.Field(
2641
+ default_factory=dict, description="The assessments that comprise the challenge."
2642
+ )
2643
+
2644
+ def dependencies(self) -> list[str]:
2645
+ return []
2646
+
2647
+ def resource_allocation(self) -> Optional[ResourceAllocation]:
2648
+ return None
2649
+
2650
+
2651
+ class ChallengeSubmission(DyffEntity):
2652
+ """A submission of an inference system to a challenge by a team.
2653
+
2654
+ All of the constituent resources must already exist. Creating a Submission simply
2655
+ indicates that the participating team wishes to submit the inference service as an
2656
+ official entry. Most challenges will limit the number of submissions that can be
2657
+ made in a given time interval.
2658
+ """
2659
+
2660
+ kind: Literal["Submission"] = Entities.Submission.value
2661
+
2662
+ challenge: str = pydantic.Field(
2663
+ description="The ID of the challenge being submitted to."
2664
+ )
2665
+ task: str = pydantic.Field(
2666
+ description="The key of the task within the challenge being submitted to."
2667
+ )
2668
+ team: str = pydantic.Field(description="The ID of the team making the submission.")
2669
+ inferenceService: str = pydantic.Field(
2670
+ description="The ID of the inference service being submitted."
2671
+ )
2672
+
2673
+ def dependencies(self) -> list[str]:
2674
+ return []
2675
+
2676
+ def resource_allocation(self) -> Optional[ResourceAllocation]:
2677
+ return None
2678
+
2679
+
2297
2680
  # ---------------------------------------------------------------------------
2298
2681
  # Status enumerations
2299
2682
 
@@ -2539,9 +2922,12 @@ _ENTITY_CLASS = {
2539
2922
  Entities.Artifact: OCIArtifact,
2540
2923
  Entities.Audit: Audit,
2541
2924
  Entities.AuditProcedure: AuditProcedure,
2925
+ Entities.Challenge: Challenge,
2542
2926
  Entities.Dataset: Dataset,
2543
2927
  Entities.DataSource: DataSource,
2544
2928
  Entities.Evaluation: Evaluation,
2929
+ Entities.Family: Family,
2930
+ Entities.Hazard: Hazard,
2545
2931
  Entities.InferenceService: InferenceService,
2546
2932
  Entities.InferenceSession: InferenceSession,
2547
2933
  Entities.Measurement: Measurement,
@@ -2550,6 +2936,8 @@ _ENTITY_CLASS = {
2550
2936
  Entities.Module: Module,
2551
2937
  Entities.Report: Report,
2552
2938
  Entities.SafetyCase: SafetyCase,
2939
+ Entities.Team: Team,
2940
+ Entities.UseCase: UseCase,
2553
2941
  }
2554
2942
 
2555
2943
 
@@ -2560,6 +2948,7 @@ def entity_class(kind: Entities):
2560
2948
  _DyffEntityTypeRevisable = Union[
2561
2949
  Audit,
2562
2950
  AuditProcedure,
2951
+ Challenge,
2563
2952
  DataSource,
2564
2953
  Dataset,
2565
2954
  Evaluation,
@@ -2574,6 +2963,7 @@ _DyffEntityTypeRevisable = Union[
2574
2963
  OCIArtifact,
2575
2964
  Report,
2576
2965
  SafetyCase,
2966
+ Team,
2577
2967
  UseCase,
2578
2968
  ]
2579
2969
 
@@ -2651,6 +3041,12 @@ __all__ = [
2651
3041
  "Audit",
2652
3042
  "AuditProcedure",
2653
3043
  "AuditRequirement",
3044
+ "Challenge",
3045
+ "ChallengeContent",
3046
+ "ChallengeContentPage",
3047
+ "ChallengeSubmission",
3048
+ "ChallengeTask",
3049
+ "ChallengeTaskBase",
2654
3050
  "Concern",
2655
3051
  "ConcernBase",
2656
3052
  "Container",
@@ -2771,6 +3167,9 @@ __all__ = [
2771
3167
  "TagName",
2772
3168
  "TagNameType",
2773
3169
  "TaskSchema",
3170
+ "Team",
3171
+ "TeamMember",
3172
+ "TeamAffiliation",
2774
3173
  "UseCase",
2775
3174
  "Volume",
2776
3175
  "VolumeMount",
@@ -2793,6 +3192,7 @@ __all__ = [
2793
3192
  "ModelStatusReason",
2794
3193
  "ReportStatus",
2795
3194
  "ReportStatusReason",
3195
+ "body_maxlen",
2796
3196
  "identifier_regex",
2797
3197
  "identifier_maxlen",
2798
3198
  "is_status_terminal",
@@ -26,6 +26,8 @@ from .base import DyffBaseModel, JsonMergePatchSemantics
26
26
  from .platform import (
27
27
  AnalysisBase,
28
28
  AnalysisScope,
29
+ ChallengeContent,
30
+ ChallengeTaskBase,
29
31
  ConcernBase,
30
32
  DatasetBase,
31
33
  DataView,
@@ -138,6 +140,17 @@ class ArtifactCreateRequest(DyffEntityCreateRequest):
138
140
  pass
139
141
 
140
142
 
143
+ class ChallengeCreateRequest(DyffEntityCreateRequest):
144
+ content: ChallengeContent = pydantic.Field(
145
+ default_factory=ChallengeContent,
146
+ description="Content of the challenge view in the Dyff App.",
147
+ )
148
+
149
+
150
+ class ChallengeTaskCreateRequest(DyffEntityCreateRequest, ChallengeTaskBase):
151
+ pass
152
+
153
+
141
154
  class ConcernCreateRequest(DyffEntityCreateRequest, ConcernBase):
142
155
  @pydantic.field_validator("documentation", check_fields=False)
143
156
  def _validate_documentation(
@@ -288,6 +301,10 @@ class ReportCreateRequest(DyffEntityCreateRequest, ReportBase):
288
301
  # ----------------------------------------------------------------------------
289
302
 
290
303
 
304
+ class ChallengeContentEditRequest(DyffRequestBase, commands.EditChallengeContentPatch):
305
+ pass
306
+
307
+
291
308
  class DocumentationEditRequest(
292
309
  DyffRequestBase, commands.EditEntityDocumentationAttributes
293
310
  ):
@@ -473,6 +490,9 @@ __all__ = [
473
490
  "ArtifactCreateRequest",
474
491
  "ArtifactQueryRequest",
475
492
  "AuditQueryRequest",
493
+ "ChallengeContentEditRequest",
494
+ "ChallengeCreateRequest",
495
+ "ChallengeTaskCreateRequest",
476
496
  "ConcernCreateRequest",
477
497
  "DyffEntityCreateRequest",
478
498
  "DyffEntityQueryRequest",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dyff-schema
3
- Version: 0.35.5
3
+ Version: 0.36.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
@@ -21,6 +21,8 @@ Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  License-File: NOTICE
23
23
  Requires-Dist: canonicaljson==2.0.0
24
+ Requires-Dist: email-validator
25
+ Requires-Dist: google-i18n-address
24
26
  Requires-Dist: hypothesis
25
27
  Requires-Dist: hypothesis-jsonschema
26
28
  Requires-Dist: jsonpath-ng
@@ -1,4 +1,6 @@
1
1
  canonicaljson==2.0.0
2
+ email-validator
3
+ google-i18n-address
2
4
  hypothesis
3
5
  hypothesis-jsonschema
4
6
  jsonpath-ng
@@ -31,6 +31,8 @@ classifiers=[
31
31
 
32
32
  dependencies = [
33
33
  "canonicaljson==2.0.0",
34
+ "email-validator",
35
+ "google-i18n-address",
34
36
  "hypothesis",
35
37
  "hypothesis-jsonschema",
36
38
  "jsonpath-ng",
@@ -1,2 +0,0 @@
1
- __version__ = version = "0.35.5"
2
- __version_tuple__ = version_tuple = (0, 35, 5)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes