Metadata-Version: 2.4
Name: token-sentinel
Version: 1.0.3
Summary: Predictive token-waste detection for AI agents
Project-URL: Homepage, https://tokensentinel.dev
Project-URL: Documentation, https://docs.tokensentinel.dev
Project-URL: Issues, https://github.com/tokensentinel/tokensentinel-sdk-python/issues
Project-URL: Repository, https://github.com/tokensentinel/tokensentinel-sdk-python
Project-URL: Changelog, https://github.com/tokensentinel/tokensentinel-sdk-python/blob/main/CHANGELOG.md
Author-email: TokenSentinel contributors <hello@tokensentinel.dev>
License: Apache-2.0
License-File: LICENSE
Keywords: agents,anthropic,cost,finops,llm,observability,openai,tokens
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.10
Provides-Extra: all
Requires-Dist: anthropic>=0.39.0; extra == 'all'
Requires-Dist: boto3>=1.35.0; extra == 'all'
Requires-Dist: cohere>=5.0; extra == 'all'
Requires-Dist: deepgram-sdk>=3.0; extra == 'all'
Requires-Dist: elevenlabs>=1.0; extra == 'all'
Requires-Dist: google-genai>=1.0.0; extra == 'all'
Requires-Dist: imagehash>=4.3; extra == 'all'
Requires-Dist: langchain-core>=0.3; extra == 'all'
Requires-Dist: mutagen>=1.47; extra == 'all'
Requires-Dist: numpy>=1.26.0; extra == 'all'
Requires-Dist: openai>=1.50.0; extra == 'all'
Requires-Dist: opentelemetry-api>=1.27; extra == 'all'
Requires-Dist: opentelemetry-sdk>=1.27; extra == 'all'
Requires-Dist: opentelemetry-semantic-conventions>=0.48b0; extra == 'all'
Requires-Dist: pillow>=10; extra == 'all'
Requires-Dist: replicate>=0.40; extra == 'all'
Requires-Dist: sentence-transformers>=2.7.0; extra == 'all'
Requires-Dist: tiktoken>=0.7.0; extra == 'all'
Requires-Dist: voyageai>=0.2; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.39.0; extra == 'anthropic'
Provides-Extra: audio-metadata
Requires-Dist: mutagen>=1.47; extra == 'audio-metadata'
Provides-Extra: bedrock
Requires-Dist: boto3>=1.35.0; extra == 'bedrock'
Provides-Extra: cohere
Requires-Dist: cohere>=5.0; extra == 'cohere'
Provides-Extra: deepgram
Requires-Dist: deepgram-sdk>=3.0; extra == 'deepgram'
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=7.0; extra == 'dev'
Requires-Dist: pytest-mock>=3.15; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Provides-Extra: elevenlabs
Requires-Dist: elevenlabs>=1.0; extra == 'elevenlabs'
Provides-Extra: embeddings
Requires-Dist: numpy>=1.26.0; extra == 'embeddings'
Requires-Dist: sentence-transformers>=2.7.0; extra == 'embeddings'
Provides-Extra: gemini
Requires-Dist: google-genai>=1.0.0; extra == 'gemini'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3; extra == 'langchain'
Provides-Extra: openai
Requires-Dist: openai>=1.50.0; extra == 'openai'
Provides-Extra: otel
Requires-Dist: opentelemetry-api>=1.27; extra == 'otel'
Requires-Dist: opentelemetry-sdk>=1.27; extra == 'otel'
Requires-Dist: opentelemetry-semantic-conventions>=0.48b0; extra == 'otel'
Provides-Extra: replicate
Requires-Dist: replicate>=0.40; extra == 'replicate'
Provides-Extra: tiktoken
Requires-Dist: tiktoken>=0.7.0; extra == 'tiktoken'
Provides-Extra: vision-perceptual
Requires-Dist: imagehash>=4.3; extra == 'vision-perceptual'
Requires-Dist: pillow>=10; extra == 'vision-perceptual'
Provides-Extra: voyage
Requires-Dist: voyageai>=0.2; extra == 'voyage'
Description-Content-Type: text/markdown

# token-sentinel

Predictive **token-waste detection** for AI agents — a Python SDK that runs
**in-process**, watches LLM calls after each provider response, and fires a
typed callback so you can log, alert, or hard-stop the next turn.

Observability tools show the bill after the fact. TokenSentinel names the waste
pattern **while the session is still running**. Detection is post-call (the
turn that just finished is already billed); intervention saves **subsequent**
turns.

**Docs:** [https://docs.tokensentinel.dev](https://docs.tokensentinel.dev)

## Install

```bash
pip install token-sentinel[anthropic]   # or [openai], [gemini], …
# optional: local token estimates when usage is missing
pip install token-sentinel[tiktoken]
```

## Quick start

```python
from token_sentinel import Sentinel
import anthropic

sentinel = Sentinel(project="my-agent", mode="log")  # log | alert | block

@sentinel.on_waste  # or @sentinel.on_leak — same callback
def handle(event):
    print(f"{event.type} conf={event.confidence:.2f} burn≈${event.estimated_burn:.4f}")

client = sentinel.wrap(anthropic.Anthropic())
client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=100,
    messages=[{"role": "user", "content": "Hello"}],
)
```

### Tune rule thresholds

Every rule exposes flat config keys `"rule_name.param"`. Example — require five
similar tool calls (default is **3**) before `tool_loop` fires:

```python
sentinel = Sentinel(
    project="my-agent",
    mode="log",
    config={
        "tool_loop.min_calls": 5,
        "tool_loop.cosine_threshold": 0.80,
        "tool_loop.window_seconds": 120,
    },
)
```

Full parameter tables: [docs/user/04-waste-rules.md](https://github.com/tokensentinel/tokensentinel-sdk-python/blob/main/docs/user/04-waste-rules.md).

## What it catches (15 rules)

| Rule | Signal |
|---|---|
| **tool_loop** | Same tool, ≥N cosine-similar calls in a window |
| **context_bloat** | Prompt-tokens-per-turn slope rising |
| **embedding_waste** | Same embedding lookup repeated in session |
| **zombie** | Calls continue with no user-facing output |
| **model_misroute** | Classification-shaped prompt on a frontier model |
| **retry_storm** | Same call retried many times unchanged |
| **tool_definition_bloat** | Huge tool JSON / MCP tool lists |
| **retrieval_thrash** | Overlapping retrieval queries |
| **vision_re_upload** | Same image re-uploaded across turns |
| **vision_high_detail_misroute** | High-detail flag on low-detail work |
| **vision_cost_concentration** | Vision spend concentrated in few sessions |
| **audio_multichannel_doubling** | Multichannel STT billing trap |
| **voice_switching_loop** | Same text, many voice IDs |
| **rerank_thrash** | Duplicate Cohere rerank requests |
| **repair_loop** | Correction churn + similar regenerations |

## Providers

Native wrappers (`pip install token-sentinel[<extra>]`):

| Provider | Extra | Streaming |
|---|---|---|
| Anthropic | `anthropic` | yes |
| OpenAI (+ Whisper) | `openai` | yes |
| Google Gemini / Vertex | `gemini` | yes |
| AWS Bedrock | `bedrock` | yes |
| Voyage, Cohere, Replicate, Deepgram, ElevenLabs | matching extra | varies |

**OpenAI-compatible hosts** (same wrapper, set `base_url`): DeepSeek, Together,
Fireworks, Groq, OpenRouter, Mistral, Perplexity, vLLM, Ollama, TGI, LM Studio,
xAI Grok (`https://api.x.ai/v1`), etc.

```python
import openai
client = sentinel.wrap(
    openai.OpenAI(api_key="…", base_url="https://api.deepseek.com")
)
```

Pass a stable `_sentinel_session_id=...` on each call (or use
`Sentinel.session(...)`) so multi-turn rules see history.

## Modes

| Mode | Behavior |
|---|---|
| `log` | Emit events to your handler. Default. Safe for prod day one. |
| `alert` | Same local behavior as `log` (mode stamp for optional cloud). |
| `block` | Raise `LeakDetected` / `WasteDetected` **after** the provider returns. |

## Cost estimates (`estimated_burn`)

Burn figures are **approximate FinOps signals**, not invoices:

- **Model-aware rates** for major Anthropic / OpenAI / Gemini / DeepSeek / Cohere / Mistral families (input vs output priced separately).
- **Prompt-cache reads** (OpenAI `cached_tokens`, Anthropic `cache_read_input_tokens`) discounted when present on the usage payload.
- **Unknown models** fall back to a flat average (~$9e-6 / token).
- Optional **`tiktoken`** fills missing token counts when usage was omitted.

Override rates with `Sentinel(pricing_table={...})` or
`from token_sentinel import estimate_usd, ModelRate, default_pricing_table`.

## Frameworks

Instruments at the LLM-client layer, so MCP hosts, RAG pipelines, LangChain /
LangGraph / CrewAI / AutoGen / Pydantic AI work when traffic bottoms out in a
wrapped client. Enrichers: `pip install token-sentinel[langchain]` or `[otel]`.

## Optional cloud

Nothing phones home unless you pass both `cloud_endpoint=` and `api_key=` on
`Sentinel(...)`. For hosted dashboard, Composite Signals and policy features — see [tokensentinel.dev](https://tokensentinel.dev). This package is
**Apache-2.0** and fully usable offline.

## Status

**1.0.3** — 15 rules, model-aware burn estimates, cache-aware usage, optional
tiktoken fallback, OSS-focused docs.

Public API (`Sentinel`, `wrap`, `on_leak` / `on_waste`, `record_call`, `session`,
`mark_long_running`, events/exceptions, pricing helpers) follows semver —
pin deliberately (`token-sentinel>=1.0,<2`).

## More docs

- [User guide](https://github.com/tokensentinel/tokensentinel-sdk-python/tree/main/docs/user/) — install, modes, rules, providers, API reference  
- [Architecture](https://github.com/tokensentinel/tokensentinel-sdk-python/blob/main/docs/architecture.md) · [Waste taxonomy](https://github.com/tokensentinel/tokensentinel-sdk-python/blob/main/docs/waste-taxonomy.md)  
- [CHANGELOG](https://github.com/tokensentinel/tokensentinel-sdk-python/blob/main/CHANGELOG.md)

## Contact

[support@tokensentinel.dev](mailto:support@tokensentinel.dev) ·
[hello@tokensentinel.dev](mailto:hello@tokensentinel.dev) ·
[tokensentinel.dev](https://tokensentinel.dev)

## License

Apache-2.0 — see [LICENSE](https://github.com/tokensentinel/tokensentinel-sdk-python/blob/main/LICENSE).
