netra-sdk 0.1.39__tar.gz → 0.1.40__tar.gz
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.
Potentially problematic release.
This version of netra-sdk might be problematic. Click here for more details.
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/PKG-INFO +1 -1
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/__init__.py +11 -40
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/config.py +7 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/session_manager.py +53 -61
- netra_sdk-0.1.40/netra/version.py +1 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/pyproject.toml +1 -1
- netra_sdk-0.1.39/netra/version.py +0 -1
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/LICENCE +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/README.md +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/anonymizer/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/anonymizer/anonymizer.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/anonymizer/base.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/anonymizer/fp_anonymizer.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/decorators.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/exceptions/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/exceptions/injection.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/exceptions/pii.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/input_scanner.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/aiohttp/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/aiohttp/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/cohere/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/cohere/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/fastapi/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/fastapi/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/google_genai/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/google_genai/config.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/google_genai/utils.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/google_genai/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/httpx/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/httpx/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/instruments.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/litellm/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/litellm/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/litellm/wrappers.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/mistralai/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/mistralai/config.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/mistralai/utils.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/mistralai/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/openai/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/openai/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/openai/wrappers.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/pydantic_ai/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/pydantic_ai/utils.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/pydantic_ai/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/pydantic_ai/wrappers.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/weaviate/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/instrumentation/weaviate/version.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/pii.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/processors/__init__.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/processors/instrumentation_span_processor.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/processors/scrubbing_span_processor.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/processors/session_span_processor.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/scanner.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/span_wrapper.py +0 -0
- {netra_sdk-0.1.39 → netra_sdk-0.1.40}/netra/tracer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: netra-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.40
|
|
4
4
|
Summary: A Python SDK for AI application observability that provides OpenTelemetry-based monitoring, tracing, and PII protection for LLM and vector database applications. Enables easy instrumentation, session tracking, and privacy-focused data collection for AI systems in production environments.
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: netra,tracing,observability,sdk,ai,llm,vector,database
|
|
@@ -9,11 +9,11 @@ from opentelemetry.trace import SpanKind
|
|
|
9
9
|
|
|
10
10
|
from netra.instrumentation.instruments import InstrumentSet, NetraInstruments
|
|
11
11
|
|
|
12
|
-
from .config import Config
|
|
12
|
+
from .config import Config, ConversationType
|
|
13
13
|
|
|
14
14
|
# Instrumentor functions
|
|
15
15
|
from .instrumentation import init_instrumentations
|
|
16
|
-
from .session_manager import SessionManager
|
|
16
|
+
from .session_manager import ConversationType, SessionManager
|
|
17
17
|
from .span_wrapper import ActionModel, SpanWrapper, UsageModel
|
|
18
18
|
from .tracer import Tracer
|
|
19
19
|
|
|
@@ -248,6 +248,15 @@ class Netra:
|
|
|
248
248
|
else:
|
|
249
249
|
logger.warning("Both event_name and attributes must be provided for custom events.")
|
|
250
250
|
|
|
251
|
+
@classmethod
|
|
252
|
+
def add_conversation(cls, type: ConversationType, field_name: str, value: Any) -> None:
|
|
253
|
+
"""
|
|
254
|
+
Append a conversation entry and set span attribute 'conversation' as an array.
|
|
255
|
+
If a conversation array already exists for the current active span, this appends
|
|
256
|
+
to it; otherwise, it initializes a new array.
|
|
257
|
+
"""
|
|
258
|
+
SessionManager.add_conversation(type=type, field_name=field_name, value=value)
|
|
259
|
+
|
|
251
260
|
@classmethod
|
|
252
261
|
def start_span(
|
|
253
262
|
cls,
|
|
@@ -260,43 +269,5 @@ class Netra:
|
|
|
260
269
|
"""
|
|
261
270
|
return SpanWrapper(name, attributes, module_name)
|
|
262
271
|
|
|
263
|
-
@classmethod
|
|
264
|
-
def set_input(cls, value: Any, span_name: Optional[str] = None) -> None:
|
|
265
|
-
"""
|
|
266
|
-
Set custom attribute `netra.span.input` on a target span.
|
|
267
|
-
|
|
268
|
-
Args:
|
|
269
|
-
value: Input payload to record (string or JSON-serializable object)
|
|
270
|
-
span_name: Optional. When provided, sets the attribute on the span registered
|
|
271
|
-
with this name. Otherwise sets on the active span.
|
|
272
|
-
"""
|
|
273
|
-
SessionManager.set_attribute_on_target_span(f"{Config.LIBRARY_NAME}.span.input", value, span_name)
|
|
274
|
-
|
|
275
|
-
@classmethod
|
|
276
|
-
def set_output(cls, value: Any, span_name: Optional[str] = None) -> None:
|
|
277
|
-
"""
|
|
278
|
-
Set custom attribute `netra.span.output` on a target span.
|
|
279
|
-
|
|
280
|
-
Args:
|
|
281
|
-
value: Output payload to record (string or JSON-serializable object)
|
|
282
|
-
span_name: Optional. When provided, sets the attribute on the span registered
|
|
283
|
-
with this name. Otherwise sets on the active span.
|
|
284
|
-
"""
|
|
285
|
-
if value:
|
|
286
|
-
SessionManager.set_attribute_on_target_span(f"{Config.LIBRARY_NAME}.span.output", value, span_name)
|
|
287
|
-
|
|
288
|
-
@classmethod
|
|
289
|
-
def set_prompt(cls, value: Any, span_name: Optional[str] = None) -> None:
|
|
290
|
-
"""
|
|
291
|
-
Set custom attribute `netra.span.prompt` on a target span.
|
|
292
|
-
|
|
293
|
-
Args:
|
|
294
|
-
value: Prompt payload to record (string or JSON-serializable object)
|
|
295
|
-
span_name: Optional. When provided, sets the attribute on the span registered
|
|
296
|
-
with this name. Otherwise sets on the active span.
|
|
297
|
-
"""
|
|
298
|
-
if value:
|
|
299
|
-
SessionManager.set_attribute_on_target_span(f"{Config.LIBRARY_NAME}.span.prompt", value, span_name)
|
|
300
|
-
|
|
301
272
|
|
|
302
273
|
__all__ = ["Netra", "UsageModel", "ActionModel"]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
|
+
from enum import Enum
|
|
3
4
|
from typing import Any, Dict, List, Optional
|
|
4
5
|
|
|
5
6
|
from opentelemetry.util.re import parse_env_headers
|
|
@@ -7,6 +8,12 @@ from opentelemetry.util.re import parse_env_headers
|
|
|
7
8
|
from netra.version import __version__
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
class ConversationType(str, Enum):
|
|
12
|
+
INPUT = "input"
|
|
13
|
+
OUTPUT = "output"
|
|
14
|
+
SYSTEM = "system"
|
|
15
|
+
|
|
16
|
+
|
|
10
17
|
class Config:
|
|
11
18
|
"""
|
|
12
19
|
Holds configuration options for the tracer:
|
|
@@ -5,17 +5,24 @@ Handles automatic session and user ID management for applications.
|
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
7
|
from datetime import datetime
|
|
8
|
+
from enum import Enum
|
|
8
9
|
from typing import Any, Dict, List, Optional, Union
|
|
9
10
|
|
|
10
11
|
from opentelemetry import baggage
|
|
11
12
|
from opentelemetry import context as otel_context
|
|
12
13
|
from opentelemetry import trace
|
|
13
14
|
|
|
14
|
-
from .config import Config
|
|
15
|
+
from netra.config import Config
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
18
19
|
|
|
20
|
+
class ConversationType(str, Enum):
|
|
21
|
+
INPUT = "input"
|
|
22
|
+
OUTPUT = "output"
|
|
23
|
+
SYSTEM = "system"
|
|
24
|
+
|
|
25
|
+
|
|
19
26
|
class SessionManager:
|
|
20
27
|
"""Manages session and user context for applications."""
|
|
21
28
|
|
|
@@ -254,79 +261,64 @@ class SessionManager:
|
|
|
254
261
|
except Exception as e:
|
|
255
262
|
logger.exception(f"Failed to add custom event: {name} - {e}")
|
|
256
263
|
|
|
257
|
-
@
|
|
258
|
-
def
|
|
264
|
+
@staticmethod
|
|
265
|
+
def add_conversation(type: ConversationType, field_name: str, value: Any) -> None:
|
|
259
266
|
"""
|
|
260
|
-
|
|
267
|
+
Append a conversation entry and set span attribute 'conversation' as an array.
|
|
261
268
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
269
|
+
Stored attribute format:
|
|
270
|
+
conversation: [
|
|
271
|
+
{ "type": "input/output/system", "field_name": "sample_name", "value": "sample_value" },
|
|
272
|
+
...
|
|
273
|
+
]
|
|
267
274
|
"""
|
|
268
|
-
try:
|
|
269
|
-
# Convert attribute value to a JSON-safe string representation
|
|
270
|
-
try:
|
|
271
|
-
if isinstance(attr_value, str):
|
|
272
|
-
attr_str = attr_value
|
|
273
|
-
else:
|
|
274
|
-
import json
|
|
275
275
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
276
|
+
# Hard runtime validation of input types and values
|
|
277
|
+
if not isinstance(type, ConversationType):
|
|
278
|
+
raise TypeError("type must be a ConversationType enum value (input, output, system)")
|
|
279
|
+
normalized_type = type.value
|
|
280
|
+
|
|
281
|
+
if not isinstance(field_name, str):
|
|
282
|
+
raise TypeError(f"field_name must be a string, got {type(field_name)}")
|
|
283
|
+
if not field_name:
|
|
284
|
+
raise ValueError("field_name must be a non-empty string")
|
|
285
|
+
|
|
286
|
+
if value is None:
|
|
287
|
+
raise ValueError("value must not be None")
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
span = trace.get_current_span()
|
|
291
|
+
if not (span and getattr(span, "is_recording", lambda: False)()):
|
|
292
|
+
logger.warning("No active span to add conversation attribute.")
|
|
287
293
|
return
|
|
288
294
|
|
|
289
|
-
|
|
290
|
-
|
|
295
|
+
existing: List[Dict[str, Any]] = []
|
|
296
|
+
raw_data = None
|
|
291
297
|
|
|
292
|
-
# Determine current trace_id from the active/current span
|
|
293
|
-
current_span = trace.get_current_span()
|
|
294
|
-
has_valid_current = getattr(current_span, "is_recording", None) is not None and current_span.is_recording()
|
|
295
|
-
base_span = current_span if has_valid_current else cls.get_current_span()
|
|
296
|
-
trace_id: Optional[int] = None
|
|
297
298
|
try:
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
attrs = getattr(span, "_attributes", None)
|
|
300
|
+
if attrs is not None and hasattr(attrs, "get"):
|
|
301
|
+
raw_data = attrs.get("conversation")
|
|
301
302
|
except Exception:
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
# Find the earliest active span in this process that belongs to the same trace
|
|
305
|
-
if trace_id is not None:
|
|
303
|
+
logger.exception("Failed to retrieve conversation attribute")
|
|
304
|
+
if raw_data:
|
|
306
305
|
try:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if sc is None:
|
|
314
|
-
continue
|
|
315
|
-
if getattr(sc, "trace_id", None) == trace_id:
|
|
316
|
-
candidate = s
|
|
317
|
-
break
|
|
306
|
+
import json
|
|
307
|
+
|
|
308
|
+
if isinstance(raw_data, str):
|
|
309
|
+
parsed = json.loads(raw_data)
|
|
310
|
+
if isinstance(parsed, list):
|
|
311
|
+
existing = parsed
|
|
318
312
|
except Exception:
|
|
319
|
-
|
|
313
|
+
existing = []
|
|
320
314
|
|
|
321
|
-
#
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return
|
|
327
|
-
candidate.set_attribute(attr_key, attr_str)
|
|
315
|
+
# Append new entry
|
|
316
|
+
entry: Dict[str, Any] = {"type": normalized_type, "field_name": field_name, "value": value}
|
|
317
|
+
existing.append(entry)
|
|
318
|
+
|
|
319
|
+
SessionManager.set_attribute_on_active_span("conversation", existing)
|
|
328
320
|
except Exception as e:
|
|
329
|
-
logger.exception("Failed
|
|
321
|
+
logger.exception("Failed to add conversation attribute: %s", e)
|
|
330
322
|
|
|
331
323
|
@staticmethod
|
|
332
324
|
def set_attribute_on_active_span(attr_key: str, attr_value: Any) -> None:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.40"
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "netra-sdk"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.40"
|
|
8
8
|
description = "A Python SDK for AI application observability that provides OpenTelemetry-based monitoring, tracing, and PII protection for LLM and vector database applications. Enables easy instrumentation, session tracking, and privacy-focused data collection for AI systems in production environments."
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Sooraj Thomas",email = "sooraj@keyvalue.systems"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.39"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|