nvidia-nat-opentelemetry 1.1.0a20251020__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nat/meta/pypi.md +23 -0
- nat/plugins/opentelemetry/__init__.py +24 -0
- nat/plugins/opentelemetry/mixin/__init__.py +14 -0
- nat/plugins/opentelemetry/mixin/otlp_span_exporter_mixin.py +69 -0
- nat/plugins/opentelemetry/otel_span.py +525 -0
- nat/plugins/opentelemetry/otel_span_exporter.py +165 -0
- nat/plugins/opentelemetry/otlp_span_adapter_exporter.py +94 -0
- nat/plugins/opentelemetry/otlp_span_redaction_adapter_exporter.py +144 -0
- nat/plugins/opentelemetry/register.py +194 -0
- nat/plugins/opentelemetry/span_converter.py +228 -0
- nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/METADATA +47 -0
- nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/RECORD +17 -0
- nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/WHEEL +5 -0
- nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/entry_points.txt +2 -0
- nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/licenses/LICENSE-3rd-party.txt +5478 -0
- nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/licenses/LICENSE.md +201 -0
- nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
import time
|
|
18
|
+
|
|
19
|
+
from openinference.semconv.trace import OpenInferenceSpanKindValues
|
|
20
|
+
from openinference.semconv.trace import SpanAttributes
|
|
21
|
+
|
|
22
|
+
from nat.data_models.span import Span
|
|
23
|
+
from nat.data_models.span import SpanStatusCode
|
|
24
|
+
from nat.plugins.opentelemetry.otel_span import OtelSpan
|
|
25
|
+
from opentelemetry.trace import SpanContext
|
|
26
|
+
from opentelemetry.trace import SpanKind
|
|
27
|
+
from opentelemetry.trace import Status
|
|
28
|
+
from opentelemetry.trace import StatusCode
|
|
29
|
+
from opentelemetry.trace import TraceFlags
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
SPAN_EVENT_TYPE_TO_SPAN_KIND_MAP = {
|
|
34
|
+
"LLM_START": OpenInferenceSpanKindValues.LLM,
|
|
35
|
+
"LLM_END": OpenInferenceSpanKindValues.LLM,
|
|
36
|
+
"LLM_NEW_TOKEN": OpenInferenceSpanKindValues.LLM,
|
|
37
|
+
"TOOL_START": OpenInferenceSpanKindValues.TOOL,
|
|
38
|
+
"TOOL_END": OpenInferenceSpanKindValues.TOOL,
|
|
39
|
+
"FUNCTION_START": OpenInferenceSpanKindValues.CHAIN,
|
|
40
|
+
"FUNCTION_END": OpenInferenceSpanKindValues.CHAIN,
|
|
41
|
+
"WORKFLOW_START": OpenInferenceSpanKindValues.CHAIN,
|
|
42
|
+
"WORKFLOW_END": OpenInferenceSpanKindValues.CHAIN,
|
|
43
|
+
"TASK_START": OpenInferenceSpanKindValues.CHAIN,
|
|
44
|
+
"TASK_END": OpenInferenceSpanKindValues.CHAIN,
|
|
45
|
+
"CUSTOM_START": OpenInferenceSpanKindValues.CHAIN,
|
|
46
|
+
"CUSTOM_END": OpenInferenceSpanKindValues.CHAIN,
|
|
47
|
+
"EMBEDDER_START": OpenInferenceSpanKindValues.EMBEDDING,
|
|
48
|
+
"EMBEDDER_END": OpenInferenceSpanKindValues.EMBEDDING,
|
|
49
|
+
"RETRIEVER_START": OpenInferenceSpanKindValues.RETRIEVER,
|
|
50
|
+
"RETRIEVER_END": OpenInferenceSpanKindValues.RETRIEVER,
|
|
51
|
+
"AGENT_START": OpenInferenceSpanKindValues.AGENT,
|
|
52
|
+
"AGENT_END": OpenInferenceSpanKindValues.AGENT,
|
|
53
|
+
"RERANKER_START": OpenInferenceSpanKindValues.RERANKER,
|
|
54
|
+
"RERANKER_END": OpenInferenceSpanKindValues.RERANKER,
|
|
55
|
+
"GUARDRAIL_START": OpenInferenceSpanKindValues.GUARDRAIL,
|
|
56
|
+
"GUARDRAIL_END": OpenInferenceSpanKindValues.GUARDRAIL,
|
|
57
|
+
"EVALUATOR_START": OpenInferenceSpanKindValues.EVALUATOR,
|
|
58
|
+
"EVALUATOR_END": OpenInferenceSpanKindValues.EVALUATOR,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Reuse expensive objects to avoid repeated creation
|
|
63
|
+
class _SharedObjects:
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self.resource = None # type: ignore
|
|
67
|
+
self.instrumentation_scope = None # type: ignore
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
_shared = _SharedObjects()
|
|
71
|
+
_SAMPLED_TRACE_FLAGS = TraceFlags(1)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _get_shared_resource():
|
|
75
|
+
"""Get shared resource object to avoid repeated creation."""
|
|
76
|
+
if _shared.resource is None:
|
|
77
|
+
from opentelemetry.sdk.resources import Resource
|
|
78
|
+
_shared.resource = Resource.create() # type: ignore
|
|
79
|
+
return _shared.resource
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _get_shared_instrumentation_scope():
|
|
83
|
+
"""Get shared instrumentation scope to avoid repeated creation."""
|
|
84
|
+
if _shared.instrumentation_scope is None:
|
|
85
|
+
from opentelemetry.sdk.trace import InstrumentationScope
|
|
86
|
+
_shared.instrumentation_scope = InstrumentationScope("nat", "1.0.0") # type: ignore
|
|
87
|
+
return _shared.instrumentation_scope
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def convert_event_type_to_span_kind(event_type: str) -> OpenInferenceSpanKindValues:
|
|
91
|
+
"""Convert an event type to a span kind.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
event_type (str): The event type to convert
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
OpenInferenceSpanKindValues: The corresponding span kind
|
|
98
|
+
"""
|
|
99
|
+
return SPAN_EVENT_TYPE_TO_SPAN_KIND_MAP.get(event_type, OpenInferenceSpanKindValues.UNKNOWN)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def convert_span_status_code(nat_status_code: SpanStatusCode) -> StatusCode:
|
|
103
|
+
"""Convert NAT SpanStatusCode to OpenTelemetry StatusCode.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
nat_status_code (SpanStatusCode): The NAT span status code to convert
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
StatusCode: The corresponding OpenTelemetry StatusCode
|
|
110
|
+
"""
|
|
111
|
+
status_map = {
|
|
112
|
+
SpanStatusCode.OK: StatusCode.OK,
|
|
113
|
+
SpanStatusCode.ERROR: StatusCode.ERROR,
|
|
114
|
+
SpanStatusCode.UNSET: StatusCode.UNSET,
|
|
115
|
+
}
|
|
116
|
+
return status_map.get(nat_status_code, StatusCode.UNSET)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def convert_span_to_otel(nat_span: Span) -> OtelSpan:
|
|
120
|
+
"""Convert a NAT Span to an OtelSpan using ultra-fast conversion.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
nat_span (Span): The NAT span to convert
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
OtelSpan: The converted OtelSpan with proper parent hierarchy.
|
|
127
|
+
"""
|
|
128
|
+
# Fast path for spans without context
|
|
129
|
+
if not nat_span.context:
|
|
130
|
+
# Create minimal OtelSpan bypassing expensive constructor
|
|
131
|
+
otel_span = object.__new__(OtelSpan) # Bypass __init__
|
|
132
|
+
otel_span._name = nat_span.name
|
|
133
|
+
otel_span._context = None # type: ignore
|
|
134
|
+
otel_span._parent = None
|
|
135
|
+
otel_span._attributes = nat_span.attributes.copy()
|
|
136
|
+
otel_span._events = []
|
|
137
|
+
otel_span._links = []
|
|
138
|
+
otel_span._kind = SpanKind.INTERNAL
|
|
139
|
+
otel_span._start_time = nat_span.start_time
|
|
140
|
+
otel_span._end_time = nat_span.end_time
|
|
141
|
+
otel_span._status = Status(StatusCode.UNSET)
|
|
142
|
+
otel_span._ended = False
|
|
143
|
+
otel_span._resource = _get_shared_resource() # type: ignore
|
|
144
|
+
otel_span._instrumentation_scope = _get_shared_instrumentation_scope() # type: ignore
|
|
145
|
+
otel_span._dropped_attributes = 0
|
|
146
|
+
otel_span._dropped_events = 0
|
|
147
|
+
otel_span._dropped_links = 0
|
|
148
|
+
otel_span._status_description = None
|
|
149
|
+
return otel_span
|
|
150
|
+
|
|
151
|
+
# Process parent efficiently (if needed)
|
|
152
|
+
parent_otel_span = None
|
|
153
|
+
trace_id = nat_span.context.trace_id
|
|
154
|
+
|
|
155
|
+
if nat_span.parent:
|
|
156
|
+
parent_otel_span = convert_span_to_otel(nat_span.parent)
|
|
157
|
+
parent_context = parent_otel_span.get_span_context()
|
|
158
|
+
trace_id = parent_context.trace_id
|
|
159
|
+
|
|
160
|
+
# Create SpanContext efficiently
|
|
161
|
+
otel_span_context = SpanContext(
|
|
162
|
+
trace_id=trace_id,
|
|
163
|
+
span_id=nat_span.context.span_id,
|
|
164
|
+
is_remote=False,
|
|
165
|
+
trace_flags=_SAMPLED_TRACE_FLAGS, # Reuse flags object
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Create OtelSpan bypassing expensive constructor
|
|
169
|
+
otel_span = object.__new__(OtelSpan) # Bypass __init__
|
|
170
|
+
otel_span._name = nat_span.name
|
|
171
|
+
otel_span._context = otel_span_context
|
|
172
|
+
otel_span._parent = parent_otel_span
|
|
173
|
+
otel_span._attributes = nat_span.attributes.copy()
|
|
174
|
+
otel_span._events = []
|
|
175
|
+
otel_span._links = []
|
|
176
|
+
otel_span._kind = SpanKind.INTERNAL
|
|
177
|
+
otel_span._start_time = nat_span.start_time
|
|
178
|
+
otel_span._end_time = nat_span.end_time
|
|
179
|
+
|
|
180
|
+
# Reuse status conversion
|
|
181
|
+
status_code = convert_span_status_code(nat_span.status.code)
|
|
182
|
+
otel_span._status = Status(status_code, nat_span.status.message)
|
|
183
|
+
|
|
184
|
+
otel_span._ended = False
|
|
185
|
+
otel_span._resource = _get_shared_resource() # type: ignore
|
|
186
|
+
otel_span._instrumentation_scope = _get_shared_instrumentation_scope() # type: ignore
|
|
187
|
+
otel_span._dropped_attributes = 0
|
|
188
|
+
otel_span._dropped_events = 0
|
|
189
|
+
otel_span._dropped_links = 0
|
|
190
|
+
otel_span._status_description = None
|
|
191
|
+
|
|
192
|
+
# Set span kind efficiently (direct attribute modification)
|
|
193
|
+
event_type = nat_span.attributes.get("nat.event_type", "UNKNOWN")
|
|
194
|
+
span_kind = SPAN_EVENT_TYPE_TO_SPAN_KIND_MAP.get(event_type, OpenInferenceSpanKindValues.UNKNOWN)
|
|
195
|
+
otel_span._attributes[SpanAttributes.OPENINFERENCE_SPAN_KIND] = span_kind.value
|
|
196
|
+
|
|
197
|
+
# Process events (only if they exist)
|
|
198
|
+
if nat_span.events:
|
|
199
|
+
for nat_event in nat_span.events:
|
|
200
|
+
# Optimize timestamp handling
|
|
201
|
+
if isinstance(nat_event.timestamp, int):
|
|
202
|
+
event_timestamp_ns = nat_event.timestamp
|
|
203
|
+
elif nat_event.timestamp:
|
|
204
|
+
event_timestamp_ns = int(nat_event.timestamp)
|
|
205
|
+
else:
|
|
206
|
+
event_timestamp_ns = int(time.time() * 1e9)
|
|
207
|
+
|
|
208
|
+
# Add event directly to internal list (bypass add_event method)
|
|
209
|
+
otel_span._events.append({
|
|
210
|
+
"name": nat_event.name, "attributes": nat_event.attributes, "timestamp": event_timestamp_ns
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
return otel_span
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def convert_spans_to_otel_batch(spans: list[Span]) -> list[OtelSpan]:
|
|
217
|
+
"""Convert a list of NAT spans to OtelSpans using stateless conversion.
|
|
218
|
+
|
|
219
|
+
This is useful for batch processing or demos. Each span is converted
|
|
220
|
+
independently using the stateless approach.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
spans (list[Span]): List of NAT spans to convert
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
list[OtelSpan]: List of converted OtelSpans with proper parent-child relationships
|
|
227
|
+
"""
|
|
228
|
+
return [convert_span_to_otel(span) for span in spans]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nvidia-nat-opentelemetry
|
|
3
|
+
Version: 1.1.0a20251020
|
|
4
|
+
Summary: Subpackage for OpenTelemetry integration in NeMo Agent toolkit
|
|
5
|
+
Author: NVIDIA Corporation
|
|
6
|
+
Maintainer: NVIDIA Corporation
|
|
7
|
+
License: Apache-2.0
|
|
8
|
+
Project-URL: documentation, https://docs.nvidia.com/nemo/agent-toolkit/latest/
|
|
9
|
+
Project-URL: source, https://github.com/NVIDIA/NeMo-Agent-Toolkit
|
|
10
|
+
Keywords: ai,observability,opentelemetry
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Requires-Python: <3.14,>=3.11
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE-3rd-party.txt
|
|
18
|
+
License-File: LICENSE.md
|
|
19
|
+
Requires-Dist: nvidia-nat==v1.1.0a20251020
|
|
20
|
+
Requires-Dist: opentelemetry-api~=1.2
|
|
21
|
+
Requires-Dist: opentelemetry-exporter-otlp~=1.3
|
|
22
|
+
Requires-Dist: opentelemetry-sdk~=1.3
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
<!--
|
|
26
|
+
SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
27
|
+
SPDX-License-Identifier: Apache-2.0
|
|
28
|
+
|
|
29
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
30
|
+
you may not use this file except in compliance with the License.
|
|
31
|
+
You may obtain a copy of the License at
|
|
32
|
+
|
|
33
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
34
|
+
|
|
35
|
+
Unless required by applicable law or agreed to in writing, software
|
|
36
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
37
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
38
|
+
See the License for the specific language governing permissions and
|
|
39
|
+
limitations under the License.
|
|
40
|
+
-->
|
|
41
|
+
|
|
42
|
+
.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
nat/meta/pypi.md,sha256=_o1o1BLPY1pvjCkklWxlm7LlIDMPCk-2Rho85NUuN8U,1109
|
|
2
|
+
nat/plugins/opentelemetry/__init__.py,sha256=j-YKuxwSIzGziyDyunw8s_W7WiFiqKLdFjw-utwHPFk,1079
|
|
3
|
+
nat/plugins/opentelemetry/otel_span.py,sha256=MC_ROZ8gSTu0gxRaaz77UDbn1ouZTZP3N_-0PcN140U,16564
|
|
4
|
+
nat/plugins/opentelemetry/otel_span_exporter.py,sha256=YO7JsQgi8Cf2OQBJ_s78HwJjrWx9SdqMvPen3Pa2_bI,6533
|
|
5
|
+
nat/plugins/opentelemetry/otlp_span_adapter_exporter.py,sha256=6xQHkKDhQk3-kObqj6kRv8ZlJtIV2Qqox6YkY0PCOYs,3945
|
|
6
|
+
nat/plugins/opentelemetry/otlp_span_redaction_adapter_exporter.py,sha256=qRrUIxYRlCDaAkEGlFTaEkCJCsb68GBo11EGEiE6S8E,7217
|
|
7
|
+
nat/plugins/opentelemetry/register.py,sha256=KnhV-axY0kJzZ3RReG4e_mTFR1dMr7d3a6ysYiCLTUI,9063
|
|
8
|
+
nat/plugins/opentelemetry/span_converter.py,sha256=Gz3KvRNQeEBBlpaPO8YRAJkw4fmzV7m9bT6dGX0IV2E,8846
|
|
9
|
+
nat/plugins/opentelemetry/mixin/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
|
|
10
|
+
nat/plugins/opentelemetry/mixin/otlp_span_exporter_mixin.py,sha256=3vK6DkTJXp6ZFH3AgNYUuuMOzjyskh_nVUWK-qMYKzM,2809
|
|
11
|
+
nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/licenses/LICENSE-3rd-party.txt,sha256=fOk5jMmCX9YoKWyYzTtfgl-SUy477audFC5hNY4oP7Q,284609
|
|
12
|
+
nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
13
|
+
nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/METADATA,sha256=VX0u9wnfXTPSmp6LHHEYLOW9UjuxwiXbQSLToGR24zU,2039
|
|
14
|
+
nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/entry_points.txt,sha256=gmEKhCafyibUJLGxbn8luTK0UTgIvV2vAtr4uZ8M85I,72
|
|
16
|
+
nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
|
|
17
|
+
nvidia_nat_opentelemetry-1.1.0a20251020.dist-info/RECORD,,
|