Skip to main content

Accounting for LLM token usage

Project description

datasette-llm-accountant

PyPI Changelog Tests License

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:

  1. All accountants are called in sequence to reserve the amount
  2. If any accountant fails, all previous reservations are rolled back
  3. 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 amount
  • ReservationExceededError - Actual cost exceeds reserved amount
  • ModelPricingNotFoundError - 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

datasette_llm_accountant-0.1a1.tar.gz (16.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

datasette_llm_accountant-0.1a1-py3-none-any.whl (13.3 kB view details)

Uploaded Python 3

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

Hashes for datasette_llm_accountant-0.1a1.tar.gz
Algorithm Hash digest
SHA256 123f03ef7d9dfb036ce5eaf512540d8b42ab87d912d6c26648c4b1e172e97256
MD5 7a1d38fa1339c37bf83d9eb430b28b93
BLAKE2b-256 f55424cf13e0965511fac4d0ce3ec3e4ca8fbd477b23afffe5f87057f4cd7411

See more details on using hashes here.

Provenance

The following attestation bundles were made for datasette_llm_accountant-0.1a1.tar.gz:

Publisher: publish.yml on datasette/datasette-llm-accountant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file datasette_llm_accountant-0.1a1-py3-none-any.whl.

File metadata

File hashes

Hashes for datasette_llm_accountant-0.1a1-py3-none-any.whl
Algorithm Hash digest
SHA256 454d1d3a768ac218ccb6b722272c4173a92aa75b83adb2e5638c1dab1989b30b
MD5 2546e037cb4e1d801c82610f99209614
BLAKE2b-256 ef573bafc4e59b0379afad4792e1d36d27de116751ef50282a2373e5086812c3

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page