llm-watchdog
Production-grade silent failure detection for LLM applications.
Traditional monitoring (Datadog, New Relic) shows 200 OK in 1.2 seconds — but it cannot detect hallucinations, PII leaks, topic drift, or quality degradation in your LLM responses. llm-watchdog fills that gap.
pip install llm-watchdog
Why llm-watchdog?
| Problem | Traditional APM | llm-watchdog |
|---|---|---|
| Hallucination risk | Blind | Scored 0–1 |
| PII leaks in output | Blind | Detected + alerted |
| Topic drift | Blind | Keyword + coverage guard |
| Toxicity | Blind | Pattern-matched |
| Quality degradation | Blind | Refusal + repetition check |
| Semantic drift over time | Blind | PSI-based drift detector |
Quickstart
from llm_watchdog import LlmWatchdog
watcher = LlmWatchdog()
result = watcher.watch(
prompt="What is the capital of France?",
response="The capital of France is Paris.",
)
print(result.passed) # True
print(result.overall_score) # 0.0
print(result.overall_risk) # RiskLevel.LOW
Alert Hooks
from llm_watchdog import LlmWatchdog, AlertEvent
watcher = LlmWatchdog(pii_threshold=0.1)
def my_alert(event: AlertEvent):
print(f"ALERT: {event.failure_type.value} — score={event.score:.2f}")
watcher.on_alert(my_alert)
watcher.watch("Tell me about John", "Contact john@example.com at 555-555-5555")
# ALERT: pii_leak — score=0.40
Async Support
import asyncio
from llm_watchdog import LlmWatchdog
watcher = LlmWatchdog()
async def main():
result = await watcher.awatch("prompt", "response")
print(result.overall_risk)
asyncio.run(main())
Batch Watching
from llm_watchdog import LlmWatchdog, batch_watch, abatch_watch
import asyncio
watcher = LlmWatchdog()
pairs = [("prompt1", "response1"), ("prompt2", "response2")]
# Sync batch
results = batch_watch(watcher, pairs, max_workers=8)
# Async batch
results = asyncio.run(abatch_watch(watcher, pairs, concurrency=8))
Topic Guard
from llm_watchdog import LlmWatchdog
watcher = LlmWatchdog(
topic_allowed=["python", "code", "programming", "function"],
topic_blocked=["politics", "religion", "violence"],
)
result = watcher.watch("How do I code?", "This involves violent politics.")
# topic drift detected
Advanced Features
Caching
from llm_watchdog import LlmWatchdog, WatchCache
watcher = LlmWatchdog()
cache = WatchCache(max_size=512, ttl=300)
cached_watch = cache.memoize(watcher)
result = cached_watch("prompt", "response")
print(cache.stats())
# {'hits': 0, 'misses': 1, 'hit_rate': 0.0, 'evictions': 0, 'size': 1}
Pipeline
from llm_watchdog import LlmWatchdog, WatchPipeline
watcher = LlmWatchdog()
result = watcher.watch("prompt", "response")
pipeline = WatchPipeline()
pipeline.add_step("log", lambda r: r)
pipeline.filter(lambda r: r.passed)
out = pipeline.run(result)
print(pipeline.audit())
Validation
from llm_watchdog import LlmWatchdog, WatchValidator, ConfidenceScorer
watcher = LlmWatchdog()
result = watcher.watch("prompt", "response")
validator = WatchValidator().require_pass().max_score(0.8)
violations = validator.validate(result)
scorer = ConfidenceScorer()
print(scorer.score(result)) # 0–1, higher = safer
print(scorer.field_scores(result)) # per-detector breakdown
Drift Detection
from llm_watchdog import DriftDetector
detector = DriftDetector(threshold=0.1)
detector.set_baseline([0.1, 0.2, 0.1, 0.15])
print(detector.is_drifting([0.5, 0.6, 0.4, 0.55])) # True
print(detector.psi([0.5, 0.6, 0.4, 0.55])) # PSI value
Streaming
from llm_watchdog import LlmWatchdog, StreamingWatcher
watcher = LlmWatchdog()
sw = StreamingWatcher(watcher)
pairs = [("prompt1", "response1"), ("prompt2", "response2")]
for result in sw.stream(pairs):
print(result.overall_risk)
Diff & Regression Tracking
from llm_watchdog import LlmWatchdog, WatchDiff, RegressionTracker
watcher = LlmWatchdog()
r1 = watcher.watch("prompt", "safe response")
r2 = watcher.watch("prompt", "Contact john@example.com")
diff = WatchDiff(before=r1, after=r2)
print(diff.score_delta) # +0.2
print(diff.new_failures) # ['pii_leak']
print(diff.to_json())
tracker = RegressionTracker(tolerance=0.05)
tracker.record("deploy_v1", 0.10)
tracker.record("deploy_v2", 0.25)
print(tracker.is_regressing()) # True
print(tracker.summary())
Agent Session Monitoring
from llm_watchdog import LlmWatchdog, AgentWatchSession
watcher = LlmWatchdog()
session = AgentWatchSession(watcher, max_risk_budget=2.0)
for prompt, response in agent_turns:
session.watch_turn(prompt, response)
if session.is_over_budget():
raise RuntimeError("Agent risk budget exceeded")
print(session.session_summary())
PII Scrubbing
from llm_watchdog import PIIScrubber
scrubber = PIIScrubber()
clean = scrubber.mask("Email me at alice@example.com or call 555-123-4567")
# "Email me at [REDACTED_EMAIL] or call [REDACTED_PHONE]"
Audit Log
from llm_watchdog import LlmWatchdog, AuditLog
watcher = LlmWatchdog()
audit = AuditLog()
result = watcher.watch("prompt", "response")
audit.record(result)
audit.export_audit("audit.json")
print(audit.entries())
Cost Ledger
from llm_watchdog import CostLedger
ledger = CostLedger(cost_per_watch=0.0001)
ledger.record("default", count=100)
ledger.record("batch", count=500)
print(ledger.total_cost())
print(ledger.report())
Rate Limiter
from llm_watchdog import LlmWatchdog, RateLimiter
import asyncio
rl = RateLimiter(rate=10, capacity=10) # 10 calls/sec
# Sync
rl.acquire()
# Async
async def main():
await rl.async_acquire()
asyncio.run(main())
FastAPI Middleware
from fastapi import FastAPI
from llm_watchdog import LlmWatchdog
from llm_watchdog.middleware import create_fastapi_middleware
app = FastAPI()
watcher = LlmWatchdog()
app.add_middleware(create_fastapi_middleware(watcher))
Flask Middleware
from flask import Flask
from llm_watchdog import LlmWatchdog
from llm_watchdog.middleware import create_flask_middleware
app = Flask(__name__)
watcher = LlmWatchdog()
create_flask_middleware(app, watcher)
CLI
llm-watchdog --prompt "What is the capital?" --response "Paris is the capital."
llm-watchdog --prompt "Tell me about John" --response "Call 555-123-4567" --json
Configuration
| Parameter | Default | Description |
|---|---|---|
hallucination_threshold |
0.5 | Flag score above this |
pii_threshold |
0.1 | Any PII triggers flag |
toxicity_threshold |
0.3 | Toxic content threshold |
quality_threshold |
0.4 | Low-quality response threshold |
topic_allowed |
None | Keywords expected in response |
topic_blocked |
None | Keywords that always trigger flag |
block_on_critical |
False | Raise exception on CRITICAL risk |
All Exports
from llm_watchdog import (
# Core
LlmWatchdog,
# Models
WatchResult, DetectionResult, AlertEvent, DriftSnapshot, RiskLevel, FailureType,
# Exceptions
LlmWatchdogError, HallucinationDetectedError, PIILeakDetectedError,
TopicDriftError, SemanticDriftError, BudgetExceededError, AlertDeliveryError,
# Detectors
HallucinationDetector, PIIDetector, TopicGuard, ToxicityDetector, QualityDetector,
# Advanced
WatchCache, WatchPipeline, WatchValidator, ConfidenceScorer,
RateLimiter, batch_watch, abatch_watch,
OperationProfiler, DriftDetector, StreamingWatcher,
WatchDiff, RegressionTracker, AgentWatchSession,
PIIScrubber, AuditLog, CostLedger,
)
Installation
pip install llm-watchdog # core only
pip install llm-watchdog[fastapi] # with FastAPI middleware
pip install llm-watchdog[flask] # with Flask middleware
pip install llm-watchdog[opentelemetry] # with OTEL tracing
pip install llm-watchdog[all] # everything
Changelog
v1.0.2 (2026-04-10)
- Added Changelog section to README for release traceability
- Fixed GitHub URL in package metadata
v1.0.1
- Added advanced features: hallucination detection, PII detection, semantic drift monitoring, quality scoring
- Renamed package from
promptwatchtollm_watchdog
v1.0.0
- Initial release: production-grade silent failure detection for LLM applications
Keywords
llm monitoring, ai observability, hallucination detection, pii detection, semantic drift, production ai monitoring, llm alerts, ai safety, prompt monitoring, silent failure detection, llm quality, topic drift, ai reliability, llm guardrails, prompt injection, ai production
Contributing
Contributions are welcome! Here's how to get started:
- Fork the repository on GitHub
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes and add tests
- Run the test suite:
pytest tests/ -v - Submit a pull request
Please open an issue first for major changes to discuss the approach.
Author
Mahesh Makvana — GitHub · PyPI
MIT License
Metadata
Release files for llm-watchdog 1.0.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| llm_watchdog-1.0.8.tar.gz | 25.2 kB | Without provenance | Details for llm_watchdog-1.0.8.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| llm_watchdog-1.0.8-py3-none-any.whl | Python 3 | none | any | Details for llm_watchdog-1.0.8-py3-none-any.whl |
Total release size: 50.6 kB
Release files / llm_watchdog-1.0.8.tar.gz
| Download URL | llm_watchdog-1.0.8.tar.gz |
|---|---|
| Size | 25.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5f5059b0d3e8d9faabc02ab450a537f000eab5722d1100ce05829d42e9e54661
|
|
BLAKE2b-256 checksum How to use checksums |
a55afdca2954c1290973f32c5d4705f1080a3364d81aa2f7531abd23e01af7ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.12
|
Release files / llm_watchdog-1.0.8-py3-none-any.whl
| Download URL | llm_watchdog-1.0.8-py3-none-any.whl |
|---|---|
| Size | 25.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8c1d897272dd3de5740f5ce1fe5d756ae8c5fc1140a78a494ddb7defae089a3c
|
|
BLAKE2b-256 checksum How to use checksums |
782f368835175d8b9ec1ae8e1c709080ffedf77052acea85aa03fc6c0897ef0a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.12
|
Security for llm-watchdog 1.0.8
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.