Accounting for LLM token usage
Project description
datasette-llm-accountant
Accounting for LLM token usage
Installation
Install this plugin in the same environment as Datasette.
datasette install datasette-llm-accountant
Overview
This plugin provides a library for other Datasette plugins to interact with LLMs via a layer that does accounting and cost tracking. It wraps the llm library with automatic cost calculation and spending limits.
Usage
Basic Usage
from datasette_llm_accountant import LlmWrapper
# In your Datasette plugin
llm = LlmWrapper(datasette)
# Get an async model wrapped with accounting
model = llm.get_async_model("gpt-4o-mini")
# Use with automatic reservation (defaults to 50 cents)
response = await model.prompt("Write a poem about a pirate")
Manual Reservations
You can manually reserve an amount and track spending across multiple prompts:
# Reserve $4.50 for multiple prompts
async with model.reserve(usd=4.50) as tx:
response1 = await tx.prompt("First question")
response2 = await tx.prompt("Follow-up question")
# Total spending is tracked cumulatively
You can also specify reservations in nanocents (1/1,000,000,000 of a cent):
async with model.reserve(nanocents=50_000_000_000) as tx: # 50 cents
response = await tx.prompt("a poem about a pirate")
Cost Protection
If a prompt exceeds your reservation, a ReservationExceededError is raised:
from datasette_llm_accountant import ReservationExceededError
try:
async with model.reserve(usd=0.01) as tx: # Very small reservation
# This might exceed the reservation
response = await tx.prompt("Write a very long story...")
except ReservationExceededError as e:
print(f"Exceeded budget: {e}")
Creating Accountant Plugins
Other plugins can implement accountants to track and limit LLM spending. Create a plugin that implements the register_llm_accountants hook:
from datasette import hookimpl
from datasette_llm_accountant import Accountant, Tx, InsufficientBalanceError
class DatabaseAccountant(Accountant):
"""Tracks spending in a database."""
async def reserve(self, nanocents: int) -> Tx:
# Check if user has sufficient balance
# If not, raise InsufficientBalanceError
# Otherwise, create and return a transaction ID
tx_id = await self.create_reservation(nanocents)
return Tx(tx_id)
async def settle(self, tx: Tx, nanocents: int):
# Record the actual amount spent for this transaction
await self.record_settlement(tx, nanocents)
async def rollback(self, tx: Tx):
# Optional: Release/cancel the reservation
# Default implementation settles for 0
await self.cancel_reservation(tx)
@hookimpl
def register_llm_accountants(datasette):
return [DatabaseAccountant(datasette)]
Multiple Accountants
The system supports multiple accountants. When a reservation is made:
- All accountants are called in sequence to reserve the amount
- If any accountant fails, all previous reservations are rolled back
- When the transaction completes, all accountants are settled with the actual cost
This allows for multiple layers of accounting (e.g., per-user limits, per-project limits, global limits).
Cost Calculation
Costs are automatically calculated based on token usage and current pricing data from llm-prices.com. The library:
- Tracks input and output tokens separately
- Handles cached input tokens when available
- Converts costs to nanocents for precise accounting
- Works with 80+ models across multiple providers
API Reference
LlmWrapper
wrapper = LlmWrapper(datasette)
model = wrapper.get_async_model("model-id")
AccountedModel
# Direct prompt with auto-reservation
response = await model.prompt("text", usd=0.5)
# Manual reservation
async with model.reserve(usd=1.0) as tx:
response = await tx.prompt("text")
Accountant Base Class
class Accountant(ABC):
async def reserve(self, nanocents: int) -> Tx: ...
async def settle(self, tx: Tx, nanocents: int): ...
async def rollback(self, tx: Tx): ... # Optional override
Exceptions
InsufficientBalanceError- Accountant cannot reserve the requested amountReservationExceededError- Actual cost exceeds reserved amountModelPricingNotFoundError- Pricing data not available for model
Development
To set up this plugin locally, first checkout the code. Then create a new virtual environment:
cd datasette-llm-accountant
python -m venv venv
source venv/bin/activate
Now install the dependencies and test dependencies:
pip install -e '.[test]'
To run the tests:
python -m pytest
Project details
Release history Release notifications | RSS feed
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 datasette_llm_accountant-0.1a1.tar.gz.
File metadata
- Download URL: datasette_llm_accountant-0.1a1.tar.gz
- Upload date:
- Size: 16.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
123f03ef7d9dfb036ce5eaf512540d8b42ab87d912d6c26648c4b1e172e97256
|
|
| MD5 |
7a1d38fa1339c37bf83d9eb430b28b93
|
|
| BLAKE2b-256 |
f55424cf13e0965511fac4d0ce3ec3e4ca8fbd477b23afffe5f87057f4cd7411
|
Provenance
The following attestation bundles were made for datasette_llm_accountant-0.1a1.tar.gz:
Publisher:
publish.yml on datasette/datasette-llm-accountant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
datasette_llm_accountant-0.1a1.tar.gz -
Subject digest:
123f03ef7d9dfb036ce5eaf512540d8b42ab87d912d6c26648c4b1e172e97256 - Sigstore transparency entry: 725583439
- Sigstore integration time:
-
Permalink:
datasette/datasette-llm-accountant@aa77e8fd33d5152ed4cfc6648e6579e4aa9afa59 -
Branch / Tag:
refs/tags/0.1a1 - Owner: https://github.com/datasette
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@aa77e8fd33d5152ed4cfc6648e6579e4aa9afa59 -
Trigger Event:
release
-
Statement type:
File details
Details for the file datasette_llm_accountant-0.1a1-py3-none-any.whl.
File metadata
- Download URL: datasette_llm_accountant-0.1a1-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
454d1d3a768ac218ccb6b722272c4173a92aa75b83adb2e5638c1dab1989b30b
|
|
| MD5 |
2546e037cb4e1d801c82610f99209614
|
|
| BLAKE2b-256 |
ef573bafc4e59b0379afad4792e1d36d27de116751ef50282a2373e5086812c3
|
Provenance
The following attestation bundles were made for datasette_llm_accountant-0.1a1-py3-none-any.whl:
Publisher:
publish.yml on datasette/datasette-llm-accountant
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
datasette_llm_accountant-0.1a1-py3-none-any.whl -
Subject digest:
454d1d3a768ac218ccb6b722272c4173a92aa75b83adb2e5638c1dab1989b30b - Sigstore transparency entry: 725583442
- Sigstore integration time:
-
Permalink:
datasette/datasette-llm-accountant@aa77e8fd33d5152ed4cfc6648e6579e4aa9afa59 -
Branch / Tag:
refs/tags/0.1a1 - Owner: https://github.com/datasette
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@aa77e8fd33d5152ed4cfc6648e6579e4aa9afa59 -
Trigger Event:
release
-
Statement type: