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 |
|---|---|---|
|
|
Must be |
|
|
|
|
Exporter default |
OTLP endpoint. |
|
|
String-to-string exporter headers. |
|
|
String-to-string OTLP resource attributes. |
|
|
|
|
Omitted |
Optional |
|
Omitted |
Optional |
|
Omitted |
Optional instrumentation scope name. |
|
|
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#
transportis nothttp_binaryorgrpc.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.