netra-sdk 0.1.14__tar.gz → 0.1.16__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.14 → netra_sdk-0.1.16}/PKG-INFO +2 -2
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/__init__.py +20 -5
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/openai/wrappers.py +32 -21
- netra_sdk-0.1.16/netra/version.py +1 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/pyproject.toml +2 -2
- netra_sdk-0.1.14/netra/version.py +0 -2
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/LICENCE +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/README.md +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/anonymizer/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/anonymizer/anonymizer.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/anonymizer/base.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/anonymizer/fp_anonymizer.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/config.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/decorators.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/exceptions/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/exceptions/injection.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/exceptions/pii.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/input_scanner.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/aiohttp/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/aiohttp/version.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/cohere/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/cohere/version.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/fastapi/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/fastapi/version.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/google_genai/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/google_genai/config.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/google_genai/utils.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/google_genai/version.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/httpx/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/httpx/version.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/instruments.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/mistralai/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/mistralai/config.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/mistralai/utils.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/mistralai/version.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/openai/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/openai/version.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/weaviate/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/instrumentation/weaviate/version.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/pii.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/processors/__init__.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/processors/session_span_processor.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/scanner.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/session_manager.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/netra/span_wrapper.py +0 -0
- {netra_sdk-0.1.14 → netra_sdk-0.1.16}/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.16
|
|
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
|
|
@@ -67,7 +67,7 @@ Requires-Dist: opentelemetry-instrumentation-urllib (>=0.55b1,<1.0.0)
|
|
|
67
67
|
Requires-Dist: opentelemetry-instrumentation-urllib3 (>=0.55b1,<1.0.0)
|
|
68
68
|
Requires-Dist: opentelemetry-sdk (>=1.34.0,<2.0.0)
|
|
69
69
|
Requires-Dist: presidio-analyzer (>=2.2.358,<3.0.0)
|
|
70
|
-
Requires-Dist: traceloop-sdk (>=0.40.7,<0.
|
|
70
|
+
Requires-Dist: traceloop-sdk (>=0.40.7,<0.43.0)
|
|
71
71
|
Project-URL: Bug Tracker, https://github.com/KeyValueSoftwareSystems/netra-sdk-py/issues
|
|
72
72
|
Project-URL: Documentation, https://github.com/KeyValueSoftwareSystems/netra-sdk-py/blob/main/README.md
|
|
73
73
|
Project-URL: Homepage, https://github.com/KeyValueSoftwareSystems/netra-sdk-py
|
|
@@ -88,7 +88,10 @@ class Netra:
|
|
|
88
88
|
Args:
|
|
89
89
|
session_id: Session identifier
|
|
90
90
|
"""
|
|
91
|
-
|
|
91
|
+
if session_id:
|
|
92
|
+
SessionManager.set_session_context("session_id", session_id)
|
|
93
|
+
else:
|
|
94
|
+
logger.warning("Session ID must be provided for setting session_id.")
|
|
92
95
|
|
|
93
96
|
@classmethod
|
|
94
97
|
def set_user_id(cls, user_id: str) -> None:
|
|
@@ -98,7 +101,10 @@ class Netra:
|
|
|
98
101
|
Args:
|
|
99
102
|
user_id: User identifier
|
|
100
103
|
"""
|
|
101
|
-
|
|
104
|
+
if user_id:
|
|
105
|
+
SessionManager.set_session_context("user_id", user_id)
|
|
106
|
+
else:
|
|
107
|
+
logger.warning("User ID must be provided for setting user_id.")
|
|
102
108
|
|
|
103
109
|
@classmethod
|
|
104
110
|
def set_tenant_id(cls, tenant_id: str) -> None:
|
|
@@ -108,7 +114,10 @@ class Netra:
|
|
|
108
114
|
Args:
|
|
109
115
|
user_account_id: User account identifier
|
|
110
116
|
"""
|
|
111
|
-
|
|
117
|
+
if tenant_id:
|
|
118
|
+
SessionManager.set_session_context("tenant_id", tenant_id)
|
|
119
|
+
else:
|
|
120
|
+
logger.warning("Tenant ID must be provided for setting tenant_id.")
|
|
112
121
|
|
|
113
122
|
@classmethod
|
|
114
123
|
def set_custom_attributes(cls, key: str, value: Any) -> None:
|
|
@@ -119,7 +128,10 @@ class Netra:
|
|
|
119
128
|
key: Custom attribute key
|
|
120
129
|
value: Custom attribute value
|
|
121
130
|
"""
|
|
122
|
-
|
|
131
|
+
if key and value:
|
|
132
|
+
SessionManager.set_session_context("custom_attributes", {key: value})
|
|
133
|
+
else:
|
|
134
|
+
logger.warning("Both key and value must be provided for custom attributes.")
|
|
123
135
|
|
|
124
136
|
@classmethod
|
|
125
137
|
def set_custom_event(cls, event_name: str, attributes: Any) -> None:
|
|
@@ -130,7 +142,10 @@ class Netra:
|
|
|
130
142
|
event_name: Name of the custom event
|
|
131
143
|
attributes: Attributes of the custom event
|
|
132
144
|
"""
|
|
133
|
-
|
|
145
|
+
if event_name and attributes:
|
|
146
|
+
SessionManager.set_custom_event(event_name, attributes)
|
|
147
|
+
else:
|
|
148
|
+
logger.warning("Both event_name and attributes must be provided for custom events.")
|
|
134
149
|
|
|
135
150
|
@classmethod
|
|
136
151
|
def start_span(
|
|
@@ -12,6 +12,9 @@ from typing import Any, AsyncIterator, Callable, Dict, Iterator, Tuple
|
|
|
12
12
|
|
|
13
13
|
from opentelemetry import context as context_api
|
|
14
14
|
from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
|
|
15
|
+
from opentelemetry.semconv_ai import (
|
|
16
|
+
SpanAttributes,
|
|
17
|
+
)
|
|
15
18
|
from opentelemetry.trace import Span, SpanKind, Tracer
|
|
16
19
|
from opentelemetry.trace.status import Status, StatusCode
|
|
17
20
|
from wrapt import ObjectProxy
|
|
@@ -55,34 +58,39 @@ def set_request_attributes(span: Span, kwargs: Dict[str, Any], operation_type: s
|
|
|
55
58
|
return
|
|
56
59
|
|
|
57
60
|
# Set operation type
|
|
58
|
-
span.set_attribute("
|
|
61
|
+
span.set_attribute(f"{SpanAttributes.LLM_REQUEST_TYPE}", operation_type)
|
|
59
62
|
|
|
60
63
|
# Common attributes
|
|
61
64
|
if kwargs.get("model"):
|
|
62
|
-
span.set_attribute("
|
|
65
|
+
span.set_attribute(f"{SpanAttributes.LLM_REQUEST_MODEL}", kwargs["model"])
|
|
63
66
|
|
|
64
67
|
if kwargs.get("temperature") is not None:
|
|
65
|
-
span.set_attribute("
|
|
68
|
+
span.set_attribute(f"{SpanAttributes.LLM_REQUEST_TEMPERATURE}", kwargs["temperature"])
|
|
66
69
|
|
|
67
70
|
if kwargs.get("max_tokens") is not None:
|
|
68
|
-
span.set_attribute("
|
|
71
|
+
span.set_attribute(f"{SpanAttributes.LLM_REQUEST_MAX_TOKENS}", kwargs["max_tokens"])
|
|
69
72
|
|
|
70
73
|
if kwargs.get("stream") is not None:
|
|
71
|
-
span.set_attribute("
|
|
74
|
+
span.set_attribute("gen_ai.stream", kwargs["stream"])
|
|
72
75
|
|
|
73
76
|
# Chat-specific attributes
|
|
74
77
|
if operation_type == "chat" and kwargs.get("messages"):
|
|
75
78
|
messages = kwargs["messages"]
|
|
76
79
|
if isinstance(messages, list) and len(messages) > 0:
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
for index, message in enumerate(messages):
|
|
81
|
+
if hasattr(message, "content"):
|
|
82
|
+
span.set_attribute(f"{SpanAttributes.LLM_PROMPTS}.{index}.role", "user")
|
|
83
|
+
span.set_attribute(f"{SpanAttributes.LLM_PROMPTS}.{index}.content", message.content)
|
|
84
|
+
elif isinstance(message, dict):
|
|
85
|
+
span.set_attribute(f"{SpanAttributes.LLM_PROMPTS}.{index}.role", message.get("role", "user"))
|
|
86
|
+
span.set_attribute(f"{SpanAttributes.LLM_PROMPTS}.{index}.content", str(message.get("content", "")))
|
|
79
87
|
|
|
80
88
|
# Response-specific attributes
|
|
81
89
|
if operation_type == "response":
|
|
82
90
|
if kwargs.get("instructions"):
|
|
83
|
-
span.set_attribute("
|
|
91
|
+
span.set_attribute("gen_ai.instructions", kwargs["instructions"])
|
|
84
92
|
if kwargs.get("input"):
|
|
85
|
-
span.set_attribute("
|
|
93
|
+
span.set_attribute("gen_ai.input", kwargs["input"])
|
|
86
94
|
|
|
87
95
|
|
|
88
96
|
def set_response_attributes(span: Span, response_dict: Dict[str, Any]) -> None:
|
|
@@ -91,33 +99,36 @@ def set_response_attributes(span: Span, response_dict: Dict[str, Any]) -> None:
|
|
|
91
99
|
return
|
|
92
100
|
|
|
93
101
|
if response_dict.get("model"):
|
|
94
|
-
span.set_attribute("
|
|
102
|
+
span.set_attribute(f"{SpanAttributes.LLM_RESPONSE_MODEL}", response_dict["model"])
|
|
95
103
|
|
|
96
104
|
if response_dict.get("id"):
|
|
97
|
-
span.set_attribute("
|
|
105
|
+
span.set_attribute("gen_ai.response.id", response_dict["id"])
|
|
98
106
|
|
|
99
107
|
# Usage information
|
|
100
108
|
usage = response_dict.get("usage", {})
|
|
101
109
|
if usage:
|
|
102
110
|
if usage.get("prompt_tokens"):
|
|
103
|
-
span.set_attribute("
|
|
111
|
+
span.set_attribute(f"{SpanAttributes.LLM_USAGE_PROMPT_TOKENS}", usage["prompt_tokens"])
|
|
104
112
|
if usage.get("completion_tokens"):
|
|
105
|
-
span.set_attribute("
|
|
113
|
+
span.set_attribute(f"{SpanAttributes.LLM_USAGE_COMPLETION_TOKENS}", usage["completion_tokens"])
|
|
114
|
+
if usage.get("cache_read_input_token"):
|
|
115
|
+
span.set_attribute(f"{SpanAttributes.LLM_USAGE_CACHE_READ_INPUT_TOKENS}", usage["cache_read_input_token"])
|
|
106
116
|
if usage.get("total_tokens"):
|
|
107
|
-
span.set_attribute("
|
|
117
|
+
span.set_attribute(f"{SpanAttributes.LLM_USAGE_TOTAL_TOKENS}", usage["total_tokens"])
|
|
108
118
|
|
|
109
119
|
# Response content
|
|
110
120
|
choices = response_dict.get("choices", [])
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
for index, choice in enumerate(choices):
|
|
122
|
+
if choice.get("message", {}).get("role"):
|
|
123
|
+
span.set_attribute(f"{SpanAttributes.LLM_COMPLETIONS}.{index}.role", choice["message"]["role"])
|
|
124
|
+
if choice.get("message", {}).get("content"):
|
|
125
|
+
span.set_attribute(f"{SpanAttributes.LLM_COMPLETIONS}.{index}.content", choice["message"]["content"])
|
|
126
|
+
if choice.get("finish_reason"):
|
|
127
|
+
span.set_attribute(f"{SpanAttributes.LLM_COMPLETIONS}.{index}.finish_reason", choice["finish_reason"])
|
|
117
128
|
|
|
118
129
|
# For responses.create
|
|
119
130
|
if response_dict.get("output_text"):
|
|
120
|
-
span.set_attribute("
|
|
131
|
+
span.set_attribute("gen_ai.response.output_text", response_dict["output_text"])
|
|
121
132
|
|
|
122
133
|
|
|
123
134
|
def chat_wrapper(tracer: Tracer) -> Callable[..., Any]:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.16"
|
|
@@ -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.16"
|
|
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"}
|
|
@@ -32,7 +32,7 @@ dependencies = [
|
|
|
32
32
|
"opentelemetry-api>=1.34.0,<2.0.0",
|
|
33
33
|
"opentelemetry-sdk>=1.34.0,<2.0.0",
|
|
34
34
|
"opentelemetry-instrumentation-fastapi>=0.55b1,<1.0.0",
|
|
35
|
-
"traceloop-sdk>=0.40.7,<0.
|
|
35
|
+
"traceloop-sdk>=0.40.7,<0.43.0",
|
|
36
36
|
"presidio-analyzer>=2.2.358,<3.0.0",
|
|
37
37
|
"opentelemetry-instrumentation-httpx>=0.55b1,<1.0.0",
|
|
38
38
|
"opentelemetry-instrumentation-aiohttp-client>=0.55b1,<1.0.0",
|
|
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
|