netra-sdk 0.1.6__py3-none-any.whl → 0.1.9__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 +5 -5
- netra/config.py +7 -2
- netra/instrumentation/__init__.py +26 -1
- netra/instrumentation/fastapi/__init__.py +363 -0
- netra/instrumentation/fastapi/version.py +1 -0
- netra/instrumentation/instruments.py +1 -0
- netra/instrumentation/openai/__init__.py +153 -0
- netra/instrumentation/openai/version.py +1 -0
- netra/instrumentation/openai/wrappers.py +554 -0
- netra/processors/__init__.py +1 -2
- netra/{session.py → span_wrapper.py} +38 -38
- netra/tracer.py +1 -2
- netra/version.py +2 -1
- {netra_sdk-0.1.6.dist-info → netra_sdk-0.1.9.dist-info}/METADATA +72 -22
- {netra_sdk-0.1.6.dist-info → netra_sdk-0.1.9.dist-info}/RECORD +17 -13
- netra/processors/error_detection_processor.py +0 -84
- {netra_sdk-0.1.6.dist-info → netra_sdk-0.1.9.dist-info}/LICENCE +0 -0
- {netra_sdk-0.1.6.dist-info → netra_sdk-0.1.9.dist-info}/WHEEL +0 -0
|
@@ -16,10 +16,18 @@ logging.basicConfig(level=logging.INFO)
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
class ActionModel(BaseModel): # type: ignore[misc]
|
|
20
|
+
action: str
|
|
21
|
+
action_type: str
|
|
22
|
+
success: bool
|
|
23
|
+
affected_records: Optional[List[Dict[str, str]]] = None
|
|
24
|
+
metadata: Optional[Dict[str, str]] = None
|
|
25
|
+
|
|
26
|
+
|
|
19
27
|
class UsageModel(BaseModel): # type: ignore[misc]
|
|
20
28
|
model: str
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
usage_type: str
|
|
30
|
+
units_used: Optional[int] = None
|
|
23
31
|
cost_in_usd: Optional[float] = None
|
|
24
32
|
|
|
25
33
|
|
|
@@ -28,27 +36,25 @@ class ATTRIBUTE:
|
|
|
28
36
|
MODEL = "model"
|
|
29
37
|
PROMPT = "prompt"
|
|
30
38
|
NEGATIVE_PROMPT = "negative_prompt"
|
|
31
|
-
HEIGHT = "height"
|
|
32
|
-
WIDTH = "width"
|
|
33
|
-
OUTPUT_TYPE = "output_type"
|
|
34
39
|
USAGE = "usage"
|
|
35
40
|
STATUS = "status"
|
|
36
41
|
DURATION_MS = "duration_ms"
|
|
37
42
|
ERROR_MESSAGE = "error_message"
|
|
43
|
+
ACTION = "action"
|
|
38
44
|
|
|
39
45
|
|
|
40
|
-
class
|
|
46
|
+
class SpanWrapper:
|
|
41
47
|
"""
|
|
42
48
|
Context manager for tracking observability data for external API calls.
|
|
43
49
|
|
|
44
50
|
Usage:
|
|
45
|
-
with combat.
|
|
46
|
-
|
|
51
|
+
with combat.start_span("video_gen_task") as span:
|
|
52
|
+
span.set_prompt("A cat playing piano").set_image_height("1024")
|
|
47
53
|
|
|
48
54
|
# External API call
|
|
49
55
|
result = external_api.generate_video(...)
|
|
50
56
|
|
|
51
|
-
|
|
57
|
+
span.set_usage(usage_data)
|
|
52
58
|
"""
|
|
53
59
|
|
|
54
60
|
def __init__(self, name: str, attributes: Optional[Dict[str, str]] = None, module_name: str = "combat_sdk"):
|
|
@@ -65,8 +71,8 @@ class Session:
|
|
|
65
71
|
self.span: Optional[trace.Span] = None
|
|
66
72
|
self.context_token: Optional[Any] = None
|
|
67
73
|
|
|
68
|
-
def __enter__(self) -> "
|
|
69
|
-
"""Start the
|
|
74
|
+
def __enter__(self) -> "SpanWrapper":
|
|
75
|
+
"""Start the span wrapper, begin time tracking, and create OpenTelemetry span."""
|
|
70
76
|
self.start_time = time.time()
|
|
71
77
|
|
|
72
78
|
# Create OpenTelemetry span
|
|
@@ -76,11 +82,11 @@ class Session:
|
|
|
76
82
|
ctx = set_span_in_context(self.span)
|
|
77
83
|
self.context_token = context_api.attach(ctx)
|
|
78
84
|
|
|
79
|
-
logger.info(f"Started
|
|
85
|
+
logger.info(f"Started span wrapper: {self.name}")
|
|
80
86
|
return self
|
|
81
87
|
|
|
82
88
|
def __exit__(self, exc_type: Optional[type], exc_val: Optional[Exception], exc_tb: Any) -> Literal[False]:
|
|
83
|
-
"""End the
|
|
89
|
+
"""End the span wrapper, calculate duration, handle errors, and close OpenTelemetry span."""
|
|
84
90
|
self.end_time = time.time()
|
|
85
91
|
duration_ms = (self.end_time - self.start_time) * 1000 if self.start_time is not None else None
|
|
86
92
|
|
|
@@ -101,7 +107,7 @@ class Session:
|
|
|
101
107
|
self.span.set_status(Status(StatusCode.ERROR, self.error_message))
|
|
102
108
|
if exc_val is not None:
|
|
103
109
|
self.span.record_exception(exc_val)
|
|
104
|
-
logger.error(f"
|
|
110
|
+
logger.error(f"Span wrapper {self.name} failed: {self.error_message}")
|
|
105
111
|
|
|
106
112
|
self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.STATUS}", self.status)
|
|
107
113
|
|
|
@@ -117,15 +123,15 @@ class Session:
|
|
|
117
123
|
context_api.detach(self.context_token)
|
|
118
124
|
|
|
119
125
|
logger.info(
|
|
120
|
-
f"Ended
|
|
126
|
+
f"Ended span wrapper: {self.name} (Status: {self.status}, Duration: {duration_ms:.2f}ms)"
|
|
121
127
|
if duration_ms is not None
|
|
122
|
-
else f"Ended
|
|
128
|
+
else f"Ended span wrapper: {self.name} (Status: {self.status})"
|
|
123
129
|
)
|
|
124
130
|
|
|
125
131
|
# Don't suppress exceptions
|
|
126
132
|
return False
|
|
127
133
|
|
|
128
|
-
def set_attribute(self, key: str, value: str) -> "
|
|
134
|
+
def set_attribute(self, key: str, value: str) -> "SpanWrapper":
|
|
129
135
|
"""Set a single attribute and return self for method chaining."""
|
|
130
136
|
self.attributes[key] = value
|
|
131
137
|
# Also set on the span if it exists
|
|
@@ -133,41 +139,35 @@ class Session:
|
|
|
133
139
|
self.span.set_attribute(key, value)
|
|
134
140
|
return self
|
|
135
141
|
|
|
136
|
-
def set_prompt(self, prompt: str) -> "
|
|
142
|
+
def set_prompt(self, prompt: str) -> "SpanWrapper":
|
|
137
143
|
"""Set the input prompt."""
|
|
138
144
|
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.PROMPT}", prompt)
|
|
139
145
|
|
|
140
|
-
def set_negative_prompt(self, negative_prompt: str) -> "
|
|
146
|
+
def set_negative_prompt(self, negative_prompt: str) -> "SpanWrapper":
|
|
141
147
|
"""Set the negative prompt."""
|
|
142
148
|
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.NEGATIVE_PROMPT}", negative_prompt)
|
|
143
149
|
|
|
144
|
-
def
|
|
145
|
-
"""Set the height."""
|
|
146
|
-
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.HEIGHT}", height)
|
|
147
|
-
|
|
148
|
-
def set_width(self, width: str) -> "Session":
|
|
149
|
-
"""Set the width."""
|
|
150
|
-
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.WIDTH}", width)
|
|
151
|
-
|
|
152
|
-
def set_output_type(self, output_type: str) -> "Session":
|
|
153
|
-
"""Set the output type."""
|
|
154
|
-
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.OUTPUT_TYPE}", output_type)
|
|
155
|
-
|
|
156
|
-
def set_usage(self, usage: List[UsageModel]) -> "Session":
|
|
150
|
+
def set_usage(self, usage: List[UsageModel]) -> "SpanWrapper":
|
|
157
151
|
"""Set the usage data as a JSON string."""
|
|
158
152
|
usage_dict = [u.model_dump() for u in usage]
|
|
159
153
|
usage_json = json.dumps(usage_dict)
|
|
160
154
|
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.USAGE}", usage_json)
|
|
161
155
|
|
|
162
|
-
def
|
|
156
|
+
def set_action(self, action: List[ActionModel]) -> "SpanWrapper":
|
|
157
|
+
"""Set the action data as a JSON string."""
|
|
158
|
+
action_dict = [a.model_dump() for a in action]
|
|
159
|
+
action_json = json.dumps(action_dict)
|
|
160
|
+
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.ACTION}", action_json)
|
|
161
|
+
|
|
162
|
+
def set_model(self, model: str) -> "SpanWrapper":
|
|
163
163
|
"""Set the model used."""
|
|
164
164
|
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.MODEL}", model)
|
|
165
165
|
|
|
166
|
-
def set_llm_system(self, system: str) -> "
|
|
166
|
+
def set_llm_system(self, system: str) -> "SpanWrapper":
|
|
167
167
|
"""Set the LLM system used."""
|
|
168
168
|
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.LLM_SYSTEM}", system)
|
|
169
169
|
|
|
170
|
-
def set_error(self, error_message: str) -> "
|
|
170
|
+
def set_error(self, error_message: str) -> "SpanWrapper":
|
|
171
171
|
"""Manually set an error message."""
|
|
172
172
|
self.status = "error"
|
|
173
173
|
self.error_message = error_message
|
|
@@ -175,14 +175,14 @@ class Session:
|
|
|
175
175
|
self.span.set_status(Status(StatusCode.ERROR, error_message))
|
|
176
176
|
return self.set_attribute(f"{Config.LIBRARY_NAME}.{ATTRIBUTE.ERROR_MESSAGE}", error_message)
|
|
177
177
|
|
|
178
|
-
def set_success(self) -> "
|
|
179
|
-
"""Manually mark the
|
|
178
|
+
def set_success(self) -> "SpanWrapper":
|
|
179
|
+
"""Manually mark the span wrapper as successful."""
|
|
180
180
|
self.status = "success"
|
|
181
181
|
if self.span:
|
|
182
182
|
self.span.set_status(Status(StatusCode.OK))
|
|
183
183
|
return self
|
|
184
184
|
|
|
185
|
-
def add_event(self, name: str, attributes: Optional[Dict[str, str]] = None) -> "
|
|
185
|
+
def add_event(self, name: str, attributes: Optional[Dict[str, str]] = None) -> "SpanWrapper":
|
|
186
186
|
"""Add an event to the span."""
|
|
187
187
|
if self.span:
|
|
188
188
|
self.span.add_event(name, attributes or {})
|
netra/tracer.py
CHANGED
|
@@ -66,10 +66,9 @@ class Tracer:
|
|
|
66
66
|
headers=self.cfg.headers,
|
|
67
67
|
)
|
|
68
68
|
# Add span processors for session span processing and data aggregation processing
|
|
69
|
-
from netra.processors import
|
|
69
|
+
from netra.processors import SessionSpanProcessor
|
|
70
70
|
|
|
71
71
|
provider.add_span_processor(SessionSpanProcessor())
|
|
72
|
-
provider.add_span_processor(ErrorDetectionProcessor())
|
|
73
72
|
|
|
74
73
|
# Install appropriate span processor
|
|
75
74
|
if self.cfg.disable_batch:
|
netra/version.py
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.9"
|
|
2
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: netra-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
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
|
|
@@ -402,46 +402,96 @@ Netra.set_custom_event(event_name="conversion", attributes={
|
|
|
402
402
|
"value": 99.99
|
|
403
403
|
})
|
|
404
404
|
```
|
|
405
|
-
## 🔄 Custom
|
|
405
|
+
## 🔄 Custom Span Tracking
|
|
406
406
|
|
|
407
|
-
Use the custom
|
|
407
|
+
Use the custom span tracking utility to track external API calls with detailed observability:
|
|
408
408
|
|
|
409
409
|
```python
|
|
410
|
-
from netra import Netra,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
session.set_width("1024")
|
|
420
|
-
session.set_model("dall-e-3")
|
|
421
|
-
session.set_llm_system("openai")
|
|
410
|
+
from netra import Netra, UsageModel
|
|
411
|
+
|
|
412
|
+
# Start a new span
|
|
413
|
+
with Netra.start_span("image_generation") as span:
|
|
414
|
+
# Set span attributes
|
|
415
|
+
span.set_prompt("A beautiful sunset over mountains")
|
|
416
|
+
span.set_negative_prompt("blurry, low quality")
|
|
417
|
+
span.set_model("dall-e-3")
|
|
418
|
+
span.set_llm_system("openai")
|
|
422
419
|
|
|
423
420
|
# Set usage data with UsageModel
|
|
424
421
|
usage_data = [
|
|
425
422
|
UsageModel(
|
|
426
423
|
model="dall-e-3",
|
|
427
|
-
|
|
428
|
-
|
|
424
|
+
usage_type="image_generation",
|
|
425
|
+
units_used=1,
|
|
429
426
|
cost_in_usd=0.02
|
|
430
427
|
)
|
|
431
428
|
]
|
|
432
|
-
|
|
429
|
+
span.set_usage(usage_data)
|
|
433
430
|
|
|
434
431
|
# Your API calls here
|
|
435
432
|
# ...
|
|
436
433
|
|
|
437
434
|
# Set custom attributes
|
|
438
|
-
|
|
435
|
+
span.set_attribute("custom_key", "custom_value")
|
|
439
436
|
|
|
440
437
|
# Add events
|
|
441
|
-
|
|
442
|
-
|
|
438
|
+
span.add_event("generation_started", {"step": "1", "status": "processing"})
|
|
439
|
+
span.add_event("processing_completed", {"step": "rendering"})
|
|
440
|
+
|
|
441
|
+
# Get the current active open telemetry span
|
|
442
|
+
current_span = span.get_current_span()
|
|
443
|
+
|
|
444
|
+
# Track database operations and other actions
|
|
445
|
+
action = ActionModel(
|
|
446
|
+
action="DB",
|
|
447
|
+
action_type="INSERT",
|
|
448
|
+
affected_records=[
|
|
449
|
+
{"record_id": "user_123", "record_type": "user"},
|
|
450
|
+
{"record_id": "profile_456", "record_type": "profile"}
|
|
451
|
+
],
|
|
452
|
+
metadata={
|
|
453
|
+
"table": "users",
|
|
454
|
+
"operation_id": "tx_789",
|
|
455
|
+
"duration_ms": "45"
|
|
456
|
+
},
|
|
457
|
+
success=True
|
|
458
|
+
)
|
|
459
|
+
span.set_action([action])
|
|
460
|
+
|
|
461
|
+
# Record API calls
|
|
462
|
+
api_action = ActionModel(
|
|
463
|
+
action="API",
|
|
464
|
+
action_type="CALL",
|
|
465
|
+
metadata={
|
|
466
|
+
"endpoint": "/api/v1/process",
|
|
467
|
+
"method": "POST",
|
|
468
|
+
"status_code": 200,
|
|
469
|
+
"duration_ms": "120"
|
|
470
|
+
},
|
|
471
|
+
success=True
|
|
472
|
+
)
|
|
473
|
+
span.set_action([api_action])
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### Action Tracking Schema
|
|
477
|
+
|
|
478
|
+
Action tracking follows this schema:
|
|
443
479
|
|
|
444
|
-
|
|
480
|
+
```python
|
|
481
|
+
[
|
|
482
|
+
{
|
|
483
|
+
"action": str, # Type of action (e.g., "DB", "API", "CACHE")
|
|
484
|
+
"action_type": str, # Action subtype (e.g., "INSERT", "SELECT", "CALL")
|
|
485
|
+
"affected_records": [ # Optional: List of records affected
|
|
486
|
+
{
|
|
487
|
+
"record_id": str, # ID of the affected record
|
|
488
|
+
"record_type": str # Type of the record
|
|
489
|
+
}
|
|
490
|
+
],
|
|
491
|
+
"metadata": Dict[str, str], # Additional metadata as key-value pairs
|
|
492
|
+
"success": bool # Whether the action succeeded
|
|
493
|
+
}
|
|
494
|
+
]
|
|
445
495
|
```
|
|
446
496
|
|
|
447
497
|
## 🔧 Advanced Configuration
|
|
@@ -1,42 +1,46 @@
|
|
|
1
|
-
netra/__init__.py,sha256=
|
|
1
|
+
netra/__init__.py,sha256=0w42KiANH31q0sXFL2hLxRjueepfo_jbx2oMDc5miFo,4733
|
|
2
2
|
netra/anonymizer/__init__.py,sha256=KeGPPZqKVZbtkbirEKYTYhj6aZHlakjdQhD7QHqBRio,133
|
|
3
3
|
netra/anonymizer/anonymizer.py,sha256=1VeYAsFpF_tYDlqJF-Q82-ZXGOR4YWBqrKUsRw3qOrA,3539
|
|
4
4
|
netra/anonymizer/base.py,sha256=ytPxHCUD2OXlEY6fNTuMmwImNdIjgj294I41FIgoXpU,5946
|
|
5
5
|
netra/anonymizer/fp_anonymizer.py,sha256=_6svIYmE0eejdIMkhKBUWCNjGtGimtrGtbLvPSOp8W4,6493
|
|
6
|
-
netra/config.py,sha256=
|
|
6
|
+
netra/config.py,sha256=S9GsCvwtakrmryAaV-AhyVB_wAQ6tjwPLLZQemLgXko,5006
|
|
7
7
|
netra/decorators.py,sha256=V_WpZ2IgW2Y7B_WnSXmKUGGhkM5Cra2TwONddmJpPaI,6837
|
|
8
8
|
netra/exceptions/__init__.py,sha256=uDgcBxmC4WhdS7HRYQk_TtJyxH1s1o6wZmcsnSHLAcM,174
|
|
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=Mz7BlRp4zgKAD3oLBGfBzh5naVVgAP0sLw4ISDydhLw,39567
|
|
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
|
|
16
16
|
netra/instrumentation/cohere/version.py,sha256=eFXRvO5AgP2DDj5tMt7hkuzi30NOLEqZ9dJpPaHFZLs,23
|
|
17
|
+
netra/instrumentation/fastapi/__init__.py,sha256=v955lLezcI-3toA60RVkTYqlstu-QjMOAbLkM9uHHU4,16611
|
|
18
|
+
netra/instrumentation/fastapi/version.py,sha256=FX3pdiCFGDsISNiq2rT0sQFwNLZ5IGKhiVknXkSE3O0,24
|
|
17
19
|
netra/instrumentation/google_genai/__init__.py,sha256=470x3o5_NDQHRNT4o-IWnSZw1JjPnAoFSZ-mpaWvAuE,17894
|
|
18
20
|
netra/instrumentation/google_genai/config.py,sha256=XCyo3mk30qkvqyCqeTrKwROahu0gcOEwmbDLOo53J5k,121
|
|
19
21
|
netra/instrumentation/google_genai/utils.py,sha256=2OeSN5jUaMKF4x5zWiW65R1LB_a44Roi00isv2vxFME,921
|
|
20
22
|
netra/instrumentation/google_genai/version.py,sha256=Hww1duZrC8kYK7ThBSQVyz0HNOb0ys_o8Pln-wVQ1hI,23
|
|
21
23
|
netra/instrumentation/httpx/__init__.py,sha256=w1su_eQP_w5ZJHq0Lf-4miF5zM4OOW0ItmRp0wi85Ew,19388
|
|
22
24
|
netra/instrumentation/httpx/version.py,sha256=ZRQKbgDaGz_yuLk-cUKuk6ZBKCSRKZC8nQd041NRNXk,23
|
|
23
|
-
netra/instrumentation/instruments.py,sha256=
|
|
25
|
+
netra/instrumentation/instruments.py,sha256=_25TCDiBsgqP_fzL4OgwWx2UKD8EnzvLn0WA8t3lB_U,4269
|
|
24
26
|
netra/instrumentation/mistralai/__init__.py,sha256=RE0b-rS6iXdoynJMFKHL9s97eYo5HghrJa013fR4ZhI,18910
|
|
25
27
|
netra/instrumentation/mistralai/config.py,sha256=XCyo3mk30qkvqyCqeTrKwROahu0gcOEwmbDLOo53J5k,121
|
|
26
28
|
netra/instrumentation/mistralai/utils.py,sha256=nhdIer5gJFxuGwg8FCT222hggDHeMQDhJctnDSwLqcc,894
|
|
27
29
|
netra/instrumentation/mistralai/version.py,sha256=d6593s-XBNvVxri9lr2qLUDZQ3Zk3-VXHEwdb4pj8qA,22
|
|
30
|
+
netra/instrumentation/openai/__init__.py,sha256=HztqLMw8Tf30-Ydqr4N7FcvAwj-5cnGZNqI-S3wIZ_4,5143
|
|
31
|
+
netra/instrumentation/openai/version.py,sha256=_J-N1qG50GykJDM356BSQf0E8LoLbB8AaC3RKho494A,23
|
|
32
|
+
netra/instrumentation/openai/wrappers.py,sha256=hXUgWKAs2_LCKIBnScoIJt_AkHhdQKnZWk7D94UjPGU,20685
|
|
28
33
|
netra/instrumentation/weaviate/__init__.py,sha256=EOlpWxobOLHYKqo_kMct_7nu26x1hr8qkeG5_h99wtg,4330
|
|
29
34
|
netra/instrumentation/weaviate/version.py,sha256=PiCZHjonujPbnIn0KmD3Yl68hrjPRG_oKe5vJF3mmG8,24
|
|
30
35
|
netra/pii.py,sha256=S7GnVzoNJEzKiUWnqN9bOCKPeNLsriztgB2E6Rx-yJU,27023
|
|
31
|
-
netra/processors/__init__.py,sha256=
|
|
32
|
-
netra/processors/error_detection_processor.py,sha256=TtSZoJ7BCMHlVaXWYfqLHSZ6uIx43tdqYb7AFXpt0BA,2898
|
|
36
|
+
netra/processors/__init__.py,sha256=wfnSskRBtMT90hO7LqFJoEW374LgoH_gnTxhynqtByI,109
|
|
33
37
|
netra/processors/session_span_processor.py,sha256=qcsBl-LnILWefsftI8NQhXDGb94OWPc8LvzhVA0JS_c,2432
|
|
34
38
|
netra/scanner.py,sha256=wqjMZnEbVvrGMiUSI352grUyHpkk94oBfHfMiXPhpGU,3866
|
|
35
|
-
netra/session.py,sha256=o1wXrPzMauqj_3P-iNBHVlcIR7zcKcbsmkrcHjQMKuY,7263
|
|
36
39
|
netra/session_manager.py,sha256=EVcnWcSj4NdkH--HmqHx0mmzivQiM4GCyFLu6lwi33M,6252
|
|
37
|
-
netra/
|
|
38
|
-
netra/
|
|
39
|
-
|
|
40
|
-
netra_sdk-0.1.
|
|
41
|
-
netra_sdk-0.1.
|
|
42
|
-
netra_sdk-0.1.
|
|
40
|
+
netra/span_wrapper.py,sha256=MMFuQTqEmQ33pj6qUeIx7EbJJoEz_hzW-4dt1Y2N7s8,7286
|
|
41
|
+
netra/tracer.py,sha256=In5QPVLz_6BxrolWpav9EuR9_hirD2UUIlyY75QUaKk,3450
|
|
42
|
+
netra/version.py,sha256=_sUAMIoa3JJyDFRRMIhTB30E34m3PK89cv561Stxyb4,23
|
|
43
|
+
netra_sdk-0.1.9.dist-info/LICENCE,sha256=8B_UoZ-BAl0AqiHAHUETCgd3I2B9yYJ1WEQtVb_qFMA,11359
|
|
44
|
+
netra_sdk-0.1.9.dist-info/METADATA,sha256=jTsIP9rcBdVgL0x4sBtmEQL4NLWxnl5yHwteWhdiUdQ,25100
|
|
45
|
+
netra_sdk-0.1.9.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
46
|
+
netra_sdk-0.1.9.dist-info/RECORD,,
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from typing import Any, Optional, Union
|
|
3
|
-
|
|
4
|
-
import httpx
|
|
5
|
-
from opentelemetry.sdk.trace import SpanProcessor
|
|
6
|
-
from opentelemetry.trace import Context, Span, Status, StatusCode
|
|
7
|
-
|
|
8
|
-
from netra import Netra
|
|
9
|
-
|
|
10
|
-
logger = logging.getLogger(__name__)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class ErrorDetectionProcessor(SpanProcessor): # type: ignore[misc]
|
|
14
|
-
"""
|
|
15
|
-
OpenTelemetry span processor that monitors for error attributes in spans and creates custom events.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
def __init__(self) -> None:
|
|
19
|
-
pass
|
|
20
|
-
|
|
21
|
-
def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None:
|
|
22
|
-
"""Called when a span starts."""
|
|
23
|
-
span_id = self._get_span_id(span)
|
|
24
|
-
if not span_id:
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
# Wrap span methods to capture data
|
|
28
|
-
self._wrap_span_methods(span, span_id)
|
|
29
|
-
|
|
30
|
-
def on_end(self, span: Span) -> None:
|
|
31
|
-
"""Called when a span ends."""
|
|
32
|
-
|
|
33
|
-
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
34
|
-
"""Force flush any pending data."""
|
|
35
|
-
return True
|
|
36
|
-
|
|
37
|
-
def shutdown(self) -> bool:
|
|
38
|
-
"""Shutdown the processor."""
|
|
39
|
-
return True
|
|
40
|
-
|
|
41
|
-
def _get_span_id(self, span: Span) -> Optional[str]:
|
|
42
|
-
"""Get a unique identifier for the span."""
|
|
43
|
-
try:
|
|
44
|
-
span_context = span.get_span_context()
|
|
45
|
-
return f"{span_context.trace_id:032x}-{span_context.span_id:016x}"
|
|
46
|
-
except Exception:
|
|
47
|
-
return None
|
|
48
|
-
|
|
49
|
-
def _status_code_processing(self, status_code: int) -> None:
|
|
50
|
-
if httpx.codes.is_error(status_code):
|
|
51
|
-
event_attributes = {"has_error": True, "status_code": status_code}
|
|
52
|
-
Netra.set_custom_event(event_name="error_detected", attributes=event_attributes)
|
|
53
|
-
|
|
54
|
-
def _wrap_span_methods(self, span: Span, span_id: str) -> Any:
|
|
55
|
-
"""Wrap span methods to capture attributes and events."""
|
|
56
|
-
# Wrap set_attribute
|
|
57
|
-
original_set_attribute = span.set_attribute
|
|
58
|
-
|
|
59
|
-
def wrapped_set_attribute(key: str, value: Any) -> Any:
|
|
60
|
-
# Status code processing
|
|
61
|
-
if key == "http.status_code":
|
|
62
|
-
self._status_code_processing(value)
|
|
63
|
-
|
|
64
|
-
return original_set_attribute(key, value)
|
|
65
|
-
|
|
66
|
-
# Wrap set_status
|
|
67
|
-
original_set_status = span.set_status
|
|
68
|
-
|
|
69
|
-
def wrapped_set_status(status: Union[Status, StatusCode]) -> Any:
|
|
70
|
-
# Check if status code is ERROR
|
|
71
|
-
if isinstance(status, Status):
|
|
72
|
-
status_code = status.status_code
|
|
73
|
-
elif isinstance(status, StatusCode):
|
|
74
|
-
status_code = status
|
|
75
|
-
if status_code == StatusCode.ERROR:
|
|
76
|
-
event_attributes = {
|
|
77
|
-
"has_error": True,
|
|
78
|
-
}
|
|
79
|
-
Netra.set_custom_event(event_name="error_detected", attributes=event_attributes)
|
|
80
|
-
|
|
81
|
-
return original_set_status(status)
|
|
82
|
-
|
|
83
|
-
span.set_attribute = wrapped_set_attribute
|
|
84
|
-
span.set_status = wrapped_set_status
|
|
File without changes
|
|
File without changes
|