Skip to content

Latest commit

 

History

History
247 lines (188 loc) · 12.6 KB

File metadata and controls

247 lines (188 loc) · 12.6 KB

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation, integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # Pinned upstream checkouts for sample integration patches
patches/      # NeMo Relay patch sets applied to third_party checkouts
skills/       # Published Codex/agent skills for NeMo Relay usage patterns

Prerequisites

Install the tools needed for the surfaces you touch. For a full repository validation environment, install all of these:

Tool Version / Notes Required For
Rust Docs minimum is 1.86 or newer; the repo pins the active toolchain in rust-toolchain.toml Rust core, native bindings, FFI, WebAssembly
Python 3.11 or newer Python package, PyO3 builds, docs tooling
Node.js 24 or newer, with npm Node.js binding, WebAssembly JS tests, generated API docs
Go 1.21 or newer Experimental Go binding
uv Current project workflow tool Python environments, docs dependencies, pre-commit
just 1.40 or newer Canonical build, test, docs, package task runner
wasm-pack 0.14.0 or newer WebAssembly build and integration tests
cargo-deny Current stable Rust dependency auditing
cargo-nextest 0.9.111 or newer CI-style Rust test runs
cargo-llvm-cov 0.8.5 or newer CI-style coverage reports

Common setup commands:

cargo install just --locked
cargo install cargo-deny --locked
cargo install cargo-nextest --version 0.9.111 --locked
cargo install cargo-llvm-cov --version 0.8.5 --locked
cargo install wasm-pack --version 0.14.0 --locked

uv sync
uv run pre-commit install

npm install --ignore-scripts

uv sync installs Python development and test dependencies, including maturin, ruff, ty, and pre-commit. Documentation recipes sync the docs dependency group as needed, but Python, Node.js, npm, uv, and just still need to exist on PATH.

Build, Test, And Docs Commands

Prefer the repository just recipes over raw tool commands. Use raw cargo, pytest, go test, npm, or wasm-pack commands only for focused debugging or targeted single-test reruns that do not have a just recipe.

Discover the current task surface with:

just --list

Build targets:

just build-rust
just build-python
just build-node
just build-go
just build-wasm
just build-all

Test targets:

just test-rust
just ci=true test-rust       # CI-style Rust test run; uses nextest and coverage tooling when available
just test-python
just test-node
just test-go
just test-wasm
just test-all

Documentation targets:

just docs
just docs-api-reference

Package targets:

just package-python
just package-node
just package-wasm

Cleanup:

just clean

Focused fallback commands are acceptable for narrow loops:

cargo test -p nemo-relay -- <test_name>
uv run pytest python/tests/test_scope.py
uv run pytest -k "test_name"
cd crates/node && node --test --test-name-pattern="pattern" tests/*.mjs
cd go/nemo_relay && go test -v -run TestFoo ./...
wasm-pack test --node crates/wasm

Validation Expectations

Run tests for every language affected by a change. If you touch the Rust core runtime, middleware semantics, event shape, scope behavior, typed codecs, plugins, or observability, expect to validate every affected binding because the bindings share the same runtime contract.

Minimum guidance:

  • Rust core or adaptive changes: just test-rust; add binding tests when public behavior changes.
  • Python binding or wrapper changes: just test-python.
  • Node.js binding or wrapper changes: just test-node.
  • Go binding or raw FFI changes: just test-go and the relevant Rust/FFI checks.
  • WebAssembly binding changes: just test-wasm.
  • Documentation site changes: just docs. Run docs link validation with just docs-linkcheck when links change. The recipes regenerate the ignored Fern API reference pages before validation.
  • Cross-language API changes: run the touched binding tests and update docs, package READMEs, and generated surfaces where applicable.

Before review, prefer uv run pre-commit run --all-files when the change crosses languages or tooling. The hooks enforce SPDX headers, file hygiene, Ruff, ty, Markdown link checks, Cargo formatting/lints/audits, Go formatting/vet, Node formatting, and public docstring checks.

Key Conventions

These conventions keep source, documentation, and binding behavior consistent across the repository.

  • Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.
  • SKILL.md files are skill entrypoints and do not need SPDX headers, but they must always start with YAML frontmatter containing at least name and description.
  • Follow binding naming conventions: Rust and Python snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase for public APIs, Node.js camelCase.
  • Preserve the shared runtime model across bindings. Do not add behavior to one primary binding without considering Rust, Python, and Node.js parity.
  • Prefer documented public APIs and stable wrapper commands. Do not rely on internal helpers in examples or user-facing docs.
  • Keep primary documentation focused on Rust, Python, and Node.js. Treat Go, WebAssembly, and raw FFI as experimental and source-first unless binding-support guidance changes.
  • Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
  • Use Result<T> with FlowError in core runtime paths. Keep errors explicit and binding-appropriate at the wrapper layer.
  • Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
  • Do not hand-edit generated or packaged outputs unless the repository workflow expects them to be checked in. Regenerate through the documented recipe or script.

Runtime Patterns

These runtime patterns describe the shared semantics that bindings and integrations must preserve.

  • Scope stacks are hierarchical and always have a root scope. They establish parent-child event relationships, visibility for scope-local middleware and subscribers, cleanup boundaries, and concurrent request isolation.
  • Scope-local middleware and subscribers are owned by a scope and disappear when that scope closes. Global registrations stay process-wide until removed.
  • Middleware is priority-ordered after merging global and visible scope-local entries.
  • Intercepts change the real execution path. Request intercepts rewrite the request. Execution intercepts wrap or replace the callback. Stream execution intercepts handle streaming lifecycle behavior.
  • Guardrails either block execution or sanitize emitted observability payloads. Sanitize guardrails do not rewrite the real callback arguments or return value.
  • Managed execution order is conditional guardrails, request intercepts, sanitize-request guardrails for start events, execution intercepts, callback execution, then sanitize-response guardrails for end events.
  • Events use ATOF 0.1 as the canonical event format. Scope events use start/end pairs; mark events record runtime checkpoints.
  • LLM and tool event metadata belongs in the category profile, such as model_name, tool_call_id, and custom subtype fields.
  • Exporters can transform runtime events to ATIF trajectories, OpenTelemetry traces, or OpenInference-compatible output. Root scope identity is used to isolate concurrent agents.

Binding Notes

These notes summarize how each language binding relates to the Rust runtime source of truth.

  • Rust is the source of truth for runtime behavior. Binding APIs should mirror the Rust semantics unless a language-specific wrapper intentionally improves ergonomics.
  • Python wrapper modules live under python/nemo_relay/; the native extension is built from crates/python with maturin.
  • Node.js public entry points include the main runtime package plus nemo-relay-node/typed, nemo-relay-node/plugin, and nemo-relay-node/adaptive.
  • Go uses the C FFI and requires the FFI library build before tests; just test-go handles the library path setup.
  • WebAssembly includes Rust wasm-bindgen tests plus JS wrapper/package tests; just test-wasm runs both paths.

Third-Party Integrations And Patches

Patch-based Integrations

Sample integrations are maintained as patch sets, not as primary package source. The pinned upstream checkouts are listed in third_party/sources.lock, local checkouts live under third_party/, and NeMo Relay patches live under patches/.

Current integration patch sets include:

  • Hermes Agent: third_party/hermes-agent
  • LangChain: third_party/langchain
  • LangChain NVIDIA: third_party/langchain-nvidia
  • LangGraph: third_party/langgraph
  • OpenClaw: third_party/openclaw
  • opencode: third_party/opencode

Use the stable root-level wrappers:

./scripts/bootstrap-third-party.sh
./scripts/apply-patches.sh
./scripts/apply-patches.sh --check
./scripts/generate-patches.sh

apply-patches.sh expects clean third-party checkouts. After editing an integration checkout, run ./scripts/generate-patches.sh to regenerate patch files and verify they apply to a clean detached checkout.

Public API-based Integrations

Some integrations can be implemented using public APIs without patching. Currently the Python based integrations are located under python/nemo_relay/integrations/ with their own README files and test suites.

Current public API-based integrations include:

  • LangChain: python/nemo_relay/integrations/langchain

Documentation And Contribution Workflow

These workflow notes keep public documentation, examples, and PR preparation aligned with repository expectations.

  • Update README.md, fern/, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change.
  • Keep release-process details in maintainer docs such as RELEASING.md. Do not move release-history policy into user-facing docs or CHANGELOG.md.
  • Keep stable public wrappers at the scripts/ root in docs and examples. Reference namespaced helper paths only when documenting internal maintenance work.
  • Use branch prefixes from the contributor docs: feat/, fix/, docs/, test/, or refactor/.
  • Use signed-off commits for PR work: git commit -s.
  • Before creating, opening, publishing, or editing a pull request, read .github/pull_request_template.md and use it as the PR body skeleton. Preserve its visible headings, checklist items, and related-issue guidance; fill the sections instead of replacing them with a generic summary.
  • If repo-local PR guidance such as the prepare-pr skill conflicts with generic GitHub connector or plugin guidance, follow the repo-local PR guidance for PR body format and review handoff details.
  • PR descriptions should include what changed, why, how it was tested, and any breaking changes within the repository template format.