Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2cfc7bd
Drop support for Python 3.10
dagardner-nv Jul 7, 2026
810d539
Begin tracking the uv.lock file
dagardner-nv Jul 7, 2026
8525fb4
Update docs
dagardner-nv Jul 7, 2026
ca74415
Remove unused/redundant pyproject.toml file
dagardner-nv Jul 7, 2026
01ebeb1
First pass at project files for adapters
dagardner-nv Jul 7, 2026
167fd64
WIP
dagardner-nv Jul 7, 2026
c79ad9a
Move adapter dependencies down into the toml files for the adapters
dagardner-nv Jul 7, 2026
fd753b8
split python packages. The {version} placeholder is setuptools specif…
dagardner-nv Jul 7, 2026
13a1fa3
Remove sys.path manipulation
dagardner-nv Jul 7, 2026
859d37f
Remove old python 3.10 work-around
dagardner-nv Jul 7, 2026
0109f8a
Remove sys.path manipulation fixtures
dagardner-nv Jul 7, 2026
b92d6e9
Add jsut recipe for building wheels, and a CI stage
dagardner-nv Jul 7, 2026
6b2c617
Add a just recipe for tracking uv.lock files
dagardner-nv Jul 7, 2026
13a7de0
Add lock files
dagardner-nv Jul 7, 2026
905e889
Remove -> None return hints
dagardner-nv Jul 7, 2026
475fe17
Add runtime extra
dagardner-nv Jul 7, 2026
345b369
Docs is a group not an extra
dagardner-nv Jul 7, 2026
b47d278
Set entry points for adapter wheels
dagardner-nv Jul 7, 2026
bed27ab
WIP
dagardner-nv Jul 7, 2026
13bff58
Remove command, depend on #! in script
dagardner-nv Jul 7, 2026
6c9d927
Set +x
dagardner-nv Jul 7, 2026
ac1cf09
Run the adapter directly
dagardner-nv Jul 7, 2026
911b286
Set 'fetch-depth: 0' for checkouts that require version tags
dagardner-nv Jul 7, 2026
aa4b391
Ensure that the adapters are built/installed by default
dagardner-nv Jul 8, 2026
5db8b39
Consolidate the projects array
dagardner-nv Jul 8, 2026
ebe35fb
Add build dirs to the clean script [skip ci]
dagardner-nv Jul 8, 2026
5de8302
Revert changes to runtime.rs
dagardner-nv Jul 8, 2026
e91f091
Update the adapters to be installed as modules
dagardner-nv Jul 8, 2026
fcfd556
Set adapters to python
dagardner-nv Jul 8, 2026
f5eedc7
Change the python adapter invocation from 'python <script>' to 'pytho…
dagardner-nv Jul 8, 2026
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
52 changes: 48 additions & 4 deletions .github/workflows/ci_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ concurrency:
group: ci-python-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

defaults:
run:
shell: bash
Expand All @@ -24,6 +21,8 @@ jobs:
name: Test
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
Expand Down Expand Up @@ -56,9 +55,54 @@ jobs:
- name: Build SDK with native extension
run: |
uv venv --python 3.12 .venv
uv sync --group test --no-group dev --extra harbor --extra hermes --extra relay
uv sync --group test --no-group dev --extra codex --extra harbor --extra hermes --extra relay --extra runtime

- name: Run pytest
run: |
set -euo pipefail
just test-python

build-wheels:
name: Build wheels
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
fetch-depth: 0

- name: Set up Rust
uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4
with:
toolchain: stable
cache: false

- name: Cache cargo build
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
shared-key: fabric-python-wheels

- name: Install just
uses: taiki-e/install-action@c070f87102a1c75b3183910f391c1cb887fe13c8 # v2.77.6
with:
tool: just@1.50.0

- name: Set up uv
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Build wheels
run: just wheels
Comment thread
dagardner-nv marked this conversation as resolved.

