An open API service indexing awesome lists of open source software.

https://github.com/remo12262/token-saver

Drop-in Anthropic client wrapper for token counting, cost analysis, semantic compression, overflow recovery, and a Claude Code context-guard hook.
https://github.com/remo12262/token-saver

anthropic claude cli cost-reduction llm made-in-italy open-source pulcini python remo token-optimization

Last synced: 22 days ago
JSON representation

Drop-in Anthropic client wrapper for token counting, cost analysis, semantic compression, overflow recovery, and a Claude Code context-guard hook.

Awesome Lists containing this project

README

          



tests


PyPI

Python
License
dependencies

tsave


Stop discovering costs after the fact. See where your tokens go before you run a single line.


Smetti di scoprire i costi a consuntivo. Vedi dove vanno i token prima ancora di eseguire il codice.

---

## ๐Ÿ‡ฌ๐Ÿ‡ง English

I got tired of watching my Anthropic bill grow without knowing why.

So I built this: a wrapper around the official SDK that tells you โ€” **before you run your code** โ€” exactly where your tokens are going and what to do about it.

```bash
pip install tsave
tsave scan chatbot.py
```

No API key needed for that last command. It reads your Python file, walks the AST, and tells you what's wrong.

---

### What it actually does

#### ๐Ÿ” Scan your code before you run it

This is the part I'm most proud of. Point it at a `.py` file and it finds patterns like API calls inside loops, system prompts sent without `cache_control`, conversation history growing unbounded โ€” the kind of stuff that quietly triples your bill.

Each finding comes with:
- the line number
- an estimate of how many tokens you're burning
- a ready-to-paste fix

#### ๐Ÿช™ Count tokens accurately

Not with tiktoken โ€” tiktoken undercounts Claude by 15โ€“20%. tsave uses the official Anthropic `count_tokens` API, the same one that feeds the billing system.

#### ๐Ÿ“‰ Compress long conversations

When a chat history gets long, tsave summarizes the older turns while keeping recent context intact. In practice, this cuts **65โ€“70% of tokens** on multi-turn workloads.

#### ๐Ÿ“Š Track what you spend

Every `client.create()` call gets logged. At the end of a session you can ask for a usage summary, an average cost per request, and a monthly projection.

#### ๐Ÿช Guard your Claude Code sessions

`tsave guard` is a PreToolUse hook for Claude Code โ€” it catches waste at the *session* level, before the API is even involved. It intercepts `Read` and `Bash` calls and flags the two patterns that quietly bloat a session's context: reading a whole large file when you only needed a slice, and shell commands that dump unbounded output (`cat` with no pipe, `find` with no `-maxdepth`, `git log` with no limit, unscoped recursive `grep`, noisy package installs).

It's pure local heuristics โ€” file-size estimation and regex, no API calls โ€” because a hook runs synchronously on every matching tool call and can't add latency or cost of its own.

Add it to `.claude/settings.json`:

```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Bash",
"hooks": [{ "type": "command", "command": "tsave guard" }]
}
]
}
}
```

#### ๐Ÿงพ Recover from overflow without losing the thread

When a conversation overflows and gets compressed, a narrative summary alone can lose the concrete thread of what you were doing. `RecoveryContract` rides alongside the compressed history and captures the handful of facts that actually matter for continuing correctly: the active goal, the last successful tool call, open files/context, the error that triggered recovery, and any assumptions stated along the way.

Like everything else here, it's built from local heuristics โ€” regex and `tool_use` block scanning โ€” so it adds no extra API call, cost, or latency on top of the compression that was already happening.

```python
result = client.compress(model="claude-sonnet-4-6", messages=long_chat, keep_last_n=4)
print(result.format())
# Original: 1,131 tokens (13 messages)
# Compressed: 363 tokens (3 messages) โ€” 67.9% reduction
#
# [Recovery Contract]
# - Active goal: ...
# - Last successful action: ...
# - Open files/context: ...
```

---

### Numbers

Real runs on real workloads, not synthetic benchmarks:

| Scenario | Before | After | At 1K req/day |
|---|---|---|---|
| Multi-turn chatbot (50 turns) | 12,400 tokens | 4,100 tokens **โˆ’66.9%** | saves $7.47/day |
| RAG pipeline (full doc per call) | 18,200 tokens | 5,600 tokens **โˆ’69.2%** | saves $11.34/day |
| Batch classifier (loop + Opus) | 8,500 tokens | 2,800 tokens **โˆ’67.1%** | saves $8.55/day |

*Sonnet 4.6 pricing, $3/MTok input.*

---

### Usage

```python
from tsave import TsaveClient

client = TsaveClient()

# count tokens before spending them
tc = client.count_tokens(model="claude-sonnet-4-6", messages=messages)
print(tc.format())
# 847 input tokens | est. $0.0025

# compress a long conversation
result = client.compress(model="claude-sonnet-4-6", messages=long_chat, keep_last_n=4)
print(result.format())
# Original: 1,131 tokens (13 messages)
# Compressed: 363 tokens (3 messages) โ€” 67.9% reduction

# make the actual call โ€” usage is tracked automatically
response = client.create(model="claude-sonnet-4-6", max_tokens=1024, messages=messages)

# see where you stand
print(client.usage_summary())
print(client.monthly_projection(requests_per_day=500).format())
# Monthly (30 days): $410.40
```

The CLI gives you the same things without writing any code:

```bash
tsave scan myapp.py # static analysis, no API key
tsave analyze # token breakdown of a conversation
tsave cost # cost estimate
tsave compress # compress a conversation file
```

---

### What the scanner catches

| Pattern | What it means |
|---|---|
| `api-in-loop` | You're making a full API request on every loop iteration |
| `full-file-per-call` | You're reading an entire file and passing it raw to the API |
| `no-model-routing` | You're using Opus where Haiku would work fine |
| `system-prompt-redefined` | Your system prompt gets recreated on every call |
| `uncached-system-prompt` | Your system prompt is in a loop without `cache_control` |
| `uncompressed-history` | Your message history keeps growing with no compression |

> Files that can't be parsed (syntax errors, unsupported encodings) are reported as "could not analyze" โ€” never silently skipped or marked clean. The CLI exits non-zero on them. UTF-8 BOM files are handled transparently.

---

### Development

```bash
git clone https://github.com/remo12262/token-saver.git
cd token-saver
pip install -e ".[dev]"
pytest
# 147 tests, all pass without an API key
```

---

### Models & pricing

| Model | Input | Output |
|---|---|---|
| Claude Opus 4.8 / 4.7 / 4.6 | $5.00/MTok | $25.00/MTok |
| Claude Sonnet 4.6 | $3.00/MTok | $15.00/MTok |
| Claude Haiku 4.5 | $1.00/MTok | $5.00/MTok |

MIT license. Built in one evening with Claude Code.

---
---
---

Built in one evening with Claude Code.

## ๐Ÿ‡ฎ๐Ÿ‡น Italiano

Mi ero stancato di guardare la mia bolletta Anthropic crescere senza capire perchรฉ.

Cosรฌ ho costruito questo: un wrapper attorno all'SDK ufficiale che ti dice โ€” **prima di eseguire il codice** โ€” esattamente dove stanno andando i tuoi token e cosa fare al riguardo.

```bash
pip install tsave
tsave scan chatbot.py
```

Per quest'ultimo comando non serve nessuna API key. Legge il file Python, analizza l'AST, e ti dice cosa c'รจ che non va.

---

### Cosa fa concretamente

#### ๐Ÿ” Analizza il codice prima che tu lo esegua

Questa รจ la parte di cui vado piรน fiero. Puntalo su un file `.py` e trova pattern come chiamate API dentro i loop, system prompt inviati senza `cache_control`, cronologie di conversazione che crescono senza controllo โ€” il tipo di cose che silenziosamente triplicano la bolletta.

Ogni finding mostra:
- il numero di riga
- una stima dei token sprecati
- una correzione pronta da incollare

#### ๐Ÿช™ Conta i token in modo preciso

Non con tiktoken โ€” tiktoken sottostima Claude del 15โ€“20%. tsave usa l'API ufficiale `count_tokens` di Anthropic, la stessa che alimenta il sistema di fatturazione.

#### ๐Ÿ“‰ Comprime le conversazioni lunghe

Quando una cronologia di chat diventa lunga, tsave riassume i turni piรน vecchi mantenendo il contesto recente intatto. In pratica, questo taglia il **65โ€“70% dei token** sui workload multi-turno.

#### ๐Ÿ“Š Traccia quello che spendi

Ogni chiamata `client.create()` viene registrata. A fine sessione puoi richiedere un riepilogo dei consumi, il costo medio per richiesta e una proiezione mensile.

#### ๐Ÿช Proteggi le tue sessioni Claude Code

`tsave guard` รจ un hook PreToolUse per Claude Code โ€” intercetta lo spreco a livello di *sessione*, prima ancora che l'API entri in gioco. Controlla le chiamate `Read` e `Bash` e segnala i due pattern che gonfiano silenziosamente il contesto: leggere un intero file grande quando serviva solo una porzione, e comandi shell che riversano output senza limiti (`cat` senza pipe, `find` senza `-maxdepth`, `git log` senza limite, `grep` ricorsivo non delimitato, installazioni di pacchetti rumorose).

รˆ tutto euristica locale โ€” stima della dimensione file e regex, nessuna chiamata API โ€” perchรฉ un hook gira in modo sincrono su ogni tool call che corrisponde, e non puรฒ permettersi latenza o costo propri.

Aggiungilo a `.claude/settings.json`:

```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Read|Bash",
"hooks": [{ "type": "command", "command": "tsave guard" }]
}
]
}
}
```

#### ๐Ÿงพ Recupera da un overflow senza perdere il filo

