> This page is for NeMo Fabric, version v0.6.0.
> For other versions, use one of these documentation indexes:
> - Latest (v0.6.0) (default): https://docs.nvidia.com/nemo/fabric/latest/llms.txt
> - dev: https://docs.nvidia.com/nemo/fabric/dev/llms.txt
> - v0.6.0: https://docs.nvidia.com/nemo/fabric/v0.6.0/llms.txt
> - v0.5.0: https://docs.nvidia.com/nemo/fabric/v0.5.0/llms.txt
> - v0.4.0: https://docs.nvidia.com/nemo/fabric/v0.4.0/llms.txt
> - v0.3.0: https://docs.nvidia.com/nemo/fabric/v0.3.0/llms.txt
> - v0.2.0: https://docs.nvidia.com/nemo/fabric/v0.2.0/llms.txt
> - v0.1.1: https://docs.nvidia.com/nemo/fabric/v0.1.1/llms.txt
> - v0.1.0: https://docs.nvidia.com/nemo/fabric/v0.1.0/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/fabric/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/fabric/_mcp/server.

# Use the Pi Adapter

> Install and configure the NVIDIA NeMo Fabric harness integration for Pi.

Use the NVIDIA NeMo Fabric `nvidia.fabric.pi` adapter to run tasks with the Pi
SDK in a persistent Node.js process.

## Invocation Usage

Each invocation reads only new entries from Pi's raw session ledger, including
retained failed attempts, compaction and branch summaries, nested tool usage,
and auxiliary usage records. Automatic cache warming is disabled so background
refresh calls cannot escape the invocation boundary. Session history and previous invocations are not
counted again. Input totals include cache reads and cache writes; output totals
already include reasoning tokens. Available breakdowns are returned through
`nemo_fabric.detailed_token_usage.v1`.

Missing or inconsistent usage leaves the complete counters unknown. Pi 1.0.3
initializes unreported provider usage to zero, so an all-zero record also remains
unknown. A failed or retried summary can omit attempt usage from the ledger;
complete counters remain unknown in that case. Nonzero records can retain real
zero input, output, or cache dimensions. Configured-rate SDK cost estimates are
not published as provider-reported `cost_usd`.

## Install the Adapter

Pi requires Node.js 22.19.0 or newer.

### Install for Consumers

Install the npm adapter in the project that owns the NeMo Fabric configuration,
then install the supported Pi SDK package set:

```bash
npm install nemo-fabric-adapters-pi
npm install --save-exact @earendil-works/pi-ai@1.0.3 @earendil-works/pi-coding-agent@1.0.3 @earendil-works/pi-mcp@1.0.3 @earendil-works/pi-codemode@1.0.3
```

The Pi packages are optional peers: installing `nemo-fabric-adapters-pi` alone
does not install the harness. Starting the adapter without compatible Pi
packages returns `pi_harness_unavailable`.

The supported SDK packages are pinned together at 1.0.3. The coding-agent
package otherwise permits newer `pi-codemode` versions with incompatible exports;
installing all four packages keeps fresh installs on the tested combination.

### Install for Source Development

For focused Pi development in the NeMo Fabric source tree, install the Pi
adapter workspace and its pinned harness from the repository root:

```bash
just install-typescript-pi
```

To install and build all maintained TypeScript workspaces instead, run:

```bash
just build-typescript
```

The full build installs the required dependencies.

### Install NeMo Relay

Relay-enabled Pi runs require a NeMo Relay CLI in the `>=0.9.0,<0.12.0` range. Install the command-line tool:

```bash
pip install "nemo-relay-cli-bin>=0.9.0,<0.12.0"
```

The adapter finds `nemo-relay` on `PATH` by default. To select a specific executable, set `FABRIC_NEMO_RELAY_COMMAND` to its absolute path:

```bash
FABRIC_NEMO_RELAY_COMMAND="/absolute/path/to/nemo-relay" uv run python your_app.py
```

The adapter uses the selected executable to start the Relay gateway. With Relay 0.11, the Pi extension also uses that executable to forward hook events. Relay 0.9 and 0.10 extensions send hook events directly over HTTP. When the runtime stops, the adapter restores the extension's previous command setting.

Use the [Pi extension](https://github.com/NVIDIA/NeMo-Relay/tree/0.11.0/crates/cli/assets/pi-extension) from the same Relay release as the command-line tool. Configure the extension path as shown in the next section.

## Configure the Adapter

Select the Pi harness integration and point NeMo Fabric discovery at the
adapter descriptor. The following example uses the descriptor from an installed
npm package:

```python
from nemo_fabric import DiscoveryConfig, FabricConfig, HarnessConfig
from nemo_fabric import MetadataConfig, ModelConfig, ToolsConfig

config = FabricConfig(
    metadata=MetadataConfig(name="pi-review"),
    discovery=DiscoveryConfig(
        local_paths=[
            "./node_modules/nemo-fabric-adapters-pi/pi.fabric-adapter.json"
        ]
    ),
    harness=HarnessConfig(adapter_id="nvidia.fabric.pi"),
    models={
        "default": ModelConfig(
            provider="nvidia",
            model="nvidia/nemotron-3-nano-omni-30b-a3b-reasoning",
            api_key_env="NVIDIA_API_KEY",
            base_url="https://integrate.api.nvidia.com/v1",
        )
    },
    tools=ToolsConfig(enabled=["read"], blocked=[]),
)
```

For a source build, set `discovery.local_paths` to
`adapters/typescript/pi/pi.fabric-adapter.json` instead.

The adapter supports one selected model, an optional base URL override, `replace`
system instructions, tool policy, explicit skill paths, and explicit local Pi
extensions. Set `models.<role>.api_key_env` to the name of the environment
variable that contains the provider credential.

The selected model can be either a model Pi already ships (addressed by its
built-in `provider`/`model`) or a model served by an OpenAI-compatible gateway
that Pi does not ship. For a Pi-known model, set only `provider`, `model`, and
`api_key_env`; its catalog metadata is used as-is, and an optional `base_url`
redirects its endpoint without changing any other property. A `provider`/`model`
pair that does not exactly match a Pi built-in is treated as a new
gateway-served model (there is no aliasing onto a built-in by a different id).

For a gateway model, set `base_url` to the gateway endpoint and declare the
model's wire protocol with the model's `api` field. This value is the
protocol your gateway speaks, not the model's vendor: a Bedrock- or
Anthropic-trained model served over an OpenAI-compatible `/v1/chat/completions`
endpoint uses `openai-completions`; use `anthropic-messages` only when the
endpoint speaks Anthropic's native Messages API. `api` is required for
a model Pi does not already know — the adapter does not guess it — and must be
one of:

* `openai-completions`
* `openai-responses`
* `azure-openai-responses`
* `openai-codex-responses`
* `anthropic-messages`
* `bedrock-converse-stream`
* `google-generative-ai`
* `google-vertex`
* `mistral-conversations`
* `pi-messages`

The following Pi-specific model fields are set directly on the model alongside `api`:

* `context_window` — the model's context window, in tokens.
* `max_tokens` — the maximum response length, in tokens.
* `cost` — an object with `input`, `output`, `cacheRead`, and `cacheWrite`
  per-million-token rates.
* `reasoning` — whether the model supports reasoning/thinking.
* `input` — the accepted input modalities, a list of `"text"` and/or `"image"`.
* `name` — a display name for the model.

Any field you set overrides. For a Pi-known model, any field you omit keeps that model's existing catalog value; the adapter supplies no defaults of its own. A gateway model Pi does not ship has no existing entry to fall back on, so a complete definition is required: `api`, `base_url`, `context_window`, `max_tokens`, `input`, and `cost`. Adapter startup rejects missing required metadata before invoking the model. The `reasoning` and `name` fields remain optional. The example below registers an NVIDIA Nemotron model from the OpenAI-compatible [NVIDIA Build](https://build.nvidia.com) API, which Pi does not ship:

```python
from nemo_fabric import ModelConfig

# api is the gateway's wire protocol: NVIDIA Build serves this model over an
# OpenAI-compatible endpoint.
ModelConfig(
    provider="nvidia-build",
    model="nvidia/nemotron-3-super-120b-a12b",
    api_key_env="NVIDIA_API_KEY",
    base_url="https://integrate.api.nvidia.com/v1",
    api="openai-completions",
    context_window=256_000,
    max_tokens=32_768,
    reasoning=True,
    input=["text"],
    cost={"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0},
    name="Nemotron 3 Super 120B (NVIDIA Build)",
)
```

The example uses zero cost rates, so Pi calculates zero token cost. Set the rates for your endpoint when you need cost estimates.

## Configure Skills and Extensions

Add normalized skill paths with the Python SDK. NeMo Fabric resolves these
paths from the `base_dir` passed to `plan()`, `doctor()`, or `run()`:

```python
config.add_skill_path("./skills/code-review")
```

Pi extensions are adapter-specific trusted code. Configure extension files
relative to `environment.workspace`:

```python
config.harness.settings["extensions"] = ["extensions/review.ts"]
```

The adapter loads only the skill and extension paths in the configuration. It
does not load ambient Pi resources from the user profile or workspace.

## Configure MCP Servers

Add normalized MCP servers through the Python SDK:

```python
config.add_mcp_server(
    "repo",
    transport="stdio",
    url="repo-mcp",
    args=["--root", "."],
    allowed_tools=["search", "read_file"],
    exposure="harness_native",
)
config.add_mcp_server(
    "docs",
    transport="streamable-http",
    url="https://mcp.example.com/mcp",
    custom_headers={"Authorization": "Bearer ${MCP_TOKEN}"},
    exposure="harness_native",
)
```

The adapter maps stdio and streamable HTTP servers to Pi's native MCP extension. Stdio servers can define `args` and `env`, but not HTTP headers. Streamable HTTP servers can define `custom_headers`, but not process arguments or environment variables. Remote endpoints require HTTPS, except for loopback development endpoints.

Header values can reference `${NAME}`. Resolution checks the NeMo Fabric runtime environment first and then the adapter process environment. The adapter maps `allowed_tools` and `blocked_tools` to Pi's per-server tool exposure. Normalized MCP authentication and MCP extensions are not supported. Only servers supplied by NeMo Fabric are loaded; ambient Pi MCP configuration remains disabled.

## Configure NeMo Relay

Enable Relay with the standard NeMo Fabric configuration and provide the Relay
Pi extension as an adapter setting:

```python
config.runtime.artifacts = "./artifacts/pi"
config.enable_relay(output_dir="./artifacts/relay")
config.harness.settings["relay_extension_path"] = (
    "/path/to/NeMo-Relay/crates/cli/assets/pi-extension"
)
```

Relay requires `runtime.artifacts` so NeMo Fabric can create the runtime-owned
configuration passed to the adapter. The extension path can be absolute or
relative to `environment.workspace`. It can identify a JavaScript or TypeScript
file or a Pi extension package directory. Unlike user-configured Pi extensions,
the Relay extension does not need to remain inside `environment.workspace` when
an absolute path is used.

When the runtime starts, the adapter validates the Relay CLI version, writes an
explicit `plugins.toml`, starts a loopback gateway, and loads the extension into
the isolated Pi session. The result includes `relay_runtime` and
`relay_artifacts` in `output`. The gateway can produce ATOF, ATIF,
OpenTelemetry, and OpenInference output from the Relay observability
configuration.

The adapter supports one Relay-enabled Pi runtime per adapter process because
the Relay Pi extension receives its gateway and upstream configuration through
process environment variables. NeMo Fabric starts each runtime in a separate
adapter process, so concurrent NeMo Fabric runtimes remain isolated. Direct
embedders must likewise place concurrent Relay-enabled Pi runtimes in separate
processes.

The matching Relay Pi extension emits `turn_end` and closes the per-turn
scope, but its local ATIF trajectory remains attached to Pi's session-level
agent scope. Relay therefore finalizes that trajectory only after the Pi session
closes. It is not included in the invocation's `relay_artifacts`. The adapter
does not wait for local ATIF during invocation. It collects per-invocation
artifacts such as ATOF and returns the result. After runtime shutdown, retrieve
the finalized file directly from the ATIF output directory. For the
configuration above and the default filename template, it is written to
`./artifacts/relay/<runtime_id>/trajectory-<session_id>.atif.json`. Invocation
results do not prevent subsequent turns.

Session, turn, and tool telemetry does not depend on model redirection. Model
telemetry is available only when Relay supports the selected model API and the
gateway upstream matches the model endpoint. Relay records a skipped redirect
as a `model_redirect` mark with the reason in configured ATOF artifacts. Pi
emits its startup redirect marks before NeMo Fabric registers an invocation, so
they are not included in the per-invocation records from `invoke_stream()`.

Install the matching collector for the embedded streaming path:

```bash
pip install "nemo-fabric[streaming]"
```

Start the runtime with streaming enabled to consume live model-turn ATOF
records for successful Relay redirects in one Pi invocation:

```python
from nemo_fabric import Fabric

async with await Fabric().start_runtime(config, streaming=True) as runtime:
    stream = runtime.invoke_stream(input="Review the latest patch")
    async for record in stream:
        print(record)
    result = await stream.result()
```

The terminal `RunResult` remains separate from the ATOF records. Fully consume
each stream, or call `await stream.aclose()` if iteration stops early, before
starting another invocation. The same runtime can then alternate
`invoke_stream()` and `invoke()` calls. The embedded collector serializes both
methods behind one Pi invocation lease. Streaming capture begins at the first
Pi `turn_start` and closes at `agent_settled`. If Relay output is interrupted or
late, the collector waits for a bounded interval, then admits the next native
invocation and uses Pi's cumulative turn count to discard ambiguous delayed
records until a higher `turn_start` arrives. This can thin the ATOF stream but
does not block later invocations. Increase `completion_wait_timeout` from its
one-second default when Relay delivery can take longer. Use the default embedded
collector for Pi streaming. The Pi extension does not attach NeMo Fabric
request IDs, so
`start_runtime(..., streaming=True, launch_collector=False)` cannot correlate
its records through an externally managed collector.

This Relay-backed path runs the adapter's ordinary `invoke` operation. It is
independent of native OpenAI streaming, so the adapter descriptor's
`capabilities.streaming` value remains `false`.

## Configure a NeMo Fabric Tool Definition

Pi accepts trusted local JavaScript and TypeScript tool factories. The
normalized definition uses `kind: "module"`; `ref` is relative to the NeMo Fabric
workspace and may include a named export after `#`.

Create `tools/review-context.js` in the configured workspace:

```javascript
export function createTool({ name, settings }) {
  return {
    name,
    label: "Review Context",
    description: "Return configured context for a code review.",
    parameters: { type: "object", properties: {} },
    async execute() {
      return {
        content: [{ type: "text", text: JSON.stringify(settings) }],
        details: {},
      };
    },
  };
}
```

Register and enable the tool:

```python
config.add_tool_definition(
    "review_context",
    kind="module",
    ref="tools/review-context.js#createTool",
    settings={"format": "brief"},
)
config.tools.enabled = ["read", "review_context"]
```

The factory receives `{ name, settings, workspace }` and returns a Pi
`ToolDefinition` with the same name. Tool modules and explicit Pi extensions
run as trusted code in the adapter process. The adapter rejects paths outside
the workspace and duplicate names across built-ins, NeMo Fabric definitions,
and extensions.

## Understand the Runtime Lifecycle

Each NeMo Fabric runtime owns one in-memory Pi session in a persistent Node.js
adapter process. Ordered invocations reuse that session and its conversation
history until the runtime stops. Start a new runtime to change the model,
skills, extensions, custom tools, or workspace.

## Current Limitations

The current adapter does not expose native OpenAI streaming, caller-driven
cancellation, or remote-service execution. Enabling a third-party extension
can add extension-defined skills, prompts, or themes even though ambient Pi
resources remain disabled. The Relay extension does not add those resources.

The Relay Pi hook route is available only on the loopback gateway and does not
authenticate hook posts. Treat the loopback port as a local trust boundary.

## Credential-free HTTP model gateways

For Relay-enabled runs, set `harness.settings.insecure_http_model_hosts` to the exact bare DNS hostnames or IP addresses of trusted inference gateways. This requires NeMo Relay CLI 0.11.x.

Remote HTTP model endpoints require `openai-completions`, `openai-responses`, or `anthropic-messages`. Other Pi model APIs, including `mistral-conversations`, require HTTPS or loopback HTTP because their requests bypass Relay's gateway. Fabric rejects unsupported remote HTTP APIs before session startup, including hosts listed in `insecure_http_model_hosts`.

```python
config.harness.settings["insecure_http_model_hosts"] = ["inference.internal", "10.0.0.10"]
```

Entries have no scheme, port, path, wildcard, brackets, or IPv6 zone identifier. DNS matching ignores case and a trailing dot. Each listed host may use custom ports and paths. NeMo Fabric writes explicit `[]` to revoke inherited trust and preserves omission when the setting is absent. System Relay configuration has higher priority and can enforce an empty list.

Relay strips authorization, API-key, cookie, custom credential headers, and middleware-added credentials before forwarding remote HTTP model requests, including model credentials supplied to Pi. Relay rejects conflicting upstream authorization configuration. Prompts and responses remain unencrypted. HTTPS is the default; this opt-in does not authorize MCP or observability endpoints, caller-selected destinations, or remote HTTP WebSockets.