- name: Upload wheels
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: python-wheels
path: dist/*.whl
if-no-files-found: error
1 change: 1 addition & 0 deletions .github/workflows/fern-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
fetch-depth: 0

- name: Install just
uses: taiki-e/install-action@c070f87102a1c75b3183910f391c1cb887fe13c8 # v2.77.6
Expand Down
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,3 @@ integrations/harbor/demo/task/environment/vendor/

# vscode settings
.vscode/

# UV Lock
# TBD if we want to track this in git
uv.lock
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ and runs one input through the Hermes SDK adapter.
Prerequisites:

- Rust and Cargo
- Python 3.10+ for Fabric
- Python 3.11-3.13 for Hermes
- Python 3.11+ for Fabric
- Python 3.11-3.13 for Hermes Agent
- [uv](https://docs.astral.sh/uv/getting-started/installation/)
- `just` 1.50.0+
- `NVIDIA_API_KEY` for NVIDIA-hosted model access
Expand Down
2 changes: 1 addition & 1 deletion adapters/codex-cli/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Codex CLI Adapter

Runs an installed Codex CLI through Fabric's process-adapter lifecycle. The
Runs an installed Codex CLI through Fabric's Python-adapter lifecycle. The
same adapter supports one-shot and session runtime modes.

Install Fabric with the adapter dependency before running it:
Expand Down
7 changes: 3 additions & 4 deletions adapters/codex-cli/fabric-adapter.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
{
"adapter_id": "nvidia.fabric.codex.cli",
"harness": "codex",
"adapter_kind": "process",
"adapter_kind": "python",
"runner": {
"command": "python3",
"script": "src/nemo_fabric_adapters/codex_cli/adapter.py",
"stdin_payload": "fabric_request"
"module": "nemo_fabric_adapters.codex_cli.adapter",
"callable": "run_codex"
},
"requirements": {
"binaries": ["codex"]
Expand Down
37 changes: 37 additions & 0 deletions adapters/codex-cli/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

[build-system]
requires = [
"setuptools>=64",
"setuptools-scm>=8",
"setuptools_dynamic_dependencies>=1.0.0",
]
build-backend = "setuptools.build_meta"

[project]
name = "nemo-fabric-adapters-codex-cli"
description = "Codex CLI adapter for NeMo Fabric"
readme = "README.md"
requires-python = ">=3.11"
dynamic = ["version", "dependencies"]

[tool.setuptools.packages.find]
where = ["src"]
include = ["nemo_fabric_adapters.codex_cli*"]

[tool.setuptools.data-files]
"share/nemo-fabric/adapters/codex-cli" = ["fabric-adapter.json"]

[tool.setuptools_dynamic_dependencies]
dependencies = [
"nemo-fabric-adapters-common == {version}",
"tomli-w~=1.2",
]

[tool.uv.sources]
nemo-fabric-adapters-common = { path = "../common" }

[tool.setuptools_scm]
root = "../.."
git_describe_command = "git describe --long --first-parent"
16 changes: 2 additions & 14 deletions adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,17 @@
import os
import socket
import subprocess
import sys
import time
import tomllib
import urllib.error
import urllib.request
from collections.abc import Mapping
from pathlib import Path
from typing import Any, NamedTuple

import nemo_fabric_adapters.common.utils as common_utils
import tomli_w

try:
import tomllib
except ModuleNotFoundError: # pragma: no cover - exercised on Python 3.10
import tomli as tomllib

CUR_DIR = Path(__file__).parent
ADAPTERS_DIR = CUR_DIR.parent.parent.parent.parent
COMMON_DIR = (ADAPTERS_DIR / "common/src").resolve().as_posix()
if COMMON_DIR not in sys.path:
sys.path.append(COMMON_DIR)

import nemo_fabric_adapters.common.utils as common_utils # noqa: E402

SANDBOXES = {"read-only", "workspace-write", "danger-full-access"}
DEFAULT_TIMEOUT_SECONDS = 1800
RELAY_HEALTH_TIMEOUT_SECONDS = 30
Expand Down
30 changes: 30 additions & 0 deletions adapters/codex-cli/uv.lock

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

24 changes: 24 additions & 0 deletions adapters/common/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

[build-system]
requires = [
"setuptools>=64",
"setuptools-scm>=8",
"setuptools_dynamic_dependencies>=1.0.0",
]
build-backend = "setuptools.build_meta"

[project]
name = "nemo-fabric-adapters-common"
description = "Shared Python helpers for NeMo Fabric adapters"
requires-python = ">=3.11"
dynamic = ["version"]

[tool.setuptools.packages.find]
where = ["src"]
include = ["nemo_fabric_adapters.common*"]

[tool.setuptools_scm]
root = "../.."
git_describe_command = "git describe --long --first-parent"
7 changes: 7 additions & 0 deletions adapters/common/uv.lock

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

30 changes: 13 additions & 17 deletions adapters/hermes-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@ SPDX-License-Identifier: Apache-2.0

# Hermes CLI Adapter

This adapter runs Hermes through the installed `hermes` CLI. It is the process
path for local debugging, `fabric run`, and environment-backed consumers that
want Fabric to invoke a command and capture stdout, stderr, exit code, logs, and
artifacts.
This adapter runs Hermes through the installed `hermes` CLI. Fabric starts the
adapter as a Python module for local debugging, `fabric run`, and
environment-backed consumers. The adapter then invokes Hermes and captures
stdout, stderr, exit status, logs, and artifacts.

Unlike the Hermes SDK adapter, this adapter intentionally does not advertise
`runner.module` or `runner.callable`. Fabric invokes a small launcher as a
process. The launcher reads the Fabric invocation from `FABRIC_INVOCATION`,
falls back to stdin for simple/debug runs, writes Hermes-native config, and
then launches the real Hermes CLI.
Fabric invokes `runner.module` with `python -m`. The module reads the Fabric
invocation from standard input, writes Hermes-native config, and then launches
the real Hermes CLI. `runner.callable` records the corresponding reusable
Python function.

## What It Maps

Expand All @@ -29,17 +28,14 @@ before calling the CLI. It maps:

## Maintaining The Adapter

Keep `fabric-adapter.json` aligned with the process implementation:
Keep `fabric-adapter.json` aligned with the Python implementation:

- `adapter_id` is the stable id selected by `harness.adapter_id`.
- `adapter_kind` is `process` because Fabric owns process supervision,
stdout/stderr capture, exit status, logs, and artifacts.
- `runner.command` and `runner.script` define the launcher process. The script
reads Fabric payload JSON from `FABRIC_INVOCATION`, with stdin as fallback,
and writes JSON output to stdout.
- `adapter_kind` is `python` because Fabric invokes the adapter with Python.
- `runner.module` names the module that Fabric invokes with `python -m`.
`runner.callable` names the equivalent reusable Python function.
- Harness settings should use `hermes_command` and `hermes_args` for the actual
Hermes CLI command and arguments. Do not use `command` for Hermes itself;
`command` belongs to the Fabric process runner.
Hermes CLI command and arguments.
- `requirements` powers `fabric doctor`; keep required env vars and the `hermes`
binary requirement current.
- `config.accepts` must match the Fabric sections this adapter maps into Hermes.
Expand Down
7 changes: 3 additions & 4 deletions adapters/hermes-cli/fabric-adapter.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
{
"adapter_id": "nvidia.fabric.hermes.cli",
"harness": "hermes",
"adapter_kind": "process",
"adapter_kind": "python",
"runner": {
"command": "python3",
"script": "src/nemo_fabric_adapters/hermes_cli/adapter.py",
"stdin_payload": "fabric_request"
"module": "nemo_fabric_adapters.hermes_cli.adapter",
"callable": "run_hermes_cli"
},
"requirements": {
"env": ["NVIDIA_API_KEY"],
Expand Down
38 changes: 38 additions & 0 deletions adapters/hermes-cli/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

[build-system]
requires = [
"setuptools>=64",
"setuptools-scm>=8",
"setuptools_dynamic_dependencies>=1.0.0",
]
build-backend = "setuptools.build_meta"

[project]
name = "nemo-fabric-adapters-hermes-cli"
description = "Hermes CLI adapter for NeMo Fabric"
readme = "README.md"
requires-python = ">=3.11"
dynamic = ["version", "dependencies"]

[tool.setuptools.packages.find]
where = ["src"]
include = ["nemo_fabric_adapters.hermes_cli*"]

[tool.setuptools.data-files]
"share/nemo-fabric/adapters/hermes-cli" = ["fabric-adapter.json"]

[tool.setuptools_dynamic_dependencies]
dependencies = [
"nemo-fabric-adapters-common == {version}",
"pyyaml>=6.0", # needed for writing hermes config files
"tomli-w~=1.2", # Needed by adapters to write relay config files
]

[tool.uv.sources]
nemo-fabric-adapters-common = { path = "../common" }

[tool.setuptools_scm]
root = "../.."
git_describe_command = "git describe --long --first-parent"
11 changes: 2 additions & 9 deletions adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,11 @@
import json
import os
import subprocess
import sys
from pathlib import Path
from typing import Any

CUR_DIR = Path(__file__).parent
ADAPTERS_DIR = CUR_DIR.parent.parent.parent.parent
COMMON_DIR = (ADAPTERS_DIR / "common/src").resolve().as_posix()
if COMMON_DIR not in sys.path:
sys.path.append(COMMON_DIR)

import nemo_fabric_adapters.common.hermes as hermes_common # noqa: E402
import nemo_fabric_adapters.common.utils as common_utils # noqa: E402
import nemo_fabric_adapters.common.hermes as hermes_common
import nemo_fabric_adapters.common.utils as common_utils


def main() -> None:
Expand Down
Loading
Loading