Semantic Conventions
nemo.lens.semconv centralizes every attribute name constant NeMo Lens emits. Two categories of attributes coexist:
- Standard OTel semconv:
gen_ai.*andk8s.*. These track upstream OTel specs. (Resource attributes likeservice.*andhost.*are also standard OTel names but are set as literals inproviders.pyandresources/local.pyrather than mirrored assemconv.pyconstants.) - NeMo custom namespaces:
dl.*(distributed learning),rl.*,gym.*,slurm.*,nemo.*,wandb.*. These are NeMo-specific extensions that do not exist upstream.
Why Constants Instead of Strings
Using constants instead of raw strings provides three key benefits:
- Grep-ability: renaming an attribute across the codebase means changing one constant, not every call site.
- Type safety (weak but real):
DL_RANKis an exported name; typos becomeImportErrors."dl.rank"typos become silent data loss. - Central registry: one file lists every attribute NeMo Lens might emit. Easy to review, easy to document.
Callers who want the string can use DL_RANK directly; Python strings-as-constants have no boxing cost.
Version Tracking
This constant documents which upstream OTel semconv version the standard namespaces (gen_ai.*, k8s.*) are aligned with. Upstream bumps namespace conventions periodically; gen_ai.* graduated from experimental to stable around 1.30.
When upgrading NeMo Lens to a new semconv version, complete these tasks:
- Review the upstream changelog for renamed or removed attributes.
- Update
SEMCONV_VERSION. - Update any changed constants.
- Add the version bump to the NeMo Lens changelog so consumers know to update.
Stability Markers
From semconv.py:
“Stable” for custom namespaces means these names will not change in minor releases. Breaking changes bump the major version.
“Experimental” for gen_ai.* matches upstream; OTel considers them stable-in-practice but reserves the right to tweak until the full semconv 1.30 stabilization.
Namespace Conventions
NeMo Lens organizes attributes into standard upstream namespaces and NeMo-specific custom namespaces.
Standard Upstream Namespaces
Follow upstream OTel spec exactly. Do not redefine, and do not rename. If upstream says gen_ai.request.model, NeMo Lens uses gen_ai.request.model.
dl.* Distributed Learning
Shared across Megatron-LM, NeMo RL, NeMo Gym. Anything a distributed training job needs:
<project>.* Project-Specific Attributes
Use project-specific namespaces for attributes that apply to one consumer rather than the full NeMo ecosystem:
megatron.*: Megatron-LM consumer-specific span attributes (e.g., model architecturemegatron.num_layers/megatron.hidden_size, iteration-levelmegatron.skipped/megatron.update_successful). These are set as inline string attributes in the Megatron-LM fork and are not defined as constants innemo.lens.semconv. The central registry only holds the shared and standard namespaces (dl.*,gen_ai.*,rl.*,gym.*,slurm.*,nemo.*,wandb.*,k8s.*).rl.*— RL-specific (reward,kl_divergence,policy_loss)gym.*— Gym server-specific (verify.success_rate,rollout.batch_size)
nemo.* NeMo-Wide Identification
nemo.run.id— unique run identifier, shared across all ranksnemo.user.id— optional team/user label
Environment Namespaces
slurm.*— SLURM job attributesk8s.*— Kubernetes pod/node attributes (standard OTel)wandb.*— W&B Weave integration metadata
Adding New Attributes
Checklist before adding a constant:
- Is there an upstream OTel semconv name? Use it.
- Is this shared across consumers? Use
dl.*or another shared namespace. - Is this project-specific? Use
<project>.*. Such namespaces are set inline in the consumer fork (e.g. Megatron-LM), not as constants insemconv.py. - Is this a metric or a span attribute? Metric instruments go in
instruments/, span attributes go insemconv.py. - Is the attribute always available? If it is not, document it as “optional” so query authors know to handle missing values.
Do not add attributes speculatively. A constant with no call site is dead code that becomes stale.
Attribute vs. Metric Decision
Putting a continuously-varying value such as loss on a span attribute produces thousands of span-attribute time series in Jaeger that cannot be aggregated. Use a metric.
Attribute Cardinality
OTel metric attributes have a cardinality budget — each distinct combination of attribute values creates a new time series. gen_ai.operation.name="text_completion" is fine (one value). gen_ai.request.model="llama-3-8b" is fine (a handful of values). user.session.id="abc-123" is dangerous (unbounded cardinality — one time series per user session).
Rule of thumb: metric attributes should have tens to hundreds of distinct values, not millions. Use span attributes for high-cardinality values.