netra-sdk 0.1.12__py3-none-any.whl → 0.1.19__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.
Potentially problematic release.
This version of netra-sdk might be problematic. Click here for more details.
- netra/__init__.py +22 -6
- netra/anonymizer/anonymizer.py +7 -1
- netra/instrumentation/__init__.py +23 -8
- netra/instrumentation/openai/wrappers.py +32 -21
- netra/span_wrapper.py +19 -1
- netra/version.py +1 -2
- {netra_sdk-0.1.12.dist-info → netra_sdk-0.1.19.dist-info}/METADATA +36 -10
- {netra_sdk-0.1.12.dist-info → netra_sdk-0.1.19.dist-info}/RECORD +10 -10
- {netra_sdk-0.1.12.dist-info → netra_sdk-0.1.19.dist-info}/LICENCE +0 -0
- {netra_sdk-0.1.12.dist-info → netra_sdk-0.1.19.dist-info}/WHEEL +0 -0
netra/__init__.py
CHANGED
|
@@ -2,7 +2,7 @@ import logging
|
|
|
2
2
|
import threading
|
|
3
3
|
from typing import Any, Dict, Optional, Set
|
|
4
4
|
|
|
5
|
-
from netra.instrumentation.instruments import NetraInstruments
|
|
5
|
+
from netra.instrumentation.instruments import InstrumentSet, NetraInstruments
|
|
6
6
|
|
|
7
7
|
from .config import Config
|
|
8
8
|
|
|
@@ -70,6 +70,7 @@ class Netra:
|
|
|
70
70
|
|
|
71
71
|
# Instrument all supported modules
|
|
72
72
|
# Pass trace_content flag to instrumentors that can capture prompts/completions
|
|
73
|
+
|
|
73
74
|
init_instrumentations(
|
|
74
75
|
should_enrich_metrics=True,
|
|
75
76
|
base64_image_uploader=None,
|
|
@@ -88,7 +89,10 @@ class Netra:
|
|
|
88
89
|
Args:
|
|
89
90
|
session_id: Session identifier
|
|
90
91
|
"""
|
|
91
|
-
|
|
92
|
+
if session_id:
|
|
93
|
+
SessionManager.set_session_context("session_id", session_id)
|
|
94
|
+
else:
|
|
95
|
+
logger.warning("Session ID must be provided for setting session_id.")
|
|
92
96
|
|
|
93
97
|
@classmethod
|
|
94
98
|
def set_user_id(cls, user_id: str) -> None:
|
|
@@ -98,7 +102,10 @@ class Netra:
|
|
|
98
102
|
Args:
|
|
99
103
|
user_id: User identifier
|
|
100
104
|
"""
|
|
101
|
-
|
|
105
|
+
if user_id:
|
|
106
|
+
SessionManager.set_session_context("user_id", user_id)
|
|
107
|
+
else:
|
|
108
|
+
logger.warning("User ID must be provided for setting user_id.")
|
|
102
109
|
|
|
103
110
|
@classmethod
|
|
104
111
|
def set_tenant_id(cls, tenant_id: str) -> None:
|
|
@@ -108,7 +115,10 @@ class Netra:
|
|
|
108
115
|
Args:
|
|
109
116
|
user_account_id: User account identifier
|
|
110
117
|
"""
|
|
111
|
-
|
|
118
|
+
if tenant_id:
|
|
119
|
+
SessionManager.set_session_context("tenant_id", tenant_id)
|
|
120
|
+
else:
|
|
121
|
+
logger.warning("Tenant ID must be provided for setting tenant_id.")
|
|
112
122
|
|
|
113
123
|
@classmethod
|
|
114
124
|
def set_custom_attributes(cls, key: str, value: Any) -> None:
|
|
@@ -119,7 +129,10 @@ class Netra:
|
|
|
119
129
|
key: Custom attribute key
|
|
120
130
|
value: Custom attribute value
|
|
121
131
|
"""
|
|
122
|
-
|
|
132
|
+
if key and value:
|
|
133
|
+
SessionManager.set_session_context("custom_attributes", {key: value})
|
|
134
|
+
else:
|
|
135
|
+
logger.warning("Both key and value must be provided for custom attributes.")
|
|
123
136
|
|
|
124
137
|
@classmethod
|
|
125
138
|
def set_custom_event(cls, event_name: str, attributes: Any) -> None:
|
|
@@ -130,7 +143,10 @@ class Netra:
|
|
|
130
143
|
event_name: Name of the custom event
|
|
131
144
|
attributes: Attributes of the custom event
|
|
132
145
|
"""
|
|
133
|
-
|
|
146
|
+
if event_name and attributes:
|
|
147
|
+
SessionManager.set_custom_event(event_name, attributes)
|
|
148
|
+
else:
|
|
149
|
+
logger.warning("Both event_name and attributes must be provided for custom events.")
|
|
134
150
|
|
|
135
151
|
@classmethod
|
|
136
152
|
def start_span(
|
netra/anonymizer/anonymizer.py
CHANGED
|
@@ -8,7 +8,13 @@ texts while maintaining privacy.
|
|
|
8
8
|
|
|
9
9
|
from typing import Callable, List, Optional
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
try:
|
|
12
|
+
from presidio_analyzer.recognizer_result import RecognizerResult
|
|
13
|
+
except Exception:
|
|
14
|
+
raise ImportError(
|
|
15
|
+
"PII Detetcion requires the 'presidio' packages: Install them explicitly as they are not available with the base SDK. Use pip install 'netra-sdk[presidio]' to install them."
|
|
16
|
+
)
|
|
17
|
+
|
|
12
18
|
|
|
13
19
|
from .base import AnonymizationResult, BaseAnonymizer
|
|
14
20
|
from .fp_anonymizer import FormatPreservingEmailAnonymizer
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from contextlib import redirect_stderr, redirect_stdout
|
|
3
|
+
from io import StringIO
|
|
2
4
|
from typing import Any, Callable, Optional, Set
|
|
3
5
|
|
|
4
6
|
from traceloop.sdk import Instruments, Telemetry
|
|
@@ -31,6 +33,20 @@ def init_instrumentations(
|
|
|
31
33
|
netra_custom_block_instruments.add(getattr(CustomInstruments, instrument.name))
|
|
32
34
|
else:
|
|
33
35
|
traceloop_block_instruments.add(getattr(Instruments, instrument.name))
|
|
36
|
+
|
|
37
|
+
# If no instruments are provided for instrumentation
|
|
38
|
+
if instruments is None:
|
|
39
|
+
traceloop_block_instruments = set(Instruments)
|
|
40
|
+
netra_custom_block_instruments = set(CustomInstruments)
|
|
41
|
+
|
|
42
|
+
# If only custom instruments from netra are provided for instrumentation
|
|
43
|
+
if instruments is not None and not traceloop_instruments and not traceloop_block_instruments:
|
|
44
|
+
traceloop_block_instruments = set(Instruments)
|
|
45
|
+
|
|
46
|
+
# If only traceloop instruments are provided for instrumentation
|
|
47
|
+
if instruments is not None and not netra_custom_instruments and not netra_custom_block_instruments:
|
|
48
|
+
netra_custom_block_instruments = set(CustomInstruments)
|
|
49
|
+
|
|
34
50
|
traceloop_block_instruments.update(
|
|
35
51
|
{
|
|
36
52
|
Instruments.WEAVIATE,
|
|
@@ -40,15 +56,14 @@ def init_instrumentations(
|
|
|
40
56
|
Instruments.OPENAI,
|
|
41
57
|
}
|
|
42
58
|
)
|
|
43
|
-
if instruments is not None and traceloop_instruments is None and traceloop_block_instruments is None:
|
|
44
|
-
traceloop_block_instruments = set(Instruments)
|
|
45
59
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
with redirect_stdout(StringIO()), redirect_stderr(StringIO()):
|
|
61
|
+
init_instrumentations(
|
|
62
|
+
should_enrich_metrics=should_enrich_metrics,
|
|
63
|
+
base64_image_uploader=base64_image_uploader,
|
|
64
|
+
instruments=traceloop_instruments,
|
|
65
|
+
block_instruments=traceloop_block_instruments,
|
|
66
|
+
)
|
|
52
67
|
|
|
53
68
|
netra_custom_instruments = netra_custom_instruments or set(CustomInstruments)
|
|
54
69
|
netra_custom_instruments = netra_custom_instruments - netra_custom_block_instruments
|
|
@@ -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]:
|
netra/span_wrapper.py
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
+
import re
|
|
3
4
|
import time
|
|
5
|
+
from datetime import datetime, timezone
|
|
4
6
|
from typing import Any, Dict, List, Literal, Optional
|
|
5
7
|
|
|
6
8
|
from opentelemetry import context as context_api
|
|
7
9
|
from opentelemetry import trace
|
|
8
10
|
from opentelemetry.trace import SpanKind, Status, StatusCode
|
|
9
11
|
from opentelemetry.trace.propagation import set_span_in_context
|
|
10
|
-
from pydantic import BaseModel
|
|
12
|
+
from pydantic import BaseModel, field_validator
|
|
11
13
|
|
|
12
14
|
from netra.config import Config
|
|
13
15
|
|
|
@@ -17,12 +19,28 @@ logger = logging.getLogger(__name__)
|
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
class ActionModel(BaseModel): # type: ignore[misc]
|
|
22
|
+
start_time: str = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
|
20
23
|
action: str
|
|
21
24
|
action_type: str
|
|
22
25
|
success: bool
|
|
23
26
|
affected_records: Optional[List[Dict[str, str]]] = None
|
|
24
27
|
metadata: Optional[Dict[str, str]] = None
|
|
25
28
|
|
|
29
|
+
@field_validator("start_time") # type: ignore[misc]
|
|
30
|
+
@classmethod
|
|
31
|
+
def validate_time_format(cls, value: str) -> str:
|
|
32
|
+
"""Validate that start_time is in ISO 8601 format with microseconds and Z suffix."""
|
|
33
|
+
# Pattern for ISO 8601 with microseconds: YYYY-MM-DDTHH:MM:SS.ffffffZ
|
|
34
|
+
pattern = r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z$"
|
|
35
|
+
|
|
36
|
+
if not re.match(pattern, value):
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"start_time must be in ISO 8601 format with microseconds: "
|
|
39
|
+
f"YYYY-MM-DDTHH:MM:SS.ffffffZ (e.g., 2025-07-18T14:30:45.123456Z). "
|
|
40
|
+
f"Got: {value}"
|
|
41
|
+
)
|
|
42
|
+
return value
|
|
43
|
+
|
|
26
44
|
|
|
27
45
|
class UsageModel(BaseModel): # type: ignore[misc]
|
|
28
46
|
model: str
|
netra/version.py
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
2
|
-
|
|
1
|
+
__version__ = "0.1.19"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: netra-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.19
|
|
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
|
|
@@ -18,7 +18,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
19
|
Classifier: Typing :: Typed
|
|
20
20
|
Provides-Extra: llm-guard
|
|
21
|
-
|
|
21
|
+
Provides-Extra: presidio
|
|
22
|
+
Requires-Dist: llm-guard (==0.3.16) ; extra == "llm-guard"
|
|
22
23
|
Requires-Dist: opentelemetry-api (>=1.34.0,<2.0.0)
|
|
23
24
|
Requires-Dist: opentelemetry-instrumentation-aio-pika (>=0.55b1,<1.0.0)
|
|
24
25
|
Requires-Dist: opentelemetry-instrumentation-aiohttp-client (>=0.55b1,<1.0.0)
|
|
@@ -66,8 +67,9 @@ Requires-Dist: opentelemetry-instrumentation-tortoiseorm (>=0.55b1,<1.0.0)
|
|
|
66
67
|
Requires-Dist: opentelemetry-instrumentation-urllib (>=0.55b1,<1.0.0)
|
|
67
68
|
Requires-Dist: opentelemetry-instrumentation-urllib3 (>=0.55b1,<1.0.0)
|
|
68
69
|
Requires-Dist: opentelemetry-sdk (>=1.34.0,<2.0.0)
|
|
69
|
-
Requires-Dist: presidio-analyzer (
|
|
70
|
-
Requires-Dist:
|
|
70
|
+
Requires-Dist: presidio-analyzer (==2.2.358) ; extra == "presidio"
|
|
71
|
+
Requires-Dist: presidio-anonymizer (==2.2.358) ; extra == "presidio"
|
|
72
|
+
Requires-Dist: traceloop-sdk (>=0.40.7,<0.43.0)
|
|
71
73
|
Project-URL: Bug Tracker, https://github.com/KeyValueSoftwareSystems/netra-sdk-py/issues
|
|
72
74
|
Project-URL: Documentation, https://github.com/KeyValueSoftwareSystems/netra-sdk-py/blob/main/README.md
|
|
73
75
|
Project-URL: Homepage, https://github.com/KeyValueSoftwareSystems/netra-sdk-py
|
|
@@ -108,6 +110,21 @@ poetry add netra-sdk
|
|
|
108
110
|
|
|
109
111
|
Netra SDK supports optional dependencies for enhanced functionality:
|
|
110
112
|
|
|
113
|
+
#### Presidio for PII Detection
|
|
114
|
+
To use the PII detection features provided by Netra SDK:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pip install 'netra-sdk[presidio]'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Or, using Poetry:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
poetry add netra-sdk --extras "presidio"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
111
128
|
#### LLM-Guard for Prompt Injection Protection
|
|
112
129
|
|
|
113
130
|
To use the full functionality of prompt injection scanning provided by llm-guard:
|
|
@@ -132,9 +149,10 @@ Initialize the Netra SDK at the start of your application:
|
|
|
132
149
|
|
|
133
150
|
```python
|
|
134
151
|
from netra import Netra
|
|
152
|
+
from netra.instrumentation.instruments import InstrumentSet
|
|
135
153
|
|
|
136
154
|
# Initialize with default settings
|
|
137
|
-
Netra.init(app_name="Your application name")
|
|
155
|
+
Netra.init(app_name="Your application name", instruments={InstrumentSet.OPENAI, InstrumentSet.ANTHROPIC})
|
|
138
156
|
|
|
139
157
|
# Or with custom configuration
|
|
140
158
|
api_key = "Your API key"
|
|
@@ -143,7 +161,8 @@ Netra.init(
|
|
|
143
161
|
app_name="Your application name",
|
|
144
162
|
headers=headers,
|
|
145
163
|
trace_content=True,
|
|
146
|
-
environment="Your Application environment"
|
|
164
|
+
environment="Your Application environment",
|
|
165
|
+
instruments={InstrumentSet.OPENAI, InstrumentSet.ANTHROPIC},
|
|
147
166
|
)
|
|
148
167
|
```
|
|
149
168
|
|
|
@@ -375,9 +394,10 @@ Track user sessions and add custom context:
|
|
|
375
394
|
|
|
376
395
|
```python
|
|
377
396
|
from netra import Netra
|
|
397
|
+
from netra.instrumentation.instruments import InstrumentSet
|
|
378
398
|
|
|
379
399
|
# Initialize SDK
|
|
380
|
-
Netra.init(app_name="My App")
|
|
400
|
+
Netra.init(app_name="My App", instruments={InstrumentSet.OPENAI})
|
|
381
401
|
|
|
382
402
|
# Set session identification
|
|
383
403
|
Netra.set_session_id("unique-session-id")
|
|
@@ -443,6 +463,7 @@ with Netra.start_span("image_generation") as span:
|
|
|
443
463
|
|
|
444
464
|
# Track database operations and other actions
|
|
445
465
|
action = ActionModel(
|
|
466
|
+
start_time="2025-07-18T10:29:30.855287Z",
|
|
446
467
|
action="DB",
|
|
447
468
|
action_type="INSERT",
|
|
448
469
|
affected_records=[
|
|
@@ -460,6 +481,7 @@ with Netra.start_span("image_generation") as span:
|
|
|
460
481
|
|
|
461
482
|
# Record API calls
|
|
462
483
|
api_action = ActionModel(
|
|
484
|
+
start_time="2025-07-18T10:29:30.855287Z",
|
|
463
485
|
action="API",
|
|
464
486
|
action_type="CALL",
|
|
465
487
|
metadata={
|
|
@@ -480,6 +502,7 @@ Action tracking follows this schema:
|
|
|
480
502
|
```python
|
|
481
503
|
[
|
|
482
504
|
{
|
|
505
|
+
"start_time": str, # Start time of the action in ISO 8601 format with microseconds and Z suffix (e.g., 2025-07-18T14:30:45.123456Z)
|
|
483
506
|
"action": str, # Type of action (e.g., "DB", "API", "CACHE")
|
|
484
507
|
"action_type": str, # Action subtype (e.g., "INSERT", "SELECT", "CALL")
|
|
485
508
|
"affected_records": [ # Optional: List of records affected
|
|
@@ -552,13 +575,15 @@ You can also configure the SDK programmatically when initializing:
|
|
|
552
575
|
|
|
553
576
|
```python
|
|
554
577
|
from netra import Netra
|
|
578
|
+
from netra.instrumentation.instruments import InstrumentSet
|
|
555
579
|
|
|
556
580
|
Netra.init(
|
|
557
581
|
app_name="my-ai-service",
|
|
558
582
|
environment="production",
|
|
559
583
|
resource_attributes={"team": "ai", "version": "1.0.0"},
|
|
560
584
|
trace_content=True,
|
|
561
|
-
disable_batch=False
|
|
585
|
+
disable_batch=False,
|
|
586
|
+
instruments={InstrumentSet.OPENAI}
|
|
562
587
|
)
|
|
563
588
|
```
|
|
564
589
|
|
|
@@ -570,7 +595,7 @@ Control which instrumentations are enabled:
|
|
|
570
595
|
from netra import Netra
|
|
571
596
|
from netra.instrumentation.instruments import InstrumentSet
|
|
572
597
|
|
|
573
|
-
# Enable specific instruments
|
|
598
|
+
# Enable specific instruments
|
|
574
599
|
Netra.init(
|
|
575
600
|
app_name="Selective App",
|
|
576
601
|
instruments={
|
|
@@ -618,9 +643,10 @@ export NETRA_HEADERS="authorization=Bearer your-token"
|
|
|
618
643
|
|
|
619
644
|
```python
|
|
620
645
|
from netra import Netra
|
|
646
|
+
from netra.instrumentation.instruments import InstrumentSet
|
|
621
647
|
|
|
622
648
|
# Simple initialization - SDK automatically picks up environment variables
|
|
623
|
-
Netra.init(app_name="Your App")
|
|
649
|
+
Netra.init(app_name="Your App", instruments={InstrumentSet})
|
|
624
650
|
# No endpoint configuration needed in code!
|
|
625
651
|
```
|
|
626
652
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
netra/__init__.py,sha256=
|
|
1
|
+
netra/__init__.py,sha256=49R2bZrVu30HhpE3gfpu0b5C5cNovC6WyNyUwHCOfQ8,5392
|
|
2
2
|
netra/anonymizer/__init__.py,sha256=KeGPPZqKVZbtkbirEKYTYhj6aZHlakjdQhD7QHqBRio,133
|
|
3
|
-
netra/anonymizer/anonymizer.py,sha256=
|
|
3
|
+
netra/anonymizer/anonymizer.py,sha256=IcrYkdwWrFauGWUeAW-0RwrSUM8VSZCFNtoywZhvIqU,3778
|
|
4
4
|
netra/anonymizer/base.py,sha256=ytPxHCUD2OXlEY6fNTuMmwImNdIjgj294I41FIgoXpU,5946
|
|
5
5
|
netra/anonymizer/fp_anonymizer.py,sha256=_6svIYmE0eejdIMkhKBUWCNjGtGimtrGtbLvPSOp8W4,6493
|
|
6
6
|
netra/config.py,sha256=S9GsCvwtakrmryAaV-AhyVB_wAQ6tjwPLLZQemLgXko,5006
|
|
@@ -9,7 +9,7 @@ netra/exceptions/__init__.py,sha256=uDgcBxmC4WhdS7HRYQk_TtJyxH1s1o6wZmcsnSHLAcM,
|
|
|
9
9
|
netra/exceptions/injection.py,sha256=ke4eUXRYUFJkMZgdSyPPkPt5PdxToTI6xLEBI0hTWUQ,1332
|
|
10
10
|
netra/exceptions/pii.py,sha256=MT4p_x-zH3VtYudTSxw1Z9qQZADJDspq64WrYqSWlZc,2438
|
|
11
11
|
netra/input_scanner.py,sha256=bzP3s7YudGHQrIbUgQGrcIBEJ6CmOewzuYNSu75cVXM,4988
|
|
12
|
-
netra/instrumentation/__init__.py,sha256=
|
|
12
|
+
netra/instrumentation/__init__.py,sha256=ckV_tYPCQhEQ03tT0NU0ZrPD0o_1x0RnxLja3Esi97Q,40252
|
|
13
13
|
netra/instrumentation/aiohttp/__init__.py,sha256=M1kuF0R3gKY5rlbhEC1AR13UWHelmfokluL2yFysKWc,14398
|
|
14
14
|
netra/instrumentation/aiohttp/version.py,sha256=Zy-0Aukx-HS_Mo3NKPWg-hlUoWKDzS0w58gLoVtJec8,24
|
|
15
15
|
netra/instrumentation/cohere/__init__.py,sha256=3XwmCAZwZiMkHdNN3YvcBOLsNCx80ymbU31TyMzv1IY,17685
|
|
@@ -29,7 +29,7 @@ netra/instrumentation/mistralai/utils.py,sha256=nhdIer5gJFxuGwg8FCT222hggDHeMQDh
|
|
|
29
29
|
netra/instrumentation/mistralai/version.py,sha256=d6593s-XBNvVxri9lr2qLUDZQ3Zk3-VXHEwdb4pj8qA,22
|
|
30
30
|
netra/instrumentation/openai/__init__.py,sha256=HztqLMw8Tf30-Ydqr4N7FcvAwj-5cnGZNqI-S3wIZ_4,5143
|
|
31
31
|
netra/instrumentation/openai/version.py,sha256=_J-N1qG50GykJDM356BSQf0E8LoLbB8AaC3RKho494A,23
|
|
32
|
-
netra/instrumentation/openai/wrappers.py,sha256=
|
|
32
|
+
netra/instrumentation/openai/wrappers.py,sha256=4VQwIBLYaGovO9gE5TSMC-Ot84IaDuDhGqHndgR-Am4,21637
|
|
33
33
|
netra/instrumentation/weaviate/__init__.py,sha256=EOlpWxobOLHYKqo_kMct_7nu26x1hr8qkeG5_h99wtg,4330
|
|
34
34
|
netra/instrumentation/weaviate/version.py,sha256=PiCZHjonujPbnIn0KmD3Yl68hrjPRG_oKe5vJF3mmG8,24
|
|
35
35
|
netra/pii.py,sha256=S7GnVzoNJEzKiUWnqN9bOCKPeNLsriztgB2E6Rx-yJU,27023
|
|
@@ -37,10 +37,10 @@ netra/processors/__init__.py,sha256=wfnSskRBtMT90hO7LqFJoEW374LgoH_gnTxhynqtByI,
|
|
|
37
37
|
netra/processors/session_span_processor.py,sha256=qcsBl-LnILWefsftI8NQhXDGb94OWPc8LvzhVA0JS_c,2432
|
|
38
38
|
netra/scanner.py,sha256=wqjMZnEbVvrGMiUSI352grUyHpkk94oBfHfMiXPhpGU,3866
|
|
39
39
|
netra/session_manager.py,sha256=EVcnWcSj4NdkH--HmqHx0mmzivQiM4GCyFLu6lwi33M,6252
|
|
40
|
-
netra/span_wrapper.py,sha256=
|
|
40
|
+
netra/span_wrapper.py,sha256=DA5jjXkHBUJ8_mdlYP06rcZzFoSih4gdP71Wwr3btcQ,8104
|
|
41
41
|
netra/tracer.py,sha256=In5QPVLz_6BxrolWpav9EuR9_hirD2UUIlyY75QUaKk,3450
|
|
42
|
-
netra/version.py,sha256=
|
|
43
|
-
netra_sdk-0.1.
|
|
44
|
-
netra_sdk-0.1.
|
|
45
|
-
netra_sdk-0.1.
|
|
46
|
-
netra_sdk-0.1.
|
|
42
|
+
netra/version.py,sha256=cAJAbAh288a9AL-3yxwFzEM1L26izSJ6wma5aiml_9Y,23
|
|
43
|
+
netra_sdk-0.1.19.dist-info/LICENCE,sha256=8B_UoZ-BAl0AqiHAHUETCgd3I2B9yYJ1WEQtVb_qFMA,11359
|
|
44
|
+
netra_sdk-0.1.19.dist-info/METADATA,sha256=KagkHr7HnyLUoVx62n5KWlWWA2ohbi3TMjpa8ds_VEo,26133
|
|
45
|
+
netra_sdk-0.1.19.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
46
|
+
netra_sdk-0.1.19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|