drift-detection 0.1.0__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.
- drift_detection-0.1.0/.github/workflows/publish.yml +21 -0
- drift_detection-0.1.0/.gitignore +218 -0
- drift_detection-0.1.0/LICENSE +21 -0
- drift_detection-0.1.0/PKG-INFO +181 -0
- drift_detection-0.1.0/README.md +148 -0
- drift_detection-0.1.0/drift/__init__.py +28 -0
- drift_detection-0.1.0/drift/callbacks/__init__.py +1 -0
- drift_detection-0.1.0/drift/callbacks/langchain.py +243 -0
- drift_detection-0.1.0/drift/core.py +190 -0
- drift_detection-0.1.0/drift/detectors/__init__.py +34 -0
- drift_detection-0.1.0/drift/detectors/latency.py +148 -0
- drift_detection-0.1.0/drift/detectors/output_drift.py +213 -0
- drift_detection-0.1.0/drift/detectors/sequence.py +153 -0
- drift_detection-0.1.0/drift/models.py +101 -0
- drift_detection-0.1.0/examples/demo.py +128 -0
- drift_detection-0.1.0/examples/quickstart_langchain.py +34 -0
- drift_detection-0.1.0/pyproject.toml +58 -0
- drift_detection-0.1.0/tests/__init__.py +0 -0
- drift_detection-0.1.0/tests/test_detectors.py +185 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
- run: pip install build
|
|
20
|
+
- run: python -m build
|
|
21
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dombinic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: drift-detection
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Statistical anomaly detection for AI agent workflows
|
|
5
|
+
Project-URL: Homepage, https://github.com/dombinic/Drift
|
|
6
|
+
Project-URL: Repository, https://github.com/dombinic/Drift
|
|
7
|
+
Project-URL: Issues, https://github.com/dombinic/Drift/issues
|
|
8
|
+
Author: Dominic
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,ai,anomaly-detection,langchain,llm,monitoring,observability
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: numpy>=1.24.0
|
|
24
|
+
Provides-Extra: all
|
|
25
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'all'
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
30
|
+
Provides-Extra: langchain
|
|
31
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'langchain'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# Drift
|
|
35
|
+
|
|
36
|
+
**Statistical anomaly detection for AI agent workflows.**
|
|
37
|
+
|
|
38
|
+
Catch silent failures, hallucination drift, and off-script behavior before they corrupt your data.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
Your AI agents are failing silently. A tool call takes 10x longer than usual. The agent calls `delete_file` when it's never done that before. Output quality gradually degrades over hundreds of runs. Traditional monitoring tools weren't built for non-deterministic systems — Drift is.
|
|
43
|
+
|
|
44
|
+
## What it does
|
|
45
|
+
|
|
46
|
+
Drift hooks into your agent's execution and applies statistical anomaly detection to the event stream:
|
|
47
|
+
|
|
48
|
+
- **Latency & token SPC** — Flags when a tool call or LLM response takes significantly longer or uses significantly more tokens than its rolling baseline. Catches hung API calls, runaway generation, and upstream provider issues.
|
|
49
|
+
|
|
50
|
+
- **Sequence anomaly detection** — Builds a transition matrix of tool-call sequences and flags when the agent takes a path that's never or rarely been seen. Catches agents going off-script, skipping required steps, or entering novel execution paths.
|
|
51
|
+
|
|
52
|
+
- **Output drift detection** — Tracks output length, vocabulary diversity, and structural patterns over time. Flags when outputs shift significantly from baseline. Catches hallucination drift, prompt injection effects, and gradual quality degradation.
|
|
53
|
+
|
|
54
|
+
## Install
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install driftguard
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
With LangChain support:
|
|
61
|
+
```bash
|
|
62
|
+
pip install driftguard[langchain]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quickstart
|
|
66
|
+
|
|
67
|
+
### With LangChain
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from drift import DriftGuard
|
|
71
|
+
from drift.callbacks.langchain import DriftCallbackHandler
|
|
72
|
+
|
|
73
|
+
guard = DriftGuard(on_anomaly=lambda a: print(f"🚨 {a}"))
|
|
74
|
+
handler = DriftCallbackHandler(guard)
|
|
75
|
+
|
|
76
|
+
# Use with any LangChain agent, chain, or LLM
|
|
77
|
+
agent.run("your query", callbacks=[handler])
|
|
78
|
+
|
|
79
|
+
# See what happened
|
|
80
|
+
guard.report()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Standalone (no framework required)
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from drift import DriftGuard, AgentEvent, EventType
|
|
87
|
+
|
|
88
|
+
guard = DriftGuard(on_anomaly=lambda a: print(f"🚨 {a}"))
|
|
89
|
+
|
|
90
|
+
# Feed events from any source
|
|
91
|
+
guard.ingest(AgentEvent(
|
|
92
|
+
event_type=EventType.TOOL_END,
|
|
93
|
+
name="search_web",
|
|
94
|
+
latency_ms=150.0,
|
|
95
|
+
token_count=85,
|
|
96
|
+
output_text="Found 3 results for query...",
|
|
97
|
+
))
|
|
98
|
+
|
|
99
|
+
guard.report()
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Run the demo
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
python examples/demo.py
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This simulates normal agent operation, builds baselines, then injects latency spikes, sequence anomalies, and output drift — showing each detector catching real failure modes.
|
|
109
|
+
|
|
110
|
+
## Architecture
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
drift/
|
|
114
|
+
├── core.py # DriftGuard engine — orchestrates detectors
|
|
115
|
+
├── models.py # AgentEvent, Anomaly, Severity data models
|
|
116
|
+
├── detectors/
|
|
117
|
+
│ ├── latency.py # Statistical process control on latency/tokens
|
|
118
|
+
│ ├── sequence.py # Action transition probability anomalies
|
|
119
|
+
│ └── output_drift.py # Output distribution shift detection
|
|
120
|
+
└── callbacks/
|
|
121
|
+
└── langchain.py # LangChain callback integration
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Design principles:**
|
|
125
|
+
|
|
126
|
+
1. **Zero-overhead default** — Detectors use numpy for fast rolling statistics. No embedding models, no external services, no network calls.
|
|
127
|
+
2. **Per-tool baselines** — Each tool and model gets its own statistical baseline, so a slow tool won't pollute the baseline for a fast one.
|
|
128
|
+
3. **Framework-agnostic core** — The detection engine works with raw `AgentEvent` objects. Framework integrations (LangChain, CrewAI, etc.) are thin adapters that translate framework callbacks into events.
|
|
129
|
+
4. **Non-blocking** — Drift never throws exceptions that would crash your agent. Detector errors are caught and logged to stderr.
|
|
130
|
+
|
|
131
|
+
## Detectors
|
|
132
|
+
|
|
133
|
+
| Detector | What it catches | Method |
|
|
134
|
+
|----------|----------------|--------|
|
|
135
|
+
| `LatencyDetector` | Hung calls, slow APIs, runaway generation | Rolling z-score on latency and token counts |
|
|
136
|
+
| `SequenceDetector` | Off-script behavior, unexpected tool calls | First-order Markov transition probabilities |
|
|
137
|
+
| `OutputDriftDetector` | Hallucination drift, prompt injection, quality degradation | Output length, vocab diversity, structural pattern tracking |
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
Each detector is independently configurable:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from drift import DriftGuard
|
|
145
|
+
from drift.detectors.latency import LatencyDetector, LatencyDetectorConfig
|
|
146
|
+
from drift.detectors.sequence import SequenceDetector, SequenceDetectorConfig
|
|
147
|
+
|
|
148
|
+
guard = DriftGuard(detectors=[
|
|
149
|
+
LatencyDetector(LatencyDetectorConfig(
|
|
150
|
+
window_size=100, # Longer baseline window
|
|
151
|
+
z_threshold=2.5, # More sensitive
|
|
152
|
+
min_samples=10, # Require more data before alerting
|
|
153
|
+
)),
|
|
154
|
+
SequenceDetector(SequenceDetectorConfig(
|
|
155
|
+
min_observations=20, # Require more transitions before flagging
|
|
156
|
+
)),
|
|
157
|
+
])
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Roadmap
|
|
161
|
+
|
|
162
|
+
- [ ] CrewAI callback handler
|
|
163
|
+
- [ ] OpenAI Agents SDK integration
|
|
164
|
+
- [ ] Slack / PagerDuty alerting
|
|
165
|
+
- [ ] Persistent baselines (save/load detector state)
|
|
166
|
+
- [ ] Embedding-based output drift (optional dependency)
|
|
167
|
+
- [ ] Web dashboard
|
|
168
|
+
- [ ] Cost anomaly detection (track spend per run)
|
|
169
|
+
|
|
170
|
+
## Contributing
|
|
171
|
+
|
|
172
|
+
Issues and PRs welcome. Run tests with:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
pip install -e ".[dev]"
|
|
176
|
+
pytest
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Drift
|
|
2
|
+
|
|
3
|
+
**Statistical anomaly detection for AI agent workflows.**
|
|
4
|
+
|
|
5
|
+
Catch silent failures, hallucination drift, and off-script behavior before they corrupt your data.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Your AI agents are failing silently. A tool call takes 10x longer than usual. The agent calls `delete_file` when it's never done that before. Output quality gradually degrades over hundreds of runs. Traditional monitoring tools weren't built for non-deterministic systems — Drift is.
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
Drift hooks into your agent's execution and applies statistical anomaly detection to the event stream:
|
|
14
|
+
|
|
15
|
+
- **Latency & token SPC** — Flags when a tool call or LLM response takes significantly longer or uses significantly more tokens than its rolling baseline. Catches hung API calls, runaway generation, and upstream provider issues.
|
|
16
|
+
|
|
17
|
+
- **Sequence anomaly detection** — Builds a transition matrix of tool-call sequences and flags when the agent takes a path that's never or rarely been seen. Catches agents going off-script, skipping required steps, or entering novel execution paths.
|
|
18
|
+
|
|
19
|
+
- **Output drift detection** — Tracks output length, vocabulary diversity, and structural patterns over time. Flags when outputs shift significantly from baseline. Catches hallucination drift, prompt injection effects, and gradual quality degradation.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install driftguard
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
With LangChain support:
|
|
28
|
+
```bash
|
|
29
|
+
pip install driftguard[langchain]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quickstart
|
|
33
|
+
|
|
34
|
+
### With LangChain
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from drift import DriftGuard
|
|
38
|
+
from drift.callbacks.langchain import DriftCallbackHandler
|
|
39
|
+
|
|
40
|
+
guard = DriftGuard(on_anomaly=lambda a: print(f"🚨 {a}"))
|
|
41
|
+
handler = DriftCallbackHandler(guard)
|
|
42
|
+
|
|
43
|
+
# Use with any LangChain agent, chain, or LLM
|
|
44
|
+
agent.run("your query", callbacks=[handler])
|
|
45
|
+
|
|
46
|
+
# See what happened
|
|
47
|
+
guard.report()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Standalone (no framework required)
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from drift import DriftGuard, AgentEvent, EventType
|
|
54
|
+
|
|
55
|
+
guard = DriftGuard(on_anomaly=lambda a: print(f"🚨 {a}"))
|
|
56
|
+
|
|
57
|
+
# Feed events from any source
|
|
58
|
+
guard.ingest(AgentEvent(
|
|
59
|
+
event_type=EventType.TOOL_END,
|
|
60
|
+
name="search_web",
|
|
61
|
+
latency_ms=150.0,
|
|
62
|
+
token_count=85,
|
|
63
|
+
output_text="Found 3 results for query...",
|
|
64
|
+
))
|
|
65
|
+
|
|
66
|
+
guard.report()
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Run the demo
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python examples/demo.py
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This simulates normal agent operation, builds baselines, then injects latency spikes, sequence anomalies, and output drift — showing each detector catching real failure modes.
|
|
76
|
+
|
|
77
|
+
## Architecture
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
drift/
|
|
81
|
+
├── core.py # DriftGuard engine — orchestrates detectors
|
|
82
|
+
├── models.py # AgentEvent, Anomaly, Severity data models
|
|
83
|
+
├── detectors/
|
|
84
|
+
│ ├── latency.py # Statistical process control on latency/tokens
|
|
85
|
+
│ ├── sequence.py # Action transition probability anomalies
|
|
86
|
+
│ └── output_drift.py # Output distribution shift detection
|
|
87
|
+
└── callbacks/
|
|
88
|
+
└── langchain.py # LangChain callback integration
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Design principles:**
|
|
92
|
+
|
|
93
|
+
1. **Zero-overhead default** — Detectors use numpy for fast rolling statistics. No embedding models, no external services, no network calls.
|
|
94
|
+
2. **Per-tool baselines** — Each tool and model gets its own statistical baseline, so a slow tool won't pollute the baseline for a fast one.
|
|
95
|
+
3. **Framework-agnostic core** — The detection engine works with raw `AgentEvent` objects. Framework integrations (LangChain, CrewAI, etc.) are thin adapters that translate framework callbacks into events.
|
|
96
|
+
4. **Non-blocking** — Drift never throws exceptions that would crash your agent. Detector errors are caught and logged to stderr.
|
|
97
|
+
|
|
98
|
+
## Detectors
|
|
99
|
+
|
|
100
|
+
| Detector | What it catches | Method |
|
|
101
|
+
|----------|----------------|--------|
|
|
102
|
+
| `LatencyDetector` | Hung calls, slow APIs, runaway generation | Rolling z-score on latency and token counts |
|
|
103
|
+
| `SequenceDetector` | Off-script behavior, unexpected tool calls | First-order Markov transition probabilities |
|
|
104
|
+
| `OutputDriftDetector` | Hallucination drift, prompt injection, quality degradation | Output length, vocab diversity, structural pattern tracking |
|
|
105
|
+
|
|
106
|
+
## Configuration
|
|
107
|
+
|
|
108
|
+
Each detector is independently configurable:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from drift import DriftGuard
|
|
112
|
+
from drift.detectors.latency import LatencyDetector, LatencyDetectorConfig
|
|
113
|
+
from drift.detectors.sequence import SequenceDetector, SequenceDetectorConfig
|
|
114
|
+
|
|
115
|
+
guard = DriftGuard(detectors=[
|
|
116
|
+
LatencyDetector(LatencyDetectorConfig(
|
|
117
|
+
window_size=100, # Longer baseline window
|
|
118
|
+
z_threshold=2.5, # More sensitive
|
|
119
|
+
min_samples=10, # Require more data before alerting
|
|
120
|
+
)),
|
|
121
|
+
SequenceDetector(SequenceDetectorConfig(
|
|
122
|
+
min_observations=20, # Require more transitions before flagging
|
|
123
|
+
)),
|
|
124
|
+
])
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Roadmap
|
|
128
|
+
|
|
129
|
+
- [ ] CrewAI callback handler
|
|
130
|
+
- [ ] OpenAI Agents SDK integration
|
|
131
|
+
- [ ] Slack / PagerDuty alerting
|
|
132
|
+
- [ ] Persistent baselines (save/load detector state)
|
|
133
|
+
- [ ] Embedding-based output drift (optional dependency)
|
|
134
|
+
- [ ] Web dashboard
|
|
135
|
+
- [ ] Cost anomaly detection (track spend per run)
|
|
136
|
+
|
|
137
|
+
## Contributing
|
|
138
|
+
|
|
139
|
+
Issues and PRs welcome. Run tests with:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
pip install -e ".[dev]"
|
|
143
|
+
pytest
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Drift — Statistical anomaly detection for AI agent workflows.
|
|
2
|
+
|
|
3
|
+
Catch silent failures, hallucination drift, and off-script behavior
|
|
4
|
+
in your LangChain, CrewAI, and custom AI agents.
|
|
5
|
+
|
|
6
|
+
Quickstart:
|
|
7
|
+
from drift import DriftGuard
|
|
8
|
+
from drift.callbacks.langchain import DriftCallbackHandler
|
|
9
|
+
|
|
10
|
+
guard = DriftGuard()
|
|
11
|
+
handler = DriftCallbackHandler(guard)
|
|
12
|
+
agent.run("your query", callbacks=[handler])
|
|
13
|
+
guard.report()
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from drift.core import DriftGuard
|
|
17
|
+
from drift.models import AgentEvent, Anomaly, AnomalyType, EventType, Severity
|
|
18
|
+
|
|
19
|
+
__version__ = "0.1.0"
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"DriftGuard",
|
|
23
|
+
"AgentEvent",
|
|
24
|
+
"Anomaly",
|
|
25
|
+
"AnomalyType",
|
|
26
|
+
"EventType",
|
|
27
|
+
"Severity",
|
|
28
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Framework callback integrations for Drift."""
|