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.

plugins.toml Example#

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:

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.

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.

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"])

await plugin.initialize(config)
try:
    # Run instrumented application work here.
    pass
finally:
    plugin.clear()
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();
}
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.

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()
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();
}
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.