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
8 changes: 5 additions & 3 deletions .github/workflows/ci_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ defaults:

jobs:
test:
name: Test (${{ matrix.arch }})
name: Test (Python ${{ matrix.python-version }}, ${{ matrix.arch }})
strategy:
fail-fast: false
matrix:
arch: [x86_64, arm64]
python-version: ['3.11', '3.12', '3.13', '3.14']
include:
- arch: x86_64
runner: ubuntu-24.04
Expand Down Expand Up @@ -81,8 +83,8 @@ jobs:
# imports it to read Hermes config fixtures, so install it for the tests.
- name: Build SDK with native extension
run: |
uv venv --python 3.12 .venv
uv sync --group test --no-group dev --extra claude --extra codex --extra deepagents --extra harbor --extra hermes --extra relay --extra runtime
uv venv --python ${{ matrix.python-version }} .venv
uv sync --group test --no-group dev --extra claude --extra codex --extra deepagents ${{ matrix.python-version != '3.11' && '--extra harbor' || '' }} ${{ matrix.python-version != '3.14' && '--extra hermes' || '' }} --extra relay --extra runtime
Comment thread
dagardner-nv marked this conversation as resolved.
Comment thread
dagardner-nv marked this conversation as resolved.

- name: Run pytest
run: |
Expand Down
3 changes: 2 additions & 1 deletion adapters/hermes/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ classifiers = [
license = "Apache-2.0"
license-files = ["LICENSE"]
readme = "README.md"
requires-python = ">=3.11"
# Hermes Agent does not currently support Python 3.14 or later.
requires-python = ">=3.11,<3.14"
dependencies = [
"nemo-fabric-adapters-common == 0.1.0",
"pyyaml>=6.0", # needed for writing hermes config files
Expand Down
20 changes: 1 addition & 19 deletions adapters/hermes/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ adapters-deepagents = [
]

adapters-hermes = [
"nemo-fabric-adapters-hermes == 0.1.0",
"nemo-fabric-adapters-hermes == 0.1.0; python_version < '3.14'",
]

codex = [
Expand All @@ -72,8 +72,8 @@ harbor = [
]

hermes = [
"nemo-fabric-adapters-hermes == 0.1.0",
"hermes-agent>=0.17.0",
"nemo-fabric-adapters-hermes == 0.1.0; python_version < '3.14'",
"hermes-agent>=0.17.0; python_version < '3.14'",
]

relay = [
Expand All @@ -91,7 +91,7 @@ adapters = [
"nemo-fabric-adapters-claude",
"nemo-fabric-adapters-codex-cli",
"nemo-fabric-adapters-deepagents",
"nemo-fabric-adapters-hermes",
"nemo-fabric-adapters-hermes; python_version < '3.14'",
]

dev = [
Expand Down
13 changes: 9 additions & 4 deletions tests/adapters/test_hermes_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from __future__ import annotations

import importlib.util
import inspect
import json
import os
Expand All @@ -14,12 +15,16 @@
from unittest.mock import MagicMock

import pytest
from hermes_state import SessionDB
from run_agent import AIAgent

import nemo_fabric_adapters.common.utils as common_utils
pytestmark = pytest.mark.usefixtures("requires_hermes_agent")

from nemo_fabric_adapters.hermes import adapter # noqa: E402
if importlib.util.find_spec("run_agent") is not None:
from hermes_state import SessionDB
from run_agent import AIAgent

import nemo_fabric_adapters.common.utils as common_utils

from nemo_fabric_adapters.hermes import adapter


@pytest.mark.parametrize("providers", [None, {"relay": {}}])
Expand Down
17 changes: 17 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,23 @@
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))


@pytest.fixture(name="requires_harbor", scope="session")
def requires_harbor_fixture():
try:
import harbor # noqa: F401
except ModuleNotFoundError:
pytest.skip("Harbor is not installed")


@pytest.fixture(name="requires_hermes_agent", scope="session")
def requires_hermes_agent_fixture():
try:
import run_agent # noqa: F401
except ModuleNotFoundError:
pytest.skip("Hermes Agent is not installed")


@pytest.fixture(name="restore_environ", autouse=True)
def restore_environ_fixture():
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/test_harbor_swebench_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
RUN_ENV = "RUN_FABRIC_HARBOR_SWEBENCH_DOCKER"
VERIFY_ENV = "RUN_FABRIC_HARBOR_SWEBENCH_VERIFY"


@pytest.mark.usefixtures("requires_harbor")
def test_harbor_swebench_task(hermes_shim_agent_dir: Path):
if os.environ.get(RUN_ENV) != "1":
pytest.skip(f"set {RUN_ENV}=1 to run the Docker-backed SWE-Bench test")
Expand Down
8 changes: 7 additions & 1 deletion tests/e2e/test_hermes_config_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@

from __future__ import annotations

import importlib.util
from pathlib import Path

from nemo_fabric_adapters.hermes import adapter as hermes_adapter
import pytest
import yaml

pytestmark = pytest.mark.usefixtures("requires_hermes_agent")

if importlib.util.find_spec("run_agent") is not None:
from nemo_fabric_adapters.hermes import adapter as hermes_adapter


def test_hermes_config_mapping(tmp_path: Path):
hermes_home = tmp_path / "home"
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/test_hermes_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
)
from nemo_fabric import Fabric

pytestmark = pytest.mark.usefixtures("requires_hermes_agent")


class TestHermesE2E:
"""End-to-end Hermes relay assertions."""
Expand All @@ -38,8 +40,6 @@ async def run_hermes_with_relay(
code_review_agent_dir: Path,
api_server: str,
):
pytest.importorskip("run_agent", reason="hermes extra is required")

os.environ["ADAPTER_PYTHON"] = sys.executable

self.code_review_agent_dir = code_review_agent_dir
Expand Down
1 change: 1 addition & 0 deletions tests/integrations/test_harbor_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
SDK_INTEGRATION_README = ROOT / "python" / "src" / "nemo_fabric" / "integrations" / "harbor" / "README.md"
HARBOR_PACKAGE_INIT = SDK_INTEGRATION_README.parent / "__init__.py"

pytestmark = pytest.mark.usefixtures("requires_harbor")

def load_codex_adapter():
path = ROOT / "adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py"
Expand Down
65 changes: 2 additions & 63 deletions tests/python/test_harbor_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,81 +7,20 @@

import json
import shlex
import sys
import types
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import pytest


def install_harbor_stubs() -> None:
"""Install minimal Harbor stubs for this smoke when Harbor is not present."""

class BaseAgent:
def __init__(self, logs_dir: Path, *args: Any, **kwargs: Any) -> None:
self.logs_dir = logs_dir
self.model_name = kwargs.get("model_name")
self.skills_dir = kwargs.get("skills_dir")
self.mcp_servers = kwargs.get("mcp_servers", [])
self.extra_env = kwargs.get("extra_env")

class BaseEnvironment:
pass

class AgentContext:
def __init__(self) -> None:
self.metadata: dict[str, Any] | None = None

class MCPServerConfig:
def __init__(
self,
*,
name: str,
transport: str,
url: str | None = None,
command: str | None = None,
args: list[str] | None = None,
) -> None:
self.name = name
self.transport = transport
self.url = url
self.command = command
self.args = args or []

def model_dump(self, *, mode: str) -> dict[str, Any]:
assert mode == "python"
return vars(self)

modules = {
"harbor": types.ModuleType("harbor"),
"harbor.agents": types.ModuleType("harbor.agents"),
"harbor.agents.base": types.ModuleType("harbor.agents.base"),
"harbor.environments": types.ModuleType("harbor.environments"),
"harbor.environments.base": types.ModuleType("harbor.environments.base"),
"harbor.models": types.ModuleType("harbor.models"),
"harbor.models.agent": types.ModuleType("harbor.models.agent"),
"harbor.models.agent.context": types.ModuleType("harbor.models.agent.context"),
"harbor.models.task": types.ModuleType("harbor.models.task"),
"harbor.models.task.config": types.ModuleType("harbor.models.task.config"),
}
modules["harbor.agents.base"].BaseAgent = BaseAgent
modules["harbor.environments.base"].BaseEnvironment = BaseEnvironment
modules["harbor.models.agent.context"].AgentContext = AgentContext
modules["harbor.models.task.config"].MCPServerConfig = MCPServerConfig
sys.modules.update(modules)

pytestmark = pytest.mark.usefixtures("requires_harbor")

try:
from nemo_fabric.integrations.harbor import FabricAgent
from harbor.models.agent.context import AgentContext
from harbor.models.task.config import MCPServerConfig
except ImportError:
install_harbor_stubs()
from nemo_fabric.integrations.harbor import FabricAgent
from harbor.models.agent.context import AgentContext
from harbor.models.task.config import MCPServerConfig
pass


@dataclass
Expand Down
Loading
Loading