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.

Files changed (49) hide show
  1. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/PKG-INFO +72 -22
  2. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/README.md +71 -21
  3. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/__init__.py +5 -5
  4. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/config.py +7 -2
  5. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/__init__.py +26 -1
  6. netra_sdk-0.1.9/netra/instrumentation/fastapi/__init__.py +363 -0
  7. netra_sdk-0.1.9/netra/instrumentation/fastapi/version.py +1 -0
  8. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/instruments.py +1 -0
  9. netra_sdk-0.1.9/netra/instrumentation/openai/__init__.py +153 -0
  10. netra_sdk-0.1.9/netra/instrumentation/openai/version.py +1 -0
  11. netra_sdk-0.1.9/netra/instrumentation/openai/wrappers.py +554 -0
  12. netra_sdk-0.1.9/netra/processors/__init__.py +3 -0
  13. netra_sdk-0.1.6/netra/session.py → netra_sdk-0.1.9/netra/span_wrapper.py +38 -38
  14. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/tracer.py +1 -2
  15. netra_sdk-0.1.9/netra/version.py +2 -0
  16. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/pyproject.toml +1 -1
  17. netra_sdk-0.1.6/netra/processors/__init__.py +0 -4
  18. netra_sdk-0.1.6/netra/processors/error_detection_processor.py +0 -84
  19. netra_sdk-0.1.6/netra/version.py +0 -1
  20. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/LICENCE +0 -0
  21. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/anonymizer/__init__.py +0 -0
  22. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/anonymizer/anonymizer.py +0 -0
  23. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/anonymizer/base.py +0 -0
  24. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/anonymizer/fp_anonymizer.py +0 -0
  25. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/decorators.py +0 -0
  26. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/exceptions/__init__.py +0 -0
  27. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/exceptions/injection.py +0 -0
  28. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/exceptions/pii.py +0 -0
  29. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/input_scanner.py +0 -0
  30. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/aiohttp/__init__.py +0 -0
  31. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/aiohttp/version.py +0 -0
  32. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/cohere/__init__.py +0 -0
  33. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/cohere/version.py +0 -0
  34. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/google_genai/__init__.py +0 -0
  35. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/google_genai/config.py +0 -0
  36. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/google_genai/utils.py +0 -0
  37. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/google_genai/version.py +0 -0
  38. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/httpx/__init__.py +0 -0
  39. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/httpx/version.py +0 -0
  40. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/mistralai/__init__.py +0 -0
  41. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/mistralai/config.py +0 -0
  42. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/mistralai/utils.py +0 -0
  43. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/mistralai/version.py +0 -0
  44. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/weaviate/__init__.py +0 -0
  45. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/instrumentation/weaviate/version.py +0 -0
  46. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/pii.py +0 -0
  47. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/processors/session_span_processor.py +0 -0
  48. {netra_sdk-0.1.6 → netra_sdk-0.1.9}/netra/scanner.py +0 -0
  49. {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.6
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 Session Tracking
405
+ ## 🔄 Custom Span Tracking
406
406
 
407
- Use the custom session tracking utility to track external API calls with detailed observability:
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, Session
411
- from netra.session import UsageModel
412
-
413
- # Start a new session
414
- with Netra.start_session("image_generation") as session:
415
- # Set session attributes
416
- session.set_prompt("A beautiful sunset over mountains")
417
- session.set_negative_prompt("blurry, low quality")
418
- session.set_height("1024")
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
- type="image_generation",
428
- unit_used=1,
424
+ usage_type="image_generation",
425
+ units_used=1,
429
426
  cost_in_usd=0.02
430
427
  )
431
428
  ]
432
- session.set_usage(usage_data)
429
+ span.set_usage(usage_data)
433
430
 
434
431
  # Your API calls here
435
432
  # ...
436
433
 
437
434
  # Set custom attributes
438
- session.set_attribute("custom_key", "custom_value")
435
+ span.set_attribute("custom_key", "custom_value")
439
436
 
440
437
  # Add events
441
- session.add_event("generation_started", {"step": 1, "status": "processing"})
442
- session.add_event("processing_completed", {"step": "rendering"})
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
- # Session automatically captures duration, status, and any errors
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 Session Tracking
329
+ ## 🔄 Custom Span Tracking
330
330
 
331
- Use the custom session tracking utility to track external API calls with detailed observability:
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, Session
335
- from netra.session import UsageModel
336
-
337
- # Start a new session
338
- with Netra.start_session("image_generation") as session:
339
- # Set session attributes
340
- session.set_prompt("A beautiful sunset over mountains")
341
- session.set_negative_prompt("blurry, low quality")
342
- session.set_height("1024")
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
- type="image_generation",
352
- unit_used=1,
348
+ usage_type="image_generation",
349
+ units_used=1,
353
350
  cost_in_usd=0.02
354
351
  )
355
352
  ]
356
- session.set_usage(usage_data)
353
+ span.set_usage(usage_data)
357
354
 
358
355
  # Your API calls here
359
356
  # ...
360
357
 
361
358
  # Set custom attributes
362
- session.set_attribute("custom_key", "custom_value")
359
+ span.set_attribute("custom_key", "custom_value")
363
360
 
364
361
  # Add events
365
- session.add_event("generation_started", {"step": 1, "status": "processing"})
366
- session.add_event("processing_completed", {"step": "rendering"})
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
- # Session automatically captures duration, status, and any errors
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 start_session(
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
- ) -> Session:
141
+ ) -> SpanWrapper:
142
142
  """
143
143
  Start a new session.
144
144
  """
145
- return Session(name, attributes, module_name)
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 == "https://api.dev.getcombat.ai" and not self.api_key:
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 opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
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: