dyff-schema 0.38.12__tar.gz → 0.39.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.
- {dyff_schema-0.38.12/dyff_schema.egg-info → dyff_schema-0.39.0}/PKG-INFO +1 -1
- dyff_schema-0.39.0/dyff/schema/_version.py +2 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/platform.py +146 -23
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/requests.py +3 -5
- {dyff_schema-0.38.12 → dyff_schema-0.39.0/dyff_schema.egg-info}/PKG-INFO +1 -1
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff_schema.egg-info/SOURCES.txt +2 -1
- dyff_schema-0.39.0/tests/test_score_curves.py +139 -0
- dyff_schema-0.38.12/dyff/schema/_version.py +0 -2
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/.gitignore +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/.gitlab-ci.yml +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/.idea/dyff-schema.iml +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/.licenserc.yaml +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/.pre-commit-config.yaml +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/.prettierignore +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/.secrets.baseline +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/CODE_OF_CONDUCT.md +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/LICENSE +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/NOTICE +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/README.md +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/__init__.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/adapters.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/annotations.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/base.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/commands.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/copydoc.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/dataset/__init__.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/dataset/arrow.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/dataset/binary.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/dataset/classification.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/dataset/embedding.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/dataset/text.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/dataset/vision.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/errors.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/ids.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/io/__init__.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/io/vllm.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/platform.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/py.typed +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/quantity.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/requests.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/responses.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/test.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/__init__.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/__init__.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/adapters.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/base.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/commands.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/dataset/__init__.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/dataset/arrow.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/dataset/binary.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/dataset/classification.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/dataset/embedding.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/dataset/text.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/dataset/vision.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/io/__init__.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/io/vllm.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/oci.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/responses.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/test.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/v0/r1/version.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff/schema/version.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff_schema.egg-info/dependency_links.txt +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff_schema.egg-info/requires.txt +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/dyff_schema.egg-info/top_level.txt +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/makefile +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/pyproject.toml +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/setup.cfg +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/tests/test_adapters.py +0 -0
- {dyff_schema-0.38.12 → dyff_schema-0.39.0}/tests/test_import.py +0 -0
|
@@ -216,6 +216,7 @@ class Entities(str, enum.Enum):
|
|
|
216
216
|
AuditProcedure = "AuditProcedure"
|
|
217
217
|
Challenge = "Challenge"
|
|
218
218
|
Concern = "Concern"
|
|
219
|
+
Curve = "Curve"
|
|
219
220
|
DataSource = "DataSource"
|
|
220
221
|
Dataset = "Dataset"
|
|
221
222
|
Documentation = "Documentation"
|
|
@@ -256,6 +257,7 @@ class Resources(str, enum.Enum):
|
|
|
256
257
|
AuditProcedure = "auditprocedures"
|
|
257
258
|
Challenge = "challenges"
|
|
258
259
|
Concern = "concerns"
|
|
260
|
+
Curve = "curves"
|
|
259
261
|
Dataset = "datasets"
|
|
260
262
|
DataSource = "datasources"
|
|
261
263
|
Descriptor = "descriptors"
|
|
@@ -309,6 +311,7 @@ EntityKindLiteral = Literal[
|
|
|
309
311
|
"Audit",
|
|
310
312
|
"AuditProcedure",
|
|
311
313
|
"Challenge",
|
|
314
|
+
"Curve",
|
|
312
315
|
"DataSource",
|
|
313
316
|
"Dataset",
|
|
314
317
|
"Evaluation",
|
|
@@ -435,7 +438,7 @@ class Labeled(DyffSchemaBaseModel):
|
|
|
435
438
|
class Annotation(DyffSchemaBaseModel):
|
|
436
439
|
key: str = pydantic.Field(
|
|
437
440
|
pattern=_k8s_label_key_regex(),
|
|
438
|
-
max_length=
|
|
441
|
+
max_length=_k8s_label_key_maxlen(),
|
|
439
442
|
description="The annotation key. A DNS label with an optional DNS domain prefix."
|
|
440
443
|
" For example: 'my-key', 'your.com/key_0'. Names prefixed with"
|
|
441
444
|
" 'dyff.io/', 'subdomain.dyff.io/', etc. are reserved.\n\n"
|
|
@@ -541,6 +544,7 @@ class DyffEntity(Status, Labeled, SchemaVersion, DyffModelWithID):
|
|
|
541
544
|
"Audit",
|
|
542
545
|
"AuditProcedure",
|
|
543
546
|
"Challenge",
|
|
547
|
+
"Curve",
|
|
544
548
|
"DataSource",
|
|
545
549
|
"Dataset",
|
|
546
550
|
"Evaluation",
|
|
@@ -1442,6 +1446,21 @@ class VolumeMount(DyffSchemaBaseModel):
|
|
|
1442
1446
|
default=None, description="Configuration for Scratch volume mounts."
|
|
1443
1447
|
)
|
|
1444
1448
|
|
|
1449
|
+
@pydantic.model_validator(mode="after")
|
|
1450
|
+
def _validate_kind_matches_payload(self):
|
|
1451
|
+
"""Ensure payload fields match declared kind."""
|
|
1452
|
+
if self.kind == VolumeMountKind.data:
|
|
1453
|
+
if self.data is None or self.scratch is not None:
|
|
1454
|
+
raise ValueError(
|
|
1455
|
+
"VolumeMount(kind='Data') requires .data and forbids .scratch"
|
|
1456
|
+
)
|
|
1457
|
+
elif self.kind == VolumeMountKind.scratch:
|
|
1458
|
+
if self.scratch is None or self.data is not None:
|
|
1459
|
+
raise ValueError(
|
|
1460
|
+
"VolumeMount(kind='Scratch') requires .scratch and forbids .data"
|
|
1461
|
+
)
|
|
1462
|
+
return self
|
|
1463
|
+
|
|
1445
1464
|
|
|
1446
1465
|
class Container(DyffSchemaBaseModel):
|
|
1447
1466
|
"""Configuration of a runnable container backed by either an image hosted in an
|
|
@@ -1489,6 +1508,15 @@ class Container(DyffSchemaBaseModel):
|
|
|
1489
1508
|
" which might be configured in the container image.",
|
|
1490
1509
|
)
|
|
1491
1510
|
|
|
1511
|
+
@pydantic.model_validator(mode="after")
|
|
1512
|
+
def _validate_image_or_ref(self):
|
|
1513
|
+
"""Exactly one of image or imageRef must be set."""
|
|
1514
|
+
has_image = self.image is not None
|
|
1515
|
+
has_ref = self.imageRef is not None
|
|
1516
|
+
if has_image == has_ref:
|
|
1517
|
+
raise ValueError("Exactly one of 'image' or 'imageRef' must be set.")
|
|
1518
|
+
return self
|
|
1519
|
+
|
|
1492
1520
|
|
|
1493
1521
|
class InferenceServiceRunner(Container):
|
|
1494
1522
|
"""Configuration of the runtime environment to use to run an inference service.
|
|
@@ -2101,6 +2129,34 @@ class ScoreSpec(DyffSchemaBaseModel):
|
|
|
2101
2129
|
return format.format(quantity=quantity, unit=unit)
|
|
2102
2130
|
|
|
2103
2131
|
|
|
2132
|
+
class CurveSpec(DyffSchemaBaseModel):
|
|
2133
|
+
"""Metadata describing the curve dimensions and how to read them.
|
|
2134
|
+
|
|
2135
|
+
- `dimensions` supplies per-dimension metadata (name -> ScoreSpec).
|
|
2136
|
+
- Use these keys as columns in `CurveData.points`.
|
|
2137
|
+
"""
|
|
2138
|
+
|
|
2139
|
+
name: str = pydantic.Field(
|
|
2140
|
+
description="Unique key for this curve within the Method context.",
|
|
2141
|
+
pattern=identifier_regex(),
|
|
2142
|
+
max_length=identifier_maxlen(),
|
|
2143
|
+
)
|
|
2144
|
+
title: str = pydantic.Field(
|
|
2145
|
+
description="Human-friendly title for the curve.",
|
|
2146
|
+
max_length=title_maxlen(),
|
|
2147
|
+
)
|
|
2148
|
+
summary: str = pydantic.Field(
|
|
2149
|
+
description="Short description of what the curve shows.",
|
|
2150
|
+
max_length=summary_maxlen(),
|
|
2151
|
+
)
|
|
2152
|
+
dimensions: dict[str, ScoreSpec] = pydantic.Field(
|
|
2153
|
+
description=(
|
|
2154
|
+
"Per-dimension metadata. Keys must match the columns stored in CurveData.points. "
|
|
2155
|
+
"Typical keys might include 'x', 'y', 'threshold', 'tp', 'fp', etc."
|
|
2156
|
+
)
|
|
2157
|
+
)
|
|
2158
|
+
|
|
2159
|
+
|
|
2104
2160
|
class MethodBase(DyffSchemaBaseModel):
|
|
2105
2161
|
name: str = pydantic.Field(description="Descriptive name of the Method.")
|
|
2106
2162
|
|
|
@@ -2131,7 +2187,7 @@ class MethodBase(DyffSchemaBaseModel):
|
|
|
2131
2187
|
description="Specification of the Method output."
|
|
2132
2188
|
)
|
|
2133
2189
|
|
|
2134
|
-
scores: list[ScoreSpec] = pydantic.Field(
|
|
2190
|
+
scores: list[Union[ScoreSpec, CurveSpec]] = pydantic.Field(
|
|
2135
2191
|
default_factory=list,
|
|
2136
2192
|
description="Specifications of the Scores that this Method produces.",
|
|
2137
2193
|
)
|
|
@@ -2149,12 +2205,13 @@ class MethodBase(DyffSchemaBaseModel):
|
|
|
2149
2205
|
)
|
|
2150
2206
|
|
|
2151
2207
|
@pydantic.field_validator("scores")
|
|
2152
|
-
def _scores_validator(cls, scores
|
|
2153
|
-
if
|
|
2154
|
-
|
|
2208
|
+
def _scores_validator(cls, scores):
|
|
2209
|
+
score_specs = [s for s in scores if isinstance(s, ScoreSpec)]
|
|
2210
|
+
if score_specs:
|
|
2211
|
+
primary_count = sum(s.priority == "primary" for s in score_specs)
|
|
2155
2212
|
if primary_count != 1:
|
|
2156
2213
|
raise ValueError(
|
|
2157
|
-
"scores: Must have exactly one
|
|
2214
|
+
"scores: Must have exactly one primary ScoreSpec (curves excluded)"
|
|
2158
2215
|
)
|
|
2159
2216
|
return scores
|
|
2160
2217
|
|
|
@@ -2329,17 +2386,19 @@ class ScoreData(ScoreSpec):
|
|
|
2329
2386
|
description="The Analysis that generated the current score instance."
|
|
2330
2387
|
)
|
|
2331
2388
|
|
|
2332
|
-
quantity: float = pydantic.Field(
|
|
2333
|
-
description="
|
|
2389
|
+
quantity: float | None = pydantic.Field(
|
|
2390
|
+
default=None, description="Numeric quantity for scalar scores."
|
|
2391
|
+
)
|
|
2392
|
+
points: dict[str, list[float]] | None = pydantic.Field(
|
|
2393
|
+
default=None,
|
|
2394
|
+
description="Aligned vectors for curve scores. Keys should match curve.dimensions.",
|
|
2334
2395
|
)
|
|
2335
2396
|
|
|
2336
2397
|
quantityString: str = pydantic.Field(
|
|
2337
|
-
description="
|
|
2338
|
-
" after processing with the .format specification."
|
|
2398
|
+
description="Formatted string representation of .quantity (scalar) or a summary string (curve)."
|
|
2339
2399
|
)
|
|
2340
|
-
|
|
2341
2400
|
text: str = pydantic.Field(
|
|
2342
|
-
description="
|
|
2401
|
+
description="Short text description of what the value/curve means.",
|
|
2343
2402
|
max_length=summary_maxlen(),
|
|
2344
2403
|
)
|
|
2345
2404
|
|
|
@@ -2355,6 +2414,53 @@ class Score(ScoreData):
|
|
|
2355
2414
|
id: str = pydantic.Field(description="Unique identifier of the entity")
|
|
2356
2415
|
|
|
2357
2416
|
|
|
2417
|
+
class CurveData(CurveSpec):
|
|
2418
|
+
"""An instance of a curve produced by an analysis.
|
|
2419
|
+
|
|
2420
|
+
- Inherits the curve spec (name/title/summary/dimensions) directly.
|
|
2421
|
+
- `points` is a dict-of-lists: each key is a dimension name defined in `dimensions`.
|
|
2422
|
+
- All lists must be the same (non-zero) length.
|
|
2423
|
+
|
|
2424
|
+
spec: CurveSpec = pydantic.Field(description="The CurveSpec this data conforms to.")
|
|
2425
|
+
"""
|
|
2426
|
+
|
|
2427
|
+
metadata: ScoreMetadata = pydantic.Field(
|
|
2428
|
+
description="Indexing metadata (method required).",
|
|
2429
|
+
)
|
|
2430
|
+
analysis: str = pydantic.Field(
|
|
2431
|
+
description="ID of the Analysis that produced this curve."
|
|
2432
|
+
)
|
|
2433
|
+
points: dict[str, list[float]] = pydantic.Field(
|
|
2434
|
+
description="Aligned vectors for each dimension; all lists must have equal length >= 1."
|
|
2435
|
+
)
|
|
2436
|
+
|
|
2437
|
+
@pydantic.model_validator(mode="after")
|
|
2438
|
+
def _validate_points(self):
|
|
2439
|
+
if not self.points:
|
|
2440
|
+
raise ValueError("CurveData.points must not be empty")
|
|
2441
|
+
|
|
2442
|
+
lengths = {k: len(v) for k, v in self.points.items()}
|
|
2443
|
+
uniq = set(lengths.values())
|
|
2444
|
+
if len(uniq) != 1:
|
|
2445
|
+
raise ValueError(
|
|
2446
|
+
f"All vectors must have equal length; got lengths {lengths}"
|
|
2447
|
+
)
|
|
2448
|
+
n = next(iter(uniq))
|
|
2449
|
+
if n < 1:
|
|
2450
|
+
raise ValueError("Vectors must contain at least 1 point")
|
|
2451
|
+
|
|
2452
|
+
missing = [k for k in self.dimensions.keys() if k not in self.points]
|
|
2453
|
+
if missing:
|
|
2454
|
+
raise ValueError(f"points is missing required dimensions: {missing}")
|
|
2455
|
+
return self
|
|
2456
|
+
|
|
2457
|
+
|
|
2458
|
+
class Curve(CurveData):
|
|
2459
|
+
kind: Literal["Curve"] = Entities.Curve.value
|
|
2460
|
+
|
|
2461
|
+
id: str = pydantic.Field(description="Unique identifier of the entity")
|
|
2462
|
+
|
|
2463
|
+
|
|
2358
2464
|
# ---------------------------------------------------------------------------
|
|
2359
2465
|
# OCI artifacts
|
|
2360
2466
|
|
|
@@ -2635,6 +2741,19 @@ class ChallengeTaskSchedule(DyffSchemaBaseModel):
|
|
|
2635
2741
|
description="Teams are limited to this many submissions per cycle.",
|
|
2636
2742
|
)
|
|
2637
2743
|
|
|
2744
|
+
@pydantic.model_validator(mode="after")
|
|
2745
|
+
def _validate_times(self):
|
|
2746
|
+
"""Basic temporal sanity checks."""
|
|
2747
|
+
if (
|
|
2748
|
+
self.openingTime
|
|
2749
|
+
and self.closingTime
|
|
2750
|
+
and self.closingTime <= self.openingTime
|
|
2751
|
+
):
|
|
2752
|
+
raise ValueError("closingTime must be after openingTime")
|
|
2753
|
+
if self.submissionCycleDuration <= timedelta(0):
|
|
2754
|
+
raise ValueError("submissionCycleDuration must be positive")
|
|
2755
|
+
return self
|
|
2756
|
+
|
|
2638
2757
|
|
|
2639
2758
|
class ChallengeTaskRules(DyffSchemaBaseModel):
|
|
2640
2759
|
"""The rules of the challenge."""
|
|
@@ -2656,7 +2775,7 @@ class ChallengeTaskContent(DyffSchemaBaseModel):
|
|
|
2656
2775
|
)
|
|
2657
2776
|
|
|
2658
2777
|
|
|
2659
|
-
class
|
|
2778
|
+
class SubmissionStructure(DyffSchemaBaseModel):
|
|
2660
2779
|
submissionKind: EntityKindLiteral = pydantic.Field(
|
|
2661
2780
|
default=Entities.InferenceService.value,
|
|
2662
2781
|
description="The kind of entity that you can submit to this task.",
|
|
@@ -2679,12 +2798,13 @@ class ChallengeTaskBase(DyffSchemaBaseModel):
|
|
|
2679
2798
|
" This may appear in URLs and must follow naming restrictions.",
|
|
2680
2799
|
max_length=title_maxlen(),
|
|
2681
2800
|
pattern=r"[a-z0-9-]*",
|
|
2801
|
+
min_length=1,
|
|
2682
2802
|
)
|
|
2683
2803
|
assessment: str = pydantic.Field(
|
|
2684
2804
|
description="ID of the Pipeline used to assess the submission."
|
|
2685
2805
|
)
|
|
2686
|
-
submissionStructure:
|
|
2687
|
-
default_factory=
|
|
2806
|
+
submissionStructure: SubmissionStructure = pydantic.Field(
|
|
2807
|
+
default_factory=SubmissionStructure,
|
|
2688
2808
|
description="How to run the assessment pipeline on a new submission.",
|
|
2689
2809
|
)
|
|
2690
2810
|
rules: ChallengeTaskRules = pydantic.Field(description="The rules for submissions.")
|
|
@@ -2745,14 +2865,14 @@ class Challenge(DyffEntity):
|
|
|
2745
2865
|
return None
|
|
2746
2866
|
|
|
2747
2867
|
|
|
2748
|
-
class
|
|
2868
|
+
class SubmissionBase(DyffSchemaBaseModel):
|
|
2749
2869
|
team: str = pydantic.Field(description="The ID of the team making the submission.")
|
|
2750
2870
|
submission: EntityIdentifier = pydantic.Field(
|
|
2751
2871
|
description="The resource being submitted for assessment."
|
|
2752
2872
|
)
|
|
2753
2873
|
|
|
2754
2874
|
|
|
2755
|
-
class
|
|
2875
|
+
class Submission(DyffEntity, SubmissionBase):
|
|
2756
2876
|
"""A submission of an inference system to a challenge by a team.
|
|
2757
2877
|
|
|
2758
2878
|
All of the constituent resources must already exist. Creating a Submission simply
|
|
@@ -3207,7 +3327,6 @@ _DyffEntityTypeRevisable = Union[
|
|
|
3207
3327
|
Audit,
|
|
3208
3328
|
AuditProcedure,
|
|
3209
3329
|
Challenge,
|
|
3210
|
-
ChallengeSubmission,
|
|
3211
3330
|
DataSource,
|
|
3212
3331
|
Dataset,
|
|
3213
3332
|
Evaluation,
|
|
@@ -3224,6 +3343,7 @@ _DyffEntityTypeRevisable = Union[
|
|
|
3224
3343
|
PipelineRun,
|
|
3225
3344
|
Report,
|
|
3226
3345
|
SafetyCase,
|
|
3346
|
+
Submission,
|
|
3227
3347
|
Team,
|
|
3228
3348
|
UseCase,
|
|
3229
3349
|
]
|
|
@@ -3303,7 +3423,7 @@ _ENTITY_CLASS: dict[Entities, type[DyffEntityType]] = {
|
|
|
3303
3423
|
Entities.Report: Report,
|
|
3304
3424
|
Entities.Revision: Revision,
|
|
3305
3425
|
Entities.SafetyCase: SafetyCase,
|
|
3306
|
-
Entities.Submission:
|
|
3426
|
+
Entities.Submission: Submission,
|
|
3307
3427
|
Entities.Team: Team,
|
|
3308
3428
|
Entities.UseCase: UseCase,
|
|
3309
3429
|
}
|
|
@@ -3319,7 +3439,6 @@ DyffEntityT = TypeVar(
|
|
|
3319
3439
|
Audit,
|
|
3320
3440
|
AuditProcedure,
|
|
3321
3441
|
Challenge,
|
|
3322
|
-
ChallengeSubmission,
|
|
3323
3442
|
DataSource,
|
|
3324
3443
|
Dataset,
|
|
3325
3444
|
Evaluation,
|
|
@@ -3338,6 +3457,7 @@ DyffEntityT = TypeVar(
|
|
|
3338
3457
|
Report,
|
|
3339
3458
|
Revision,
|
|
3340
3459
|
SafetyCase,
|
|
3460
|
+
Submission,
|
|
3341
3461
|
Team,
|
|
3342
3462
|
UseCase,
|
|
3343
3463
|
)
|
|
@@ -3369,9 +3489,6 @@ __all__ = [
|
|
|
3369
3489
|
"Challenge",
|
|
3370
3490
|
"ChallengeContent",
|
|
3371
3491
|
"ChallengeContentPage",
|
|
3372
|
-
"ChallengeSubmission",
|
|
3373
|
-
"ChallengeSubmissionBase",
|
|
3374
|
-
"ChallengeSubmissionStructure",
|
|
3375
3492
|
"ChallengeTask",
|
|
3376
3493
|
"ChallengeTaskBase",
|
|
3377
3494
|
"ChallengeTaskContent",
|
|
@@ -3383,6 +3500,9 @@ __all__ = [
|
|
|
3383
3500
|
"ConcernBase",
|
|
3384
3501
|
"Container",
|
|
3385
3502
|
"ContainerImageSource",
|
|
3503
|
+
"Curve",
|
|
3504
|
+
"CurveData",
|
|
3505
|
+
"CurveSpec",
|
|
3386
3506
|
"DataSchema",
|
|
3387
3507
|
"Dataset",
|
|
3388
3508
|
"DatasetBase",
|
|
@@ -3508,6 +3628,9 @@ __all__ = [
|
|
|
3508
3628
|
"ScoreMetadataRefs",
|
|
3509
3629
|
"Status",
|
|
3510
3630
|
"StorageSignedURL",
|
|
3631
|
+
"Submission",
|
|
3632
|
+
"SubmissionBase",
|
|
3633
|
+
"SubmissionStructure",
|
|
3511
3634
|
"TagName",
|
|
3512
3635
|
"TagNameType",
|
|
3513
3636
|
"TaskSchema",
|
|
@@ -27,7 +27,6 @@ from .platform import (
|
|
|
27
27
|
AnalysisRequestBase,
|
|
28
28
|
AnalysisScope,
|
|
29
29
|
ChallengeContent,
|
|
30
|
-
ChallengeSubmissionBase,
|
|
31
30
|
ChallengeTaskBase,
|
|
32
31
|
ConcernBase,
|
|
33
32
|
DatasetBase,
|
|
@@ -46,6 +45,7 @@ from .platform import (
|
|
|
46
45
|
PipelineBase,
|
|
47
46
|
PipelineRunBase,
|
|
48
47
|
ReportBase,
|
|
48
|
+
SubmissionBase,
|
|
49
49
|
TagNameType,
|
|
50
50
|
TeamBase,
|
|
51
51
|
summary_maxlen,
|
|
@@ -149,9 +149,7 @@ class ChallengeCreateRequest(DyffEntityCreateRequest):
|
|
|
149
149
|
)
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
class
|
|
153
|
-
DyffEntityCreateRequest, ChallengeSubmissionBase
|
|
154
|
-
):
|
|
152
|
+
class SubmissionCreateRequest(DyffEntityCreateRequest, SubmissionBase):
|
|
155
153
|
pass
|
|
156
154
|
|
|
157
155
|
|
|
@@ -504,7 +502,6 @@ __all__ = [
|
|
|
504
502
|
"ChallengeContentEditRequest",
|
|
505
503
|
"ChallengeCreateRequest",
|
|
506
504
|
"ChallengeQueryRequest",
|
|
507
|
-
"ChallengeSubmissionCreateRequest",
|
|
508
505
|
"ChallengeTaskCreateRequest",
|
|
509
506
|
"ChallengeTaskRulesEditRequest",
|
|
510
507
|
"ChallengeTeamCreateRequest",
|
|
@@ -544,6 +541,7 @@ __all__ = [
|
|
|
544
541
|
"ReportQueryRequest",
|
|
545
542
|
"SafetyCaseQueryRequest",
|
|
546
543
|
"ScoreQueryRequest",
|
|
544
|
+
"SubmissionCreateRequest",
|
|
547
545
|
"TeamEditRequest",
|
|
548
546
|
"TeamQueryRequest",
|
|
549
547
|
"UseCaseQueryRequest",
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 UL Research Institutes
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import pydantic
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from dyff.schema.platform import (
|
|
8
|
+
CurveData,
|
|
9
|
+
CurveSpec,
|
|
10
|
+
ScoreData,
|
|
11
|
+
ScoreMetadata,
|
|
12
|
+
ScoreMetadataRefs,
|
|
13
|
+
ScoreSpec,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _dim_spec(name: str, title: str) -> ScoreSpec:
|
|
18
|
+
return ScoreSpec(
|
|
19
|
+
name=name,
|
|
20
|
+
title=title,
|
|
21
|
+
summary=f"{title} dimension",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _minimal_curve_spec() -> CurveSpec:
|
|
26
|
+
return CurveSpec(
|
|
27
|
+
name="roc",
|
|
28
|
+
title="ROC Curve",
|
|
29
|
+
summary="Receiver operating characteristic.",
|
|
30
|
+
dimensions={
|
|
31
|
+
"x": _dim_spec("x", "False Positive Rate"),
|
|
32
|
+
"y": _dim_spec("y", "True Positive Rate"),
|
|
33
|
+
"threshold": _dim_spec("threshold", "Threshold"),
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _metadata(method_id: str = "method-123") -> ScoreMetadata:
|
|
39
|
+
return ScoreMetadata(refs=ScoreMetadataRefs(method=method_id))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_curve_data_ok_equal_length_vectors_and_required_dimensions():
|
|
43
|
+
spec = _minimal_curve_spec()
|
|
44
|
+
|
|
45
|
+
cd = CurveData(
|
|
46
|
+
name=spec.name,
|
|
47
|
+
title=spec.title,
|
|
48
|
+
summary=spec.summary,
|
|
49
|
+
dimensions=spec.dimensions,
|
|
50
|
+
metadata=_metadata(),
|
|
51
|
+
analysis="analysis-abc",
|
|
52
|
+
points={
|
|
53
|
+
"x": [0.0, 0.5, 1.0],
|
|
54
|
+
"y": [0.0, 0.7, 1.0],
|
|
55
|
+
"threshold": [1.0, 0.5, 0.0],
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
assert len(cd.points["x"]) == 3
|
|
60
|
+
for k in spec.dimensions.keys():
|
|
61
|
+
assert k in cd.points
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_curve_data_raises_when_points_missing_required_dimension():
|
|
65
|
+
spec = _minimal_curve_spec()
|
|
66
|
+
|
|
67
|
+
with pytest.raises(pydantic.ValidationError):
|
|
68
|
+
CurveData(
|
|
69
|
+
name=spec.name,
|
|
70
|
+
title=spec.title,
|
|
71
|
+
summary=spec.summary,
|
|
72
|
+
dimensions=spec.dimensions,
|
|
73
|
+
metadata=_metadata(),
|
|
74
|
+
analysis="analysis-abc",
|
|
75
|
+
points={"x": [0.0, 1.0], "y": [0.0, 1.0]},
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_curve_data_raises_when_vector_lengths_differ():
|
|
80
|
+
spec = _minimal_curve_spec()
|
|
81
|
+
|
|
82
|
+
with pytest.raises(pydantic.ValidationError):
|
|
83
|
+
CurveData(
|
|
84
|
+
name=spec.name,
|
|
85
|
+
title=spec.title,
|
|
86
|
+
summary=spec.summary,
|
|
87
|
+
dimensions=spec.dimensions,
|
|
88
|
+
metadata=_metadata(),
|
|
89
|
+
analysis="analysis-abc",
|
|
90
|
+
points={
|
|
91
|
+
"x": [0.0, 0.5, 1.0],
|
|
92
|
+
"y": [0.0, 1.0],
|
|
93
|
+
"threshold": [1.0, 0.0, -1.0],
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_curve_data_raises_when_points_empty():
|
|
99
|
+
spec = _minimal_curve_spec()
|
|
100
|
+
|
|
101
|
+
with pytest.raises(pydantic.ValidationError):
|
|
102
|
+
CurveData(
|
|
103
|
+
name=spec.name,
|
|
104
|
+
title=spec.title,
|
|
105
|
+
summary=spec.summary,
|
|
106
|
+
dimensions=spec.dimensions,
|
|
107
|
+
metadata=_metadata(),
|
|
108
|
+
analysis="analysis-abc",
|
|
109
|
+
points={},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_score_data_scalar_accepts_quantity():
|
|
114
|
+
sd_scalar = ScoreData(
|
|
115
|
+
name="auc",
|
|
116
|
+
title="AUC",
|
|
117
|
+
summary="Area under ROC",
|
|
118
|
+
metadata=_metadata(),
|
|
119
|
+
analysis="analysis-abc",
|
|
120
|
+
quantity=0.9123,
|
|
121
|
+
points=None,
|
|
122
|
+
quantityString="0.912",
|
|
123
|
+
text="Higher is better.",
|
|
124
|
+
)
|
|
125
|
+
assert sd_scalar.quantity == 0.9123
|
|
126
|
+
|
|
127
|
+
with pytest.raises(pydantic.ValidationError):
|
|
128
|
+
ScoreData(
|
|
129
|
+
scoreKind="scalar",
|
|
130
|
+
name="auc_bad",
|
|
131
|
+
title="AUC",
|
|
132
|
+
summary="Scalar must not carry points",
|
|
133
|
+
metadata=_metadata(),
|
|
134
|
+
analysis="analysis-abc",
|
|
135
|
+
quantity=0.9,
|
|
136
|
+
points={"x": [0.0, 1.0], "y": [0.0, 1.0]}, # forbidden
|
|
137
|
+
quantityString="0.900",
|
|
138
|
+
text="This should fail.",
|
|
139
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|