data-designer 0.3.8rc2__py3-none-any.whl → 0.4.0rc1__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.
- data_designer/cli/commands/__init__.py +1 -1
- data_designer/interface/__init__.py +21 -1
- data_designer/{_version.py → interface/_version.py} +2 -2
- data_designer/interface/data_designer.py +1 -7
- {data_designer-0.3.8rc2.dist-info → data_designer-0.4.0rc1.dist-info}/METADATA +10 -42
- data_designer-0.4.0rc1.dist-info/RECORD +39 -0
- data_designer/__init__.py +0 -17
- data_designer/config/__init__.py +0 -2
- data_designer/config/analysis/__init__.py +0 -2
- data_designer/config/analysis/column_profilers.py +0 -159
- data_designer/config/analysis/column_statistics.py +0 -421
- data_designer/config/analysis/dataset_profiler.py +0 -84
- data_designer/config/analysis/utils/errors.py +0 -10
- data_designer/config/analysis/utils/reporting.py +0 -192
- data_designer/config/base.py +0 -69
- data_designer/config/column_configs.py +0 -470
- data_designer/config/column_types.py +0 -141
- data_designer/config/config_builder.py +0 -595
- data_designer/config/data_designer_config.py +0 -40
- data_designer/config/dataset_builders.py +0 -13
- data_designer/config/dataset_metadata.py +0 -18
- data_designer/config/default_model_settings.py +0 -129
- data_designer/config/errors.py +0 -24
- data_designer/config/exports.py +0 -145
- data_designer/config/interface.py +0 -55
- data_designer/config/models.py +0 -455
- data_designer/config/preview_results.py +0 -41
- data_designer/config/processors.py +0 -148
- data_designer/config/run_config.py +0 -51
- data_designer/config/sampler_constraints.py +0 -52
- data_designer/config/sampler_params.py +0 -639
- data_designer/config/seed.py +0 -116
- data_designer/config/seed_source.py +0 -84
- data_designer/config/seed_source_types.py +0 -19
- data_designer/config/utils/code_lang.py +0 -82
- data_designer/config/utils/constants.py +0 -363
- data_designer/config/utils/errors.py +0 -21
- data_designer/config/utils/info.py +0 -94
- data_designer/config/utils/io_helpers.py +0 -258
- data_designer/config/utils/misc.py +0 -78
- data_designer/config/utils/numerical_helpers.py +0 -30
- data_designer/config/utils/type_helpers.py +0 -106
- data_designer/config/utils/visualization.py +0 -482
- data_designer/config/validator_params.py +0 -94
- data_designer/engine/__init__.py +0 -2
- data_designer/engine/analysis/column_profilers/base.py +0 -49
- data_designer/engine/analysis/column_profilers/judge_score_profiler.py +0 -153
- data_designer/engine/analysis/column_profilers/registry.py +0 -22
- data_designer/engine/analysis/column_statistics.py +0 -145
- data_designer/engine/analysis/dataset_profiler.py +0 -149
- data_designer/engine/analysis/errors.py +0 -9
- data_designer/engine/analysis/utils/column_statistics_calculations.py +0 -234
- data_designer/engine/analysis/utils/judge_score_processing.py +0 -132
- data_designer/engine/column_generators/__init__.py +0 -2
- data_designer/engine/column_generators/generators/__init__.py +0 -2
- data_designer/engine/column_generators/generators/base.py +0 -122
- data_designer/engine/column_generators/generators/embedding.py +0 -35
- data_designer/engine/column_generators/generators/expression.py +0 -55
- data_designer/engine/column_generators/generators/llm_completion.py +0 -113
- data_designer/engine/column_generators/generators/samplers.py +0 -69
- data_designer/engine/column_generators/generators/seed_dataset.py +0 -144
- data_designer/engine/column_generators/generators/validation.py +0 -140
- data_designer/engine/column_generators/registry.py +0 -60
- data_designer/engine/column_generators/utils/errors.py +0 -15
- data_designer/engine/column_generators/utils/generator_classification.py +0 -43
- data_designer/engine/column_generators/utils/judge_score_factory.py +0 -58
- data_designer/engine/column_generators/utils/prompt_renderer.py +0 -100
- data_designer/engine/compiler.py +0 -97
- data_designer/engine/configurable_task.py +0 -71
- data_designer/engine/dataset_builders/artifact_storage.py +0 -283
- data_designer/engine/dataset_builders/column_wise_builder.py +0 -335
- data_designer/engine/dataset_builders/errors.py +0 -15
- data_designer/engine/dataset_builders/multi_column_configs.py +0 -46
- data_designer/engine/dataset_builders/utils/__init__.py +0 -2
- data_designer/engine/dataset_builders/utils/concurrency.py +0 -212
- data_designer/engine/dataset_builders/utils/config_compiler.py +0 -62
- data_designer/engine/dataset_builders/utils/dag.py +0 -62
- data_designer/engine/dataset_builders/utils/dataset_batch_manager.py +0 -200
- data_designer/engine/dataset_builders/utils/errors.py +0 -15
- data_designer/engine/errors.py +0 -51
- data_designer/engine/model_provider.py +0 -77
- data_designer/engine/models/__init__.py +0 -2
- data_designer/engine/models/errors.py +0 -300
- data_designer/engine/models/facade.py +0 -287
- data_designer/engine/models/factory.py +0 -42
- data_designer/engine/models/litellm_overrides.py +0 -179
- data_designer/engine/models/parsers/__init__.py +0 -2
- data_designer/engine/models/parsers/errors.py +0 -34
- data_designer/engine/models/parsers/parser.py +0 -235
- data_designer/engine/models/parsers/postprocessors.py +0 -93
- data_designer/engine/models/parsers/tag_parsers.py +0 -62
- data_designer/engine/models/parsers/types.py +0 -84
- data_designer/engine/models/recipes/base.py +0 -81
- data_designer/engine/models/recipes/response_recipes.py +0 -293
- data_designer/engine/models/registry.py +0 -146
- data_designer/engine/models/telemetry.py +0 -359
- data_designer/engine/models/usage.py +0 -73
- data_designer/engine/models/utils.py +0 -38
- data_designer/engine/processing/ginja/__init__.py +0 -2
- data_designer/engine/processing/ginja/ast.py +0 -65
- data_designer/engine/processing/ginja/environment.py +0 -463
- data_designer/engine/processing/ginja/exceptions.py +0 -56
- data_designer/engine/processing/ginja/record.py +0 -32
- data_designer/engine/processing/gsonschema/__init__.py +0 -2
- data_designer/engine/processing/gsonschema/exceptions.py +0 -15
- data_designer/engine/processing/gsonschema/schema_transformers.py +0 -83
- data_designer/engine/processing/gsonschema/types.py +0 -10
- data_designer/engine/processing/gsonschema/validators.py +0 -202
- data_designer/engine/processing/processors/base.py +0 -13
- data_designer/engine/processing/processors/drop_columns.py +0 -42
- data_designer/engine/processing/processors/registry.py +0 -25
- data_designer/engine/processing/processors/schema_transform.py +0 -49
- data_designer/engine/processing/utils.py +0 -169
- data_designer/engine/registry/base.py +0 -99
- data_designer/engine/registry/data_designer_registry.py +0 -39
- data_designer/engine/registry/errors.py +0 -12
- data_designer/engine/resources/managed_dataset_generator.py +0 -39
- data_designer/engine/resources/managed_dataset_repository.py +0 -197
- data_designer/engine/resources/managed_storage.py +0 -65
- data_designer/engine/resources/resource_provider.py +0 -77
- data_designer/engine/resources/seed_reader.py +0 -154
- data_designer/engine/sampling_gen/column.py +0 -91
- data_designer/engine/sampling_gen/constraints.py +0 -100
- data_designer/engine/sampling_gen/data_sources/base.py +0 -217
- data_designer/engine/sampling_gen/data_sources/errors.py +0 -12
- data_designer/engine/sampling_gen/data_sources/sources.py +0 -347
- data_designer/engine/sampling_gen/entities/__init__.py +0 -2
- data_designer/engine/sampling_gen/entities/assets/zip_area_code_map.parquet +0 -0
- data_designer/engine/sampling_gen/entities/dataset_based_person_fields.py +0 -86
- data_designer/engine/sampling_gen/entities/email_address_utils.py +0 -171
- data_designer/engine/sampling_gen/entities/errors.py +0 -10
- data_designer/engine/sampling_gen/entities/national_id_utils.py +0 -102
- data_designer/engine/sampling_gen/entities/person.py +0 -144
- data_designer/engine/sampling_gen/entities/phone_number.py +0 -128
- data_designer/engine/sampling_gen/errors.py +0 -26
- data_designer/engine/sampling_gen/generator.py +0 -122
- data_designer/engine/sampling_gen/jinja_utils.py +0 -64
- data_designer/engine/sampling_gen/people_gen.py +0 -199
- data_designer/engine/sampling_gen/person_constants.py +0 -56
- data_designer/engine/sampling_gen/schema.py +0 -147
- data_designer/engine/sampling_gen/schema_builder.py +0 -61
- data_designer/engine/sampling_gen/utils.py +0 -46
- data_designer/engine/secret_resolver.py +0 -82
- data_designer/engine/validation.py +0 -367
- data_designer/engine/validators/__init__.py +0 -19
- data_designer/engine/validators/base.py +0 -38
- data_designer/engine/validators/local_callable.py +0 -39
- data_designer/engine/validators/python.py +0 -254
- data_designer/engine/validators/remote.py +0 -89
- data_designer/engine/validators/sql.py +0 -65
- data_designer/errors.py +0 -7
- data_designer/essentials/__init__.py +0 -33
- data_designer/lazy_heavy_imports.py +0 -54
- data_designer/logging.py +0 -163
- data_designer/plugin_manager.py +0 -78
- data_designer/plugins/__init__.py +0 -8
- data_designer/plugins/errors.py +0 -15
- data_designer/plugins/plugin.py +0 -141
- data_designer/plugins/registry.py +0 -88
- data_designer/plugins/testing/__init__.py +0 -10
- data_designer/plugins/testing/stubs.py +0 -116
- data_designer/plugins/testing/utils.py +0 -20
- data_designer-0.3.8rc2.dist-info/RECORD +0 -196
- data_designer-0.3.8rc2.dist-info/licenses/LICENSE +0 -201
- {data_designer-0.3.8rc2.dist-info → data_designer-0.4.0rc1.dist-info}/WHEEL +0 -0
- {data_designer-0.3.8rc2.dist-info → data_designer-0.4.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
"""
|
|
5
|
-
Telemetry handler for NeMo products.
|
|
6
|
-
|
|
7
|
-
Environment variables:
|
|
8
|
-
- NEMO_TELEMETRY_ENABLED: Whether telemetry is enabled.
|
|
9
|
-
- NEMO_DEPLOYMENT_TYPE: The deployment type the event came from.
|
|
10
|
-
- NEMO_TELEMETRY_ENDPOINT: The endpoint to send the telemetry events to.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
import asyncio
|
|
16
|
-
import os
|
|
17
|
-
import platform
|
|
18
|
-
from dataclasses import dataclass
|
|
19
|
-
from datetime import datetime, timezone
|
|
20
|
-
from enum import Enum
|
|
21
|
-
from typing import TYPE_CHECKING, Any, ClassVar
|
|
22
|
-
|
|
23
|
-
from pydantic import BaseModel, Field
|
|
24
|
-
|
|
25
|
-
from data_designer.lazy_heavy_imports import httpx
|
|
26
|
-
|
|
27
|
-
if TYPE_CHECKING:
|
|
28
|
-
import httpx
|
|
29
|
-
|
|
30
|
-
TELEMETRY_ENABLED = os.getenv("NEMO_TELEMETRY_ENABLED", "true").lower() in ("1", "true", "yes")
|
|
31
|
-
CLIENT_ID = "184482118588404"
|
|
32
|
-
NEMO_TELEMETRY_VERSION = "nemo-telemetry/1.0"
|
|
33
|
-
MAX_RETRIES = 3
|
|
34
|
-
NEMO_TELEMETRY_ENDPOINT = os.getenv(
|
|
35
|
-
"NEMO_TELEMETRY_ENDPOINT", "https://events.telemetry.data.nvidia.com/v1.1/events/json"
|
|
36
|
-
).lower()
|
|
37
|
-
CPU_ARCHITECTURE = platform.uname().machine
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class NemoSourceEnum(str, Enum):
|
|
41
|
-
INFERENCE = "inference"
|
|
42
|
-
AUDITOR = "auditor"
|
|
43
|
-
DATADESIGNER = "datadesigner"
|
|
44
|
-
EVALUATOR = "evaluator"
|
|
45
|
-
GUARDRAILS = "guardrails"
|
|
46
|
-
UNDEFINED = "undefined"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class DeploymentTypeEnum(str, Enum):
|
|
50
|
-
LIBRARY = "library"
|
|
51
|
-
API = "api"
|
|
52
|
-
UNDEFINED = "undefined"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
_deployment_type_raw = os.getenv("NEMO_DEPLOYMENT_TYPE", "library").lower()
|
|
56
|
-
try:
|
|
57
|
-
DEPLOYMENT_TYPE = DeploymentTypeEnum(_deployment_type_raw)
|
|
58
|
-
except ValueError:
|
|
59
|
-
valid_values = [e.value for e in DeploymentTypeEnum]
|
|
60
|
-
raise ValueError(
|
|
61
|
-
f"Invalid NEMO_DEPLOYMENT_TYPE: {_deployment_type_raw!r}. Must be one of: {valid_values}"
|
|
62
|
-
) from None
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class TaskStatusEnum(str, Enum):
|
|
66
|
-
SUCCESS = "success"
|
|
67
|
-
FAILURE = "failure"
|
|
68
|
-
UNDEFINED = "undefined"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class TelemetryEvent(BaseModel):
|
|
72
|
-
_event_name: ClassVar[str] # Subclasses must define this
|
|
73
|
-
_schema_version: ClassVar[str] = "1.3"
|
|
74
|
-
|
|
75
|
-
def __init_subclass__(cls, **kwargs: Any) -> None:
|
|
76
|
-
super().__init_subclass__(**kwargs)
|
|
77
|
-
if "_event_name" not in cls.__dict__:
|
|
78
|
-
raise TypeError(f"{cls.__name__} must define '_event_name' class variable")
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class InferenceEvent(TelemetryEvent):
|
|
82
|
-
_event_name: ClassVar[str] = "inference_event"
|
|
83
|
-
|
|
84
|
-
nemo_source: NemoSourceEnum = Field(
|
|
85
|
-
...,
|
|
86
|
-
alias="nemoSource",
|
|
87
|
-
description="The NeMo product that created the event (i.e. data-designer).",
|
|
88
|
-
)
|
|
89
|
-
task: str = Field(
|
|
90
|
-
...,
|
|
91
|
-
description="The type of task that was performed that generated the inference event (i.e. preview-job, batch-job).",
|
|
92
|
-
)
|
|
93
|
-
task_status: TaskStatusEnum = Field(
|
|
94
|
-
...,
|
|
95
|
-
alias="taskStatus",
|
|
96
|
-
description="The status of the task.",
|
|
97
|
-
)
|
|
98
|
-
deployment_type: DeploymentTypeEnum = Field(
|
|
99
|
-
default=DEPLOYMENT_TYPE,
|
|
100
|
-
alias="deploymentType",
|
|
101
|
-
description="The deployment type the event came from.",
|
|
102
|
-
)
|
|
103
|
-
model: str = Field(
|
|
104
|
-
...,
|
|
105
|
-
description="The name of the model that was used.",
|
|
106
|
-
)
|
|
107
|
-
model_group: str = Field(
|
|
108
|
-
default="undefined",
|
|
109
|
-
alias="modelGroup",
|
|
110
|
-
description="An optional identifier to group models together.",
|
|
111
|
-
)
|
|
112
|
-
input_bytes: int = Field(
|
|
113
|
-
default=-1,
|
|
114
|
-
alias="inputBytes",
|
|
115
|
-
description="Number of bytes provided as input to the model. -1 if not available.",
|
|
116
|
-
ge=-9223372036854775808,
|
|
117
|
-
le=9223372036854775807,
|
|
118
|
-
)
|
|
119
|
-
input_tokens: int = Field(
|
|
120
|
-
default=-1,
|
|
121
|
-
alias="inputTokens",
|
|
122
|
-
description="Number of tokens provided as input to the model. -1 if not available.",
|
|
123
|
-
ge=-9223372036854775808,
|
|
124
|
-
le=9223372036854775807,
|
|
125
|
-
)
|
|
126
|
-
output_bytes: int = Field(
|
|
127
|
-
default=-1,
|
|
128
|
-
alias="outputBytes",
|
|
129
|
-
description="Number of bytes returned by the model. -1 if not available.",
|
|
130
|
-
ge=-9223372036854775808,
|
|
131
|
-
le=9223372036854775807,
|
|
132
|
-
)
|
|
133
|
-
output_tokens: int = Field(
|
|
134
|
-
default=-1,
|
|
135
|
-
alias="outputTokens",
|
|
136
|
-
description="Number of tokens returned by the model. -1 if not available.",
|
|
137
|
-
ge=-9223372036854775808,
|
|
138
|
-
le=9223372036854775807,
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
model_config = {"populate_by_name": True}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
@dataclass
|
|
145
|
-
class QueuedEvent:
|
|
146
|
-
event: TelemetryEvent
|
|
147
|
-
timestamp: datetime
|
|
148
|
-
retry_count: int = 0
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def _get_iso_timestamp(dt: datetime | None = None) -> str:
|
|
152
|
-
if dt is None:
|
|
153
|
-
dt = datetime.now(timezone.utc)
|
|
154
|
-
return dt.strftime("%Y-%m-%dT%H:%M:%S.") + f"{dt.microsecond // 1000:03d}Z"
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def build_payload(
|
|
158
|
-
events: list[QueuedEvent], *, source_client_version: str, session_id: str = "undefined"
|
|
159
|
-
) -> dict[str, Any]:
|
|
160
|
-
return {
|
|
161
|
-
"browserType": "undefined", # do not change
|
|
162
|
-
"clientId": CLIENT_ID,
|
|
163
|
-
"clientType": "Native", # do not change
|
|
164
|
-
"clientVariant": "Release", # do not change
|
|
165
|
-
"clientVer": source_client_version,
|
|
166
|
-
"cpuArchitecture": CPU_ARCHITECTURE,
|
|
167
|
-
"deviceGdprBehOptIn": "None", # do not change
|
|
168
|
-
"deviceGdprFuncOptIn": "None", # do not change
|
|
169
|
-
"deviceGdprTechOptIn": "None", # do not change
|
|
170
|
-
"deviceId": "undefined", # do not change
|
|
171
|
-
"deviceMake": "undefined", # do not change
|
|
172
|
-
"deviceModel": "undefined", # do not change
|
|
173
|
-
"deviceOS": "undefined", # do not change
|
|
174
|
-
"deviceOSVersion": "undefined", # do not change
|
|
175
|
-
"deviceType": "undefined", # do not change
|
|
176
|
-
"eventProtocol": "1.6", # do not change
|
|
177
|
-
"eventSchemaVer": events[0].event._schema_version,
|
|
178
|
-
"eventSysVer": NEMO_TELEMETRY_VERSION,
|
|
179
|
-
"externalUserId": "undefined", # do not change
|
|
180
|
-
"gdprBehOptIn": "None", # do not change
|
|
181
|
-
"gdprFuncOptIn": "None", # do not change
|
|
182
|
-
"gdprTechOptIn": "None", # do not change
|
|
183
|
-
"idpId": "undefined", # do not change
|
|
184
|
-
"integrationId": "undefined", # do not change
|
|
185
|
-
"productName": "undefined", # do not change
|
|
186
|
-
"productVersion": "undefined", # do not change
|
|
187
|
-
"sentTs": _get_iso_timestamp(),
|
|
188
|
-
"sessionId": session_id,
|
|
189
|
-
"userId": "undefined", # do not change
|
|
190
|
-
"events": [
|
|
191
|
-
{
|
|
192
|
-
"ts": _get_iso_timestamp(queued.timestamp),
|
|
193
|
-
"parameters": queued.event.model_dump(by_alias=True),
|
|
194
|
-
"name": queued.event._event_name,
|
|
195
|
-
}
|
|
196
|
-
for queued in events
|
|
197
|
-
],
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
class TelemetryHandler:
|
|
202
|
-
"""
|
|
203
|
-
Handles telemetry event batching, flushing, and retry logic for NeMo products.
|
|
204
|
-
|
|
205
|
-
Args:
|
|
206
|
-
flush_interval_seconds (float): The interval in seconds to flush the events.
|
|
207
|
-
max_queue_size (int): The maximum number of events to queue before flushing.
|
|
208
|
-
max_retries (int): The maximum number of times to retry sending an event.
|
|
209
|
-
source_client_version (str): The version of the source client. This should be the version of
|
|
210
|
-
the actual NeMo product that is sending the events, typically the same as the version of
|
|
211
|
-
a PyPi package that a user would install.
|
|
212
|
-
session_id (str): An optional session ID to associate with the events.
|
|
213
|
-
This should be a unique identifier for the session, such as a UUID.
|
|
214
|
-
It is used to group events together.
|
|
215
|
-
"""
|
|
216
|
-
|
|
217
|
-
def __init__(
|
|
218
|
-
self,
|
|
219
|
-
flush_interval_seconds: float = 120.0,
|
|
220
|
-
max_queue_size: int = 50,
|
|
221
|
-
max_retries: int = MAX_RETRIES,
|
|
222
|
-
source_client_version: str = "undefined",
|
|
223
|
-
session_id: str = "undefined",
|
|
224
|
-
):
|
|
225
|
-
self._flush_interval = flush_interval_seconds
|
|
226
|
-
self._max_queue_size = max_queue_size
|
|
227
|
-
self._max_retries = max_retries
|
|
228
|
-
self._events: list[QueuedEvent] = []
|
|
229
|
-
self._dlq: list[QueuedEvent] = [] # Dead letter queue for retry
|
|
230
|
-
self._flush_signal = asyncio.Event()
|
|
231
|
-
self._timer_task: asyncio.Task | None = None
|
|
232
|
-
self._running = False
|
|
233
|
-
self._source_client_version = source_client_version
|
|
234
|
-
self._session_id = session_id
|
|
235
|
-
|
|
236
|
-
async def astart(self) -> None:
|
|
237
|
-
if self._running:
|
|
238
|
-
return
|
|
239
|
-
self._running = True
|
|
240
|
-
self._timer_task = asyncio.create_task(self._timer_loop())
|
|
241
|
-
|
|
242
|
-
async def astop(self) -> None:
|
|
243
|
-
self._running = False
|
|
244
|
-
self._flush_signal.set()
|
|
245
|
-
if self._timer_task:
|
|
246
|
-
self._timer_task.cancel()
|
|
247
|
-
try:
|
|
248
|
-
await self._timer_task
|
|
249
|
-
except asyncio.CancelledError:
|
|
250
|
-
pass
|
|
251
|
-
self._timer_task = None
|
|
252
|
-
await self._flush_events()
|
|
253
|
-
|
|
254
|
-
async def aflush(self) -> None:
|
|
255
|
-
self._flush_signal.set()
|
|
256
|
-
|
|
257
|
-
def start(self) -> None:
|
|
258
|
-
self._run_sync(self.astart())
|
|
259
|
-
|
|
260
|
-
def stop(self) -> None:
|
|
261
|
-
self._run_sync(self.astop())
|
|
262
|
-
|
|
263
|
-
def flush(self) -> None:
|
|
264
|
-
self._flush_signal.set()
|
|
265
|
-
|
|
266
|
-
def enqueue(self, event: TelemetryEvent) -> None:
|
|
267
|
-
if not TELEMETRY_ENABLED:
|
|
268
|
-
return
|
|
269
|
-
if not isinstance(event, TelemetryEvent):
|
|
270
|
-
# Silently fail as we prioritize not disrupting upstream call sites and telemetry is best effort
|
|
271
|
-
return
|
|
272
|
-
queued = QueuedEvent(event=event, timestamp=datetime.now(timezone.utc))
|
|
273
|
-
self._events.append(queued)
|
|
274
|
-
if len(self._events) >= self._max_queue_size:
|
|
275
|
-
self._flush_signal.set()
|
|
276
|
-
|
|
277
|
-
def _run_sync(self, coro: Any) -> Any:
|
|
278
|
-
try:
|
|
279
|
-
loop = asyncio.get_running_loop()
|
|
280
|
-
except RuntimeError:
|
|
281
|
-
loop = None
|
|
282
|
-
|
|
283
|
-
if loop and loop.is_running():
|
|
284
|
-
import concurrent.futures
|
|
285
|
-
|
|
286
|
-
with concurrent.futures.ThreadPoolExecutor() as pool:
|
|
287
|
-
future = pool.submit(asyncio.run, coro)
|
|
288
|
-
return future.result()
|
|
289
|
-
else:
|
|
290
|
-
return asyncio.run(coro)
|
|
291
|
-
|
|
292
|
-
def __enter__(self) -> TelemetryHandler:
|
|
293
|
-
self.start()
|
|
294
|
-
return self
|
|
295
|
-
|
|
296
|
-
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
297
|
-
self.stop()
|
|
298
|
-
|
|
299
|
-
async def __aenter__(self) -> TelemetryHandler:
|
|
300
|
-
await self.astart()
|
|
301
|
-
return self
|
|
302
|
-
|
|
303
|
-
async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
304
|
-
await self.astop()
|
|
305
|
-
|
|
306
|
-
async def _timer_loop(self) -> None:
|
|
307
|
-
while self._running:
|
|
308
|
-
try:
|
|
309
|
-
await asyncio.wait_for(
|
|
310
|
-
self._flush_signal.wait(),
|
|
311
|
-
timeout=self._flush_interval,
|
|
312
|
-
)
|
|
313
|
-
except asyncio.TimeoutError:
|
|
314
|
-
pass
|
|
315
|
-
self._flush_signal.clear()
|
|
316
|
-
await self._flush_events()
|
|
317
|
-
|
|
318
|
-
async def _flush_events(self) -> None:
|
|
319
|
-
dlq_events, self._dlq = self._dlq, []
|
|
320
|
-
new_events, self._events = self._events, []
|
|
321
|
-
events_to_send = dlq_events + new_events
|
|
322
|
-
if events_to_send:
|
|
323
|
-
await self._send_events(events_to_send)
|
|
324
|
-
|
|
325
|
-
async def _send_events(self, events: list[QueuedEvent]) -> None:
|
|
326
|
-
async with httpx.AsyncClient() as client:
|
|
327
|
-
await self._send_events_with_client(client, events)
|
|
328
|
-
|
|
329
|
-
async def _send_events_with_client(self, client: httpx.AsyncClient, events: list[QueuedEvent]) -> None:
|
|
330
|
-
if not events:
|
|
331
|
-
return
|
|
332
|
-
|
|
333
|
-
payload = build_payload(events, source_client_version=self._source_client_version, session_id=self._session_id)
|
|
334
|
-
try:
|
|
335
|
-
response = await client.post(NEMO_TELEMETRY_ENDPOINT, json=payload)
|
|
336
|
-
# 2xx, 400, 422 are all considered complete (no retry)
|
|
337
|
-
# 400/422 indicate bad payload which retrying won't fix
|
|
338
|
-
if response.status_code in (400, 422) or response.is_success:
|
|
339
|
-
return
|
|
340
|
-
# 413 (payload too large) - split and retry
|
|
341
|
-
if response.status_code == 413:
|
|
342
|
-
if len(events) == 1:
|
|
343
|
-
# Can't split further, drop the event
|
|
344
|
-
return
|
|
345
|
-
mid = len(events) // 2
|
|
346
|
-
await self._send_events_with_client(client, events[:mid])
|
|
347
|
-
await self._send_events_with_client(client, events[mid:])
|
|
348
|
-
return
|
|
349
|
-
if response.status_code == 408 or response.status_code >= 500:
|
|
350
|
-
self._add_to_dlq(events)
|
|
351
|
-
except httpx.HTTPError:
|
|
352
|
-
self._add_to_dlq(events)
|
|
353
|
-
|
|
354
|
-
def _add_to_dlq(self, events: list[QueuedEvent]) -> None:
|
|
355
|
-
for queued in events:
|
|
356
|
-
queued.retry_count += 1
|
|
357
|
-
if queued.retry_count > self._max_retries:
|
|
358
|
-
continue
|
|
359
|
-
self._dlq.append(queued)
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import logging
|
|
7
|
-
|
|
8
|
-
from pydantic import BaseModel, computed_field
|
|
9
|
-
|
|
10
|
-
logger = logging.getLogger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class TokenUsageStats(BaseModel):
|
|
14
|
-
input_tokens: int = 0
|
|
15
|
-
output_tokens: int = 0
|
|
16
|
-
|
|
17
|
-
@computed_field
|
|
18
|
-
def total_tokens(self) -> int:
|
|
19
|
-
return self.input_tokens + self.output_tokens
|
|
20
|
-
|
|
21
|
-
@property
|
|
22
|
-
def has_usage(self) -> bool:
|
|
23
|
-
return self.total_tokens > 0
|
|
24
|
-
|
|
25
|
-
def extend(self, *, input_tokens: int, output_tokens: int) -> None:
|
|
26
|
-
self.input_tokens += input_tokens
|
|
27
|
-
self.output_tokens += output_tokens
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class RequestUsageStats(BaseModel):
|
|
31
|
-
successful_requests: int = 0
|
|
32
|
-
failed_requests: int = 0
|
|
33
|
-
|
|
34
|
-
@computed_field
|
|
35
|
-
def total_requests(self) -> int:
|
|
36
|
-
return self.successful_requests + self.failed_requests
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def has_usage(self) -> bool:
|
|
40
|
-
return self.total_requests > 0
|
|
41
|
-
|
|
42
|
-
def extend(self, *, successful_requests: int, failed_requests: int) -> None:
|
|
43
|
-
self.successful_requests += successful_requests
|
|
44
|
-
self.failed_requests += failed_requests
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class ModelUsageStats(BaseModel):
|
|
48
|
-
token_usage: TokenUsageStats = TokenUsageStats()
|
|
49
|
-
request_usage: RequestUsageStats = RequestUsageStats()
|
|
50
|
-
|
|
51
|
-
@property
|
|
52
|
-
def has_usage(self) -> bool:
|
|
53
|
-
return self.token_usage.has_usage and self.request_usage.has_usage
|
|
54
|
-
|
|
55
|
-
def extend(
|
|
56
|
-
self, *, token_usage: TokenUsageStats | None = None, request_usage: RequestUsageStats | None = None
|
|
57
|
-
) -> None:
|
|
58
|
-
if token_usage is not None:
|
|
59
|
-
self.token_usage.extend(input_tokens=token_usage.input_tokens, output_tokens=token_usage.output_tokens)
|
|
60
|
-
if request_usage is not None:
|
|
61
|
-
self.request_usage.extend(
|
|
62
|
-
successful_requests=request_usage.successful_requests, failed_requests=request_usage.failed_requests
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
def get_usage_stats(self, *, total_time_elapsed: float) -> dict:
|
|
66
|
-
return self.model_dump() | {
|
|
67
|
-
"tokens_per_second": int(self.token_usage.total_tokens / total_time_elapsed)
|
|
68
|
-
if total_time_elapsed > 0
|
|
69
|
-
else 0,
|
|
70
|
-
"requests_per_minute": int(self.request_usage.total_requests / total_time_elapsed * 60)
|
|
71
|
-
if total_time_elapsed > 0
|
|
72
|
-
else 0,
|
|
73
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def prompt_to_messages(
|
|
10
|
-
*,
|
|
11
|
-
user_prompt: str,
|
|
12
|
-
system_prompt: str | None = None,
|
|
13
|
-
multi_modal_context: list[dict[str, Any]] | None = None,
|
|
14
|
-
) -> list[dict[str, str | list[dict]]]:
|
|
15
|
-
"""Convert a user and system prompt into Messages format.
|
|
16
|
-
|
|
17
|
-
Args:
|
|
18
|
-
user_prompt (str): A user prompt.
|
|
19
|
-
system_prompt (str, optional): An optional system prompt.
|
|
20
|
-
"""
|
|
21
|
-
user_content = user_prompt
|
|
22
|
-
if multi_modal_context and len(multi_modal_context) > 0:
|
|
23
|
-
user_content = []
|
|
24
|
-
user_content.append({"type": "text", "text": user_prompt})
|
|
25
|
-
for context in multi_modal_context:
|
|
26
|
-
user_content.append(context)
|
|
27
|
-
return (
|
|
28
|
-
[
|
|
29
|
-
str_to_message(content=system_prompt, role="system"),
|
|
30
|
-
str_to_message(content=user_content, role="user"),
|
|
31
|
-
]
|
|
32
|
-
if system_prompt
|
|
33
|
-
else [str_to_message(content=user_content, role="user")]
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def str_to_message(content: str | list[dict], role: str = "user") -> dict[str, str | list[dict]]:
|
|
38
|
-
return {"content": content, "role": role}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
from collections import deque
|
|
7
|
-
|
|
8
|
-
from jinja2 import nodes as j_nodes
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def ast_max_depth(node: j_nodes.Node) -> int:
|
|
12
|
-
"""Calculate the depth of a Jinja AST from a given node.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
node (jinja2.nodes.Node): The starting Jinja2 AST node
|
|
16
|
-
|
|
17
|
-
Returns:
|
|
18
|
-
int: The maximum depth of the tree
|
|
19
|
-
"""
|
|
20
|
-
# Each entry is (node, depth)
|
|
21
|
-
queue = deque([(node, 1)])
|
|
22
|
-
max_depth = 0
|
|
23
|
-
|
|
24
|
-
while queue:
|
|
25
|
-
current_node, current_depth = queue.popleft()
|
|
26
|
-
|
|
27
|
-
# Update maximum depth seen so far
|
|
28
|
-
max_depth = max(max_depth, current_depth)
|
|
29
|
-
|
|
30
|
-
# Add all children with incremented depth
|
|
31
|
-
for child in current_node.iter_child_nodes():
|
|
32
|
-
queue.append((child, current_depth + 1))
|
|
33
|
-
|
|
34
|
-
return max_depth
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def ast_descendant_count(ast: j_nodes.Node, only_type: type[j_nodes.Node] | None = None) -> int:
|
|
38
|
-
"""Count the number of nodes which descend from the given node.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
ast (jinja2.nodes.Node): The starting Jinja2 AST node
|
|
42
|
-
only_type (Type[jinja2.nodes.Node]): If specified, then only
|
|
43
|
-
nodes of this type will be counted.
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
int: The number of nodes descended from the given node.
|
|
47
|
-
"""
|
|
48
|
-
if only_type is None:
|
|
49
|
-
only_type = j_nodes.Node
|
|
50
|
-
|
|
51
|
-
return len(list(ast.find_all(only_type)))
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def ast_count_name_references(ast: j_nodes.Node, name: str) -> int:
|
|
55
|
-
"""Count the number of nodes descended from the current that refer to name.
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
ast (jinja2.nodes.Node): The starting Jinja2 AST node
|
|
59
|
-
|
|
60
|
-
Returns:
|
|
61
|
-
int: The number of nodes descended from the provided node whose
|
|
62
|
-
name field matches the given name.
|
|
63
|
-
"""
|
|
64
|
-
referenced_names = [node.name for node in ast.find_all(j_nodes.Name) if node.name == name]
|
|
65
|
-
return len(referenced_names)
|