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

# OpenInference

Use the `openinference` section when you want NeMo Relay lifecycle events
exported as OTLP trace spans with OpenInference-oriented semantics.

OpenInference export maps model-centric payloads directly into trace
attributes. Scope, tool, and LLM start inputs become `input.value`; end outputs
become `output.value`; LLM usage metadata maps to token-count attributes when
the provider response includes usage information. For LLM spans, NeMo Relay
emits flattened request and response message attributes from typed codec
annotations and supported replay request/response payloads. Typed codec
annotations also provide tool schema, finish-reason, and invocation-parameter
attributes when those fields are available.

## `plugins.toml` Example

```toml
version = 1

[[components]]
kind = "observability"
enabled = true

[components.config]
version = 1

[components.config.openinference]
enabled = true
transport = "http_binary"
endpoint = "http://localhost:6006/v1/traces"
service_name = "agent-service"
service_namespace = "nemo"
service_version = "1.0.0"
instrumentation_scope = "nemo-relay-openinference"
timeout_millis = 3000

[components.config.openinference.headers]
authorization = "Bearer <token>"

[components.config.openinference.resource_attributes]
"deployment.environment" = "dev"
```

This configuration registers a plugin-owned OpenInference subscriber and sends
OpenInference-style OTLP spans to Phoenix or another compatible backend.

## Fields

OpenInference uses the same OTLP section shape as
[OpenTelemetry](/observability-plugin/opentelemetry):

| Field                   | Default          | Notes                                                    |
| ----------------------- | ---------------- | -------------------------------------------------------- |
| `enabled`               | `false`          | Must be `true` to construct and register the subscriber. |
| `transport`             | `http_binary`    | `http_binary` or `grpc`.                                 |
| `endpoint`              | Exporter default | OTLP endpoint.                                           |
| `headers`               | `{}`             | String-to-string exporter headers.                       |
| `resource_attributes`   | `{}`             | String-to-string OTLP resource attributes.               |
| `service_name`          | `nemo-relay`     | `service.name` resource attribute.                       |
| `service_namespace`     | Omitted          | Optional `service.namespace`.                            |
| `service_version`       | Omitted          | Optional `service.version`.                              |
| `instrumentation_scope` | Omitted          | Optional instrumentation scope name.                     |
| `timeout_millis`        | `3000`           | Export timeout.                                          |

## Expected Output

The backend should show OpenInference-oriented spans for scopes, tools, and LLM
calls grouped by root scope. LLM usage metadata appears as token counters when
provider responses include usage information. LLM request and response
messages, system prompts, and model-emitted tool calls are emitted as
flattened OpenInference attributes when available from codec annotations or
supported replay request/response payloads. Tool schemas, finish reasons,
and invocation parameters are emitted when typed codec annotations supply
them. Exported LLM attributes exclude request headers and other non-observable
transport metadata.

Each lifecycle span includes `nemo_relay.uuid` and `nemo_relay.parent_uuid`
attributes. These values match ATIF `step.extra.ancestry.function_id` and
`step.extra.ancestry.parent_id` for the same events. For plugin-managed ATIF,
the root agent span's `nemo_relay.uuid` also matches the ATIF `session_id`.
Backend-native `trace_id` and `span_id` values are not written into ATIF.

Redact sensitive event payloads with sanitize guardrails before production
export.

## Plugin Configuration

Use plugin configuration when the application should let NeMo Relay own the
OpenInference subscriber lifecycle.

```python
from nemo_relay import plugin
from nemo_relay.observability import ComponentSpec, ObservabilityConfig, OtlpConfig

config = plugin.PluginConfig(
    components=[
        ComponentSpec(
            ObservabilityConfig(
                openinference=OtlpConfig(
                    enabled=True,
                    transport="http_binary",
                    endpoint="http://localhost:6006/v1/traces",
                    service_name="agent-service",
                    service_namespace="nemo",
                    service_version="1.0.0",
                    instrumentation_scope="nemo-relay-openinference",
                    resource_attributes={"deployment.environment": "dev"},
                    headers={"authorization": "Bearer <token>"},
                )
            )
        )
    ]
)

report = plugin.validate(config)
if any(diagnostic["level"] == "error" for diagnostic in report["diagnostics"]):
    raise RuntimeError(report["diagnostics"])

async with plugin.plugin(config):
    # Run instrumented application work here.
    pass
```

```js
const plugin = require("nemo-relay-node/plugin");
const observability = require("nemo-relay-node/observability");

await plugin.initialize({
  version: 1,
  components: [
    observability.ComponentSpec({
      version: 1,
      openinference: observability.otlpConfig({
        enabled: true,
        transport: "http_binary",
        endpoint: "http://localhost:6006/v1/traces",
        service_name: "agent-service",
        service_namespace: "nemo",
        service_version: "1.0.0",
        instrumentation_scope: "nemo-relay-openinference",
        resource_attributes: {
          "deployment.environment": "dev",
        },
        headers: {
          authorization: "Bearer <token>",
        },
      }),
    }),
  ],
});

try {
  // Run instrumented application work here.
} finally {
  plugin.clear();
}
```

```rust
use nemo_relay::observability::plugin_component::{
    ComponentSpec, ObservabilityConfig, OtlpSectionConfig,
};
use nemo_relay::plugin::{initialize_plugins, validate_plugin_config, PluginConfig};

let component = ComponentSpec::new(ObservabilityConfig {
    openinference: Some(OtlpSectionConfig {
        enabled: true,
        transport: "http_binary".into(),
        endpoint: Some("http://localhost:6006/v1/traces".into()),
        service_name: "agent-service".into(),
        service_namespace: Some("nemo".into()),
        service_version: Some("1.0.0".into()),
        instrumentation_scope: Some("nemo-relay-openinference".into()),
        resource_attributes: [("deployment.environment".into(), "dev".into())].into(),
        headers: [("authorization".into(), "Bearer <token>".into())].into(),
        ..OtlpSectionConfig::default()
    }),
    ..ObservabilityConfig::default()
});

let config = PluginConfig {
    version: 1,
    components: vec![component.into()],
    policy: Default::default(),
};

let report = validate_plugin_config(&config);
assert!(!report.has_errors());

let active = initialize_plugins(config).await?;
```

## Manual API

Use the manual subscriber API when you need an explicit subscriber name or
direct `force_flush` control.

```python
from nemo_relay import OpenInferenceConfig, OpenInferenceSubscriber

config = OpenInferenceConfig()
config.transport = "http_binary"
config.endpoint = "http://localhost:6006/v1/traces"
config.service_name = "agent-service"
config.set_resource_attribute("deployment.environment", "dev")

subscriber = OpenInferenceSubscriber(config)
subscriber.register("openinference-exporter")

# Run instrumented application work here.

subscriber.force_flush()
subscriber.deregister("openinference-exporter")
subscriber.shutdown()
```

```js
const { OpenInferenceSubscriber } = require("nemo-relay-node");

const subscriber = new OpenInferenceSubscriber({
  transport: "http_binary",
  endpoint: "http://localhost:6006/v1/traces",
  serviceName: "agent-service",
  resourceAttributes: {
    "deployment.environment": "dev",
  },
});
subscriber.register("openinference-exporter");

try {
  // Run instrumented application work here.

  subscriber.forceFlush();
} finally {
  subscriber.deregister("openinference-exporter");
  subscriber.shutdown();
}
```

```rust
use nemo_relay::observability::openinference::{
    OpenInferenceConfig, OpenInferenceSubscriber,
};

let config = OpenInferenceConfig::new()
    .with_service_name("agent-service")
    .with_endpoint("http://localhost:6006/v1/traces")
    .with_resource_attribute("deployment.environment", "dev");
let subscriber = OpenInferenceSubscriber::new(config)?;
subscriber.register("openinference-exporter")?;

// Run instrumented application work here.

subscriber.force_flush()?;
let _ = subscriber.deregister("openinference-exporter")?;
subscriber.shutdown()?;
```

## Common Validation Failures

* `transport` is not `http_binary` or `grpc`.
* Headers or resource attributes are not string-to-string maps.
* The OpenInference feature is unavailable in the current build or target.
* Tool and LLM calls do not use managed helpers, so spans contain only scope
  lifecycle data.