netra-sdk 0.1.6__tar.gz → 0.1.9__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.6 → netra_sdk-0.1.9}/PKG-INFO +72 -22
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/README.md +71 -21
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/__init__.py +5 -5
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/config.py +7 -2
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/__init__.py +26 -1
- netra_sdk-0.1.9/netra/instrumentation/fastapi/__init__.py +363 -0
- netra_sdk-0.1.9/netra/instrumentation/fastapi/version.py +1 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/instruments.py +1 -0
- netra_sdk-0.1.9/netra/instrumentation/openai/__init__.py +153 -0
- netra_sdk-0.1.9/netra/instrumentation/openai/version.py +1 -0
- netra_sdk-0.1.9/netra/instrumentation/openai/wrappers.py +554 -0
- netra_sdk-0.1.9/netra/processors/__init__.py +3 -0
- netra_sdk-0.1.6/netra/session.py → netra_sdk-0.1.9/netra/span_wrapper.py +38 -38
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/tracer.py +1 -2
- netra_sdk-0.1.9/netra/version.py +2 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/pyproject.toml +1 -1
- netra_sdk-0.1.6/netra/processors/__init__.py +0 -4
- netra_sdk-0.1.6/netra/processors/error_detection_processor.py +0 -84
- netra_sdk-0.1.6/netra/version.py +0 -1
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/LICENCE +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/anonymizer/__init__.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/anonymizer/anonymizer.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/anonymizer/base.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/anonymizer/fp_anonymizer.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/decorators.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/exceptions/__init__.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/exceptions/injection.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/exceptions/pii.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/input_scanner.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/aiohttp/__init__.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/aiohttp/version.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/cohere/__init__.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/cohere/version.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/google_genai/__init__.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/google_genai/config.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/google_genai/utils.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/google_genai/version.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/httpx/__init__.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/httpx/version.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/mistralai/__init__.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/mistralai/config.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/mistralai/utils.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/mistralai/version.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/weaviate/__init__.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/weaviate/version.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/pii.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/processors/session_span_processor.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/scanner.py +0 -0
- {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/session_manager.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.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
|
|
@@ -326,46 +326,96 @@ Netra.set_custom_event(event_name="conversion", attributes={
|
|
|
326
326
|
"value": 99.99
|
|
327
327
|
})
|
|
328
328
|
```
|
|
329
|
-
## 🔄 Custom
|
|
329
|
+
## 🔄 Custom Span Tracking
|
|
330
330
|
|
|
331
|
-
Use the custom
|
|
331
|
+
Use the custom span tracking utility to track external API calls with detailed observability:
|
|
332
332
|
|
|
333
333
|
```python
|
|
334
|
-
from netra import Netra,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
session.set_width("1024")
|
|
344
|
-
session.set_model("dall-e-3")
|
|
345
|
-
session.set_llm_system("openai")
|
|
334
|
+
from netra import Netra, UsageModel
|
|
335
|
+
|
|
336
|
+
# Start a new span
|
|
337
|
+
with Netra.start_span("image_generation") as span:
|
|
338
|
+
# Set span attributes
|
|
339
|
+
span.set_prompt("A beautiful sunset over mountains")
|
|
340
|
+
span.set_negative_prompt("blurry, low quality")
|
|
341
|
+
span.set_model("dall-e-3")
|
|
342
|
+
span.set_llm_system("openai")
|
|
346
343
|
|
|
347
344
|
# Set usage data with UsageModel
|
|
348
345
|
usage_data = [
|
|
349
346
|
UsageModel(
|
|
350
347
|
model="dall-e-3",
|
|
351
|
-
|
|
352
|
-
|
|
348
|
+
usage_type="image_generation",
|
|
349
|
+
units_used=1,
|
|
353
350
|
cost_in_usd=0.02
|
|
354
351
|
)
|
|
355
352
|
]
|
|
356
|
-
|
|
353
|
+
span.set_usage(usage_data)
|
|
357
354
|
|
|
358
355
|
# Your API calls here
|
|
359
356
|
# ...
|
|
360
357
|
|
|
361
358
|
# Set custom attributes
|
|
362
|
-
|
|
359
|
+
span.set_attribute("custom_key", "custom_value")
|
|
363
360
|
|
|
364
361
|
# Add events
|
|
365
|
-
|
|
366
|
-
|
|
362
|
+
span.add_event("generation_started", {"step": "1", "status": "processing"})
|
|
363
|
+
span.add_event("processing_completed", {"step": "rendering"})
|
|
364
|
+
|
|
365
|
+
# Get the current active open telemetry span
|
|
366
|
+
current_span = span.get_current_span()
|
|
367
|
+
|
|
368
|
+
# Track database operations and other actions
|
|
369
|
+
action = ActionModel(
|
|
370
|
+
action="DB",
|
|
371
|
+
action_type="INSERT",
|
|
372
|
+
affected_records=[
|
|
373
|
+
{"record_id": "user_123", "record_type": "user"},
|
|
374
|
+
{"record_id": "profile_456", "record_type": "profile"}
|
|
375
|
+
],
|
|
376
|
+
metadata={
|
|
377
|
+
"table": "users",
|
|
378
|
+
"operation_id": "tx_789",
|
|
379
|
+
"duration_ms": "45"
|
|
380
|
+
},
|
|
381
|
+
success=True
|
|
382
|
+
)
|
|
383
|
+
span.set_action([action])
|
|
384
|
+
|
|
385
|
+
# Record API calls
|
|
386
|
+
api_action = ActionModel(
|
|
387
|
+
action="API",
|
|
388
|
+
action_type="CALL",
|
|
389
|
+
metadata={
|
|
390
|
+
"endpoint": "/api/v1/process",
|
|
391
|
+
"method": "POST",
|
|
392
|
+
"status_code": 200,
|
|
393
|
+
"duration_ms": "120"
|
|
394
|
+
},
|
|
395
|
+
success=True
|
|
396
|
+
)
|
|
397
|
+
span.set_action([api_action])
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Action Tracking Schema
|
|
401
|
+
|
|
402
|
+
Action tracking follows this schema:
|
|
367
403
|
|
|
368
|
-
|
|
404
|
+
```python
|
|
405
|
+
[
|
|
406
|
+
{
|
|
407
|
+
"action": str, # Type of action (e.g., "DB", "API", "CACHE")
|
|
408
|
+
"action_type": str, # Action subtype (e.g., "INSERT", "SELECT", "CALL")
|
|
409
|
+
"affected_records": [ # Optional: List of records affected
|
|
410
|
+
{
|
|
411
|
+
"record_id": str, # ID of the affected record
|
|
412
|
+
"record_type": str # Type of the record
|
|
413
|
+
}
|
|
414
|
+
],
|
|
415
|
+
"metadata": Dict[str, str], # Additional metadata as key-value pairs
|
|
416
|
+
"success": bool # Whether the action succeeded
|
|
417
|
+
}
|
|
418
|
+
]
|
|
369
419
|
```
|
|
370
420
|
|
|
371
421
|
## 🔧 Advanced Configuration
|
|
@@ -8,8 +8,8 @@ from .config import Config
|
|
|
8
8
|
|
|
9
9
|
# Instrumentor functions
|
|
10
10
|
from .instrumentation import init_instrumentations
|
|
11
|
-
from .session import Session
|
|
12
11
|
from .session_manager import SessionManager
|
|
12
|
+
from .span_wrapper import ActionModel, SpanWrapper, UsageModel
|
|
13
13
|
from .tracer import Tracer
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
@@ -133,16 +133,16 @@ class Netra:
|
|
|
133
133
|
SessionManager.set_custom_event(event_name, attributes)
|
|
134
134
|
|
|
135
135
|
@classmethod
|
|
136
|
-
def
|
|
136
|
+
def start_span(
|
|
137
137
|
cls,
|
|
138
138
|
name: str,
|
|
139
139
|
attributes: Optional[Dict[str, str]] = None,
|
|
140
140
|
module_name: str = "combat_sdk",
|
|
141
|
-
) ->
|
|
141
|
+
) -> SpanWrapper:
|
|
142
142
|
"""
|
|
143
143
|
Start a new session.
|
|
144
144
|
"""
|
|
145
|
-
return
|
|
145
|
+
return SpanWrapper(name, attributes, module_name)
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
__all__ = ["Netra"]
|
|
148
|
+
__all__ = ["Netra", "UsageModel", "ActionModel"]
|
|
@@ -51,7 +51,7 @@ class Config:
|
|
|
51
51
|
if isinstance(headers, str):
|
|
52
52
|
self.headers = parse_env_headers(headers)
|
|
53
53
|
|
|
54
|
-
if self.otlp_endpoint
|
|
54
|
+
if self.otlp_endpoint in ["https://api.dev.getcombat.ai", "https://api.eu.getnetra.ai"] and not self.api_key:
|
|
55
55
|
print("Error: Missing Netra API key, go to https://app.dev.getcombat.ai/api-key to create one")
|
|
56
56
|
print("Set the NETRA_API_KEY environment variable to the key")
|
|
57
57
|
return
|
|
@@ -59,7 +59,7 @@ class Config:
|
|
|
59
59
|
# Handle API key authentication based on OTLP endpoint
|
|
60
60
|
if self.api_key and self.otlp_endpoint:
|
|
61
61
|
# For Netra endpoints, use x-api-key header
|
|
62
|
-
if "getcombat" in self.otlp_endpoint.lower():
|
|
62
|
+
if "getcombat" in self.otlp_endpoint.lower() or "getnetra" in self.otlp_endpoint.lower():
|
|
63
63
|
if not self.headers:
|
|
64
64
|
self.headers = {"x-api-key": self.api_key}
|
|
65
65
|
elif "x-api-key" not in self.headers:
|
|
@@ -86,6 +86,11 @@ class Config:
|
|
|
86
86
|
env_tc = os.getenv("NETRA_TRACE_CONTENT")
|
|
87
87
|
self.trace_content = False if (env_tc is not None and env_tc.lower() in ("0", "false")) else True
|
|
88
88
|
|
|
89
|
+
if not self.trace_content:
|
|
90
|
+
os.environ["TRACELOOP_TRACE_CONTENT"] = "false"
|
|
91
|
+
else:
|
|
92
|
+
os.environ["TRACELOOP_TRACE_CONTENT"] = "true"
|
|
93
|
+
|
|
89
94
|
# 7. Environment: param override, else env
|
|
90
95
|
if environment is not None:
|
|
91
96
|
self.environment = environment
|
|
@@ -37,6 +37,7 @@ def init_instrumentations(
|
|
|
37
37
|
Instruments.QDRANT,
|
|
38
38
|
Instruments.GOOGLE_GENERATIVEAI,
|
|
39
39
|
Instruments.MISTRAL,
|
|
40
|
+
Instruments.OPENAI,
|
|
40
41
|
}
|
|
41
42
|
)
|
|
42
43
|
if instruments is not None and traceloop_instruments is None and traceloop_block_instruments is None:
|
|
@@ -82,6 +83,10 @@ def init_instrumentations(
|
|
|
82
83
|
if CustomInstruments.MISTRALAI in netra_custom_instruments:
|
|
83
84
|
init_mistral_instrumentor()
|
|
84
85
|
|
|
86
|
+
# Initialize OpenAI instrumentation.
|
|
87
|
+
if CustomInstruments.OPENAI in netra_custom_instruments:
|
|
88
|
+
init_openai_instrumentation()
|
|
89
|
+
|
|
85
90
|
# Initialize aio_pika instrumentation.
|
|
86
91
|
if CustomInstruments.AIO_PIKA in netra_custom_instruments:
|
|
87
92
|
init_aio_pika_instrumentation()
|
|
@@ -282,7 +287,7 @@ def init_fastapi_instrumentation() -> bool:
|
|
|
282
287
|
"""
|
|
283
288
|
try:
|
|
284
289
|
if is_package_installed("fastapi"):
|
|
285
|
-
from
|
|
290
|
+
from netra.instrumentation.fastapi import FastAPIInstrumentor
|
|
286
291
|
|
|
287
292
|
instrumentor = FastAPIInstrumentor()
|
|
288
293
|
if not instrumentor.is_instrumented_by_opentelemetry:
|
|
@@ -416,6 +421,26 @@ def init_mistral_instrumentor() -> bool:
|
|
|
416
421
|
return False
|
|
417
422
|
|
|
418
423
|
|
|
424
|
+
def init_openai_instrumentation() -> bool:
|
|
425
|
+
"""Initialize OpenAI instrumentation.
|
|
426
|
+
|
|
427
|
+
Returns:
|
|
428
|
+
bool: True if initialization was successful, False otherwise.
|
|
429
|
+
"""
|
|
430
|
+
try:
|
|
431
|
+
if is_package_installed("openai"):
|
|
432
|
+
from netra.instrumentation.openai import NetraOpenAIInstrumentor
|
|
433
|
+
|
|
434
|
+
instrumentor = NetraOpenAIInstrumentor()
|
|
435
|
+
if not instrumentor.is_instrumented_by_opentelemetry:
|
|
436
|
+
instrumentor.instrument()
|
|
437
|
+
return True
|
|
438
|
+
except Exception as e:
|
|
439
|
+
logging.error(f"Error initializing OpenAI instrumentor: {e}")
|
|
440
|
+
Telemetry().log_exception(e)
|
|
441
|
+
return False
|
|
442
|
+
|
|
443
|
+
|
|
419
444
|
def init_aio_pika_instrumentation() -> bool:
|
|
420
445
|
"""Initialize aio_pika instrumentation."""
|
|
421
446
|
try:
|