Quando una conversazione va in overflow e viene compressa, un riassunto narrativo da solo puรฒ far perdere il filo concreto di cosa stavi facendo. `RecoveryContract` affianca la cronologia compressa e cattura le poche informazioni che contano davvero per continuare correttamente: l'obiettivo attivo, l'ultima azione/tool riuscito, i file/contesto aperti, l'errore che ha scatenato il recovery, e le eventuali assunzioni dichiarate lungo il percorso.

Come tutto il resto qui, รจ costruito con euristiche locali โ€” regex e scansione dei blocchi `tool_use` โ€” quindi non aggiunge nessuna chiamata API, costo o latenza extra oltre alla compressione che stava giร  avvenendo.

```python
result = client.compress(model="claude-sonnet-4-6", messages=long_chat, keep_last_n=4)
print(result.format())
# Originale: 1.131 token (13 messaggi)
# Compresso: 363 token (3 messaggi) โ€” riduzione del 67,9%
#
# [Recovery Contract]
# - Obiettivo attivo: ...
# - Ultima azione riuscita: ...
# - File/contesto aperti: ...
```

---

### I numeri

Risultati reali su workload reali, non benchmark sintetici:

| Scenario | Prima | Dopo | A 1.000 req/giorno |
|---|---|---|---|
| Chatbot multi-turno (50 turni) | 12.400 token | 4.100 token **โˆ’66,9%** | risparmia $7,47/giorno |
| Pipeline RAG (doc completo per chiamata) | 18.200 token | 5.600 token **โˆ’69,2%** | risparmia $11,34/giorno |
| Classificatore batch (loop + Opus) | 8.500 token | 2.800 token **โˆ’67,1%** | risparmia $8,55/giorno |

*Prezzi Sonnet 4.6, $3/MTok in input.*

---

### Utilizzo

```python
from tsave import TsaveClient

client = TsaveClient()

# conta i token prima di spenderli
tc = client.count_tokens(model="claude-sonnet-4-6", messages=messages)
print(tc.format())
# 847 input tokens | est. $0.0025

# comprimi una conversazione lunga
result = client.compress(model="claude-sonnet-4-6", messages=long_chat, keep_last_n=4)
print(result.format())
# Originale: 1.131 token (13 messaggi)
# Compresso: 363 token (3 messaggi) โ€” riduzione del 67,9%

# fai la vera chiamata โ€” l'utilizzo viene tracciato automaticamente
response = client.create(model="claude-sonnet-4-6", max_tokens=1024, messages=messages)

# vedi dove sei
print(client.usage_summary())
print(client.monthly_projection(requests_per_day=500).format())
# Mensile (30 giorni): $410.40
```

La CLI ti dร  le stesse cose senza scrivere codice:

```bash
tsave scan myapp.py # analisi statica, senza API key
tsave analyze # breakdown dei token di una conversazione
tsave cost # stima dei costi
tsave compress # comprimi un file di conversazione
```

---

### Cosa rileva lo scanner

| Pattern | Cosa significa |
|---|---|
| `api-in-loop` | Stai facendo una richiesta API completa a ogni iterazione del loop |
| `full-file-per-call` | Stai leggendo un file intero e passandolo grezzo all'API |
| `no-model-routing` | Stai usando Opus dove basterebbe Haiku |
| `system-prompt-redefined` | Il tuo system prompt viene ricreato a ogni chiamata |
| `uncached-system-prompt` | Il tuo system prompt รจ in un loop senza `cache_control` |
| `uncompressed-history` | La cronologia dei messaggi continua a crescere senza compressione |

> I file non analizzabili (errori di sintassi, encoding non supportati) vengono segnalati come "could not analyze" โ€” mai saltati in silenzio o dati per puliti. La CLI esce con codice โ‰  0 su questi file. I file con BOM UTF-8 sono gestiti in modo trasparente.

---

### Sviluppo

```bash
git clone https://github.com/remo12262/token-saver.git
cd token-saver
pip install -e ".[dev]"
pytest
# 147 test, tutti passano senza API key
```

---

### Modelli e prezzi

| Modello | Input | Output |
|---|---|---|
| Claude Opus 4.8 / 4.7 / 4.6 | $5,00/MTok | $25,00/MTok |
| Claude Sonnet 4.6 | $3,00/MTok | $15,00/MTok |
| Claude Haiku 4.5 | $1,00/MTok | $5,00/MTok |
---

๐Ÿ“ฃ **Share / Condividi**

[![LinkedIn](https://img.shields.io/badge/Share-LinkedIn-0A66C2?logo=linkedin)](https://www.linkedin.com/sharing/share-offsite/?url=https://github.com/remo12262/token-saver)
[![Twitter/X](https://img.shields.io/badge/Share-X-000000?logo=x)](https://twitter.com/intent/tweet?url=https://github.com/remo12262/token-saver&text=tsave+โ€”+predictive+token+optimizer+for+the+Anthropic+API)
[![Facebook](https://img.shields.io/badge/Share-Facebook-1877F2?logo=facebook)](https://www.facebook.com/sharer/sharer.php?u=https://github.com/remo12262/token-saver)

Licenza MIT. Costruito in una serata con Claude Code.