Skip to main content

Async LLM/embedding token usage tracking with multi-backend support (Redis, Postgres, MongoDB)

Project description

Token Usage Metrics

A lightweight, async-first Python package for tracking LLM and embedding token usage with multi-backend support (Redis, PostgreSQL, MongoDB). Built for production with lifetime retention, project-based deletion, comprehensive aggregations, and non-blocking async operations.

Features

โœจ Multi-Backend Support: Redis, PostgreSQL, and MongoDB backends with unified API ๐Ÿ“Š Rich Aggregations: Daily summaries, project/type grouping, and time-series for dashboards ๐Ÿš€ Async-First: Non-blocking operations with background flushing and circuit breakers ๐Ÿ’พ Lifetime Retention: No enforced TTL; explicit project-based deletion with date ranges ๐Ÿ” Flexible Queries: Raw event fetching with filters and cursor-based pagination ๐Ÿ›ก๏ธ Production-Ready: Structured logging, retry logic, graceful fallbacks, and health checks

Installation

# Install with Redis support (recommended for getting started)
uv add token-usage-metrics[redis]

# Or with all backends
uv add token-usage-metrics[all]

# Individual backends
uv add token-usage-metrics[postgres]  # PostgreSQL
uv add token-usage-metrics[mongo]     # MongoDB

Quick Start

import asyncio
from datetime import datetime, timezone
from token_usage_metrics import TokenUsageClient, UsageEvent, Settings

async def main():
    # Create client with Redis (default)
    settings = Settings(
        backend="redis",
        redis_url="redis://localhost:6379/0"
    )

    async with TokenUsageClient(settings) as client:
        # Log token usage (async, non-blocking)
        event = UsageEvent(
            project_name="my_app",
            request_type="chat",
            input_tokens=100,
            output_tokens=50,
            total_tokens=150,  # Optional: auto-calculated if omitted
            request_count=1,
            metadata={"model": "gpt-4", "user_id": "user_123"}
        )

        await client.log(event)

        # Fetch raw events with filters
        from token_usage_metrics import UsageFilter

        filters = UsageFilter(
            project_name="my_app",
            time_from=datetime.now(timezone.utc) - timedelta(days=7)
        )

        events, cursor = await client.fetch_raw(filters)
        print(f"Found {len(events)} events")

        # Get daily aggregates for graphs
        from token_usage_metrics import AggregateSpec

        spec = AggregateSpec()
        daily_data = await client.summary_by_day(spec, filters)

        for bucket in daily_data:
            print(f"{bucket.start.date()}: {bucket.metrics}")

asyncio.run(main())

Configuration

Configure via environment variables (prefix: TUM_) or Settings object:

from token_usage_metrics import Settings

settings = Settings(
    # Backend selection
    backend="redis",  # redis | postgres | mongodb

    # Redis settings
    redis_url="redis://localhost:6379/0",
    redis_pool_size=10,

    # Postgres settings
    postgres_dsn="postgresql://user:pass@localhost:5432/token_usage",

    # MongoDB settings
    mongodb_url="mongodb://localhost:27017",
    mongodb_database="token_usage",

    # Queue/buffering
    buffer_size=1000,
    flush_interval=1.0,  # seconds
    flush_batch_size=200,

    # Resilience
    max_retries=3,
    circuit_breaker_threshold=5,
)

Or via .env:

TUM_BACKEND=redis
TUM_REDIS_URL=redis://localhost:6379/0
TUM_BUFFER_SIZE=1000
TUM_FLUSH_INTERVAL=1.0

Backend Setup

Redis

# Docker
docker run -d -p 6379:6379 redis:7-alpine

# Or use managed Redis (AWS ElastiCache, Redis Cloud, etc.)

Schema: Hash-per-event + day-partitioned ZSETs + daily aggregate hashes. Optimized for fast writes and efficient date-range queries.

PostgreSQL

# Docker
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=pass -e POSTGRES_DB=token_usage postgres:16-alpine

# Tables auto-created on first connection

Schema: usage_events table + daily_aggregates table with indexes on (project, timestamp), (type, timestamp).

MongoDB

# Docker
docker run -d -p 27017:27017 mongo:7

# Collections auto-created with indexes

Schema: usage_events collection + daily_aggregates collection with compound indexes.

API Reference

Logging Events

# Single event (async, non-blocking)
await client.log(event)

# Multiple events
await client.log_many([event1, event2, event3])

# Force flush pending events
flushed_count = await client.flush(timeout=5.0)

Fetching Raw Events

from token_usage_metrics import UsageFilter

