algorithm-nexus 0.2.1__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.
- algorithm_nexus/__init__.py +4 -0
- algorithm_nexus/cli.py +101 -0
- algorithm_nexus/commands/__init__.py +6 -0
- algorithm_nexus/commands/ado_validator.py +176 -0
- algorithm_nexus/commands/benchmark_manager.py +1264 -0
- algorithm_nexus/commands/get.py +120 -0
- algorithm_nexus/commands/list.py +349 -0
- algorithm_nexus/commands/run.py +162 -0
- algorithm_nexus/commands/utils.py +584 -0
- algorithm_nexus/commands/validate.py +683 -0
- algorithm_nexus/commands/venv_manager.py +158 -0
- algorithm_nexus/models.py +408 -0
- algorithm_nexus-0.2.1.dist-info/METADATA +319 -0
- algorithm_nexus-0.2.1.dist-info/RECORD +18 -0
- algorithm_nexus-0.2.1.dist-info/WHEEL +5 -0
- algorithm_nexus-0.2.1.dist-info/entry_points.txt +2 -0
- algorithm_nexus-0.2.1.dist-info/licenses/LICENSE +201 -0
- algorithm_nexus-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Copyright IBM Corp. 2026
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Virtual environment management for benchmark validation."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import tempfile
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
except ImportError:
|
|
17
|
+
print(
|
|
18
|
+
"Error: CLI dependencies are not installed.\n"
|
|
19
|
+
"Please install them with: pip install algorithm-nexus[cli]",
|
|
20
|
+
file=sys.stderr,
|
|
21
|
+
)
|
|
22
|
+
sys.exit(1)
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_temp_venv(system_site_packages: bool = True) -> Path:
|
|
28
|
+
"""Create a temporary virtual environment using uv.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
system_site_packages: Whether to give access to system site packages (default: True)
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Path to the created virtual environment
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
subprocess.CalledProcessError: If venv creation fails
|
|
38
|
+
RuntimeError: If uv is not available
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Create temporary directory
|
|
42
|
+
temp_dir = tempfile.mkdtemp(prefix="nexus-validate-")
|
|
43
|
+
venv_path = Path(temp_dir) / "venv"
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
console.print(f" Creating virtual environment with uv: {venv_path}")
|
|
47
|
+
cmd = ["uv", "venv", str(venv_path)]
|
|
48
|
+
if system_site_packages:
|
|
49
|
+
cmd.append("--system-site-packages")
|
|
50
|
+
subprocess.run( # noqa: S603
|
|
51
|
+
cmd,
|
|
52
|
+
check=True,
|
|
53
|
+
capture_output=True,
|
|
54
|
+
text=True,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return venv_path
|
|
58
|
+
|
|
59
|
+
except subprocess.CalledProcessError as e:
|
|
60
|
+
# Clean up on failure
|
|
61
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
62
|
+
console.print(f"[red]Error creating venv:[/red] {e.stderr}")
|
|
63
|
+
raise
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def install_packages(
|
|
67
|
+
venv_path: Path,
|
|
68
|
+
requirements: list[str],
|
|
69
|
+
verbose: bool = False,
|
|
70
|
+
) -> bool:
|
|
71
|
+
"""Install packages in the virtual environment using uv.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
venv_path: Path to the virtual environment
|
|
75
|
+
requirements: List of package requirement specifiers
|
|
76
|
+
verbose: Whether to show installation output
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
True if installation succeeded, False otherwise
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
RuntimeError: If uv is not available
|
|
83
|
+
"""
|
|
84
|
+
if not requirements:
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
python_path = venv_path / "bin" / "python"
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
console.print(f" Installing packages with uv: {', '.join(requirements)}")
|
|
91
|
+
subprocess.run( # noqa: S603
|
|
92
|
+
# Forcing installing ado-core for extra safety. However,
|
|
93
|
+
# ado-core is most probably already in the requirements of the benchmark
|
|
94
|
+
# package.
|
|
95
|
+
[ # noqa: S607
|
|
96
|
+
"uv",
|
|
97
|
+
"pip",
|
|
98
|
+
"install",
|
|
99
|
+
"--python",
|
|
100
|
+
str(python_path),
|
|
101
|
+
"ado-core",
|
|
102
|
+
*requirements,
|
|
103
|
+
],
|
|
104
|
+
check=True,
|
|
105
|
+
capture_output=True,
|
|
106
|
+
text=True,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
except subprocess.CalledProcessError as e:
|
|
112
|
+
console.print(f"[red]Error installing packages:[/red] {e.stderr}")
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def run_in_venv(
|
|
117
|
+
venv_path: Path,
|
|
118
|
+
command: list[str],
|
|
119
|
+
capture_output: bool = True,
|
|
120
|
+
) -> subprocess.CompletedProcess:
|
|
121
|
+
"""Execute a command in the virtual environment context.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
venv_path: Path to the virtual environment
|
|
125
|
+
command: Command to execute (without python prefix)
|
|
126
|
+
capture_output: Whether to capture stdout/stderr
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
CompletedProcess instance with execution results
|
|
130
|
+
"""
|
|
131
|
+
python_path = venv_path / "bin" / "python"
|
|
132
|
+
full_command = [str(python_path), *command]
|
|
133
|
+
|
|
134
|
+
return subprocess.run( # noqa: S603
|
|
135
|
+
full_command,
|
|
136
|
+
capture_output=capture_output,
|
|
137
|
+
text=True,
|
|
138
|
+
check=False,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def cleanup_venv(venv_path: Path) -> None:
|
|
143
|
+
"""Remove the temporary virtual environment.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
venv_path: Path to the virtual environment to remove
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
# Remove the parent temp directory (which contains the venv)
|
|
150
|
+
temp_dir = venv_path.parent
|
|
151
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
console.print(
|
|
154
|
+
f"[yellow]Warning:[/yellow] Failed to clean up venv at {venv_path}: {e}"
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# Made with Bob
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
# Copyright IBM Corp. 2026
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Pydantic models for Nexus package YAML validation."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
from typing import Annotated, Literal
|
|
11
|
+
|
|
12
|
+
from pydantic import AfterValidator, computed_field
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from ado.schema.property import Property
|
|
16
|
+
from ado.schema.property_value import PropertyValue
|
|
17
|
+
from ado.schema.reference import ExperimentReference
|
|
18
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
19
|
+
except ImportError:
|
|
20
|
+
print(
|
|
21
|
+
"Error: CLI dependencies are not installed.\n"
|
|
22
|
+
"Please install them with: pip install algorithm-nexus[cli]",
|
|
23
|
+
file=sys.stderr,
|
|
24
|
+
)
|
|
25
|
+
sys.exit(1)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def validate_hf_model_id(v: str) -> str:
|
|
29
|
+
"""Validate HuggingFace model ID format and constraints.
|
|
30
|
+
https://huggingface.co/docs/hub/en/security-sso-okta-scim#step-5-assign-users-or-groups
|
|
31
|
+
|
|
32
|
+
Rules:
|
|
33
|
+
- Only alphanumeric characters, dashes, dots, and underscores are accepted
|
|
34
|
+
- Double dashes (--) are forbidden
|
|
35
|
+
- Cannot start or end with a dash
|
|
36
|
+
- Digit-only names are not accepted (must contain at least one letter)
|
|
37
|
+
- Format: org-name/model-name where both follow the same rules except length
|
|
38
|
+
- Maximum length is 42 for org-name and 96 for model-name, minimum length is 2 for both
|
|
39
|
+
"""
|
|
40
|
+
# Combined regex pattern that validates the entire model ID:
|
|
41
|
+
# - org-name: 2-42 chars, alphanumeric + dashes + dots + underscores, no double dashes, must have letter
|
|
42
|
+
# - model-name: 2-96 chars, same rules as org-name
|
|
43
|
+
# - Separated by exactly one slash
|
|
44
|
+
# Split validation into org and model parts to ensure each has at least one letter
|
|
45
|
+
pattern = re.compile(
|
|
46
|
+
r"^"
|
|
47
|
+
r"(?=(?:[a-zA-Z0-9._-]*[a-zA-Z][a-zA-Z0-9._-]*)/)" # org must contain at least one letter
|
|
48
|
+
r"(?!.*--)" # no double dashes in org
|
|
49
|
+
r"[a-zA-Z0-9][a-zA-Z0-9._-]{0,40}[a-zA-Z0-9]" # org-name: 2-42 chars
|
|
50
|
+
r"/" # separator
|
|
51
|
+
r"(?=(?:[a-zA-Z0-9._-]*[a-zA-Z][a-zA-Z0-9._-]*)$)" # model must contain at least one letter
|
|
52
|
+
r"(?!.*--)" # no double dashes in model
|
|
53
|
+
r"[a-zA-Z0-9][a-zA-Z0-9._-]{0,94}[a-zA-Z0-9]" # model-name: 2-96 chars
|
|
54
|
+
r"$"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if not pattern.match(v):
|
|
58
|
+
msg = "Model ID must be in format 'org-name/model-name' where org-name is 2-42 characters and model-name is 2-96 characters. Both must start and end with alphanumeric, contain only alphanumeric, dashes, dots, and underscores, not have double dashes, and contain at least one letter"
|
|
59
|
+
raise ValueError(msg)
|
|
60
|
+
|
|
61
|
+
return v
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class BenchmarkPackage(BaseModel):
|
|
65
|
+
"""Benchmark package registration in nexus.yaml.
|
|
66
|
+
|
|
67
|
+
Registers a benchmark package and the experiments it exposes.
|
|
68
|
+
Each package must follow the ADO custom experiment format.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
model_config = ConfigDict(extra="forbid")
|
|
72
|
+
|
|
73
|
+
requirement_specifier: Annotated[
|
|
74
|
+
str,
|
|
75
|
+
Field(
|
|
76
|
+
min_length=1,
|
|
77
|
+
description="Python package requirement target for the benchmark package. May be a Python package name, a URL to a Python package or source repository, or a local path to a Python package within ./packages in the Nexus repository root.",
|
|
78
|
+
),
|
|
79
|
+
]
|
|
80
|
+
experiments: Annotated[
|
|
81
|
+
list[str],
|
|
82
|
+
Field(
|
|
83
|
+
min_length=1,
|
|
84
|
+
description="Experiment identifiers exposed by that benchmark package and made available to models in the Nexus package.",
|
|
85
|
+
),
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class NexusPackageInfo(BaseModel):
|
|
90
|
+
"""Package-level configuration."""
|
|
91
|
+
|
|
92
|
+
model_config = ConfigDict(extra="forbid")
|
|
93
|
+
|
|
94
|
+
name: Annotated[str, Field(min_length=1, description="Python package name")]
|
|
95
|
+
benchmark_packages: Annotated[
|
|
96
|
+
list[BenchmarkPackage] | None,
|
|
97
|
+
Field(
|
|
98
|
+
description="List of benchmark packages available to models in this package",
|
|
99
|
+
),
|
|
100
|
+
] = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class VLLMPlugins(BaseModel):
|
|
104
|
+
"""vLLM plugins configuration."""
|
|
105
|
+
|
|
106
|
+
model_config = ConfigDict(extra="forbid")
|
|
107
|
+
|
|
108
|
+
general: Annotated[
|
|
109
|
+
str | None,
|
|
110
|
+
Field(description="General vLLM plugin that loads the model class"),
|
|
111
|
+
] = None
|
|
112
|
+
io_processors: Annotated[
|
|
113
|
+
list[str] | None,
|
|
114
|
+
Field(
|
|
115
|
+
min_length=1,
|
|
116
|
+
description="List of vLLM IO processor plugins supported by this model",
|
|
117
|
+
),
|
|
118
|
+
] = None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class VLLMConfig(BaseModel):
|
|
122
|
+
"""vLLM serving configuration.
|
|
123
|
+
|
|
124
|
+
Should only be defined for models that require additional vLLM plugins
|
|
125
|
+
and belong to a Nexus Package targeting the product or candidate distribution variants.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
model_config = ConfigDict(extra="forbid")
|
|
129
|
+
|
|
130
|
+
enabled: Annotated[
|
|
131
|
+
Literal[True],
|
|
132
|
+
Field(description="Whether vLLM serving is enabled for this model"),
|
|
133
|
+
]
|
|
134
|
+
plugins: Annotated[
|
|
135
|
+
VLLMPlugins | None,
|
|
136
|
+
Field(description="vLLM plugins configuration"),
|
|
137
|
+
] = None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class ModelInfo(BaseModel):
|
|
141
|
+
"""Model-level configuration."""
|
|
142
|
+
|
|
143
|
+
model_config = ConfigDict(extra="forbid")
|
|
144
|
+
|
|
145
|
+
id: Annotated[
|
|
146
|
+
str,
|
|
147
|
+
Field(
|
|
148
|
+
min_length=5,
|
|
149
|
+
max_length=139,
|
|
150
|
+
description="Hugging Face model repository identifier",
|
|
151
|
+
),
|
|
152
|
+
AfterValidator(validate_hf_model_id),
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
owner: Annotated[
|
|
156
|
+
str | None,
|
|
157
|
+
Field(
|
|
158
|
+
# Validates the owner field against the GitHub username rules:
|
|
159
|
+
# https://docs.github.com/en/enterprise-cloud@latest/admin/managing-iam/iam-configuration-reference/username-considerations-for-external-authentication
|
|
160
|
+
# - Only contains dashes and alphanumeric characters
|
|
161
|
+
# - Does not start or end with a dash
|
|
162
|
+
# - Does not contain consecutive dashes
|
|
163
|
+
# - Has a maximum length of 39 characters
|
|
164
|
+
pattern=r"^[a-zA-Z0-9]([a-zA-Z0-9]|-[a-zA-Z0-9]){0,38}$",
|
|
165
|
+
description="Model owner GitHub identifier. If omitted, ownership defaults to the Nexus package owner.",
|
|
166
|
+
),
|
|
167
|
+
] = None
|
|
168
|
+
|
|
169
|
+
vllm: Annotated[
|
|
170
|
+
VLLMConfig | None,
|
|
171
|
+
Field(
|
|
172
|
+
description="vLLM serving configuration. Only required for models that need additional vLLM plugins and belong to a Nexus Package targeting the product or candidate distribution variants.",
|
|
173
|
+
),
|
|
174
|
+
] = None
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class AlgorithmNexusModelConfig(BaseModel):
|
|
178
|
+
"""Root model.yaml structure."""
|
|
179
|
+
|
|
180
|
+
model_config = ConfigDict(extra="forbid")
|
|
181
|
+
|
|
182
|
+
model: Annotated[ModelInfo, Field(description="Model configuration")]
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class AlgorithmNexusPackageConfig(BaseModel):
|
|
186
|
+
"""Root nexus.yaml structure."""
|
|
187
|
+
|
|
188
|
+
model_config = ConfigDict(extra="forbid")
|
|
189
|
+
|
|
190
|
+
package: Annotated[
|
|
191
|
+
NexusPackageInfo, Field(description="Package-level configuration")
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class ValidationResult(BaseModel):
|
|
196
|
+
"""Result of validating a benchmark instance."""
|
|
197
|
+
|
|
198
|
+
success: Annotated[bool, Field(description="Whether validation succeeded")]
|
|
199
|
+
instance_path: Annotated[str, Field(description="Path to the benchmark instance")]
|
|
200
|
+
errors: Annotated[list[str], Field(description="List of validation errors")]
|
|
201
|
+
warnings: Annotated[list[str], Field(description="List of validation warnings")]
|
|
202
|
+
|
|
203
|
+
@computed_field # type: ignore[prop-decorator]
|
|
204
|
+
@property
|
|
205
|
+
def status(self) -> str:
|
|
206
|
+
"""Computed status based on success field."""
|
|
207
|
+
return "success" if self.success else "failed"
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class BenchmarkExecutionResult(BaseModel):
|
|
211
|
+
"""Result of executing a benchmark instance."""
|
|
212
|
+
|
|
213
|
+
instance_path: Annotated[str, Field(description="Path to the benchmark instance")]
|
|
214
|
+
status: Annotated[
|
|
215
|
+
Literal["success", "failed", "started", "unknown"],
|
|
216
|
+
Field(description="Execution status"),
|
|
217
|
+
] = "unknown"
|
|
218
|
+
message: Annotated[
|
|
219
|
+
str, Field(description="Status message or error description")
|
|
220
|
+
] = ""
|
|
221
|
+
space_id: Annotated[str | None, Field(description="Created discovery space ID")] = (
|
|
222
|
+
None
|
|
223
|
+
)
|
|
224
|
+
operation_id: Annotated[str | None, Field(description="Created operation ID")] = (
|
|
225
|
+
None
|
|
226
|
+
)
|
|
227
|
+
ray_job_id: Annotated[
|
|
228
|
+
str | None, Field(description="Ray job ID for remote execution")
|
|
229
|
+
] = None
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class ValidationReport(BaseModel):
|
|
233
|
+
"""Aggregated result of validating all benchmark instances."""
|
|
234
|
+
|
|
235
|
+
instances: Annotated[
|
|
236
|
+
list[dict], Field(description="Per-instance validation result dicts")
|
|
237
|
+
]
|
|
238
|
+
successful: Annotated[int, Field(description="Number of instances that passed")]
|
|
239
|
+
failed: Annotated[int, Field(description="Number of instances that failed")]
|
|
240
|
+
total: Annotated[
|
|
241
|
+
int,
|
|
242
|
+
Field(
|
|
243
|
+
description="Total number of instances discovered (may exceed successful + failed under --fail-fast)"
|
|
244
|
+
),
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# ---------------------------------------------------------------------------
|
|
249
|
+
# Logical Benchmark models
|
|
250
|
+
# ---------------------------------------------------------------------------
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class FieldMapping(BaseModel):
|
|
254
|
+
"""1-to-1 mapping of a benchmark property to an experiment property."""
|
|
255
|
+
|
|
256
|
+
model_config = ConfigDict(extra="forbid")
|
|
257
|
+
|
|
258
|
+
benchmark: Annotated[
|
|
259
|
+
Property,
|
|
260
|
+
Field(description="Canonical benchmark property."),
|
|
261
|
+
]
|
|
262
|
+
experiment: Annotated[Property, Field(description="Experiment property.")]
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class CategoricalValueMapping(BaseModel):
|
|
266
|
+
"""Maps a categorical benchmark property value to a set of experiment property predicates."""
|
|
267
|
+
|
|
268
|
+
model_config = ConfigDict(extra="forbid")
|
|
269
|
+
|
|
270
|
+
categoricalValue: Annotated[
|
|
271
|
+
PropertyValue,
|
|
272
|
+
Field(description="The benchmark categorical value being mapped."),
|
|
273
|
+
]
|
|
274
|
+
predicate: Annotated[
|
|
275
|
+
list[Property],
|
|
276
|
+
Field(
|
|
277
|
+
min_length=1,
|
|
278
|
+
description="Experiment property constraints that identify this categorical value.",
|
|
279
|
+
),
|
|
280
|
+
]
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class MetricIdentifier(BaseModel):
|
|
284
|
+
"""A reference to a canonical benchmark metric by its identifier string."""
|
|
285
|
+
|
|
286
|
+
model_config = ConfigDict(extra="forbid")
|
|
287
|
+
|
|
288
|
+
identifier: Annotated[
|
|
289
|
+
str,
|
|
290
|
+
Field(min_length=1, description="Metric identifier."),
|
|
291
|
+
]
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class MetricMapping(BaseModel):
|
|
295
|
+
"""Maps a canonical benchmark metric name to an experiment metric name."""
|
|
296
|
+
|
|
297
|
+
model_config = ConfigDict(extra="forbid")
|
|
298
|
+
|
|
299
|
+
benchmark: Annotated[
|
|
300
|
+
MetricIdentifier,
|
|
301
|
+
Field(description="Canonical benchmark metric identifier."),
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
experiment: Annotated[
|
|
305
|
+
MetricIdentifier,
|
|
306
|
+
Field(description="Experiment metric identifier."),
|
|
307
|
+
]
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class LogicalBenchmarkDefinition(BaseModel):
|
|
311
|
+
"""Logical benchmark definition — the abstract problem description."""
|
|
312
|
+
|
|
313
|
+
model_config = ConfigDict(extra="forbid")
|
|
314
|
+
|
|
315
|
+
benchmarkIdentifier: Annotated[
|
|
316
|
+
str,
|
|
317
|
+
Field(
|
|
318
|
+
min_length=1, description="Canonical identifier for this logical benchmark."
|
|
319
|
+
),
|
|
320
|
+
]
|
|
321
|
+
description: Annotated[
|
|
322
|
+
str,
|
|
323
|
+
Field(
|
|
324
|
+
min_length=1,
|
|
325
|
+
description="Human-readable description of the abstract problem being evaluated.",
|
|
326
|
+
),
|
|
327
|
+
]
|
|
328
|
+
target: Annotated[
|
|
329
|
+
Property,
|
|
330
|
+
Field(
|
|
331
|
+
description="The property that identifies the quantity being benchmarked."
|
|
332
|
+
),
|
|
333
|
+
]
|
|
334
|
+
properties: Annotated[
|
|
335
|
+
list[Property],
|
|
336
|
+
Field(
|
|
337
|
+
min_length=1,
|
|
338
|
+
description="The properties on which this benchmark is evaluated.",
|
|
339
|
+
),
|
|
340
|
+
]
|
|
341
|
+
metrics: Annotated[
|
|
342
|
+
list[str] | None,
|
|
343
|
+
Field(description="Canonical metric names for this benchmark."),
|
|
344
|
+
] = None
|
|
345
|
+
owner: Annotated[
|
|
346
|
+
str | None,
|
|
347
|
+
Field(
|
|
348
|
+
description="Team or individual responsible for maintaining this definition."
|
|
349
|
+
),
|
|
350
|
+
] = None
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class BenchmarkBinding(BaseModel):
|
|
354
|
+
"""Binding that maps an experiment's properties and metrics to a logical benchmark."""
|
|
355
|
+
|
|
356
|
+
model_config = ConfigDict(extra="forbid")
|
|
357
|
+
|
|
358
|
+
benchmarkIdentifier: Annotated[
|
|
359
|
+
str,
|
|
360
|
+
Field(
|
|
361
|
+
min_length=1,
|
|
362
|
+
description="The id of the logical benchmark this experiment targets.",
|
|
363
|
+
),
|
|
364
|
+
]
|
|
365
|
+
experiment: Annotated[
|
|
366
|
+
ExperimentReference,
|
|
367
|
+
Field(description="The ado ExperimentReference object."),
|
|
368
|
+
]
|
|
369
|
+
targetMapping: Annotated[
|
|
370
|
+
str,
|
|
371
|
+
Field(
|
|
372
|
+
min_length=1,
|
|
373
|
+
description="The experiment property name that carries the benchmark target.",
|
|
374
|
+
),
|
|
375
|
+
]
|
|
376
|
+
staticFilters: Annotated[
|
|
377
|
+
list[PropertyValue] | None,
|
|
378
|
+
Field(
|
|
379
|
+
description="Static experiment property filters implicit in this logical benchmark."
|
|
380
|
+
),
|
|
381
|
+
] = None
|
|
382
|
+
propertyMapping: Annotated[
|
|
383
|
+
list[FieldMapping | CategoricalValueMapping] | None,
|
|
384
|
+
Field(
|
|
385
|
+
description="Maps experiment internal properties to canonical benchmark properties."
|
|
386
|
+
),
|
|
387
|
+
] = None
|
|
388
|
+
metricMapping: Annotated[
|
|
389
|
+
list[MetricMapping] | None,
|
|
390
|
+
Field(
|
|
391
|
+
description="Translates per-experiment metric names to canonical benchmark metric names."
|
|
392
|
+
),
|
|
393
|
+
] = None
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class LogicalBenchmarkConfig(BaseModel):
|
|
397
|
+
"""Root model for a logical benchmark YAML file (logicalBenchmark + optional bindings)."""
|
|
398
|
+
|
|
399
|
+
model_config = ConfigDict(extra="forbid")
|
|
400
|
+
|
|
401
|
+
logicalBenchmark: Annotated[
|
|
402
|
+
LogicalBenchmarkDefinition,
|
|
403
|
+
Field(description="The logical benchmark definition."),
|
|
404
|
+
]
|
|
405
|
+
bindings: Annotated[
|
|
406
|
+
list[BenchmarkBinding] | None,
|
|
407
|
+
Field(description="Benchmark bindings declared in this file."),
|
|
408
|
+
] = None
|