Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
de5eb2e
[NET-968] refactor: Remove pii detection & prompt injection detection…
akash-vijay-kv Jul 27, 2026
261b6cc
[NET-1590] fix: Add a span audio.delivery.metadata in the livekit spa…
pranavcv Sep 22, 2026
1b8f0f3
[3464] fix: Ensure shutdown flushes spans before absorbing LiveKit's …
pranavcv Oct 8, 2026
b6c739d
[1981] fix: Map LiveKit ≥1.7 lk.pii.* attributes alongside the origin…
pranavcv Nov 3, 2026
86b91e7
[1990] fix: Refactor logging to use custom logger (#434)
akash-vijay-kv Nov 3, 2026
465dc49
fix: Make the scrubber email regex linear-time (audit H-9)
RiyaSunil27 Oct 12, 2026
36fe8c8
fix: Strip whitespace from the API key and header values (audit H-2)
RiyaSunil27 Oct 12, 2026
479c3ae
fix: Redact more credential headers, matching name parts (audit H-5)
RiyaSunil27 Oct 12, 2026
b2de473
fix: Redact secret query parameters from recorded URLs (audit H-6)
RiyaSunil27 Oct 12, 2026
9b1d455
fix: Stop DSPy spans exporting provider credentials (audit H-3)
RiyaSunil27 Oct 12, 2026
adbda7e
fix: Remove debug print from the DSPy tool wrapper
RiyaSunil27 Oct 12, 2026
ae0eaa7
fix: Stop openai responses.create crashing on unset reasoning (audit …
RiyaSunil27 Oct 12, 2026
b69fe5e
fix: Stop LiteLLM Responses streams crashing on tool-call output (aud…
RiyaSunil27 Oct 12, 2026
b9f57c1
fix: Give LLM stream wrappers one hardened lifecycle (audit H-22)
RiyaSunil27 Oct 12, 2026
ce5f300
fix: Stop pydantic-ai 2.x runs crashing on result.usage() (new finding)
RiyaSunil27 Oct 12, 2026
2a49c3f
fix: Call the real pydantic-ai run_sync instead of replacing it (audi…
RiyaSunil27 Oct 12, 2026
2c7e871
fix: Make pydantic-ai uninstrument() actually unwrap (audit M-20)
RiyaSunil27 Oct 12, 2026
3a01768
fix: End decorated spans on cancellation, Ctrl-C and sys.exit (audit …
RiyaSunil27 Oct 12, 2026
a9c7b7a
fix: Keep stream spans current only while a chunk is pulled (audit H-21)
RiyaSunil27 Oct 12, 2026
2edb8f0
fix: Add session_scope and clear_session so ids can't bleed (audit H-12)
RiyaSunil27 Oct 12, 2026
7edb4ad
fix: Stop the audio uploader sending after a 401 (audit H-18)
RiyaSunil27 Oct 12, 2026
64c37c1
fix: Send the audio session terminator after every chunk is answered
RiyaSunil27 Oct 12, 2026
05f2ab7
test: Assert audio chunk sequences by value, not arrival order
RiyaSunil27 Oct 12, 2026
3345837
fix: Instrument current Cohere, Mistral and Cartesia and warn when in…
RiyaSunil27 Oct 12, 2026
cb65754
fix: Re-raise errors from DSPy calls instead of returning None
RiyaSunil27 Oct 12, 2026
904da20
fix: Honour trace_content=False in every Netra instrumentor (audit H-4)
RiyaSunil27 Oct 12, 2026
47bad15
chore: Resolve merge conflicts
RiyaSunil27 Nov 4, 2026
4f00369
fix: Remove unused import of os in utils.py
akash-vijay-kv Nov 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,32 @@ Netra.set_custom_event(event_name="conversion", attributes={
"value": 99.99
})
```

### Session context in servers and workers

`set_session_id`, `set_user_id` and `set_tenant_id` are **sticky for the current thread** (or asyncio task): the value stays set until you change or clear it. That suits scripts and single-session processes, but a server or worker that reuses threads (gunicorn/uWSGI sync workers, Celery, queue consumer loops) would carry one request's ids into the next.

For per-request or per-job ids, use `Netra.session_scope()`. The ids apply only inside the block and the previous values are restored on exit, even if the block raises:

```python
def handle(request):
with Netra.session_scope(
session_id=request.session_id,
user_id=request.user_id,
tenant_id=request.tenant_id,
):
return process(request)
```

Fields you leave out are inherited from the enclosing context (for example a tenant set once at startup).

If you use the sticky setters, clear them when the request ends:

```python
Netra.set_user_id("") # "" or None clears just that id
Netra.clear_session() # clears session_id, user_id and tenant_id
```

## 🔄 Custom Span Tracking

Use the custom span tracking utility to track external API calls with detailed observability:
Expand Down
129 changes: 0 additions & 129 deletions examples/03_pii_detection/basic_pii_detection.py

This file was deleted.

91 changes: 0 additions & 91 deletions examples/04_input_scanner/basic_input_scanning.py

This file was deleted.

31 changes: 7 additions & 24 deletions examples/05_llm_tracing/cohere.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

from netra import Netra
from netra.decorators import workflow
from netra.pii import get_default_detector

# --- Configuration ---

Expand Down Expand Up @@ -72,14 +71,12 @@ def initialize_sdks() -> Any:


@workflow(name="cohere_chat_workflow") # type: ignore[arg-type]
async def get_cohere_response_with_pii_protection(
client: Any, messages: List[Dict[str, str]], model: str
) -> Optional[Any]:
async def get_cohere_response(client: Any, messages: List[Dict[str, str]], model: str) -> Optional[Any]:
"""
Sends messages to the Cohere chat API with PII protection.
Sends messages to the Cohere chat API.

This function is wrapped with Netra's `@workflow` decorator. It scans the
latest user message for PII and masks it before sending the payload to Cohere.
This function is wrapped with Netra's `@workflow` decorator so Netra can
monitor its execution.

Args:
client: The initialized Cohere AsyncClient.
Expand All @@ -94,24 +91,10 @@ async def get_cohere_response_with_pii_protection(
return None

# Separate the latest message from the previous chat history
latest_message_content = messages[-1].get("message", "")
message_to_send = messages[-1].get("message", "")
history_for_api = messages[:-1]

logging.info("Scanning latest user message for PII.")

# 1. PII Detection and Masking
pii_detector = get_default_detector(action_type="MASK")
pii_result = pii_detector.detect(latest_message_content)

if pii_result.has_pii:
logging.warning("PII detected. Using masked text for the API call.")
message_to_send = pii_result.masked_text
logging.info("Masked input: '%s'", message_to_send)
else:
logging.info("No PII detected in the latest message.")
message_to_send = latest_message_content

# 2. Call Cohere API
# Call Cohere API
try:
logging.info(f"Sending request to Cohere model: {model}")
response = await client.chat(
Expand Down Expand Up @@ -159,7 +142,7 @@ async def main() -> None:

# Get response from Cohere
with console.status("[bold cyan]Cohere is thinking...[/bold cyan]"):
response = await get_cohere_response_with_pii_protection(
response = await get_cohere_response(
client=cohere_client, messages=chat_history, model=args.model
) # type: ignore[misc]

Expand Down
Loading
Loading