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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Please see the official [NVIDIA nvMolKit Documentation](https://nvidia-bionemo.g

## Cursor / agent skill

If you use [Cursor](https://cursor.com/) (or another agent that supports the `SKILL.md` format) to write code that calls nvMolKit, you can copy [`agent-skills/nvmolkit-usage/`](agent-skills/nvmolkit-usage/) into your project's `.cursor/skills/` (or `~/.cursor/skills/` for personal use). It gives the agent the public Python entry-point map, runtime requirements, and runnable recipes.
If you use [Cursor](https://cursor.com/) (or another agent that supports the `SKILL.md` format) to write code that calls nvMolKit, you can copy [`skills/nvmolkit-usage/`](skills/nvmolkit-usage/) into your project's `.cursor/skills/` (or `~/.cursor/skills/` for personal use). It gives the agent the public Python entry-point map, runtime requirements, runnable recipes, and evaluation definitions.

## Installation Guide

Expand Down
2 changes: 1 addition & 1 deletion docs/agent_skill.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Get the skill
-------------

The skill lives at
`agent-skills/nvmolkit-usage/ <https://github.com/NVIDIA-BioNeMo/nvMolKit/tree/main/agent-skills/nvmolkit-usage>`_
`skills/nvmolkit-usage/ <https://github.com/NVIDIA-BioNeMo/nvMolKit/tree/main/skills/nvmolkit-usage>`_
in the nvMolKit GitHub repo.

Copy that directory into one of:
Expand Down
41 changes: 38 additions & 3 deletions nvmolkit/tests/test_skill.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""Verify every Python code block in the nvmolkit-usage agent skill executes against the installed nvMolKit."""
"""Verify the nvmolkit-usage skill snippets and portable eval definitions."""

import json
import re
import subprocess
import sys
Expand All @@ -23,7 +24,9 @@
import pytest

REPO_ROOT = Path(__file__).resolve().parents[2]
SKILL_PATH = REPO_ROOT / "agent-skills" / "nvmolkit-usage" / "SKILL.md"
SKILL_PATH = REPO_ROOT / "skills" / "nvmolkit-usage" / "SKILL.md"
EVALS_PATH = SKILL_PATH.parent / "evals" / "evals.json"
TRIGGER_EVALS_PATH = SKILL_PATH.parent / "evals" / "trigger_evals.json"

_PY_BLOCK_RE = re.compile(r"```python\n(.*?)```", re.DOTALL)

Expand All @@ -33,7 +36,7 @@ def _extract_python_blocks(skill_path: Path) -> list[str]:
raise FileNotFoundError(
f"nvmolkit-usage agent skill not found at {skill_path}. "
f"test_skill expects the skill to live at "
f"agent-skills/nvmolkit-usage/SKILL.md relative to the repo root; "
f"skills/nvmolkit-usage/SKILL.md relative to the repo root; "
f"update SKILL_PATH if the skill has moved."
)
text = skill_path.read_text()
Expand All @@ -60,3 +63,35 @@ def test_skill_snippet_runs(snippet_idx: int, snippet: str, tmp_path: Path) -> N
f"--- stdout ---\n{result.stdout}\n"
f"--- stderr ---\n{result.stderr}\n"
)


def test_skill_evals_use_library_skill_schema() -> None:
data = json.loads(EVALS_PATH.read_text())

assert data["skill_name"] == "nvmolkit-usage"
assert len(data["evals"]) > 0
assert len({entry["id"] for entry in data["evals"]}) == len(data["evals"])
Comment thread
scal444 marked this conversation as resolved.
for entry in data["evals"]:
assert set(entry) == {
"id",
"prompt",
"expected_output",
"assertions",
"expected_skill",
"expected_script",
}
assert entry["id"].startswith("nvmolkit-usage-")
assert entry["prompt"].strip()
assert entry["expected_output"].strip()
assert entry["assertions"]
assert all(isinstance(assertion, str) and assertion.strip() for assertion in entry["assertions"])
assert entry["expected_skill"] == "nvmolkit-usage"
assert entry["expected_script"] is None


def test_skill_trigger_evals_cover_positive_and_negative_cases() -> None:
data = json.loads(TRIGGER_EVALS_PATH.read_text())

assert data["skill_name"] == "nvmolkit-usage"
outcomes = {entry["expected"] for entry in data["trigger_evals"]}
assert outcomes == {"trigger", "no_trigger"}
Comment thread
scal444 marked this conversation as resolved.
File renamed without changes.
Loading
Loading