wallet-helper
Never run the same heavy call twice. wallet-helper is persistent memoization for expensive calls (a paid API request, a slow model, any heavy function): an identical call is served from a local store instead of running again, across process restarts. When two identical calls start at the same time, they collapse into one, so the second waits for the first and reuses its result instead of running in parallel (single-flight).
Documentation
What it does
A heavy call is a problem you do not want to pay for twice. Two things cause a double run:
- You call it again next week. wallet-helper stores each result on disk, content-addressed by a namespace plus the inputs (arguments, a file's content, or bytes), so the repeat is served from the store rather than recomputed.
- You call it twice at once. Two threads, or two processes, launch the same slow call before either finishes. wallet-helper lets one of them run it and makes the others wait for that result, so the work happens once.
It is content-addressed, so a renamed input file still hits and two different inputs never collide. The default store is a folder of JSON files, easy to read and to delete. A SQLite backend adds a shared, concurrency-safe store and cross-process single-flight. A small HTTP server centralizes that dedup for many clients.
Status
What ships today:
- library with
Walletand the@memoizedecorator (sync andasync def), over aLedger(JSON files), aSqliteLedger(one shared file), or aRemoteLedger(an HTTP server). In-process single-flight is built in. - cross-process single-flight through the SQLite backend or the server, with a fencing token so a crashed or stalled leader cannot disrupt a new leader's lease, a lease timeout so a dead leader never blocks waiters, and a heartbeat so a long job keeps its lease. Duplicates coalesce as long as the leader finishes within its lease or keeps a heartbeat.
- time-to-live and eviction: per-entry
ttl, optional stale-while-revalidate, anevictpolicy by age or size, and an automatic size cap (max_entries). wallet-helper/cli_argparseandwallet-helper-click: inspect, clear, and evict the store.- HTTP dedup server (the
[api]extra) plusRemoteLedger, so many clients on any host share one dedup point.
Installation
The only requirement is Python 3.10 to 3.13. If you need Python itself:
- 🍎 macOS (Homebrew):
brew install python - 🐧 Ubuntu/Debian:
sudo apt update && sudo apt install -y python3 python3-pip - 🪟 Windows (PowerShell):
winget install Python.Python.3.12
Install from GitHub, pinned to the release tag:
pip install "git+https://github.com/warith-harchaoui/wallet-helper.git@v0.3.0"
The command-line and HTTP surfaces are opt-in extras:
pip install "wallet-helper[cli] @ git+https://github.com/warith-harchaoui/wallet-helper.git@v0.3.0" # click CLI variant -> click
pip install "wallet-helper[api] @ git+https://github.com/warith-harchaoui/wallet-helper.git@v0.3.0" # HTTP dedup server -> fastapi, uvicorn
Quick start
Memoize any function with one line. The result is stored on disk and reused on the next identical call, this run or next week:
from wallet_helper import memoize
@memoize
def transcribe(path):
return call_some_paid_api(path) # slow and billed; runs at most once per file
transcribe("meeting.wav") # runs, stores the result
transcribe("meeting.wav") # served from the store, no second call
A file argument is keyed by its content, not its path. So the same file reached under a different name, or a byte-for-byte copy in another folder, still hits, and two different files never collide even if their names look alike:
transcribe("meeting.wav") # runs
transcribe("archive/meeting.wav") # a copy with the same bytes, served from the store
Ignore an argument that should not change the result, such as a client handle:
@memoize(ignore=("client",))
def fetch(doc_id, client):
return client.get(doc_id)
Inspect or drop a function's cache, like functools.lru_cache:
transcribe.cache_info() # {'entries': 1, 'hits': 1}
transcribe.cache_clear() # forget this function's stored results
Set a freshness window with ttl (seconds), and share one store across a fleet by pointing at a running server:
from wallet_helper import Wallet, RemoteLedger, memoize
@memoize(ttl=3600) # results expire after an hour
def price(symbol):
return call_pricing_api(symbol)
wallet = Wallet(RemoteLedger("http://cache.internal:8000"))
@memoize(wallet=wallet) # every host dedups through one server
def transcribe(path):
return call_some_paid_api(path)
Async functions work the same. The result is cached, never the coroutine, and concurrent awaits coalesce:
@memoize
async def fetch(url):
return await http_get(url)
Two command-line tools inspect and manage the store (it defaults to $WALLET_HELPER_DIR, then ~/.cache/wallet-helper):
python -m wallet_helper.cli_argparse stats # how many results are cached and how many calls they saved
python -m wallet_helper.cli_argparse path # where the store lives
python -m wallet_helper.cli_argparse clear # empty the store
wallet-helper-click stats # same, via the click variant
For a shared store and cross-process single-flight, use the SQLite backend or the HTTP server. See EXAMPLES.md.
Built on os-helper
wallet-helper is part of the AI Helpers suite and builds on os-helper for content-addressed hashing, path helpers, temporary folders, and logging. That is one direct dependency, which pulls a few common transitive libraries (requests, pyyaml, tqdm, and so on). wallet-helper is local-first and needs no separate service, but it is not dependency-free.
Architecture
| Piece | Role |
|---|---|
make_key |
Content hash of a namespace plus a payload (arguments, file content, or bytes). |
Ledger |
Default store: one JSON file per entry. |
SqliteLedger |
One shared SQLite file, atomic reuse counters, TTL, and the claim/submit/release/extend lease. |
RemoteLedger |
A LedgerLike that talks to the server, so Wallet(RemoteLedger(url)) dedups across a fleet. |
Wallet / memoize |
Front door: lookup, single-flight (in-process or via the lease), then store. |
wallet_helper.api |
HTTP server that centralizes dedup for many clients. |
Tests
make install # editable install with dev and all extras
make lint # ruff (PEP 8 and import order)
make test # pytest and doctests
make # lint then test
CI runs the full test suite on a Python 3.10 to 3.13 matrix on Linux (🐧), plus the newest Python on macOS (🍎). On top of that, a separate lightweight install smoke test runs on all three platforms you might install on (🐧 Linux, 🍎 macOS, 🪟 Windows): it installs the package and its extras, then checks that it imports, that both command-line tools respond, and that a memoize round-trip runs the work exactly once. So installation is verified everywhere, while the exhaustive suite runs where it is fast and reliable (the cross-process test leans on spawn-based multiprocessing, which is minutes-slow on Windows runners).
The Promise
wallet-helper is part of a local-first, sovereignty-minded suite, and like os-helper it is a small toolbox rather than a service. Rather than market that, here is the honest, case-by-case reality:
-
Guaranteed local. The default
Ledger(a folder of JSON files) and theSqliteLedger(one file) live under$WALLET_HELPER_DIR, or~/.cache/wallet-helper— on your machine. Nothing is uploaded, there is no telemetry, and there is no account. Your cached results, and the inputs that key them, never leave the disk. -
Not possible to be local — the caveat. wallet-helper exists to avoid running your heavy call; it makes no network requests of its own. The one exception is by design: the optional
[api]dedup server andRemoteLedgerspeak HTTP so a fleet can share one dedup point — and they talk only to the endpoint you point them at. -
Your decision. wallet-helper stores whatever your function returns; if that function calls a paid cloud API, that is your code's choice, never wallet-helper's. Point
RemoteLedgerat your own host and the shared store stays sovereign; point it at a third party and that too is your call — never a default.
Author
License
wallet-helper is licensed under BSD-3-Clause. See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wallet_helper-0.3.1.tar.gz.
File metadata
- Download URL: wallet_helper-0.3.1.tar.gz
- Upload date:
- Size: 48.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a9b1c407335046289338fe15d43e51b15abe470d18a0cd5fa077310a392098a7
|
|
| MD5 |
6058c43a13d3d0dd60aaa51c21ce21b0
|
|
| BLAKE2b-256 |
b175dbf647e88616cf45dc09f43deff738ebae2844c53bca1f99a454b88d5569
|
File details
Details for the file wallet_helper-0.3.1-py3-none-any.whl.
File metadata
- Download URL: wallet_helper-0.3.1-py3-none-any.whl
- Upload date:
- Size: 38.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
503517b0da421737b1bf5baaace28180ae1c1ed7dd35a6980b15bd8f997199c5
|
|
| MD5 |
cba03c1af1472fa562855416be65118e
|
|
| BLAKE2b-256 |
78926a943960d68cb1945e37e66408829b11cc666993ca13087fdca489c778ae
|