filters = UsageFilter(
    project_name="my_app",
    request_type="chat",
    time_from=datetime(...),
    time_to=datetime(...),
    limit=100,
    cursor=None  # For pagination
)

events, next_cursor = await client.fetch_raw(filters)

# Pagination
if next_cursor:
    more_events, cursor = await client.fetch_raw(
        UsageFilter(cursor=next_cursor, limit=100)
    )

Aggregations & Summaries

from token_usage_metrics import AggregateSpec, AggregateMetric

# Daily time-series (for graphs)
spec = AggregateSpec(
    metrics={
        AggregateMetric.SUM_TOTAL,
        AggregateMetric.COUNT_REQUESTS,
        AggregateMetric.AVG_TOTAL_PER_REQUEST
    }
)

daily_buckets = await client.summary_by_day(spec, filters)

# Grouped by project
project_summaries = await client.summary_by_project(spec, filters)
for summary in project_summaries:
    print(f"{summary.group_keys['project_name']}: {summary.metrics}")

# Grouped by request type
type_summaries = await client.summary_by_request_type(spec, filters)

Deleting Project Data

from token_usage_metrics import DeleteOptions

# Simulate deletion (dry-run)
options = DeleteOptions(
    project_name="my_app",
    time_from=datetime(...),  # Optional
    time_to=datetime(...),    # Optional
    include_aggregates=True,
    simulate=True
)

result = await client.delete_project(options)
print(f"Would delete {result.events_deleted} events and {result.aggregates_deleted} aggregates")

# Actual deletion
options.simulate = False
result = await client.delete_project(options)

Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                     TokenUsageClient                        โ”‚
โ”‚  (Async API: log, fetch_raw, summary_*, delete_project)    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚ AsyncEventQueue โ”‚  (Background flusher, circuit breaker)
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
     โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
     โ”‚   Backend Interface   โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                 โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚            โ”‚            โ”‚
โ”Œโ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Redis  โ”‚  โ”‚Postgres โ”‚  โ”‚ MongoDB โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  • Async Queue: Buffers events in memory, flushes batches periodically
  • Circuit Breaker: Auto-recovery when backend is unhealthy
  • Retry Logic: Exponential backoff with jitter for transient errors
  • Lifetime Retention: No enforced TTL (configurable per-backend if needed)

Testing

# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=token_usage_metrics

# Run specific test
uv run pytest tests/test_redis_backend.py -v

Development

# Install dev dependencies
uv add --dev ruff mypy pytest pytest-asyncio fakeredis

# Lint and format
uv run ruff check .
uv run ruff format .

# Type checking
uv run mypy token_usage_metrics

Performance

  • Redis: ~10k writes/sec (pipelined batches), ~5k reads/sec (optimized ZSETs)
  • Postgres: ~2k writes/sec (bulk inserts), ~10k reads/sec (indexed queries)
  • MongoDB: ~5k writes/sec (batched inserts), ~8k reads/sec (indexed scans)

Benchmarks on single-instance deployments. Scale horizontally for higher throughput.

License

MIT

Contributing

Contributions welcome! Please open an issue or PR on GitHub.


Built with โค๏ธ for production LLM applications.

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

token_usage_metrics-0.1.0.tar.gz (87.1 kB view details)

Uploaded Source

Built Distribution

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

token_usage_metrics-0.1.0-py3-none-any.whl (54.6 kB view details)

Uploaded Python 3

File details

Details for the file token_usage_metrics-0.1.0.tar.gz.

File metadata

  • Download URL: token_usage_metrics-0.1.0.tar.gz
  • Upload date:
  • Size: 87.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for token_usage_metrics-0.1.0.tar.gz
Algorithm Hash digest
SHA256 da120a207cb33a27736f0622ae057d12c653c9e7a7e65ffa44b853a614594f34
MD5 b86b8ad90a27f5bc16cb04b48332d727
BLAKE2b-256 f6afc411c9632a93f2fe4c9803f209c858748cd78dd63e12095ca3133f961b30

See more details on using hashes here.

Provenance

The following attestation bundles were made for token_usage_metrics-0.1.0.tar.gz:

Publisher: python-publish.yml on lazzyms/token_usage_metrics

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

File details

Details for the file token_usage_metrics-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for token_usage_metrics-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a270f68238bc3f001e1e136b087b05e520603fa283aada7e0fb8264e428e1f9a
MD5 df48ca170140031bf3193b32dbb56f92
BLAKE2b-256 2d628da9a8121c56c2c8693a3f962fcfbbf2e4e227099b3df3994c5f88cf8620

See more details on using hashes here.

Provenance

The following attestation bundles were made for token_usage_metrics-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on lazzyms/token_usage_metrics

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