diff --git a/docs/getting-started/cli-reference.md b/docs/getting-started/cli-reference.md index cf973b8..ce7675b 100644 --- a/docs/getting-started/cli-reference.md +++ b/docs/getting-started/cli-reference.md @@ -139,6 +139,189 @@ nexus get benchmark-requirements nexus get benchmark-requirements terratorch ``` +## nexus run + +Execute benchmark instances. + +### Subcommands + +#### nexus run benchmarks + +Execute benchmarks from a GitHub Pull Request. This command identifies new or +changed benchmark instances in a PR and optionally executes them using the `ado` +CLI. + +**Usage:** + +```bash +nexus run benchmarks --pr [OPTIONS] +``` + +!!! warning + + This command requires the [GitHub CLI (`gh`)](https://cli.github.com/) to be installed and + configured in the command execution environment. + +**Required Options:** + +- `--pr `: GitHub Pull Request URL (e.g., + `https://github.com/IBM/algorithm-nexus/pull/123`) + +**Optional Flags:** + +- `--remote `: Execute operations on a remote Ray cluster using the + specified configuration file. When provided, the command automatically + installs the required benchmark packages in the Ray environment. Read + [`Running ado on remote Ray clusters`](https://ibm.github.io/ado/getting-started/remote_run/) + to discover the remote context configuration format. +- `--context `: Path to ADO context YAML file (samplestore context). Read + [`Working with Contexts`](https://ibm.github.io/ado/resources/metastore/#working-with-contexts) + to discover how to manage contexts. +- `--dry-run`: List benchmark instances without executing them (dry run) +- `--output-file `: Output file path for execution results. If not + specified, results are printed to screen. +- `-o, --output-format `: Output format: 'json' or 'yaml'. Can be used + with or without `--output-file`. When used without `--output-file`, prints + formatted output to console. When used with `--output-file`, overrides format + inference from file extension (defaults to json). + +**Behavior:** + +The command automatically: + +1. Checks if the local repository is on the same commit as the PR +2. If not, checks out the PR code to a temporary directory +3. Analyzes the PR to find new or changed benchmark instances +4. Executes the benchmarks (unless `--dry-run` is specified) +5. Writes results to the output file + +**Benchmark Instance Detection:** + +A benchmark instance is detected as changed if any file within its directory is +modified in the PR. This includes: + +- Changes to `space.yaml` files +- Changes to any other files in the `benchmark_instances//` + directory +- New benchmark instance directories (any new folder under + `benchmark_instances/`) + +The detection works for both: + +- **Model-level instances**: + `packages//models//benchmark_instances//` +- **Package-level instances**: + `packages//benchmark_instances//` + +!!! note + + When not running in remote mode (`--remote` not set), the benchmark instances will be executed with + `ado` in the local environment. It is the user responsibility to ensure that the required + benchmark packages are installed in the local python environment. Benchmark packages are listed for + each nexus package in the `nexus.yaml` configuration. + +**Examples:** + +List benchmarks in a PR without executing (dry run): + +```bash +nexus run benchmarks --pr https://github.com/IBM/algorithm-nexus/pull/123 --dry-run +``` + +Execute benchmarks locally: + +```bash +nexus run benchmarks --pr https://github.com/IBM/algorithm-nexus/pull/123 +``` + +Execute benchmarks on a remote Ray cluster: + +```bash +nexus run benchmarks \ + --pr https://github.com/IBM/algorithm-nexus/pull/123 \ + --remote path/to/remote-context.yaml \ + --context path/to/ado-context.yaml +``` + +Execute benchmarks and save results to a file: + +```bash +nexus run benchmarks \ + --pr https://github.com/IBM/algorithm-nexus/pull/123 \ + --output-file benchmark_results.json +``` + +Execute benchmarks and save results in YAML format: + +```bash +nexus run benchmarks \ + --pr https://github.com/IBM/algorithm-nexus/pull/123 \ + --output-file results.yaml \ + --output-format yaml +``` + +**Output Format:** + +By default, the command outputs results in a human-readable format showing the +status, message, and IDs for each benchmark instance. + +To get structured output (JSON or YAML), use the `--output-format` option: + +```bash +# Print JSON to console +algorithm-nexus run packages/terratorch/models/prithvi/benchmark_instances/flood-test \ + --output-format json + +# Print YAML to console +algorithm-nexus run packages/terratorch/models/prithvi/benchmark_instances/flood-test \ + --output-format yaml +``` + +The structured output (JSON) has the following format: + +```json +{ + "instances": [ + { + "instance_path": "packages//models//benchmark_instances/test_benchmark", + "status": "started", + "message": "Successfully started on Ray cluster with job ID: raysubmit_snRVd4ZqTTKcaR3W | Space ID: space-a009d7-default", + "space_id": "space-a009d7-default", + "operation_id": "randomwalk-123456-default", + "ray_job_id": "raysubmit_snRVd4ZqTTKcaR3W" + } + ] +} +``` + +**Status Values:** + +- `success`: Benchmark completed successfully (local execution) +- `started`: Benchmark started on Ray cluster (remote execution) +- `failed`: Benchmark execution failed +- `unknown`: Status could not be determined + +**Execution Mode Comparison:** + +| Field | Local Mode (`--remote` not set) | Remote Mode (`--remote` set) | +| -------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| `status` | `success` (on success)
`failed` (on error)
`unknown` (if undetermined) | `started` (on success)
`failed` (on error)
`unknown` (if undetermined) | +| `operation_id` | ADO operation ID | `null` | +| `ray_job_id` | `null` | Ray job ID | +| Notes | Operation completes locally | Job submitted to Ray cluster | + +When `operation_id` is `null` (in remote mode), users will need to inspect the +Ray job logs to extract the ADO operation ID once execution completes. + +In case of failure (either mode), the `status` will be `failed` and the +`message` field will contain the reason for the failure. + +**Exit Codes:** + +- `0`: All benchmarks executed successfully +- `1`: One or more benchmarks failed or an error occurred +- `130`: Interrupted by user (Ctrl+C) + ## Common Workflows ### Validating a New Package @@ -177,6 +360,35 @@ Retrieve specific information about a package: nexus get benchmark-requirements my-package ``` +### Running Benchmarks from a Pull Request + +Execute benchmarks from a GitHub PR to validate changes: + +```bash +# First, check what benchmarks would be executed (dry run) +nexus run benchmarks \ + --pr https://github.com/IBM/algorithm-nexus/pull/123 \ + --dry-run + +# Execute benchmarks locally +nexus run benchmarks \ + --pr https://github.com/IBM/algorithm-nexus/pull/123 + +# Execute benchmarks on a remote Ray cluster +nexus run benchmarks \ + --pr https://github.com/IBM/algorithm-nexus/pull/123 \ + --remote config/remote-context.yaml \ + --context config/ado-context.yaml \ + --output-file pr123_results.json +``` + +The command will: + +1. Automatically detect if your local repo is on the PR commit +2. Checkout the PR code if needed (to a temporary directory) +3. Find all new or changed benchmark instances +4. Execute them and report results + ## Exit Codes All commands follow standard Unix exit code conventions: diff --git a/pyproject.toml b/pyproject.toml index eac456b..37c0726 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ candidate = [ "vllm==0.19.1", ] cli = [ + "ado-core>=1.8.0", "pydantic>=2.13.3", "pyyaml>=6.0.3", "rich>=15.0.0", diff --git a/src/algorithm_nexus/cli.py b/src/algorithm_nexus/cli.py index 618bcd3..bcd9880 100644 --- a/src/algorithm_nexus/cli.py +++ b/src/algorithm_nexus/cli.py @@ -24,6 +24,7 @@ list_benchmark_packages, list_packages, ) +from algorithm_nexus.commands.run import run_benchmarks from algorithm_nexus.commands.validate import validate console = Console() @@ -48,6 +49,13 @@ ) app.add_typer(get_app, name="get") +# Create subcommand group for 'run' +run_app = typer.Typer( + help="Execute benchmarks and operations.", + no_args_is_help=True, +) +app.add_typer(run_app, name="run") + @app.callback(invoke_without_command=True) def main_callback(ctx: typer.Context) -> None: @@ -65,6 +73,9 @@ def main_callback(ctx: typer.Context) -> None: # Register get commands get_app.command(name="benchmark-requirements")(get_benchmark_requirements) +# Register run commands +run_app.command(name="benchmarks")(run_benchmarks) + def main() -> None: """Entry point for the CLI application.""" diff --git a/src/algorithm_nexus/commands/list.py b/src/algorithm_nexus/commands/list.py index 06bb53e..8bb4ac3 100644 --- a/src/algorithm_nexus/commands/list.py +++ b/src/algorithm_nexus/commands/list.py @@ -340,11 +340,9 @@ def list_benchmark_experiments( # Add instructions for getting more details console.print("[bold]For further details on each experiment:[/bold]") - console.print("1. Install ado") - console.print(" [cyan]uv pip install ado-core[/cyan]") - console.print("2. Install the Benchmark package the experiment belongs to") + console.print("1. Install the Benchmark package the experiment belongs to") console.print(" [cyan]uv pip install [/cyan]") - console.print("3. Describe the experiment") + console.print("2. Describe the experiment") console.print(" [cyan]ado describe experiment [/cyan]\n") diff --git a/src/algorithm_nexus/commands/run.py b/src/algorithm_nexus/commands/run.py new file mode 100644 index 0000000..fbe234e --- /dev/null +++ b/src/algorithm_nexus/commands/run.py @@ -0,0 +1,1064 @@ +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 + +"""Run commands for Algorithm Nexus CLI.""" + +from __future__ import annotations + +import json +import re +import subprocess +import sys +import tempfile +from pathlib import Path +from typing import Annotated, Any + +try: + import typer + import yaml + from orchestrator.core.discoveryspace.config import DiscoverySpaceConfiguration + from orchestrator.core.operation.config import ( + ConfigurationMetadata, + DiscoveryOperationConfiguration, + DiscoveryOperationEnum, + DiscoveryOperationResourceConfiguration, + OperatorReference, + ) + from orchestrator.core.remotecontext.config import ( + PackageConfiguration, + RemoteExecutionContext, + ) + from orchestrator.modules.operators.randomwalk import ( + BaseSamplerConfiguration, + RandomWalkParameters, + ) + from orchestrator.utilities.output import pydantic_model_as_yaml + from rich.console import Console +except ImportError: + print( + "Error: CLI dependencies are not installed.\n" + "Please install them with: pip install algorithm-nexus[cli]", + file=sys.stderr, + ) + sys.exit(1) + +from algorithm_nexus.commands.utils import strip_ansi_codes +from algorithm_nexus.models import ( + AlgorithmNexusPackageConfig, + BenchmarkExecutionResult, +) + +console = Console() +console_err = Console(stderr=True) + + +# Random walk operation template using DiscoveryOperationResourceConfiguration +def create_random_walk_operation_config( + space_id: str, + metadata_name: str = "randomwalk-all", + metadata_description: str = "Perform a random walk on all points in a space", + custom_metadata: dict[str, Any] | None = None, +) -> DiscoveryOperationResourceConfiguration: + """Create a random walk operation configuration. + + Args: + space_id: The discovery space identifier + metadata_name: Name for the operation metadata + metadata_description: Description for the operation metadata + custom_metadata: Additional custom metadata fields + + Returns: + DiscoveryOperationResourceConfiguration object + """ + # Build metadata with custom fields + labels = {} + if custom_metadata: + labels.update(custom_metadata) + metadata = ConfigurationMetadata( + name=metadata_name, + description=metadata_description, + labels=labels or None, + ) + + operation = DiscoveryOperationConfiguration( + module=OperatorReference( + operatorName="random_walk", + operationType=DiscoveryOperationEnum.SEARCH, + ), + parameters=RandomWalkParameters( + numberEntities="all", + singleMeasurement=True, + samplerConfig=BaseSamplerConfiguration( + samplerType="generator", + mode="sequential", + ), + ), + ) + + return DiscoveryOperationResourceConfiguration( + metadata=metadata, + spaces=[space_id], + operation=operation, + actuatorConfigurationIdentifiers=[], + ) + + +class BenchmarkManager: + """Manages discovery and execution of benchmark instances from a GitHub PR.""" + + def __init__( + self, + pr_url: str, + execute: bool = True, + remote_context_file: Path | None = None, + context_file: Path | None = None, + ): + """Initialize the benchmark manager. + + Args: + pr_url: GitHub Pull Request URL + execute: Whether to execute benchmarks with ADO CLI + remote_context_file: Path to remote execution context YAML file + context_file: Path to ADO context YAML file (samplestore context) + """ + self.pr_url = pr_url + self.execute = execute + self.remote_context_file = remote_context_file + self.context_file = context_file + self.repo_root = Path.cwd() + self.temp_dir_obj = None + + def get_changed_files(self) -> list[str]: + """Get list of changed files from the PR using gh CLI. + + Returns: + List of changed file paths + """ + try: + result = subprocess.run( # noqa: S603 + ["gh", "pr", "diff", self.pr_url, "--name-only"], # noqa: S607 + capture_output=True, + text=True, + check=True, + ) + return [line.strip() for line in result.stdout.split("\n") if line.strip()] + except subprocess.CalledProcessError as e: + console_err.print(f"[red]Error:[/red] Failed to fetch PR diff: {e}") + console_err.print("Make sure 'gh' is installed and authenticated") + raise typer.Exit(code=1) + except FileNotFoundError: + console_err.print("[red]Error:[/red] GitHub CLI (gh) is not installed") + console_err.print("Install it from: https://cli.github.com/") + raise typer.Exit(code=1) + + def _parse_instance_path(self, instance_path: Path) -> tuple[str, str, str]: + """Parse benchmark instance path to extract package, model, and instance names. + + Args: + instance_path: Path to benchmark instance directory + + Returns: + Tuple of (package_name, model_name, instance_name) + For package-level instances, model_name will be "base" + + Raises: + ValueError: If the instance path format is invalid + """ + # Parse instance path using regex: + # - Model-level: packages//models//benchmark_instances/ + # - Package-level: packages//benchmark_instances/ + import re + + path_str = str(instance_path) + + # Try model-level pattern first + model_pattern = r"^packages/([^/]+)/models/([^/]+)/benchmark_instances/([^/]+)$" + match = re.match(model_pattern, path_str) + if match: + package_name, model_name, instance_name = match.groups() + return package_name, model_name, instance_name + + # Try package-level pattern + package_pattern = r"^packages/([^/]+)/benchmark_instances/([^/]+)$" + match = re.match(package_pattern, path_str) + if match: + package_name, instance_name = match.groups() + model_name = "base" + return package_name, model_name, instance_name + + # If neither pattern matches, raise an error + raise ValueError( + f"Invalid benchmark instance path format: {instance_path}. " + "Expected: packages//benchmark_instances/ or " + "packages//models//benchmark_instances/" + ) + + def find_benchmark_instances(self, changed_files: list[str]) -> list[Path]: + """Find benchmark instance directories from changed files. + + Args: + changed_files: List of changed file paths + + Returns: + List of unique benchmark instance directory paths + """ + benchmark_dirs = set() + + for file_path in changed_files: + path = Path(file_path) + if "benchmark_instances" in path.parts: + # Find the index of 'benchmark_instances' in the path parts + bench_idx = path.parts.index("benchmark_instances") + # Ensure there's at least one part after 'benchmark_instances' + if bench_idx + 1 < len(path.parts): + # Reconstruct path up to and including the first directory after 'benchmark_instances' + instance_dir = Path(*path.parts[: bench_idx + 2]) + benchmark_dirs.add(instance_dir) + + return sorted(benchmark_dirs) + + def checkout_pr_to_temp(self): + """Clone the repository and checkout the PR code in a temporary directory.""" + try: + self.temp_dir_obj = tempfile.TemporaryDirectory(prefix="pr_checkout_") + temp_path = Path(self.temp_dir_obj.name) + + console.print(f"Creating temporary clone in {temp_path}") + + # Use gh CLI to clone the repo directly from the PR URL + console.print("Cloning repository using gh CLI...") + subprocess.run( # noqa: S603 + ["gh", "repo", "clone", self.pr_url, str(temp_path)], # noqa: S607 + capture_output=True, + text=True, + check=True, + ) + + # Use gh CLI to checkout the PR using the URL + console.print("Checking out PR using gh CLI...") + subprocess.run( # noqa: S603 + ["gh", "pr", "checkout", self.pr_url], # noqa: S607 + capture_output=True, + text=True, + check=True, + cwd=temp_path, + ) + + self.repo_root = temp_path + console.print("[green]✓[/green] Successfully checked out PR code") + + except subprocess.CalledProcessError as e: + console_err.print(f"[red]Error:[/red] Failed to checkout PR: {e}") + if e.stderr: + console_err.print(f" {e.stderr}") + self.cleanup_temp_dir() + raise typer.Exit(code=1) + + def cleanup_temp_dir(self): + """Clean up the temporary directory if it was created.""" + if self.temp_dir_obj: + try: + console.print("\nCleaning up temporary directory") + self.temp_dir_obj.cleanup() + self.temp_dir_obj = None + console.print("[green]✓[/green] Temporary directory cleaned up") + except Exception as e: + console_err.print( + f"[yellow]Warning:[/yellow] Failed to clean up temporary directory: {e}" + ) + + def is_local_repo_on_pr_commit(self) -> bool: + """Check if the local repository is on the same commit as the PR head. + + Returns: + True if local repo is on PR commit, False otherwise + """ + try: + # Get PR head commit SHA + pr_number = self.pr_url.rstrip("/").split("/")[-1] + result = subprocess.run( # noqa: S603 + [ # noqa: S607 + "gh", + "pr", + "view", + pr_number, + "--json", + "headRefOid", + "--jq", + ".headRefOid", + ], + capture_output=True, + text=True, + check=True, + cwd=self.repo_root, + ) + pr_commit = result.stdout.strip() + + # Get current local commit SHA + result = subprocess.run( # noqa: S603 + ["git", "rev-parse", "HEAD"], # noqa: S607 + capture_output=True, + text=True, + check=True, + cwd=self.repo_root, + ) + local_commit = result.stdout.strip() + + return pr_commit == local_commit + except (subprocess.CalledProcessError, FileNotFoundError): + # If we can't determine, assume they're different to be safe + return False + + def get_benchmark_packages_for_instance(self, instance_path: Path) -> set[str]: + """Get the benchmark packages required for a specific benchmark instance. + + Args: + instance_path: Path to benchmark instance directory + + Returns: + Set of requirement specifiers for benchmark packages + """ + packages = set() + + try: + repo_root = self.repo_root + + space_yaml_path = repo_root / instance_path / "space.yaml" + if not space_yaml_path.is_file(): + return set() + + # Load space configuration using DiscoverySpaceConfiguration + space_config_dict = yaml.safe_load(space_yaml_path.read_text()) + space_config = DiscoverySpaceConfiguration.model_validate(space_config_dict) + + # Extract experiment identifiers + # Convert experiments to reference list for easier processing + space_config_with_refs = ( + space_config.convert_experiments_to_reference_list() + ) + + if not space_config_with_refs.experiments: + return set() + + # After conversion, experiments is a list of ExperimentReference + experiment_ids: set[str] = { + exp_ref.experimentIdentifier # type: ignore[union-attr] + for exp_ref in space_config_with_refs.experiments # type: ignore[union-attr] + } + + current_path = repo_root / instance_path + nexus_yaml_path = None + + # Find the deepest nexus.yaml file up to repo_root + for parent in [current_path, *current_path.parents]: + if parent == repo_root.parent: + break + potential_nexus = parent / "nexus.yaml" + if potential_nexus.is_file(): + nexus_yaml_path = potential_nexus + break + + if not nexus_yaml_path: + parts = instance_path.parts + if len(parts) > 0 and parts[0] == "packages" and len(parts) > 1: + potential_nexus = repo_root / "packages" / parts[1] / "nexus.yaml" + if potential_nexus.exists(): + nexus_yaml_path = potential_nexus + + if not nexus_yaml_path or not nexus_yaml_path.exists(): + return set() + + # Load nexus config using AlgorithmNexusPackageConfig + nexus_config_dict = yaml.safe_load(nexus_yaml_path.read_text()) + nexus_config = AlgorithmNexusPackageConfig.model_validate(nexus_config_dict) + + if nexus_config.package.benchmark_packages: + for bench_pkg in nexus_config.package.benchmark_packages: + pkg_experiments = set(bench_pkg.experiments) + if experiment_ids & pkg_experiments: + packages.add(bench_pkg.requirement_specifier) + + except Exception as e: + console_err.print( + f"[yellow]Warning:[/yellow] Could not determine benchmark packages for {instance_path}: {e}" + ) + return set() + + return packages + + def execute_benchmark(self, instance_path: Path) -> BenchmarkExecutionResult: + """Execute a benchmark instance using ADO CLI. + + Args: + instance_path: Path to benchmark instance directory + + Returns: + Execution result + """ + result = BenchmarkExecutionResult(instance_path=str(instance_path)) + + # Prepare remote config once for this benchmark instance + remote_config_for_space = None + remote_config_for_operation = None + temp_remote_configs = [] + + try: + # Use repo_root which is set to either local or checked out directory + space_yaml_path = self.repo_root / instance_path / "space.yaml" + + if not space_yaml_path.is_file(): + raise FileNotFoundError(f"space.yaml not found in {instance_path}") + + # If in remote mode, create remote configs with benchmark packages + if self.remote_context_file: + benchmark_packages = self.get_benchmark_packages_for_instance( + instance_path + ) + + if benchmark_packages: + console.print( + f" Installing benchmark packages in Ray environment: {', '.join(benchmark_packages)}" + ) + # Create base remote config with benchmark packages + base_remote_config = self._create_or_update_remote_config( + benchmark_packages, self.remote_context_file, self.repo_root + ) + temp_remote_configs.append(base_remote_config) + + # Load base config for space creation using RemoteExecutionContext + base_config_dict = ( + yaml.safe_load(base_remote_config.read_text()) or {} + ) + base_remote_context = RemoteExecutionContext.model_validate( + base_config_dict + ) + + # Use base config for operation (without wait: true override) + remote_config_for_operation = base_remote_config + else: + # No benchmark packages, use original config + base_config_dict = ( + yaml.safe_load(self.remote_context_file.read_text()) or {} + ) + base_remote_context = RemoteExecutionContext.model_validate( + base_config_dict + ) + + # Use original config for operation + remote_config_for_operation = self.remote_context_file + + # Create temporary config for space creation with wait: true + # Create a new RemoteExecutionContext with wait=True + space_remote_context = RemoteExecutionContext( + executionType=base_remote_context.executionType, + packages=base_remote_context.packages, + wait=True, + envVars=base_remote_context.envVars, + additionalFiles=base_remote_context.additionalFiles, + ) + + with tempfile.NamedTemporaryFile( + mode="w", + suffix=".yaml", + delete=False, + prefix="remote_config_space_", + ) as tmp_file: + remote_config_for_space = Path(tmp_file.name) + remote_config_for_space.write_text( + pydantic_model_as_yaml(space_remote_context) + ) + temp_remote_configs.append(remote_config_for_space) + + console.print(f" Creating ADO discoveryspace for: {instance_path}") + space_id = self._create_discoveryspace( + space_yaml_path, instance_path, remote_config_for_space + ) + result.space_id = space_id + console.print(f" [green]✓[/green] Successfully created space: {space_id}") + + console.print(f" Creating operation for space: {space_id}") + operation_result = self._create_operation( + space_id, instance_path, remote_config_for_operation + ) + result.operation_id = operation_result["operation_id"] + result.ray_job_id = operation_result.get("ray_job_id") + + # Set status to "started" if Ray job was successfully started, otherwise "success" + if result.ray_job_id: + result.status = "started" + message_parts = [ + f"Successfully started on Ray cluster with job ID: {result.ray_job_id}" + ] + if result.operation_id: + message_parts.append(f"Operation ID: {result.operation_id}") + message_parts.append(f"Space ID: {space_id}") + else: + result.status = "success" + message_parts = [ + f"Successfully created space {space_id} and operation {operation_result['operation_id']}" + ] + result.message = strip_ansi_codes(" | ".join(message_parts)) + + console.print( + f" [green]✓[/green] Successfully created operation: {operation_result['operation_id'] or 'pending'}" + ) + if result.ray_job_id: + console.print(f" [green]✓[/green] Ray job ID: {result.ray_job_id}") + + except FileNotFoundError as e: + result.status = "failed" + result.message = f"File not found: {e}" + console_err.print(f" [red]✗[/red] Failed: {result.message}") + + except subprocess.CalledProcessError as e: + result.status = "failed" + result.message = f"ADO CLI error: {strip_ansi_codes(e.stderr or str(e))}" + console_err.print(f" [red]✗[/red] Failed: {result.message}") + + except Exception as e: + result.status = "failed" + result.message = f"Execution error: {strip_ansi_codes(str(e))}" + console_err.print(f" [red]✗[/red] Failed: {result.message}") + + finally: + # Clean up temporary remote config files + for temp_config in temp_remote_configs: + temp_config.unlink(missing_ok=True) + + return result + + def _create_discoveryspace( + self, + space_yaml_path: Path, + instance_path: Path, + remote_config: Path | None = None, + ) -> str: + """Create a discoveryspace using ado CLI. + + Args: + space_yaml_path: Path to space.yaml file + instance_path: Path to benchmark instance directory + remote_config: Optional path to remote configuration file (with wait: true for space creation) + + Returns: + Created space identifier + """ + + # Load the space configuration from YAML + space_config_dict = yaml.safe_load(space_yaml_path.read_text()) + + # Parse the configuration using DiscoverySpaceConfiguration + space_config = DiscoverySpaceConfiguration.model_validate(space_config_dict) + + # Generate descriptive name and description from instance path + # Extract PR number from URL + pr_number = self.pr_url.rstrip("/").split("/")[-1] + + # Parse instance path to get package, model, and instance names + package_name, model_name, instance_name = self._parse_instance_path( + instance_path + ) + + # Create descriptive name: space-pr123-package-model-instance + space_name = f"space-pr{pr_number}-{package_name}-{model_name}-{instance_name}" + space_description = f"Discovery space for benchmark instance from PR #{pr_number}: {package_name}/{model_name}/{instance_name}" + + # Build custom labels with algorithm-nexus fields + labels = space_config.metadata.labels or {} + labels["algorithm-nexus.pr_url"] = self.pr_url + labels["algorithm-nexus.instance_path"] = str(instance_path) + + # Update metadata with descriptive name, description, and labels + space_config.metadata = ConfigurationMetadata( + name=space_name, + description=space_description, + labels=labels, + ) + + with tempfile.NamedTemporaryFile( + mode="w", suffix=".yaml", delete=True + ) as tmp_file: + temp_space_path = tmp_file.name + Path(temp_space_path).write_text(pydantic_model_as_yaml(space_config)) + + cmd = ["ado"] + + # Add context file if provided + if self.context_file: + cmd.extend(["--context", str(self.context_file)]) + + # Add remote flag if in remote mode + if remote_config: + cmd.extend(["--remote", str(remote_config)]) + + cmd.extend(["create", "discoveryspace", "-f", temp_space_path]) + + result = subprocess.run( # noqa: S603 + cmd, + capture_output=True, + text=True, + check=False, + ) + + if result.returncode != 0: + error_msg = result.stderr or result.stdout + raise subprocess.CalledProcessError( + result.returncode, cmd, output=result.stdout, stderr=error_msg + ) + + combined_output = result.stdout + "\n" + result.stderr + match = re.search(r"identifier[:\s]+(\S+)", combined_output) + if match: + return strip_ansi_codes(match.group(1)) + else: + output_words = result.stdout.strip().split() + if output_words: + return strip_ansi_codes(output_words[-1]) + else: + raise ValueError( + f"Could not extract space identifier from ADO output.\n" + f"Return code: {result.returncode}\n" + f"Stdout: {result.stdout[:300]}\n" + f"Stderr: {result.stderr[:300]}" + ) + + def _create_or_update_remote_config( + self, + benchmark_packages: set[str], + base_config_path: Path | None = None, + repo_root: Path | None = None, + ) -> Path: + """Create or update a remote configuration file with benchmark package dependencies. + + Args: + benchmark_packages: Set of requirement specifiers for benchmark packages + base_config_path: Optional base remote config to extend + repo_root: Repository root to resolve relative paths (defaults to self.repo_root) + + Returns: + Path to the created/updated remote config file + """ + if repo_root is None: + repo_root = self.repo_root + + # Load existing remote config (base_config_path existence is validated by CLI) + if not base_config_path: + raise ValueError( + "base_config_path must be provided with a valid RemoteExecutionContext configuration" + ) + + remote_config_dict = yaml.safe_load(base_config_path.read_text()) or {} + remote_config = RemoteExecutionContext.model_validate(remote_config_dict) + + # Get existing packages + existing_pypi_packages = set(remote_config.packages.fromPyPI) + existing_source_packages = set(remote_config.packages.fromSource) + + # Process benchmark packages + new_pypi_packages = list(existing_pypi_packages) + new_source_packages = list(existing_source_packages) + + for pkg in benchmark_packages: + # Check if package is a GitHub URL + if pkg.startswith( + ("https://github.com/", "http://github.com/", "git@github.com:") + ): + # Add git+ prefix for GitHub URLs if not already present + git_url = pkg if pkg.startswith("git+") else f"git+{pkg}" + if git_url not in existing_pypi_packages: + new_pypi_packages.append(git_url) + else: + # Check if package is a local path (relative or absolute) + pkg_path = Path(pkg) + + # Try to resolve relative to repo_root first + if not pkg_path.is_absolute(): + pkg_path = repo_root / pkg_path + + # Check if the resolved path exists and is a valid package source + if pkg_path.exists() and ( + pkg_path.is_dir() or pkg_path.suffix in [".whl", ".tar.gz", ".zip"] + ): + # Resolve to absolute path for local packages + resolved_path = str(pkg_path.resolve()) + if resolved_path not in existing_source_packages: + new_source_packages.append(resolved_path) + else: + # Treat as PyPI package + if pkg not in existing_pypi_packages: + new_pypi_packages.append(pkg) + + # Update packages configuration + remote_config.packages = PackageConfiguration( + fromPyPI=new_pypi_packages, + fromSource=new_source_packages, + ) + + with tempfile.NamedTemporaryFile( + mode="w", suffix=".yaml", delete=False, prefix="remote_config_" + ) as tmp_file: + temp_path = Path(tmp_file.name) + temp_path.write_text(pydantic_model_as_yaml(remote_config)) + return temp_path + + def _create_operation( + self, + space_id: str, + instance_path: Path | None = None, + remote_config: Path | None = None, + ) -> dict[str, str | None]: + """Create and execute an operation using ado CLI. + + Args: + space_id: Discovery space identifier + instance_path: Optional path to benchmark instance (for package resolution) + remote_config: Optional path to remote configuration file + + Returns: + Dictionary with operation_id and ray_job_id (if remote execution) + """ + with tempfile.NamedTemporaryFile( + mode="w", suffix=".yaml", delete=True + ) as tmp_file: + # Generate descriptive name and description from instance path + if instance_path: + # Extract PR number from URL + pr_number = self.pr_url.rstrip("/").split("/")[-1] + + # Parse instance path to get package, model, and instance names + package_name, model_name, instance_name = self._parse_instance_path( + instance_path + ) + + # Create descriptive name: randomwalk-pr123-package-model-instance + operation_name = f"randomwalk-pr{pr_number}-{package_name}-{model_name}-{instance_name}" + operation_description = f"Random walk for benchmark instance from PR #{pr_number}: {package_name}/{model_name}/{instance_name}" + else: + operation_name = "randomwalk-all" + operation_description = "Perform a random walk on all points in a space" + + # Create custom metadata with algorithm-nexus fields + custom_metadata = { + "algorithm-nexus.pr_url": self.pr_url, + "algorithm-nexus.instance_path": str(instance_path) + if instance_path + else "", + } + + # Create operation config using the factory function + operation_config = create_random_walk_operation_config( + space_id=space_id, + metadata_name=operation_name, + metadata_description=operation_description, + custom_metadata=custom_metadata, + ) + + operation_config_path = tmp_file.name + Path(operation_config_path).write_text( + pydantic_model_as_yaml(operation_config) + ) + + cmd = ["ado"] + + # Add context file if provided + if self.context_file: + cmd.extend(["--context", str(self.context_file)]) + + # The remote config goes right after the ado command (and context if present) + if remote_config: + cmd.extend(["--remote", str(remote_config)]) + + cmd.extend( + [ + "create", + "operation", + "-f", + operation_config_path, + ] + ) + + result = subprocess.run( # noqa: S603 + cmd, + capture_output=True, + text=True, + check=True, + ) + + # Extract Ray job ID and operation identifier + # For remote execution: Ray job ID (raysubmit_*) is available immediately + # Operation ID is only available after execution completes + + combined_output = result.stdout + "\n" + result.stderr + + # Extract Ray job ID (starts with raysubmit_) for remote execution + ray_job_id = None + if remote_config: + ray_match = re.search(r"(raysubmit_\w+)", combined_output) + if ray_match: + ray_job_id = strip_ansi_codes(ray_match.group(1)) + + # For local execution or completed remote execution, extract operation ID + # Operation ID is available after "identifier" keyword + operation_id = None + op_match = re.search(r"identifier\s+(\S+)", combined_output) + if op_match: + candidate = strip_ansi_codes(op_match.group(1)) + # Only use as operation_id if it's not a raysubmit ID + if not candidate.startswith("raysubmit_"): + operation_id = candidate + + # Fallback for local execution: use last word if no operation_id found + if not operation_id and not ray_job_id: + operation_id = strip_ansi_codes(result.stdout.strip().split()[-1]) + + return { + "operation_id": operation_id, + "ray_job_id": ray_job_id, + } + + def run(self) -> dict[str, Any]: + """Main execution method. + + Returns: + Dictionary with execution results + """ + try: + console.print("Analyzing PR for new or changed benchmark instances...") + console.print(f"PR URL: {self.pr_url}") + + # Always check if we need to checkout PR code at the beginning + if not self.is_local_repo_on_pr_commit(): + console.print("[yellow]Local repository is not on PR commit[/yellow]") + console.print("Checking out PR code to temporary directory...") + self.checkout_pr_to_temp() + else: + console.print( + "[green]✓[/green] Using local repository (already on PR commit)" + ) + + changed_files = self.get_changed_files() + + benchmark_instances = self.find_benchmark_instances(changed_files) + + if not benchmark_instances: + console.print( + "[yellow]No new or changed benchmark instances found in this PR.[/yellow]" + ) + return {"instances": []} + + console.print(f"Found {len(benchmark_instances)} benchmark instance(s):") + + results: dict[str, Any] = { + "instances": [], + } + + if self.execute: + console.print("\nExecuting benchmarks with ADO CLI...") + successful = 0 + failed = 0 + + for instance_path in benchmark_instances: + console.print(f"\nProcessing: {instance_path}") + exec_result = self.execute_benchmark(instance_path) + results["instances"].append(exec_result.model_dump()) + + if exec_result.status == "success": + successful += 1 + else: + failed += 1 + + console.print("\n" + "=" * 60) + console.print("Execution Summary:") + console.print(f" Total: {len(benchmark_instances)}") + console.print(f" Successful: {successful}") + console.print(f" Failed: {failed}") + console.print("=" * 60) + else: + for instance_path in benchmark_instances: + console.print(f" {instance_path}") + results["instances"].append({"instance_path": str(instance_path)}) + + return results + finally: + self.cleanup_temp_dir() + + +def _format_results(results: dict[str, Any], fmt: str) -> str: + """Format benchmark results as JSON or YAML string.""" + if fmt == "json": + return json.dumps(results, indent=2) + else: # yaml + return yaml.safe_dump(results, default_flow_style=False, sort_keys=False) + + +def _write_results_to_file( + results: dict[str, Any], output_file: Path, fmt: str +) -> None: + """Write benchmark results to a file in the specified format.""" + output_file.write_text(_format_results(results, fmt)) + console.print(f"\nResults written to: {output_file}") + + +def _print_structured_results(results: dict[str, Any], fmt: str) -> None: + """Print benchmark results in structured format (JSON or YAML).""" + console.print() # Blank line before output + console.print(_format_results(results, fmt)) + + +def _get_status_display(status: str) -> str: + """Get color-coded status display string.""" + status_colors = { + "success": "green", + "started": "cyan", + "failed": "red", + } + color = status_colors.get(status, "yellow") + return f"[{color}]{status}[/{color}]" + + +def _print_human_readable_results(results: dict[str, Any]) -> None: + """Print benchmark results in human-readable format.""" + console.print("\n[bold]Benchmark Execution Results[/bold]\n") + + if not results.get("instances"): + console.print("[yellow]No benchmark instances found[/yellow]") + return + + for instance in results["instances"]: + instance_path = instance.get("instance_path", "Unknown") + status = instance.get("status", "unknown") + message = instance.get("message", "") + + console.print(f"[bold]{instance_path}[/bold]") + console.print(f" Status: {_get_status_display(status)}") + + if message: + console.print(f" Message: {message}") + + if instance.get("space_id"): + console.print(f" Space ID: {instance['space_id']}") + if instance.get("operation_id"): + console.print(f" Operation ID: {instance['operation_id']}") + if instance.get("ray_job_id"): + console.print(f" Ray Job ID: {instance['ray_job_id']}") + + console.print() # Empty line between instances + + +def run_benchmarks( + pr: Annotated[ + str, + typer.Option( + "--pr", + help="GitHub Pull Request URL (e.g., https://github.com/IBM/algorithm-nexus/pull/123)", + ), + ], + remote: Annotated[ + Path | None, + typer.Option( + "--remote", + help="Execute operations on remote Ray cluster using the specified context file", + exists=True, + file_okay=True, + dir_okay=False, + readable=True, + ), + ] = None, + context: Annotated[ + Path | None, + typer.Option( + "--context", + help="Path to ADO context YAML file (samplestore context)", + exists=True, + file_okay=True, + dir_okay=False, + readable=True, + ), + ] = None, + dry_run: Annotated[ + bool, + typer.Option( + "--dry-run", + help="List benchmarks without executing them (dry run)", + ), + ] = False, + output_file: Annotated[ + Path | None, + typer.Option( + "--output-file", + help="Output file path for execution results. If not specified, results are printed to screen.", + dir_okay=False, + writable=True, + ), + ] = None, + output_format: Annotated[ + str | None, + typer.Option( + "-o", + "--output-format", + help="Output format: 'json' or 'yaml'. Only used with --output-file.", + ), + ] = None, +) -> None: + """Execute benchmarks from a GitHub Pull Request. + + Identifies new or changed benchmark instances in a PR and optionally + executes them using the ADO CLI. When executing with --remote flag, + automatically installs required benchmark packages in the Ray environment. + + The command automatically checks if the local repository is on the same + commit as the PR. If not, it will checkout the PR code in a temporary + directory. + """ + from algorithm_nexus.commands.utils import validate_output_format + + # Validate output format if specified + if output_format: + validate_output_format(output_format, allow_yaml=True, allow_csv=False) + + try: + manager = BenchmarkManager( + pr_url=pr, + execute=not dry_run, + remote_context_file=remote, + context_file=context, + ) + results = manager.run() + + # Output results + # Determine output format + if output_format: + fmt = output_format + elif output_file and ( + output_file.suffix == ".yaml" or output_file.suffix == ".yml" + ): + fmt = "yaml" + elif output_file: + fmt = "json" + else: + fmt = None # Human-readable format for console + + # Output results + if output_file: + # fmt is guaranteed to be a string when output_file is provided + # (either from output_format, file extension, or defaulted to "json") + if fmt is None: + fmt = "json" # Default fallback (should never happen based on logic above) + _write_results_to_file(results, output_file, fmt) + elif fmt in ("json", "yaml"): + _print_structured_results(results, fmt) + else: + _print_human_readable_results(results) + + if not dry_run and results.get("summary") and results["summary"]["failed"] > 0: + raise typer.Exit(code=1) + + except KeyboardInterrupt: + console_err.print("\n[yellow]Interrupted by user[/yellow]") + raise typer.Exit(code=130) + except Exception as e: + console_err.print(f"[red]Error:[/red] {e}") + raise typer.Exit(code=1) + + +# Made with Bob diff --git a/src/algorithm_nexus/commands/utils.py b/src/algorithm_nexus/commands/utils.py index 33e6f50..cc27048 100644 --- a/src/algorithm_nexus/commands/utils.py +++ b/src/algorithm_nexus/commands/utils.py @@ -8,6 +8,7 @@ import csv import io import json +import re import sys from pathlib import Path from typing import Any @@ -31,6 +32,19 @@ console = Console() +def strip_ansi_codes(text: str) -> str: + """Remove ANSI escape sequences from text. + + Args: + text: Text potentially containing ANSI codes + + Returns: + Text with ANSI codes removed + """ + ansi_escape = re.compile(r"\x1b\[[0-9;]*m") + return ansi_escape.sub("", text) + + class ValidationErrorCollector: """Collects validation errors and info messages during package validation.""" @@ -108,19 +122,27 @@ def load_yaml_file( def validate_output_format( output_format: str | None, allow_txt: bool = False, + allow_yaml: bool = False, + allow_csv: bool = True, ) -> None: """Validate the output format parameter. Args: output_format: The output format to validate allow_txt: Whether to allow 'txt' format (for requirements files) + allow_yaml: Whether to allow 'yaml' format + allow_csv: Whether to allow 'csv' format (default: True) Raises: typer.Exit: If the format is invalid """ - valid_formats = ["csv", "json"] + valid_formats = ["json"] + if allow_csv: + valid_formats.append("csv") if allow_txt: valid_formats.append("txt") + if allow_yaml: + valid_formats.append("yaml") if output_format is not None and output_format not in valid_formats: formats_str = "', '".join(valid_formats) @@ -159,7 +181,7 @@ def output_data( Args: data: List of dictionaries containing the data rows headers: List of column headers - output_format: Output format ('csv', 'json', or None for table) + output_format: Output format ('csv', 'json', 'yaml', or None for table) output_file: Optional file path to write output to table_title: Title for the table output """ @@ -170,6 +192,13 @@ def output_data( console.print(f"[green]Output written to {output_file}[/green]") else: console.print(json_output) + elif output_format == "yaml": + yaml_output = yaml.dump(data, default_flow_style=False, sort_keys=False) + if output_file: + output_file.write_text(yaml_output) + console.print(f"[green]Output written to {output_file}[/green]") + else: + console.print(yaml_output) elif output_format == "csv": # Write CSV to string first csv_buffer = io.StringIO() diff --git a/src/algorithm_nexus/models.py b/src/algorithm_nexus/models.py index d70efac..7b06a97 100644 --- a/src/algorithm_nexus/models.py +++ b/src/algorithm_nexus/models.py @@ -187,3 +187,25 @@ class AlgorithmNexusPackageConfig(BaseModel): package: Annotated[ NexusPackageInfo, Field(description="Package-level configuration") ] + + +class BenchmarkExecutionResult(BaseModel): + """Result of executing a benchmark instance.""" + + instance_path: Annotated[str, Field(description="Path to the benchmark instance")] + status: Annotated[ + Literal["success", "failed", "started", "unknown"], + Field(description="Execution status"), + ] = "unknown" + message: Annotated[ + str, Field(description="Status message or error description") + ] = "" + space_id: Annotated[str | None, Field(description="Created discovery space ID")] = ( + None + ) + operation_id: Annotated[str | None, Field(description="Created operation ID")] = ( + None + ) + ray_job_id: Annotated[ + str | None, Field(description="Ray job ID for remote execution") + ] = None diff --git a/tests/fixtures/packages/terratorch/benchmark_instances/base-test/space.yaml b/tests/fixtures/packages/terratorch/benchmark_instances/base-test/space.yaml new file mode 100644 index 0000000..eaa6d56 --- /dev/null +++ b/tests/fixtures/packages/terratorch/benchmark_instances/base-test/space.yaml @@ -0,0 +1,13 @@ +entitySpace: + - identifier: dataset + propertyDomain: + values: ["generic_dataset"] + - identifier: split + propertyDomain: + values: ["test"] + +experiments: + - actuatorIdentifier: custom_experiments + experimentIdentifier: local-segmentation-eval + +# Made with Bob diff --git a/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/fire-test/space.yaml b/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/fire-test/space.yaml new file mode 100644 index 0000000..ef93fa7 --- /dev/null +++ b/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/fire-test/space.yaml @@ -0,0 +1,13 @@ +entitySpace: + - identifier: dataset + propertyDomain: + values: ["fire_detection"] + - identifier: split + propertyDomain: + values: ["test"] + +experiments: + - actuatorIdentifier: custom_experiments + experimentIdentifier: local-segmentation-eval + +# Made with Bob diff --git a/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/config.json b/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/config.json new file mode 100644 index 0000000..11a1832 --- /dev/null +++ b/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/config.json @@ -0,0 +1,5 @@ +{ + "model": "prithvi", + "dataset": "sen1floods11", + "batch_size": 32 +} diff --git a/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/space.yaml b/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/space.yaml new file mode 100644 index 0000000..25e4cbc --- /dev/null +++ b/tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/space.yaml @@ -0,0 +1,13 @@ +entitySpace: + - identifier: dataset + propertyDomain: + values: ["sen1floods11"] + - identifier: split + propertyDomain: + values: ["test"] + +experiments: + - actuatorIdentifier: custom_experiments + experimentIdentifier: local-segmentation-eval + +# Made with Bob diff --git a/tests/fixtures/packages/terratorch/models/prithvi/model.yaml b/tests/fixtures/packages/terratorch/models/prithvi/model.yaml new file mode 100644 index 0000000..09873e1 --- /dev/null +++ b/tests/fixtures/packages/terratorch/models/prithvi/model.yaml @@ -0,0 +1,7 @@ +name: prithvi +version: 1.0.0 +description: Test fixture for prithvi model +experiments: + - local-segmentation-eval + +# Made with Bob diff --git a/tests/fixtures/packages/terratorch/nexus.yaml b/tests/fixtures/packages/terratorch/nexus.yaml new file mode 100644 index 0000000..83e33a6 --- /dev/null +++ b/tests/fixtures/packages/terratorch/nexus.yaml @@ -0,0 +1,7 @@ +name: terratorch +version: 1.0.0 +description: Test fixture for terratorch package +models: + - prithvi + +# Made with Bob diff --git a/tests/fixtures/packages/tokamind/benchmark_instances/base-test/space.yaml b/tests/fixtures/packages/tokamind/benchmark_instances/base-test/space.yaml new file mode 100644 index 0000000..a224b91 --- /dev/null +++ b/tests/fixtures/packages/tokamind/benchmark_instances/base-test/space.yaml @@ -0,0 +1,13 @@ +entitySpace: + - identifier: dataset + propertyDomain: + values: ["tokamak_data"] + - identifier: split + propertyDomain: + values: ["test"] + +experiments: + - actuatorIdentifier: custom_experiments + experimentIdentifier: local-baseline-eval + +# Made with Bob diff --git a/tests/fixtures/packages/tokamind/nexus.yaml b/tests/fixtures/packages/tokamind/nexus.yaml new file mode 100644 index 0000000..97fc05f --- /dev/null +++ b/tests/fixtures/packages/tokamind/nexus.yaml @@ -0,0 +1,7 @@ +name: tokamind +version: 1.0.0 +description: Test fixture for tokamind package +models: + - tokamind-base + +# Made with Bob diff --git a/tests/test_cli_run.py b/tests/test_cli_run.py new file mode 100644 index 0000000..83aa5ee --- /dev/null +++ b/tests/test_cli_run.py @@ -0,0 +1,308 @@ +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 + +"""Unit tests for run command helper functions.""" + +from pathlib import Path + +import pytest +from orchestrator.core.operation.config import ( + DiscoveryOperationEnum, + DiscoveryOperationResourceConfiguration, +) + +from algorithm_nexus.commands.run import ( + BenchmarkManager, + create_random_walk_operation_config, +) +from algorithm_nexus.models import BenchmarkExecutionResult + + +class TestParseInstancePath: + """Tests for _parse_instance_path method.""" + + def test_parse_model_level_instance(self) -> None: + """Test parsing model-level benchmark instance path.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + package, model, instance = manager._parse_instance_path( + Path("packages/terratorch/models/prithvi/benchmark_instances/flood-test") + ) + + assert package == "terratorch" + assert model == "prithvi" + assert instance == "flood-test" + + def test_parse_package_level_instance(self) -> None: + """Test parsing package-level benchmark instance path.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + package, model, instance = manager._parse_instance_path( + Path("packages/terratorch/benchmark_instances/base-test") + ) + + assert package == "terratorch" + assert model == "base" + assert instance == "base-test" + + def test_parse_invalid_path_too_short(self) -> None: + """Test parsing fails for path that's too short.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + with pytest.raises( + ValueError, + match="Invalid benchmark instance path format", + ): + manager._parse_instance_path(Path("packages")) + + def test_parse_invalid_model_level_path(self) -> None: + """Test parsing fails for incomplete model-level path.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + with pytest.raises( + ValueError, + match="Invalid benchmark instance path format", + ): + manager._parse_instance_path( + Path("packages/terratorch/models/prithvi/benchmark_instances") + ) + + def test_parse_invalid_package_level_path(self) -> None: + """Test parsing fails for incomplete package-level path.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + with pytest.raises( + ValueError, + match="Invalid benchmark instance path format", + ): + manager._parse_instance_path( + Path("packages/terratorch/benchmark_instances") + ) + + +class TestFindBenchmarkInstances: + """Tests for find_benchmark_instances method.""" + + def test_find_model_level_instances(self) -> None: + """Test finding model-level benchmark instances from changed files.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + changed_files = [ + "tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/space.yaml", + "tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/config.json", + "tests/fixtures/packages/terratorch/models/prithvi/model.yaml", + ] + + instances = manager.find_benchmark_instances(changed_files) + + assert len(instances) == 1 + assert instances[0] == Path( + "tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test" + ) + + def test_find_package_level_instances(self) -> None: + """Test finding package-level benchmark instances from changed files.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + changed_files = [ + "tests/fixtures/packages/terratorch/benchmark_instances/base-test/space.yaml", + "tests/fixtures/packages/terratorch/nexus.yaml", + ] + + instances = manager.find_benchmark_instances(changed_files) + + assert len(instances) == 1 + assert instances[0] == Path( + "tests/fixtures/packages/terratorch/benchmark_instances/base-test" + ) + + def test_find_multiple_instances(self) -> None: + """Test finding multiple benchmark instances.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + changed_files = [ + "tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test/space.yaml", + "tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/fire-test/space.yaml", + "tests/fixtures/packages/tokamind/benchmark_instances/base-test/space.yaml", + ] + + instances = manager.find_benchmark_instances(changed_files) + + assert len(instances) == 3 + assert ( + Path( + "tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/flood-test" + ) + in instances + ) + assert ( + Path( + "tests/fixtures/packages/terratorch/models/prithvi/benchmark_instances/fire-test" + ) + in instances + ) + assert ( + Path("tests/fixtures/packages/tokamind/benchmark_instances/base-test") + in instances + ) + + def test_find_no_instances(self) -> None: + """Test when no benchmark instances are found.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + changed_files = [ + "packages/terratorch/models/prithvi/model.yaml", + "packages/terratorch/nexus.yaml", + "README.md", + ] + + instances = manager.find_benchmark_instances(changed_files) + + assert len(instances) == 0 + + def test_find_instances_deduplicates(self) -> None: + """Test that duplicate instances are deduplicated.""" + manager = BenchmarkManager( + pr_url="https://github.com/test/repo/pull/123", execute=False + ) + + changed_files = [ + "packages/terratorch/models/prithvi/benchmark_instances/flood-test/space.yaml", + "packages/terratorch/models/prithvi/benchmark_instances/flood-test/config.json", + "packages/terratorch/models/prithvi/benchmark_instances/flood-test/data.csv", + ] + + instances = manager.find_benchmark_instances(changed_files) + + assert len(instances) == 1 + + +class TestCreateRandomWalkOperationConfig: + """Tests for create_random_walk_operation_config function.""" + + def test_create_basic_config(self) -> None: + """Test creating basic operation config.""" + config: DiscoveryOperationResourceConfiguration = ( + create_random_walk_operation_config(space_id="test-space-123") + ) + + assert config.spaces == ["test-space-123"] + assert config.metadata.name == "randomwalk-all" + assert ( + config.metadata.description + == "Perform a random walk on all points in a space" + ) + assert config.operation.module.operatorName == "random_walk" + assert config.operation.module.operationType == DiscoveryOperationEnum.SEARCH + assert config.operation.parameters.numberEntities == "all" + assert config.operation.parameters.singleMeasurement is True + + def test_create_config_with_custom_metadata(self) -> None: + """Test creating config with custom metadata.""" + custom_meta = { + "algorithm-nexus.pr_url": "https://github.com/test/repo/pull/123", + "algorithm-nexus.instance_path": "packages/test/benchmark_instances/test", + } + + config: DiscoveryOperationResourceConfiguration = ( + create_random_walk_operation_config( + space_id="test-space-123", + metadata_name="custom-walk", + metadata_description="Custom walk description", + custom_metadata=custom_meta, + ) + ) + + assert config.metadata.name == "custom-walk" + assert config.metadata.description == "Custom walk description" + # Custom metadata is stored in the labels field + assert config.metadata.labels is not None + assert ( + config.metadata.labels["algorithm-nexus.pr_url"] + == "https://github.com/test/repo/pull/123" + ) + assert ( + config.metadata.labels["algorithm-nexus.instance_path"] + == "packages/test/benchmark_instances/test" + ) + + +class TestBenchmarkExecutionResult: + """Tests for BenchmarkExecutionResult model.""" + + def test_create_with_defaults(self) -> None: + """Test creating result with default values.""" + result = BenchmarkExecutionResult( + instance_path="packages/test/benchmark_instances/test" + ) + + assert result.instance_path == "packages/test/benchmark_instances/test" + assert result.status == "unknown" + assert result.message == "" + assert result.space_id is None + assert result.operation_id is None + assert result.ray_job_id is None + + def test_create_with_all_fields(self) -> None: + """Test creating result with all fields.""" + result = BenchmarkExecutionResult( + instance_path="packages/test/benchmark_instances/test", + status="success", + message="Successfully executed", + space_id="space-123", + operation_id="op-456", + ray_job_id="raysubmit_789", + ) + + assert result.status == "success" + assert result.message == "Successfully executed" + assert result.space_id == "space-123" + assert result.operation_id == "op-456" + assert result.ray_job_id == "raysubmit_789" + + def test_status_literal_validation(self) -> None: + """Test that status field only accepts valid literals.""" + # Valid statuses + for status in ["success", "failed", "started", "unknown"]: + result = BenchmarkExecutionResult( + instance_path="test", + status=status, # type: ignore[arg-type] + ) + assert result.status == status + + def test_model_dump(self) -> None: + """Test converting model to dictionary.""" + result = BenchmarkExecutionResult( + instance_path="packages/test/benchmark_instances/test", + status="success", + space_id="space-123", + ) + + data = result.model_dump() + + assert isinstance(data, dict) + assert data["instance_path"] == "packages/test/benchmark_instances/test" + assert data["status"] == "success" + assert data["space_id"] == "space-123" + assert data["operation_id"] is None + + +# Made with Bob diff --git a/uv.lock b/uv.lock index fa66eec..8fdc8d0 100644 --- a/uv.lock +++ b/uv.lock @@ -8,14 +8,20 @@ resolution-markers = [ "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", - "extra != 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version >= '3.13' and extra != 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version < '3.13' and extra != 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", "python_full_version >= '3.13' and sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", "python_full_version >= '3.13' and sys_platform == 'emscripten' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", "python_full_version < '3.13' and sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", "python_full_version < '3.13' and sys_platform == 'emscripten' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", - "extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version >= '3.13' and sys_platform == 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version >= '3.13' and sys_platform == 'emscripten' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", ] conflicts = [[ { package = "algorithm-nexus", extra = "candidate" }, @@ -50,6 +56,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/46/02ac5e262d4af18054b3e922b2baedbb2a03289ee792162de60a865defc5/accelerate-1.13.0-py3-none-any.whl", hash = "sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0", size = 383744, upload-time = "2026-03-04T19:34:10.313Z" }, ] +[[package]] +name = "ado-core" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "build" }, + { name = "colorlog" }, + { name = "jsonpath-ng" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-15-algorithm-nexus-candidate' or extra == 'extra-15-algorithm-nexus-product'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-product')" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product')" }, + { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-15-algorithm-nexus-candidate' or extra != 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, + { name = "pydantic" }, + { name = "pymysql", extra = ["rsa"] }, + { name = "pyyaml" }, + { name = "ray", extra = ["serve"] }, + { name = "sqlalchemy" }, + { name = "typer" }, + { name = "uv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/5a/94fc06c295a606e0bedaf44d134413ff4962b40f00a874a8a69c91c12012/ado_core-1.8.0.tar.gz", hash = "sha256:3a730c324f74520adb1eb02e2a7ec9081f9dc3ecd16821c7b21bac7b5c550a9d", size = 18872858, upload-time = "2026-04-27T12:41:34.448Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/4d/4fa7ff16ec80ab0d6a497971be2d0b97a89c07bb7e6201b06bc77e76c1e0/ado_core-1.8.0-py3-none-any.whl", hash = "sha256:ff8b0cf90c855fe9a9690f6d2318e39459f9b9fbf2538aed729d3ab232f64a25", size = 414328, upload-time = "2026-04-27T12:41:31.849Z" }, +] + [[package]] name = "aenum" version = "3.1.17" @@ -106,7 +137,7 @@ dependencies = [ { name = "frozenlist" }, { name = "multidict" }, { name = "propcache" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, { name = "yarl" }, ] sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" } @@ -154,6 +185,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/0a/62e7232dc9484fbec112ceb32efb6a624cc7994ec6e2b019286f17c4e8f2/aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8", size = 447723, upload-time = "2026-06-07T21:08:00.154Z" }, ] +[[package]] +name = "aiohttp-cors" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/d89e846a5444b3d5eb8985a6ddb0daef3774928e1bfbce8e84ec97b0ffa7/aiohttp_cors-0.8.1.tar.gz", hash = "sha256:ccacf9cb84b64939ea15f859a146af1f662a6b1d68175754a07315e305fb1403", size = 38626, upload-time = "2025-03-31T14:16:20.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/3b/40a68de458904bcc143622015fff2352b6461cd92fd66d3527bf1c6f5716/aiohttp_cors-0.8.1-py3-none-any.whl", hash = "sha256:3180cf304c5c712d626b9162b195b1db7ddf976a2a25172b35bb2448b890a80d", size = 25231, upload-time = "2025-03-31T14:16:18.478Z" }, +] + [[package]] name = "aioitertools" version = "0.13.0" @@ -169,7 +212,7 @@ version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ @@ -221,6 +264,7 @@ candidate = [ { name = "vllm", version = "0.19.1", source = { registry = "https://pypi.org/simple" } }, ] cli = [ + { name = "ado-core" }, { name = "pydantic" }, { name = "pyyaml" }, { name = "rich" }, @@ -251,6 +295,7 @@ test = [ [package.metadata] requires-dist = [ + { name = "ado-core", marker = "extra == 'cli'", specifier = ">=1.8.0" }, { name = "bmfm-targets", marker = "extra == 'ecosystem'", git = "https://github.com/BiomedSciAI/biomed-multi-omic.git" }, { name = "pydantic", marker = "extra == 'cli'", specifier = ">=2.13.3" }, { name = "pyyaml", marker = "extra == 'cli'", specifier = ">=6.0.3" }, @@ -346,7 +391,7 @@ version = "4.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } wheels = [ @@ -571,6 +616,20 @@ jwt = [ { name = "pyjwt", version = "2.13.0", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-15-algorithm-nexus-candidate' or extra != 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, ] +[[package]] +name = "build" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "os_name == 'nt' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, + { name = "packaging" }, + { name = "pyproject-hooks" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/e0/df5e171f685f82f37b12e1f208064e24244911079d7b767447d1af7e0d70/build-1.5.0.tar.gz", hash = "sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647", size = 89796, upload-time = "2026-04-30T03:18:25.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl", hash = "sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f", size = 26018, upload-time = "2026-04-30T03:18:23.644Z" }, +] + [[package]] name = "cachetools" version = "7.1.4" @@ -862,6 +921,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "colorful" +version = "0.5.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/31/109ef4bedeb32b4202e02ddb133162457adc4eb890a9ed9c05c9dd126ed0/colorful-0.5.8.tar.gz", hash = "sha256:bb16502b198be2f1c42ba3c52c703d5f651d826076817185f0294c1a549a7445", size = 209361, upload-time = "2025-10-29T11:53:21.663Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/11/25cdf9d5fc21efd30134fc74c43702c6f7ef09ebae8ed927f1283403ad8d/colorful-0.5.8-py2.py3-none-any.whl", hash = "sha256:a9381fdda3337fbaba5771991020abc69676afa102646650b759927892875992", size = 201334, upload-time = "2025-10-29T11:53:20.251Z" }, +] + +[[package]] +name = "colorlog" +version = "6.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/61/f083b5ac52e505dfc1c624eafbf8c7589a0d7f32daa398d2e7590efa5fda/colorlog-6.10.1.tar.gz", hash = "sha256:eb4ae5cb65fe7fec7773c2306061a8e63e02efc2c72eba9d27b0fa23c94f1321", size = 17162, upload-time = "2025-10-16T16:14:11.978Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, +] + [[package]] name = "compressed-tensors" version = "0.13.0" @@ -1609,6 +1692,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" }, ] +[[package]] +name = "google-api-core" +version = "2.31.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/22/155cadf1d49272a9cf48f3168c0f3874fa13397297e611a5ea00cd093880/google_api_core-2.31.0.tar.gz", hash = "sha256:2be84ee0f584c48e6bde1b36766e23348b361fb7e55e56135fc76ce1c397f9c2", size = 176492, upload-time = "2026-06-03T14:52:17.257Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/40/9bdbb60b03a332bd45acb8703da08bbc27d991d35286b62e42acc86d243a/google_api_core-2.31.0-py3-none-any.whl", hash = "sha256:ef79fb3784c71cbac89cbd03301ba0c8fb8ad2aa95d7f9204dd9628f7adf59ab", size = 173102, upload-time = "2026-06-03T14:51:26.729Z" }, +] + +[[package]] +name = "google-auth" +version = "2.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyasn1-modules" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/ad/ff781329bbbdc0974a098d996e89c9e1f7024262f9e3eec442fbb9ad1ac6/google_auth-2.53.0.tar.gz", hash = "sha256:e7e6aa16f6bee7b2b264830fd04f08087a1d5a836df516251a5d15327b246c9c", size = 335844, upload-time = "2026-05-15T20:53:07.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/c9/db44165ba7c581268c6d46017ef63339110378305062830104fc7fa144cb/google_auth-2.53.0-py3-none-any.whl", hash = "sha256:6e7449917c599b35126a99ec268ec6880301f2fea41dce198fe8fd83ff642b68", size = 246071, upload-time = "2026-05-15T20:53:05.609Z" }, +] + [[package]] name = "google-crc32c" version = "1.8.0" @@ -1639,6 +1751,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, ] +[[package]] +name = "greenlet" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356, upload-time = "2026-05-20T15:05:03.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220, upload-time = "2026-05-20T13:07:28.463Z" }, + { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585, upload-time = "2026-05-20T14:00:06.141Z" }, + { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215, upload-time = "2026-05-20T14:05:42.675Z" }, + { url = "https://files.pythonhosted.org/packages/7c/6c/de5b1b388cd2d9fbdfeab324863daba37d54e6e233ddbefd70b385a8c591/greenlet-3.5.1-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:89101bfd5011e069be974903cb3a4e4523845e4ece2d62dcd8d358933c0ef249", size = 620094, upload-time = "2026-05-20T14:09:09.18Z" }, + { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358, upload-time = "2026-05-20T13:14:26.37Z" }, + { url = "https://files.pythonhosted.org/packages/4a/43/1204baffab8a6476464795a7ccf394a3248d4f22c9f87173a15b36b6d971/greenlet-3.5.1-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:e6cd99ea59dd5d89f0c956606571d79bfe6f68c9eb7f4a4083a41a7f1587edee", size = 422782, upload-time = "2026-05-20T14:01:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475, upload-time = "2026-05-20T14:02:25.29Z" }, + { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625, upload-time = "2026-05-20T13:14:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791, upload-time = "2026-05-20T13:10:39.018Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580, upload-time = "2026-05-20T13:08:45.056Z" }, + { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060, upload-time = "2026-05-20T13:08:51.899Z" }, + { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370, upload-time = "2026-05-20T14:00:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987, upload-time = "2026-05-20T14:05:44.237Z" }, + { url = "https://files.pythonhosted.org/packages/19/ba/c24110c55dffa55aa6e1d98b45310da33801aeba7686ff0190fe5d46fd32/greenlet-3.5.1-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d40a890035c0058cadbdc4af7569800fd28a0e527a0fdbb7b5f9418f176846ce", size = 622911, upload-time = "2026-05-20T14:09:10.598Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911, upload-time = "2026-05-20T13:14:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/ec/7b/d20db2e8a5ad6c038702f3179b136f93f0a3d1a21a0c0777f3e470cdf4b2/greenlet-3.5.1-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:67821bb03e4e98664490edb787ff6af501194c29bbee0f5c1dfdcf1dc3d9d436", size = 425228, upload-time = "2026-05-20T14:01:40.837Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689, upload-time = "2026-05-20T14:02:27.225Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602, upload-time = "2026-05-20T13:14:36.344Z" }, + { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683, upload-time = "2026-05-20T13:11:50.57Z" }, + { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499, upload-time = "2026-05-20T13:12:42.028Z" }, +] + [[package]] name = "grpcio" version = "1.81.0" @@ -2072,6 +2212,15 @@ signatures = [ { name = "typeshed-client" }, ] +[[package]] +name = "jsonpath-ng" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/58/250751940d75c8019659e15482d548a4aa3b6ce122c515102a4bfdac50e3/jsonpath_ng-1.8.0.tar.gz", hash = "sha256:54252968134b5e549ea5b872f1df1168bd7defe1a52fed5a358c194e1943ddc3", size = 74513, upload-time = "2026-02-24T14:42:06.182Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/99/33c7d78a3fb70d545fd5411ac67a651c81602cc09c9cf0df383733f068c5/jsonpath_ng-1.8.0-py3-none-any.whl", hash = "sha256:b8dde192f8af58d646fc031fac9c99fe4d00326afc4148f1f043c601a8cfe138", size = 67844, upload-time = "2026-02-28T00:53:19.637Z" }, +] + [[package]] name = "jsonschema" version = "4.26.0" @@ -2361,6 +2510,10 @@ wheels = [ name = "llvmlite" version = "0.47.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] sdist = { url = "https://files.pythonhosted.org/packages/01/88/a8952b6d5c21e74cbf158515b779666f692846502623e9e3c39d8e8ba25f/llvmlite-0.47.0.tar.gz", hash = "sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc", size = 193614, upload-time = "2026-03-31T18:29:53.497Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fa/48/4b7fe0e34c169fa2f12532916133e0b219d2823b540733651b34fdac509a/llvmlite-0.47.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:306a265f408c259067257a732c8e159284334018b4083a9e35f67d19792b164f", size = 37232769, upload-time = "2026-03-31T18:28:43.735Z" }, @@ -2598,6 +2751,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, ] +[[package]] +name = "msgpack" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/23/6139781ca7aadf656fa8e384fa84693ffb13f299e6931b6526427fe5e297/msgpack-1.2.0.tar.gz", hash = "sha256:8e17af38197bf58e7e819041678f6178f4491493f5b8c8580414f40f7c2c3c41", size = 183017, upload-time = "2026-06-11T04:16:10.775Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/07/dcb13f37e670257c8d0e944f116c799c34ac6968ecb48c83619f7e91d8b5/msgpack-1.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e2d6047ccd11a12c96a69f2bfe026471abef67334c3d0494a93e5310e45140a2", size = 82888, upload-time = "2026-06-11T04:15:08.992Z" }, + { url = "https://files.pythonhosted.org/packages/84/5f/6643b2a6a36ca4bc73c7674831be1d4d581cceecc7eb019dba1915951739/msgpack-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0347e3ac0dfee99086d3b68fe959da3f5f657c0019ddbaeaaa259a85f8603422", size = 82223, upload-time = "2026-06-11T04:15:10.182Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c8/9e1668b9897358e5ab39a18142e38be3cf15807e643757782da9f4a53cb3/msgpack-1.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25552ff1f2ff3dc8333e27eabb94f702da5929ed0e07969688194a3e9f12e151", size = 409700, upload-time = "2026-06-11T04:15:11.441Z" }, + { url = "https://files.pythonhosted.org/packages/38/ed/b7728573156d70b6b094233b0f38d876fc37340826cf852347ec2c7ca8ca/msgpack-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0d94420d9d52c56568159a69200af7e45eadb29615fa9d09fada140de1c38c7", size = 420090, upload-time = "2026-06-11T04:15:12.868Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f7/5ea755a89868c04f9cdf6d96d2d99da4b3d198af10e76a6082dd0fceccc0/msgpack-1.2.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d16e1f2db4a9eebc07b7cc91898d71e710f2eed8358711a605fee802caff8923", size = 378538, upload-time = "2026-06-11T04:15:14.511Z" }, + { url = "https://files.pythonhosted.org/packages/80/2d/126e59332a439c94ffd682c38ca0102b23480e2784b3dac48d8959b0bbac/msgpack-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9cb2e700e85f1e27bbb5c9de6cc1c9a4bc5ac64d5404bdcbcb37a0dc7a947a3", size = 399468, upload-time = "2026-06-11T04:15:16.133Z" }, + { url = "https://files.pythonhosted.org/packages/da/f9/7abcef683a0ad2e5ab3a4940344aad9f20cdf1f42057ecb0982cf55085d6/msgpack-1.2.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:717d0b166dd176a5f786aeafff081f6439680acf5af193eb63e6266c12b04d3d", size = 374212, upload-time = "2026-06-11T04:15:17.536Z" }, + { url = "https://files.pythonhosted.org/packages/27/23/2d62cf0e971678e96f8a3cfa9bd77fb719ddb98da73790f63c53fd847ad8/msgpack-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e87c7a21654d18111eb1a89bd5c42baba42e61887365d9e89585e112b4203f9e", size = 414361, upload-time = "2026-06-11T04:15:18.99Z" }, + { url = "https://files.pythonhosted.org/packages/32/fb/f5c153f614037aaf802d291a4653ba1bb731f56feacba886f7c21c109e56/msgpack-1.2.0-cp312-cp312-win32.whl", hash = "sha256:967e0c891f5f23ab65762f2e5dc95922759c79f1ef99ef4c7e1fdd863e0d0af9", size = 64389, upload-time = "2026-06-11T04:15:20.237Z" }, + { url = "https://files.pythonhosted.org/packages/90/af/8aafce6e5544b43b84cb670aca40c8bea7eb5ae8f42bfcbdc7098739987a/msgpack-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:6c23e33cee28dcffa112ae205661da4636fd7b06bd9ad1559a890623b92d060b", size = 71185, upload-time = "2026-06-11T04:15:21.51Z" }, + { url = "https://files.pythonhosted.org/packages/ba/08/9cc94be1fc1fe3d1379d439326259aef0344274f64623a8138feb54dff68/msgpack-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:6eeb771571f63f68045433b1a35c0256b946f31ed62f006997e40b8ad8b735af", size = 64481, upload-time = "2026-06-11T04:15:22.639Z" }, + { url = "https://files.pythonhosted.org/packages/7d/26/2902c6946ab5c8fe1e46e40842dfc32b8824464ad5cd4725364fd83f7a58/msgpack-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3a1d30df1f302f2b7a7404afbac2ab76d510036c34cf34dffb01f704a7288e45", size = 82621, upload-time = "2026-06-11T04:15:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/c9/59/7e6b812629d2f919e586041bffc130e1af32079f71bb20699eed54ed6d92/msgpack-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:581e317112260d8ca488d490cad9290a5682276f309c41c7de237a85ed8799c8", size = 81866, upload-time = "2026-06-11T04:15:25.032Z" }, + { url = "https://files.pythonhosted.org/packages/31/13/8c291196e60aafdbae38f482205d79432297749ac5d412fe638154fb6f1d/msgpack-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6827d12eacc16873eba62408a1b7bbe8ecfb4a8f7ed78a631ae9bae6ad43cf2", size = 405618, upload-time = "2026-06-11T04:15:26.235Z" }, + { url = "https://files.pythonhosted.org/packages/fb/63/68f5d0ea81e167db5f59ddb94dc6f837667062113feff1c73fabf8907061/msgpack-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a186027e4279efa4c8bf06ce30605498d7d0d3af0fba0b9799dce85a3fd4a93c", size = 416468, upload-time = "2026-06-11T04:15:27.732Z" }, + { url = "https://files.pythonhosted.org/packages/73/58/567dddf5c5a2790f673bcd7d80c83466d68e5ee9a9674ebca3db8101c0c8/msgpack-1.2.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a96142c14a11cf1a509e8b9aaf72858a3b742b7613e095ce646913e88ce7bd99", size = 374464, upload-time = "2026-06-11T04:15:29.286Z" }, + { url = "https://files.pythonhosted.org/packages/0d/30/0c2342fc9092e4498045f5f60bca6ccbe4f4d87789778c2300e6fd6efe82/msgpack-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:50c220579b68a6085b95408b2eaa486b259520f55d8e363ddc9b5d7ba5a6ac6d", size = 395879, upload-time = "2026-06-11T04:15:30.973Z" }, + { url = "https://files.pythonhosted.org/packages/b9/11/9565b29b58ce3c33e177b490478b7aaeb8f726ecaaeda26d815893c1db5a/msgpack-1.2.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4dcb9d12ab100ecacdfaaf37a3d72fe8392eacc7054afc1916b12d1b747c8446", size = 371749, upload-time = "2026-06-11T04:15:32.418Z" }, + { url = "https://files.pythonhosted.org/packages/f2/da/7bade19d60b73e2ef73fb76aaf4504c112a70cb760951b7202a0c64b5111/msgpack-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a804727188ab0ebb237fadb303b743f04925a69d8c3247292d1e33e679767c15", size = 410416, upload-time = "2026-06-11T04:15:34.053Z" }, + { url = "https://files.pythonhosted.org/packages/6d/14/c0c619571c02432208a5977a8dbdd3fc65fe1369f8226ca4b6d08cca87d8/msgpack-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1a1ac6ae1fe23298f79380e7b144c8a454e5d05616b0096584f353ba2d750114", size = 64357, upload-time = "2026-06-11T04:15:35.535Z" }, + { url = "https://files.pythonhosted.org/packages/50/a5/de06718460909aa965737fec4cfe8a15dedc6544a8c55feeb6956fa0d6e3/msgpack-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c3c80949d79578f9dc85fd9fb91edfe6694e8a729cd5744634d59d8455fdde3", size = 71057, upload-time = "2026-06-11T04:15:36.83Z" }, + { url = "https://files.pythonhosted.org/packages/c7/52/73446b0141c94a856e22b787c56709c0815fc34f185326577e15b26d8cfe/msgpack-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:fcf8f76fa587c2395fd0057c7232dbf071241f9ad280b235adb7ab585289989e", size = 64490, upload-time = "2026-06-11T04:15:38.001Z" }, +] + [[package]] name = "msgspec" version = "0.21.1" @@ -2790,6 +2973,10 @@ wheels = [ name = "numba" version = "0.65.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] dependencies = [ { name = "llvmlite", version = "0.47.0", source = { registry = "https://pypi.org/simple" } }, { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, @@ -2912,6 +3099,16 @@ wheels = [ name = "numpy" version = "2.4.6" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and extra != 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version < '3.13' and extra != 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version >= '3.13' and sys_platform == 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version >= '3.13' and sys_platform == 'emscripten' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", +] sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, @@ -3250,6 +3447,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/40/1f/c83cf5a206c263ee70448a5ae4264682555f4d0b5bed0d2cc6ca1108103d/openai_harmony-0.0.8-cp38-abi3-win_amd64.whl", hash = "sha256:39d44f0d8f466bd56698e7ead708bead3141e27b9b87e3ab7d5a6d0e4a869ee5", size = 2438369, upload-time = "2025-11-05T19:07:08.1Z" }, ] +[[package]] +name = "opencensus" +version = "0.11.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "opencensus-context" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/a7/a46dcffa1b63084f9f17fe3c8cb20724c4c8f91009fd0b2cfdb27d5d2b35/opencensus-0.11.4.tar.gz", hash = "sha256:cbef87d8b8773064ab60e5c2a1ced58bbaa38a6d052c41aec224958ce544eff2", size = 64966, upload-time = "2024-01-03T18:04:07.085Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/ed/9fbdeb23a09e430d87b7d72d430484b88184633dc50f6bfb792354b6f661/opencensus-0.11.4-py2.py3-none-any.whl", hash = "sha256:a18487ce68bc19900336e0ff4655c5a116daf10c1b3685ece8d971bddad6a864", size = 128225, upload-time = "2024-01-03T18:04:05.127Z" }, +] + +[[package]] +name = "opencensus-context" +version = "0.1.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/96/3b6f638f6275a8abbd45e582448723bffa29c1fb426721dedb5c72f7d056/opencensus-context-0.1.3.tar.gz", hash = "sha256:a03108c3c10d8c80bb5ddf5c8a1f033161fa61972a9917f9b9b3a18517f0088c", size = 4066, upload-time = "2022-08-03T22:20:22.359Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/68/162c97ea78c957d68ecf78a5c5041d2e25bd5562bdf5d89a6cbf7f8429bf/opencensus_context-0.1.3-py2.py3-none-any.whl", hash = "sha256:073bb0590007af276853009fac7e4bab1d523c3f03baf4cb4511ca38967c6039", size = 5060, upload-time = "2022-08-03T22:20:20.352Z" }, +] + [[package]] name = "opencv-python-headless" version = "4.13.0.92" @@ -3342,6 +3562,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d3/96/82cb223a1502f0787d4bbff12907f5f8d870a50731febcd5818d93ef9555/opentelemetry_exporter_otlp_proto_http-1.42.1-py3-none-any.whl", hash = "sha256:00a16da1b312a1d6c7233d600d557c91df71125af73020f3b9a7765bd699d59d", size = 21793, upload-time = "2026-05-21T16:32:35.277Z" }, ] +[[package]] +name = "opentelemetry-exporter-prometheus" +version = "0.63b1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "prometheus-client" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/2a/dfeddff262b12eff0c72f4ad9e258aab8889f48c4dc1417a0377a13bc427/opentelemetry_exporter_prometheus-0.63b1.tar.gz", hash = "sha256:31902e22c89431058a95b6dcdb644f9309f226aa4872cc755f0a780d2895e97f", size = 15234, upload-time = "2026-05-21T16:32:57.797Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/ec/d7c7435e9000fb69837cf7753b7cbbbdeb5d0585203daf1b6ebf8fa93e02/opentelemetry_exporter_prometheus-0.63b1-py3-none-any.whl", hash = "sha256:0efd00aa6b1939345ddcc6de141b83ebffa2b4401a37a68f880e54217602701d", size = 12466, upload-time = "2026-05-21T16:32:36.622Z" }, +] + [[package]] name = "opentelemetry-proto" version = "1.42.1" @@ -3443,6 +3677,10 @@ wheels = [ name = "pandas" version = "2.3.3" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] dependencies = [ { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product')" }, { name = "python-dateutil", marker = "extra == 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product')" }, @@ -3487,8 +3725,9 @@ resolution-markers = [ ] dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-15-algorithm-nexus-candidate' or extra == 'extra-15-algorithm-nexus-product'" }, - { name = "python-dateutil", marker = "extra == 'extra-15-algorithm-nexus-candidate' or extra == 'extra-15-algorithm-nexus-product'" }, - { name = "tzdata", marker = "(sys_platform == 'emscripten' and extra == 'extra-15-algorithm-nexus-candidate') or (sys_platform == 'emscripten' and extra == 'extra-15-algorithm-nexus-product') or (sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-candidate') or (sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "(extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product') or (extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product')" }, + { name = "python-dateutil", marker = "extra == 'extra-15-algorithm-nexus-candidate' or extra != 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, + { name = "tzdata", marker = "(sys_platform == 'emscripten' and extra == 'extra-15-algorithm-nexus-candidate') or (sys_platform == 'emscripten' and extra != 'extra-15-algorithm-nexus-ecosystem') or (sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-candidate') or (sys_platform == 'win32' and extra != 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } wheels = [ @@ -3756,6 +3995,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, ] +[[package]] +name = "proto-plus" +version = "1.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/56/e647b0c675392d2da368da7b6f158f7368b18542fd6f7d7400a2f39de000/proto_plus-1.28.0.tar.gz", hash = "sha256:38e5696342835b08fc116f30a25665b29531cda9d5d5643e9b81fc312385abd9", size = 57221, upload-time = "2026-05-07T08:04:50.811Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/20/b122d4626976acb81132036d2ad1bb35a1a8775fceb837ec30964622516a/proto_plus-1.28.0-py3-none-any.whl", hash = "sha256:a630604310899e73c59ec302e5765c058d412b2f090b9c79c8822589f14955b8", size = 50410, upload-time = "2026-05-07T08:03:31.962Z" }, +] + [[package]] name = "protobuf" version = "6.33.6" @@ -3802,6 +4053,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335, upload-time = "2022-10-25T20:38:27.636Z" }, ] +[[package]] +name = "py-spy" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/93/d8/5b71371f50cf153b1307e5a11ac8a4ce4d85651dae946bd7e9a064146545/py_spy-0.4.2.tar.gz", hash = "sha256:90e600b27bb6bb40479637baca5a5b4bc2ba3395c93d889e672315d93042c4ae", size = 286374, upload-time = "2026-04-24T22:08:54.906Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/21/ec030145a0c7992bd4b9eafb2f06f56358b3a5339eab4a16534baf3c69aa/py_spy-0.4.2-py2.py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1ccf688393105111684435f035bc14ec3f22117dd2b85b2414612cf27a22755a", size = 3743992, upload-time = "2026-04-24T22:08:45.438Z" }, + { url = "https://files.pythonhosted.org/packages/50/80/de5fd27243c2be03692ecd317bf0dbe24b4c6f78f689ce111e7277a7cb09/py_spy-0.4.2-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:a0e6f6810ccf0fc5e64e85e0182a5b626c4496eec01b14fb8755154b363a4831", size = 1859057, upload-time = "2026-04-24T22:08:46.946Z" }, + { url = "https://files.pythonhosted.org/packages/89/23/3eb4c23c684ebd667674ce1d076ae855e0621d1d9bd5e052aa3f7982f757/py_spy-0.4.2-py2.py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:142887e984a4e541071c99a4401ff8c3770f255d329dbd0f64e8c1dd51882cce", size = 2828136, upload-time = "2026-04-24T22:08:48.519Z" }, + { url = "https://files.pythonhosted.org/packages/ca/01/6314152cf9ad3310ebacbf2c47b5ed858086530f8e12b1a665725ca5e0f4/py_spy-0.4.2-py2.py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f1c6d9b0e2379ead5bf792df43f4cf36153aa79e6dda4fb8ac7740cf8017110", size = 2857707, upload-time = "2026-04-24T22:08:49.677Z" }, + { url = "https://files.pythonhosted.org/packages/cc/1f/0960a129d504728d28a51dbd5a04ce94031eb75bac676341da7aefdd8232/py_spy-0.4.2-py2.py3-none-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24720573f95230653b457671a1dcc3c5a381fcf4e92677761e328a430ad251b2", size = 2301852, upload-time = "2026-04-24T22:08:51.152Z" }, + { url = "https://files.pythonhosted.org/packages/f9/34/dd7d3c763a00b7b965e25a5eab0acd1a345dbaf0f45fffe595278873a1c0/py_spy-0.4.2-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:aeb0323409199c785f730645e9f4bb7a7b9ca2c481f2c331a55642b5d13fa52f", size = 2936518, upload-time = "2026-04-24T22:08:52.264Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ed/1409cdb557e558a6c98003ab12fdd4284699e158c167c187cb0f124eea4c/py_spy-0.4.2-py2.py3-none-win_amd64.whl", hash = "sha256:8b06a353c177677e4e1701b288d8c58e2f8d4208ee81a8048d9f72ba800918f8", size = 1894002, upload-time = "2026-04-24T22:08:53.811Z" }, +] + [[package]] name = "pyarrow" version = "24.0.0" @@ -3840,6 +4106,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2e/c3/94ade4906a2f88bc935772f59c934013b4205e773bcb4239db114a6da136/pyarrow_hotfix-0.7-py3-none-any.whl", hash = "sha256:3236f3b5f1260f0e2ac070a55c1a7b339c4bb7267839bd2015e283234e758100", size = 7923, upload-time = "2025-04-25T10:17:05.224Z" }, ] +[[package]] +name = "pyasn1" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + [[package]] name = "pybase64" version = "1.4.3" @@ -4088,6 +4375,10 @@ wheels = [ name = "pyjwt" version = "2.9.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] sdist = { url = "https://files.pythonhosted.org/packages/fb/68/ce067f09fca4abeca8771fe667d89cc347d1e99da3e093112ac329c6020e/pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c", size = 78825, upload-time = "2024-08-01T15:01:08.445Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344, upload-time = "2024-08-01T15:01:06.481Z" }, @@ -4098,19 +4389,12 @@ name = "pyjwt" version = "2.13.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", - "python_full_version >= '3.13' and sys_platform == 'emscripten' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", - "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", - "python_full_version < '3.13' and sys_platform == 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", - "python_full_version < '3.13' and sys_platform == 'emscripten' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product'", - "python_full_version >= '3.13' and sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", - "python_full_version >= '3.13' and sys_platform == 'emscripten' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", - "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", - "python_full_version < '3.13' and sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", - "python_full_version < '3.13' and sys_platform == 'emscripten' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", - "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", - "extra != 'extra-15-algorithm-nexus-candidate' and extra != 'extra-15-algorithm-nexus-ecosystem' and extra != 'extra-15-algorithm-nexus-product'", + "python_full_version >= '3.13' and sys_platform == 'win32'", + "python_full_version >= '3.13' and sys_platform == 'emscripten'", + "python_full_version >= '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } wheels = [ @@ -4135,6 +4419,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/85/545a951eecc270fcd688288c600017e2050a1aacb56c711d208586d3e470/pymdown_extensions-10.21.3-py3-none-any.whl", hash = "sha256:d7a5d08014fc571e80ca21dd6f854e31f94c489800350564d55d15b3c41e76b6", size = 269002, upload-time = "2026-05-13T12:57:30.296Z" }, ] +[[package]] +name = "pymysql" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/bc/1c6a92f385940f727daeecf3bacaf186e03875dff57197801046c583bcf0/pymysql-1.2.0.tar.gz", hash = "sha256:6c7b17ca686988104d7426c27895b455cdeea3e9d3ceb1270f0c3704fead8c33", size = 49021, upload-time = "2026-05-19T08:26:22.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/bd/2534e130295c8cfd4f0a2e31623baab7502278f1e97bcfe61db75656a77f/pymysql-1.2.0-py3-none-any.whl", hash = "sha256:62169ce6d5510f08e140c5e7990ee884a9764024e4a9a27b2cc11f1099322ae0", size = 45716, upload-time = "2026-05-19T08:26:20.974Z" }, +] + +[package.optional-dependencies] +rsa = [ + { name = "cryptography" }, +] + [[package]] name = "pynndescent" version = "0.6.0" @@ -4230,6 +4528,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5b/f8/1ef0129fba9a555c658e22af68989f35e7ba7b9136f25758809efec0cd6e/pyproj-3.7.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fc52ba896cfc3214dc9f9ca3c0677a623e8fdd096b257c14a31e719d21ff3fdd", size = 6262501, upload-time = "2025-08-14T12:05:01.39Z" }, ] +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, +] + [[package]] name = "pysam" version = "0.24.0" @@ -4516,6 +4823,52 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/cb/4dee9697891c9c6474b240d00e27688e03ecd882d3c83cc97eb25c2266ff/rasterio-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:a3539a2f401a7b4b2e94ff2db334878c0e15a2d1c9fe90bb0879c52f89367ae5", size = 28589538, upload-time = "2026-01-05T16:06:09.662Z" }, ] +[[package]] +name = "ray" +version = "2.55.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "filelock" }, + { name = "jsonschema" }, + { name = "msgpack" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pyyaml" }, + { name = "requests" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/3a/4d34f471a68b958b7f94c974c19ad6836a61a2dc16393df4294169a2e4b0/ray-2.55.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:137f9006eee28caab8260803cca314f37bbda3fc94fdfa31c770b5d019626ad8", size = 65822379, upload-time = "2026-04-22T20:09:58.064Z" }, + { url = "https://files.pythonhosted.org/packages/f1/13/0db535102d0256b350ca116d8987588aca1a1f9ebb4638e1e1ff88bbcef8/ray-2.55.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:26541f69bb55607ef8335baac75b2ed12ff2ce02d56313219b29eda003039221", size = 72910802, upload-time = "2026-04-22T20:10:04.382Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f8/fffadf3f4285eebd460e4d7f2ed1c0cd641ed89613c3f49eb881ee9fa7e2/ray-2.55.1-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:263705f6bab29e7622a94f82da25fd7f9cead76cdf89a07aab28f79cdf8f9d95", size = 73765203, upload-time = "2026-04-22T20:10:10.495Z" }, + { url = "https://files.pythonhosted.org/packages/10/f7/5acb86fc9625a0e6bbc40e1c7d42c60770e78585439a921c32738b6d675a/ray-2.55.1-cp312-cp312-win_amd64.whl", hash = "sha256:9ad56704c8bd7e92130162f9c58e4ef473609515637673d5a36e761f95335206", size = 27865547, upload-time = "2026-04-22T20:10:15.364Z" }, + { url = "https://files.pythonhosted.org/packages/d5/95/898699cc1a6a5f304ea95376d079843b5c05f4c8c1ec7e55a5cc7ffcea50/ray-2.55.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:f9844a9272ef2e6eb5771025866072cf4234cf4c7cc1a31e235b7de7111864be", size = 65766823, upload-time = "2026-04-22T20:10:20.786Z" }, + { url = "https://files.pythonhosted.org/packages/c9/13/87deecc090c672e45a0cf6f5eef511de448b93f37ef18fd10eb8e8557a0d/ray-2.55.1-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:b415d590e062f248907e0fe42994943f11726b7178fcf4b1cf5546721fb1a5f8", size = 72818676, upload-time = "2026-04-22T20:10:26.705Z" }, + { url = "https://files.pythonhosted.org/packages/71/d7/fc95d3b8824c62105c64aa1b59c59600b581f608d78a2af753e010936dc9/ray-2.55.1-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:1380e043eb57cde69b7e9199c6f2558ceeb8f0fc41c97d1d5e50ea042115f302", size = 73678908, upload-time = "2026-04-22T20:10:32.795Z" }, +] + +[package.optional-dependencies] +serve = [ + { name = "aiohttp" }, + { name = "aiohttp-cors" }, + { name = "colorful" }, + { name = "fastapi" }, + { name = "grpcio" }, + { name = "opencensus" }, + { name = "opentelemetry-exporter-prometheus" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "prometheus-client" }, + { name = "py-spy" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "smart-open" }, + { name = "starlette" }, + { name = "uvicorn", extra = ["standard"] }, + { name = "virtualenv" }, + { name = "watchfiles" }, +] + [[package]] name = "rdata" version = "1.0.0" @@ -4538,7 +4891,7 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } wheels = [ @@ -5273,6 +5626,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "smart-open" +version = "7.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/65/3ada667d32675399001bf022ad3d9f3989b57101351ebc71d6fbe2384634/smart_open-7.6.1.tar.gz", hash = "sha256:4347996e7ba21db7cd1e059632e0b30395407e4f6c660d2ddffc8f2a9ae5f990", size = 54754, upload-time = "2026-05-09T06:23:37.06Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/78/0f68b93564b8c6b6987a0696c582ba2591a381ab2f733a501909e949f241/smart_open-7.6.1-py3-none-any.whl", hash = "sha256:b4de6aebef023aca91cc9fb372052e1343ba3f152de215bd22391a663e3ddd21", size = 64845, upload-time = "2026-05-09T06:23:35.386Z" }, +] + [[package]] name = "smmap" version = "5.0.3" @@ -5311,6 +5676,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/29/32/f99009be2e40d2098daae9be8374dde5d976d8af145024758737af0024ab/somacore-1.0.29-py3-none-any.whl", hash = "sha256:5bb91a9c8b36d45fbe408b39311ccc09ae43fb418b322ea5d025e119b0c7d639", size = 38602, upload-time = "2025-06-16T17:57:30.315Z" }, ] +[[package]] +name = "sqlalchemy" +version = "2.0.50" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/da/6fbf010c8ebb347679d0d100b22fe9ba5e13fd04046c5df7280d2f0bf706/sqlalchemy-2.0.50.tar.gz", hash = "sha256:af5607d11ef90fd6a5c0549fe0045dce1663d427426bcfb506dcb5346a85a3b9", size = 9907424, upload-time = "2026-05-24T19:20:04.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/b0/a9d19b43f38f878b1278bca5b00b909f7540d41494396dd2561f9ad0956d/sqlalchemy-2.0.50-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae23d8b9d344d30d0a92f06d45825024a5790f1c1dd4cf452636a50d3e58cb", size = 2159807, upload-time = "2026-05-24T19:27:53.086Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/191dd58a248fd2cfd4780fa82c375c505e4ad98c8b522fa69ec492130d77/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47b71b933e7b4ebad407c8fdfd70d2c4f08b78b3238bb30eebdd6eb32ca51b89", size = 3343358, upload-time = "2026-05-24T20:09:29.279Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2b/514fce8a7df81cf5bad7ff7865de7ac0c5776a38cc043475c4703eb7fe8b/sqlalchemy-2.0.50-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:110fdac56ace278949f00de805edacbd6141e382d992f9ba28238b3a0827a600", size = 3357994, upload-time = "2026-05-24T20:17:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/35/a6/a0e283f5494f92b0d77e319ff77e437b1ffe4a051ba67c81d53234825475/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5e4ac70e9e757f6b3e87c0491ff034442ecd8dfd36d041a50564c322dafc0e", size = 3289399, upload-time = "2026-05-24T20:09:32.239Z" }, + { url = "https://files.pythonhosted.org/packages/b7/96/1b07325ba71752d6a028b77d07bed1483ad545f794e8b1dc89b3ba3b3c68/sqlalchemy-2.0.50-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:724f3dcbe53dd0151e3cb5e7ec4ba4c620bede579caacd16275dc35ce06e8615", size = 3321216, upload-time = "2026-05-24T20:17:15.581Z" }, + { url = "https://files.pythonhosted.org/packages/ed/8e/bad6ed253e8a99edfc99af02f7173ec48a1d3ed1b9b35a1b8bc1700900cc/sqlalchemy-2.0.50-cp312-cp312-win32.whl", hash = "sha256:1208050441471d003b7c8cb4054fb084f185cf35ac3f0ea270803865bca9939a", size = 2119194, upload-time = "2026-05-24T19:50:04.943Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2d/314a6690dda4b9cfc571eab1a63cf6fe6e1470aa3759ccda6aa016ee0f5a/sqlalchemy-2.0.50-cp312-cp312-win_amd64.whl", hash = "sha256:9d1af51558029a156a70986b7df88f042b3d158d7c8d8fb5072912d4b32d89c7", size = 2146186, upload-time = "2026-05-24T19:50:06.74Z" }, + { url = "https://files.pythonhosted.org/packages/0b/c4/c42356b527296e9862f67990efce31ef78b4cf69cd3f80873a528a060320/sqlalchemy-2.0.50-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:06a9210bdc5f4298cff0781087e2ff45683922252dacc452846373a58761f093", size = 2156697, upload-time = "2026-05-24T19:27:54.764Z" }, + { url = "https://files.pythonhosted.org/packages/60/a1/b1a70e3c4365ac7fe9e347f3710f19b562c866fb96d45e3c891588789a7b/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b53784972ade4f8174b9aa661f31a06f8a936d2cfdd602913ff3c6dd40ae873", size = 3284260, upload-time = "2026-05-24T20:09:34.195Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4a/f3ac3caa19f263d57b0a47f8c91bbf56583dc2d3fc63acfbf644abb24fe0/sqlalchemy-2.0.50-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31648fa14460537e768a7303b078e4344d208e0d23e06867c1f376a227ed82db", size = 3302280, upload-time = "2026-05-24T20:17:17.825Z" }, + { url = "https://files.pythonhosted.org/packages/66/55/ccada3e3d62254587819749a0bc69f41173eb48a6e385d10e66d32a9c88e/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03f4323c980ad0e918cc9e5369b015f759f4e534db5bbaf4dc36832c10d05064", size = 3231580, upload-time = "2026-05-24T20:09:36.406Z" }, + { url = "https://files.pythonhosted.org/packages/05/f6/6809349130a2de0e109e7f00fd7d431da9565b9b2868b32ee684754f672b/sqlalchemy-2.0.50-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2b9dcc43afef8ac157cd92fce96985d6b8b0cfbd3df4d666f66b4d55a75d202f", size = 3269375, upload-time = "2026-05-24T20:17:20.34Z" }, + { url = "https://files.pythonhosted.org/packages/48/84/278a811ef4e07be9c89dc5cdd7be833268509a66a68c4897cf585e67428f/sqlalchemy-2.0.50-cp313-cp313-win32.whl", hash = "sha256:60922d6599065ddca2c6f376b9aa2f41a6b85a271725e0909490bbc50b1998a5", size = 2117229, upload-time = "2026-05-24T19:50:08.215Z" }, + { url = "https://files.pythonhosted.org/packages/f6/1c/067cc6187ed32d2ec222fe6d2643acc1659a6d0659f8a7cbc5ad3ae83280/sqlalchemy-2.0.50-cp313-cp313-win_amd64.whl", hash = "sha256:287086e67275a212c4582d166a6fb03a65ccc5551d80866270ce0dd9f34eccd3", size = 2143126, upload-time = "2026-05-24T19:50:09.691Z" }, + { url = "https://files.pythonhosted.org/packages/d0/10/f7220e9b784d295d241c86ed99aeb537f92afcd469a64861f2717e9bb077/sqlalchemy-2.0.50-py3-none-any.whl", hash = "sha256:92064363517a3ff8212b5a93b8c62876579d8dfd1ca5b561335f30152d884fa9", size = 1943861, upload-time = "2026-05-24T19:59:01.119Z" }, +] + [[package]] name = "sse-starlette" version = "3.4.4" @@ -5330,7 +5722,7 @@ version = "1.2.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/25/44/ec35f1b6e83094b997da438a02c8c9b0ade2b1e84cfc48bd4656780760a6/starlette-1.2.1.tar.gz", hash = "sha256:9b9b5ebb992e67d6093741e63c2f59e4f6fff986f81163c087867bd7b924b3f6", size = 2701854, upload-time = "2026-05-31T01:07:51.847Z" } wheels = [ @@ -5636,6 +6028,10 @@ vllm = [ name = "terratorch" version = "1.2.8" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] dependencies = [ { name = "albucore" }, { name = "albumentations" }, @@ -6025,6 +6421,10 @@ wheels = [ name = "torchgeo" version = "0.2.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] dependencies = [ { name = "einops" }, { name = "fiona" }, @@ -6092,6 +6492,10 @@ wheels = [ name = "torchmetrics" version = "1.1.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] dependencies = [ { name = "lightning-utilities", marker = "extra == 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product')" }, { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-15-algorithm-nexus-ecosystem' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product')" }, @@ -6307,6 +6711,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] +[[package]] +name = "uv" +version = "0.11.20" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/80/09/c29c0b90bc9308cfa6f5d77ce9b38ce97852210fda17d79019c7bcf9c3a1/uv-0.11.20.tar.gz", hash = "sha256:a246f30931cbc93d0a39d0cfc75be045fddd45773a734ddf8afa869aabc46c63", size = 4237464, upload-time = "2026-06-10T17:20:05.905Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/8a/25fc4d94ad3896e636466ee1fb03c4077f7a151c19cb25c1e6a731fa9cbf/uv-0.11.20-py3-none-linux_armv6l.whl", hash = "sha256:f867fd0807e39653fd101e16f2292ff488de14b5ffbb86a5678a87a27aa58ea0", size = 23713010, upload-time = "2026-06-10T17:19:32.058Z" }, + { url = "https://files.pythonhosted.org/packages/02/7d/bbaad5f0c616f7824149a4ac0271db14107b859b36bd75d16db1486c495f/uv-0.11.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f3bacc52778775cef671867ddab744b50c4183bc3cd6419a5fb4eb01a02f9526", size = 22918378, upload-time = "2026-06-10T17:19:20.828Z" }, + { url = "https://files.pythonhosted.org/packages/39/3e/e3d39361b95c262b43ccfe260f41184da71c536a08f39d4ad59ab962e459/uv-0.11.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c9f2062096e146b351fd5da3cd43e15a5c43bfa37166c509d884dab3dbb72f03", size = 21716975, upload-time = "2026-06-10T17:19:51.552Z" }, + { url = "https://files.pythonhosted.org/packages/24/30/9031204d7b592d1595322d7506944793728a74795a8c4a3c38bc4a8985f0/uv-0.11.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8fe143572a1f02d536c4e9c6994f0c89d9fa58ff6e5d91de55f61660658c694b", size = 23571826, upload-time = "2026-06-10T17:19:46.206Z" }, + { url = "https://files.pythonhosted.org/packages/98/c3/7f9f00c7a152e67d59ae5f25635d19ac4252929dd6ac454cdb1dee3119fa/uv-0.11.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:3d00754be09a381030829526f7ff47c06d0e28ca90760c4439be48e71c1bf13a", size = 23249218, upload-time = "2026-06-10T17:19:26.603Z" }, + { url = "https://files.pythonhosted.org/packages/35/a9/1ce58670a89c25d4a8b84532207183b5a97b3623d9b9151afe641499fbc8/uv-0.11.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:edb59d33e602fc462b6ed8fde66d404445294de41ad4de31039f7a2c41153601", size = 23302149, upload-time = "2026-06-10T17:20:01.111Z" }, + { url = "https://files.pythonhosted.org/packages/61/cf/3a498a315364f906bd655a94fa6b5f74f5240dc90875d6ab67ef28c081ec/uv-0.11.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14026b64ccbe0174e4fcf107f585f5d23f0f5b9f5b3e8b28394fe63fff3e60ff", size = 24652804, upload-time = "2026-06-10T17:19:16.692Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8c/68e4a805e3b49d834e95b3838e53a623d2e0ad956bb44e681ece54b3308b/uv-0.11.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73dcaf5543d1b24e4c6fa4c19af033ed015304171c132670ebe9ef01ddec3d17", size = 25660209, upload-time = "2026-06-10T17:20:08.156Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/e8e205a79b9a39454c5da5c2695e4911a42860a4404739e32802e599af0c/uv-0.11.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cac8ca5d187dc5040b1c22c83f6ed789195c3fc06b6a5a2b32c747c603f07820", size = 24866467, upload-time = "2026-06-10T17:19:37.981Z" }, + { url = "https://files.pythonhosted.org/packages/7e/38/f844d125db277d8ce0c921f0219078d292d0ee72d314d0c12f4cf510aa40/uv-0.11.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61588768f04a24b0b4d87b43f03b4521ba66d5eec3ab1aca37cb59b1d52337db", size = 24957448, upload-time = "2026-06-10T17:19:40.979Z" }, + { url = "https://files.pythonhosted.org/packages/ab/7d/b0e28abfa41c424d2a3df83be2b00b2fbe3ad5795baf7361262c6d800a62/uv-0.11.20-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:2d307d47b1a0cf8f76aa69bde850be407bca9482c13cff066142e586a5c57e77", size = 23671087, upload-time = "2026-06-10T17:19:54.595Z" }, + { url = "https://files.pythonhosted.org/packages/80/ff/92bce88101ce61d708e888db6ab7f5ebf4ccd61f54d3316e7e48a4be56a5/uv-0.11.20-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:bb5839cbb68d7469925fe1599dacd16cefdb7698a7adeaf9c8e4d8a7cd4122bf", size = 24324677, upload-time = "2026-06-10T17:19:43.534Z" }, + { url = "https://files.pythonhosted.org/packages/0f/90/d308bd88c7a53cae93f7fe13ce5c621895b8fb94e37911660a3de7283b67/uv-0.11.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:efefbd491ba443b326fdd344d7efc89c1de8a006cab5bc639a4d5ce9d1dd1ab9", size = 24429959, upload-time = "2026-06-10T17:19:29.352Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e6/b87c941b93b61dceaa11f4f8d02760de7aa7d1c58ea24a2298e7c5aafb4f/uv-0.11.20-py3-none-musllinux_1_1_i686.whl", hash = "sha256:daa41b97386699212b2266a80c178140c5396e3c444ff5553f68f79812334e41", size = 23880515, upload-time = "2026-06-10T17:19:23.733Z" }, + { url = "https://files.pythonhosted.org/packages/53/ab/11b07641f8387177889f4341a36318561208c18703837bc47163244aa11b/uv-0.11.20-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:37ca61eddb940d1c698fce46d63789adfda3033344dabab0e18e2958e1a69771", size = 25171603, upload-time = "2026-06-10T17:19:34.848Z" }, + { url = "https://files.pythonhosted.org/packages/b4/7d/62af57509c7007600ce11cd5222b2cb84d5cd8f80f427a499d1b44ef3601/uv-0.11.20-py3-none-win32.whl", hash = "sha256:32893ee9f94657fbf89e22638ac88850db4529d454f102bfa7ceea5fc9fe8d77", size = 22570328, upload-time = "2026-06-10T17:19:48.808Z" }, + { url = "https://files.pythonhosted.org/packages/66/35/c9ee48cdce11f5cdac9e2be41a5446bae7382cde18d2ce1050bcd81dc05a/uv-0.11.20-py3-none-win_amd64.whl", hash = "sha256:4836044213bb23a3be1f5550db340d3a19babe1dfc3ca1313544e8b614085ce9", size = 25228859, upload-time = "2026-06-10T17:19:57.824Z" }, + { url = "https://files.pythonhosted.org/packages/8b/8d/00a382c2f8f44b328cf98f734a3fcd72957698c30bed2392bd241b573384/uv-0.11.20-py3-none-win_arm64.whl", hash = "sha256:442ae26f47bf6e58b072e99dbfd6d5296ab90308574b2c02adff1dace051008d", size = 23664943, upload-time = "2026-06-10T17:20:03.774Z" }, +] + [[package]] name = "uvicorn" version = "0.49.0" @@ -6322,11 +6752,11 @@ wheels = [ [package.optional-dependencies] standard = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "sys_platform == 'win32' or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, { name = "httptools" }, { name = "python-dotenv" }, { name = "pyyaml" }, - { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "uvloop", marker = "(platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32') or (platform_python_implementation == 'PyPy' and extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (platform_python_implementation == 'PyPy' and extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (platform_python_implementation == 'PyPy' and extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product') or (sys_platform == 'cygwin' and extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (sys_platform == 'cygwin' and extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (sys_platform == 'cygwin' and extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product') or (sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-ecosystem') or (sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-candidate' and extra == 'extra-15-algorithm-nexus-product') or (sys_platform == 'win32' and extra == 'extra-15-algorithm-nexus-ecosystem' and extra == 'extra-15-algorithm-nexus-product')" }, { name = "watchfiles" }, { name = "websockets" }, ] @@ -6821,6 +7251,10 @@ wheels = [ name = "zarr" version = "3.2.1" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version < '3.13'", +] dependencies = [ { name = "donfig" }, { name = "google-crc32c" },