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
algorithm_nexus/cli.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Copyright IBM Corp. 2026
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Command-line interface for Algorithm Nexus package validation."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
except ImportError:
|
|
14
|
+
print(
|
|
15
|
+
"Error: CLI dependencies are not installed.\n"
|
|
16
|
+
"Please install them with: pip install algorithm-nexus[cli]",
|
|
17
|
+
file=sys.stderr,
|
|
18
|
+
)
|
|
19
|
+
sys.exit(1)
|
|
20
|
+
|
|
21
|
+
from algorithm_nexus.commands.get import get_benchmark_requirements
|
|
22
|
+
from algorithm_nexus.commands.list import (
|
|
23
|
+
list_benchmark_experiments,
|
|
24
|
+
list_benchmark_packages,
|
|
25
|
+
list_packages,
|
|
26
|
+
)
|
|
27
|
+
from algorithm_nexus.commands.run import run_benchmarks
|
|
28
|
+
from algorithm_nexus.commands.validate import (
|
|
29
|
+
validate_benchmarks,
|
|
30
|
+
validate_logical_benchmarks,
|
|
31
|
+
validate_package,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
console = Console()
|
|
35
|
+
|
|
36
|
+
app = typer.Typer(
|
|
37
|
+
help="Algorithm Nexus CLI - Tools for managing and validating Nexus packages.",
|
|
38
|
+
add_completion=False,
|
|
39
|
+
no_args_is_help=True,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Create subcommand group for 'list'
|
|
43
|
+
list_app = typer.Typer(
|
|
44
|
+
help="List various resources in Nexus packages.",
|
|
45
|
+
no_args_is_help=True,
|
|
46
|
+
)
|
|
47
|
+
app.add_typer(list_app, name="list")
|
|
48
|
+
|
|
49
|
+
# Create subcommand group for 'get'
|
|
50
|
+
get_app = typer.Typer(
|
|
51
|
+
help="Get specific information about Nexus packages.",
|
|
52
|
+
no_args_is_help=True,
|
|
53
|
+
)
|
|
54
|
+
app.add_typer(get_app, name="get")
|
|
55
|
+
|
|
56
|
+
# Create subcommand group for 'run'
|
|
57
|
+
run_app = typer.Typer(
|
|
58
|
+
help="Execute benchmarks and operations.",
|
|
59
|
+
no_args_is_help=True,
|
|
60
|
+
)
|
|
61
|
+
app.add_typer(run_app, name="run")
|
|
62
|
+
|
|
63
|
+
# Create subcommand group for 'validate'
|
|
64
|
+
validate_app = typer.Typer(
|
|
65
|
+
help="Validate various aspects of Nexus packages.",
|
|
66
|
+
no_args_is_help=True,
|
|
67
|
+
)
|
|
68
|
+
app.add_typer(validate_app, name="validate")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@app.callback(invoke_without_command=True)
|
|
72
|
+
def main_callback(ctx: typer.Context) -> None:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# Register list commands
|
|
77
|
+
list_app.command(name="packages")(list_packages)
|
|
78
|
+
list_app.command(name="benchmark-packages")(list_benchmark_packages)
|
|
79
|
+
list_app.command(name="benchmark-experiments")(list_benchmark_experiments)
|
|
80
|
+
|
|
81
|
+
# Register get commands
|
|
82
|
+
get_app.command(name="benchmark-requirements")(get_benchmark_requirements)
|
|
83
|
+
|
|
84
|
+
# Register run commands
|
|
85
|
+
run_app.command(name="benchmarks")(run_benchmarks)
|
|
86
|
+
|
|
87
|
+
# Register validate commands
|
|
88
|
+
validate_app.command(name="package")(validate_package)
|
|
89
|
+
validate_app.command(name="benchmarks")(validate_benchmarks)
|
|
90
|
+
validate_app.command(name="logical-benchmarks")(validate_logical_benchmarks)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def main() -> None:
|
|
94
|
+
"""Entry point for the CLI application."""
|
|
95
|
+
app()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
|
100
|
+
|
|
101
|
+
# Made with Bob
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Copyright IBM Corp. 2026
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""ADO validation for benchmark instances."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import yaml
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from ado.core.discoveryspace.config import DiscoverySpaceConfiguration
|
|
16
|
+
from pydantic import ValidationError
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
except ImportError:
|
|
19
|
+
print(
|
|
20
|
+
"Error: CLI dependencies are not installed.\n"
|
|
21
|
+
"Please install them with: pip install algorithm-nexus[cli]",
|
|
22
|
+
file=sys.stderr,
|
|
23
|
+
)
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
|
|
26
|
+
from algorithm_nexus.models import ValidationResult
|
|
27
|
+
|
|
28
|
+
console = Console()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_space_yaml_syntax(base_path: Path, instance_path: str) -> ValidationResult:
|
|
32
|
+
"""Validate space.yaml YAML syntax and basic structure using DiscoverySpaceConfiguration.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
base_path: Base path (e.g., repo root or temp directory)
|
|
36
|
+
instance_path: Relative path to the benchmark instance from base_path
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
ValidationResult with syntax validation results
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# Construct full path to space.yaml
|
|
43
|
+
space_yaml_path = base_path / instance_path / "space.yaml"
|
|
44
|
+
|
|
45
|
+
# Check if file exists
|
|
46
|
+
if not space_yaml_path.is_file():
|
|
47
|
+
return ValidationResult(
|
|
48
|
+
success=False,
|
|
49
|
+
instance_path=instance_path,
|
|
50
|
+
errors=[f"File not found: {space_yaml_path}"],
|
|
51
|
+
warnings=[],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
errors = []
|
|
55
|
+
warnings = []
|
|
56
|
+
|
|
57
|
+
space_config = None
|
|
58
|
+
try:
|
|
59
|
+
# Load and parse YAML
|
|
60
|
+
space_config_dict = yaml.safe_load(space_yaml_path.read_text())
|
|
61
|
+
|
|
62
|
+
# Validate using DiscoverySpaceConfiguration
|
|
63
|
+
space_config = DiscoverySpaceConfiguration.model_validate(space_config_dict)
|
|
64
|
+
|
|
65
|
+
except yaml.YAMLError as e:
|
|
66
|
+
errors.append(f"Invalid YAML syntax: {e}")
|
|
67
|
+
except ValidationError as e:
|
|
68
|
+
# Extract validation errors from Pydantic
|
|
69
|
+
for error in e.errors():
|
|
70
|
+
loc = " -> ".join(str(x) for x in error["loc"])
|
|
71
|
+
msg = error["msg"]
|
|
72
|
+
errors.append(f"Validation error at {loc}: {msg}")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
errors.append(f"Unexpected error reading space.yaml: {e}")
|
|
75
|
+
|
|
76
|
+
# Check for optional but common sections using the validated model
|
|
77
|
+
if space_config:
|
|
78
|
+
space_config = space_config.convert_experiments_to_reference_list()
|
|
79
|
+
if not space_config.experiments:
|
|
80
|
+
warnings.append("No 'experiments' section found in space.yaml")
|
|
81
|
+
|
|
82
|
+
if not space_config.entitySpace:
|
|
83
|
+
warnings.append("No 'entitySpace' section found in space.yaml")
|
|
84
|
+
|
|
85
|
+
return ValidationResult(
|
|
86
|
+
success=len(errors) == 0,
|
|
87
|
+
instance_path=instance_path,
|
|
88
|
+
errors=errors,
|
|
89
|
+
warnings=warnings,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def validate_with_ado(
|
|
94
|
+
base_path: Path,
|
|
95
|
+
instance_path: str,
|
|
96
|
+
venv_path: Path,
|
|
97
|
+
) -> ValidationResult:
|
|
98
|
+
"""Run ADO validation in isolated virtual environment.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
base_path: Base path (e.g., repo root or temp directory)
|
|
102
|
+
instance_path: Relative path to the benchmark instance from base_path
|
|
103
|
+
venv_path: Path to virtual environment with ADO installed
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
ValidationResult with ADO validation results
|
|
107
|
+
"""
|
|
108
|
+
# First validate syntax
|
|
109
|
+
syntax_result = validate_space_yaml_syntax(base_path, instance_path)
|
|
110
|
+
|
|
111
|
+
if syntax_result.errors:
|
|
112
|
+
# Don't proceed with ADO validation if syntax is invalid
|
|
113
|
+
return syntax_result
|
|
114
|
+
|
|
115
|
+
# Start with syntax warnings for ADO validation
|
|
116
|
+
errors = []
|
|
117
|
+
warnings = syntax_result.warnings.copy()
|
|
118
|
+
|
|
119
|
+
# Check if ADO binary is available in the venv
|
|
120
|
+
ado_binary = venv_path / "bin" / "ado"
|
|
121
|
+
if not ado_binary.is_file():
|
|
122
|
+
errors.append(
|
|
123
|
+
"ADO binary not found in the virtual environment. "
|
|
124
|
+
"Make sure ado-core is installed."
|
|
125
|
+
)
|
|
126
|
+
return ValidationResult(
|
|
127
|
+
success=False,
|
|
128
|
+
instance_path=instance_path,
|
|
129
|
+
errors=errors,
|
|
130
|
+
warnings=warnings,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Validate with ADO using dry-run mode
|
|
134
|
+
try:
|
|
135
|
+
# Construct full path to space.yaml
|
|
136
|
+
space_yaml_path = base_path / instance_path / "space.yaml"
|
|
137
|
+
|
|
138
|
+
# Use ado create space with --dry-run flag
|
|
139
|
+
# Run from the benchmark instance directory to support relative paths in space.yaml
|
|
140
|
+
result = subprocess.run( # noqa: S603
|
|
141
|
+
[
|
|
142
|
+
str(ado_binary),
|
|
143
|
+
"create",
|
|
144
|
+
"space",
|
|
145
|
+
"-f",
|
|
146
|
+
str(space_yaml_path),
|
|
147
|
+
"--dry-run",
|
|
148
|
+
],
|
|
149
|
+
capture_output=True,
|
|
150
|
+
text=True,
|
|
151
|
+
check=False,
|
|
152
|
+
timeout=30, # 30 second timeout
|
|
153
|
+
cwd=space_yaml_path.parent, # Run from benchmark instance directory
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
if result.returncode != 0:
|
|
157
|
+
# Parse error message for more details
|
|
158
|
+
error_msg = (
|
|
159
|
+
result.stderr.strip() if result.stderr else result.stdout.strip()
|
|
160
|
+
)
|
|
161
|
+
errors.append(f"ADO validation failed: {error_msg}")
|
|
162
|
+
|
|
163
|
+
except subprocess.TimeoutExpired:
|
|
164
|
+
errors.append("ADO validation timed out after 30 seconds")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
errors.append(f"Failed to run ADO validation: {e}")
|
|
167
|
+
|
|
168
|
+
return ValidationResult(
|
|
169
|
+
success=len(errors) == 0,
|
|
170
|
+
instance_path=instance_path,
|
|
171
|
+
errors=errors,
|
|
172
|
+
warnings=warnings,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# Made with Bob
|