Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This guide covers setup, testing, and contributing to the verifiers package.

- [Setup](#setup)
- [Project Structure](#project-structure)
- [Prime CLI Plugin Export](#prime-cli-plugin-export)
- [Running Tests](#running-tests)
- [Writing Tests](#writing-tests)
- [Contributing](#contributing)
Expand Down Expand Up @@ -49,14 +50,45 @@ verifiers/
│ ├── rl/ # Training infrastructure
│ │ ├── inference/ # vLLM server utilities
│ │ └── trainer/ # Trainer implementation
│ ├── scripts/ # CLI entry points
│ ├── cli/ # Prime-facing CLI modules and plugin exports
│ ├── scripts/ # Compatibility wrappers around verifiers/cli commands
│ └── utils/ # Utilities
├── environments/ # Installable environment modules
├── configs/ # Example training configurations
├── tests/ # Test suite
└── docs/ # Documentation
```

## Prime CLI Plugin Export

Verifiers exports a plugin consumed by `prime` so command behavior is sourced from verifiers modules.

Entry point:

```python
from verifiers.cli.plugins.prime import get_plugin

plugin = get_plugin()
```

The plugin exposes:

- `api_version` (current: `1`)
- command modules:
- `eval_module` (`verifiers.cli.commands.eval`)
- `gepa_module` (`verifiers.cli.commands.gepa`)
- `install_module` (`verifiers.cli.commands.install`)
- `init_module` (`verifiers.cli.commands.init`)
- `setup_module` (`verifiers.cli.commands.setup`)
- `build_module` (`verifiers.cli.commands.build`)
- `build_module_command(module_name, args)` to construct subprocess invocation for a command module

Contributor guidance:

- Add new prime-facing command logic under `verifiers/cli/commands/`.
- Export new command modules through `PrimeCLIPlugin` in `verifiers/cli/plugins/prime.py`.
- Keep `verifiers/scripts/*` as thin compatibility wrappers that call into `verifiers/cli`.

## Running Tests

```bash
Expand Down
45 changes: 45 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Parser Classes](#parser-classes)
- [Rubric Classes](#rubric-classes)
- [Configuration Types](#configuration-types)
- [Prime CLI Plugin](#prime-cli-plugin)
- [Decorators](#decorators)
- [Utility Functions](#utility-functions)

Expand Down Expand Up @@ -640,6 +641,50 @@ Endpoints = dict[str, list[Endpoint]]

---

## Prime CLI Plugin

Verifiers exposes a plugin contract consumed by `prime` for command execution.

### PRIME_PLUGIN_API_VERSION

```python
PRIME_PLUGIN_API_VERSION = 1
```

API version for compatibility checks between `prime` and `verifiers`.

### PrimeCLIPlugin

```python
@dataclass(frozen=True)
class PrimeCLIPlugin:
api_version: int = PRIME_PLUGIN_API_VERSION
eval_module: str = "verifiers.cli.commands.eval"
gepa_module: str = "verifiers.cli.commands.gepa"
install_module: str = "verifiers.cli.commands.install"
init_module: str = "verifiers.cli.commands.init"
setup_module: str = "verifiers.cli.commands.setup"
build_module: str = "verifiers.cli.commands.build"

def build_module_command(
self, module_name: str, args: Sequence[str] | None = None
) -> list[str]:
...
```

`build_module_command` returns a subprocess command list for `python -m <module> ...`.

### get_plugin

```python
def get_plugin() -> PrimeCLIPlugin:
...
```

Returns the plugin instance consumed by `prime`.

---

## Decorators

### @vf.stop
Expand Down
5 changes: 3 additions & 2 deletions tests/test_install_utils.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import sys
from unittest.mock import MagicMock, patch

import pytest
Expand Down Expand Up @@ -86,7 +87,7 @@ def test_installed_no_version_check(self, mock_run):
)
assert is_installed("gsm8k") is True
mock_run.assert_called_once_with(
["uv", "pip", "show", "gsm8k"],
["uv", "pip", "show", "--python", sys.executable, "gsm8k"],
capture_output=True,
text=True,
)
Expand Down Expand Up @@ -122,7 +123,7 @@ def test_normalizes_package_name(self, mock_run):
mock_run.return_value = MagicMock(returncode=0, stdout="")
is_installed("my-package")
mock_run.assert_called_once_with(
["uv", "pip", "show", "my_package"],
["uv", "pip", "show", "--python", sys.executable, "my_package"],
capture_output=True,
text=True,
)
Expand Down
1 change: 1 addition & 0 deletions verifiers/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""CLI integration surfaces for host applications."""
1 change: 1 addition & 0 deletions verifiers/cli/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""CLI command modules exposed to host applications."""
7 changes: 7 additions & 0 deletions verifiers/cli/commands/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Environment build command module for external hosts."""

from verifiers.scripts.build import main


if __name__ == "__main__":
raise SystemExit(main())
7 changes: 7 additions & 0 deletions verifiers/cli/commands/eval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Evaluation command module for external hosts."""

from verifiers.scripts.eval import main


if __name__ == "__main__":
main()
7 changes: 7 additions & 0 deletions verifiers/cli/commands/gepa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""GEPA command module for external hosts."""

from verifiers.scripts.gepa import main


if __name__ == "__main__":
main()
7 changes: 7 additions & 0 deletions verifiers/cli/commands/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Environment initialization command module for external hosts."""

from verifiers.scripts.init import main


if __name__ == "__main__":
main()
7 changes: 7 additions & 0 deletions verifiers/cli/commands/install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"""Environment install command module for external hosts."""

from verifiers.scripts.install import main


if __name__ == "__main__":
main()
9 changes: 9 additions & 0 deletions verifiers/cli/commands/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""Workspace setup command module for external hosts."""

from verifiers.scripts.setup import main, run_setup

__all__ = ["main", "run_setup"]


if __name__ == "__main__":
main()
5 changes: 5 additions & 0 deletions verifiers/cli/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Plugin exports for external CLI hosts."""

from .prime import PRIME_PLUGIN_API_VERSION, PrimeCLIPlugin, get_plugin

__all__ = ["PRIME_PLUGIN_API_VERSION", "PrimeCLIPlugin", "get_plugin"]
97 changes: 97 additions & 0 deletions verifiers/cli/plugins/prime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""Prime-hosted command plugin contract."""

from __future__ import annotations

from dataclasses import dataclass
import os
from pathlib import Path
import subprocess
import sys
from functools import lru_cache
from typing import Sequence

PRIME_PLUGIN_API_VERSION = 1


def _venv_python(venv_root: Path) -> Path:
if os.name == "nt":
return venv_root / "Scripts" / "python.exe"
return venv_root / "bin" / "python"


@lru_cache(maxsize=32)
def _python_can_import_module(
python_executable: str, module_name: str, cwd: str
) -> bool:
probe = (
"import importlib.util, sys; "
"raise SystemExit(0 if importlib.util.find_spec(sys.argv[1]) else 1)"
)
try:
result = subprocess.run(
[python_executable, "-c", probe, module_name],
cwd=cwd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
)
except Exception:
return False
return result.returncode == 0


def _resolve_workspace_python(cwd: Path | None = None) -> str:
workspace = (cwd or Path.cwd()).resolve()
workspace_str = str(workspace)
module = "verifiers.cli.commands.eval"

def _usable(candidate: Path) -> bool:
return candidate.exists() and _python_can_import_module(
str(candidate), module, workspace_str
)

uv_project_env = os.environ.get("UV_PROJECT_ENVIRONMENT")
if uv_project_env:
candidate = _venv_python(Path(uv_project_env))
if _usable(candidate):
return str(candidate)

virtual_env = os.environ.get("VIRTUAL_ENV")
if virtual_env:
candidate = _venv_python(Path(virtual_env))
if _usable(candidate):
return str(candidate)

for directory in [workspace, *workspace.parents]:
if (directory / "pyproject.toml").is_file():
candidate = _venv_python(directory / ".venv")
if _usable(candidate):
return str(candidate)

return sys.executable


@dataclass(frozen=True)
class PrimeCLIPlugin:
"""Declarative command surface consumed by prime-cli."""

api_version: int = PRIME_PLUGIN_API_VERSION
eval_module: str = "verifiers.cli.commands.eval"
gepa_module: str = "verifiers.cli.commands.gepa"
install_module: str = "verifiers.cli.commands.install"
init_module: str = "verifiers.cli.commands.init"
setup_module: str = "verifiers.cli.commands.setup"
build_module: str = "verifiers.cli.commands.build"

def build_module_command(
self, module_name: str, args: Sequence[str] | None = None
) -> list[str]:
command = [_resolve_workspace_python(), "-m", module_name]
if args:
command.extend(args)
return command


def get_plugin() -> PrimeCLIPlugin:
"""Return the prime plugin definition."""
return PrimeCLIPlugin()
9 changes: 9 additions & 0 deletions verifiers/cli/tui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""TUI exports for host CLIs."""

from verifiers.scripts.tui import VerifiersTUI, main

__all__ = ["VerifiersTUI", "main"]


if __name__ == "__main__":
main()
18 changes: 11 additions & 7 deletions verifiers/utils/install_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import importlib
import logging
import subprocess
import sys
from pathlib import Path
from typing import Optional
from urllib.parse import urlparse
Expand All @@ -12,6 +13,11 @@
ENVIRONMENTS_HUB_URL = "https://api.primeintellect.ai/api/v1/environmentshub"


def _uv_pip_cmd(subcommand: str, *args: str) -> list[str]:
"""Run uv pip against the active Python interpreter for this process."""
return ["uv", "pip", subcommand, "--python", sys.executable, *args]


def normalize_package_name(name: str) -> str:
"""Normalize package name according to Python packaging standards."""
return name.replace("-", "_").lower()
Expand Down Expand Up @@ -60,7 +66,7 @@ def is_installed(env_name: str, version: Optional[str] = None) -> bool:
try:
pkg_name = normalize_package_name(env_name)
result = subprocess.run(
["uv", "pip", "show", pkg_name],
_uv_pip_cmd("show", pkg_name),
capture_output=True,
text=True,
)
Expand Down Expand Up @@ -159,17 +165,15 @@ def install_from_hub(env_id: str) -> bool:
else:
pkg_spec = pkg_name
cmd = [
"uv",
"pip",
"install",
*_uv_pip_cmd("install"),
"--upgrade",
pkg_spec,
"--extra-index-url",
simple_index_url,
]
else:
assert wheel_url is not None
cmd = ["uv", "pip", "install", "--upgrade", wheel_url]
cmd = [*_uv_pip_cmd("install"), "--upgrade", wheel_url]

logger.info(f"Installing {env_id}...")
logger.debug(f"Command: {' '.join(cmd)}")
Expand Down Expand Up @@ -204,7 +208,7 @@ def install_from_local(env_name: str, env_dir: str = "./environments") -> bool:

logger.info(f"Installing {env_name} from {env_path}...")
result = subprocess.run(
["uv", "pip", "install", "-e", str(env_path)],
[*_uv_pip_cmd("install"), "-e", str(env_path)],
capture_output=True,
text=True,
)
Expand Down Expand Up @@ -235,7 +239,7 @@ def install_from_repo(env_name: str, branch: str = "main") -> bool:

logger.info(f"Installing {env_name} from verifiers repo ({branch})...")
result = subprocess.run(
["uv", "pip", "install", f"{pkg_name} @ {url}"],
[*_uv_pip_cmd("install"), f"{pkg_name} @ {url}"],
capture_output=True,
text=True,
)
Expand Down
Loading