Skip to main content

Lightweight LLM cost tracking for OpenAI, Anthropic, and Google AI

Project description

Tokpulse - Python SDK

Lightweight, non-blocking LLM cost tracking for OpenAI, Anthropic, and Google AI.

Installation

pip install tokpulse

# With provider-specific extras
pip install tokpulse[openai]
pip install tokpulse[anthropic]
pip install tokpulse[google]
pip install tokpulse[all]

Quick Start

from tokpulse import TokpulseTracker
from openai import OpenAI

# Initialize tracker
tracker = TokpulseTracker(
    api_key="lct_your_api_key",
    project="my-app",
    environment="production",
)

# Wrap your OpenAI client
client = tracker.openai(OpenAI())

# Use as normal - tracking is automatic
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello!"}]
)

# Streaming works too
stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Tell me a story"}],
    stream=True
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")

# Flush before exit (important for serverless)
tracker.flush()

Provider Support

OpenAI

from openai import OpenAI

client = tracker.openai(OpenAI())
response = client.chat.completions.create(...)

Anthropic

from anthropic import Anthropic

client = tracker.anthropic(Anthropic())
message = client.messages.create(
    model="claude-sonnet-4-5-20250514",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}]
)

Google AI

import google.generativeai as genai

genai.configure(api_key="...")
model = tracker.google(genai.GenerativeModel("gemini-2.5-flash"))
response = model.generate_content("Hello!")

Configuration

tracker = TokpulseTracker(
    api_key="lct_xxx",             # Required
    api_url="https://...",         # API endpoint (default: api.tokpulse.io)
    allow_insecure_http=False,     # Set True only for http://localhost in local dev
    batch_size=10,                 # Trigger an early flush when this many events are queued
    flush_interval_seconds=5,      # Auto-flush interval (background thread)
    max_queue_size=1000,           # Max queued events; excess events are silently dropped
    project="my-app",              # Tag all events
    environment="production",      # Tag with environment
    debug=False,                   # Enable logging
    on_error=my_handler,           # Error callback: fn(error: Exception, event: UsageEvent)
    timeout_seconds=5,             # Request timeout
    max_retries=3,                 # Retry attempts
)

api_url must use https:// by default. Plain HTTP is only allowed for localhost (http://localhost, http://127.0.0.1, http://[::1]) when allow_insecure_http=True is explicitly set.

Context Manager

with TokpulseTracker(api_key="...") as tracker:
    client = tracker.openai(OpenAI())
    response = client.chat.completions.create(...)
# Auto-flush on exit

Manual Tracking

tracker.track(
    provider="openai",
    model="gpt-4o",
    input_tokens=100,
    output_tokens=50,
    cached_input_tokens=20,
)

track() validates inputs and raises ValueError when provider/model are empty or token counts are negative/non-integer.

Flush And Close

# Drain currently queued events
tracker.flush()

# Close: stop background workers, flush queue, and attempt pending retries once
tracker.close()

Retry backoff is scheduled in a background retry worker (non-blocking for the flush worker).

Serverless / Lambda

Always flush before the function exits:

def handler(event, context):
    response = client.chat.completions.create(...)
    tracker.flush()  # Important!
    return {"statusCode": 200, "body": response.choices[0].message.content}

Streaming Notes

Streaming usage is tracked when streams are fully consumed and also when iteration stops early (for example, break).

Async Clients

Full async wrappers are provided for OpenAI and Anthropic. Use tracker.openai_async() and tracker.anthropic_async() to get an async-native wrapper, then call await tracker.aflush() to drain the queue without blocking the event loop.

import asyncio
from openai import AsyncOpenAI
from anthropic import AsyncAnthropic
from tokpulse import TokpulseTracker

tracker = TokpulseTracker(api_key="lct_your_api_key")

# Async OpenAI
async def run_openai():
    client = tracker.openai_async(AsyncOpenAI())
    response = await client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello!"}],
    )
    await tracker.aflush()

# Async Anthropic
async def run_anthropic():
    client = tracker.anthropic_async(AsyncAnthropic())
    message = await client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello!"}],
    )
    await tracker.aflush()

# Close without blocking the event loop
async def shutdown():
    await tracker.aclose()

Streaming works the same way — iterate with async for and usage is captured in the generator's finally block.

License

MIT

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

tokpulse-0.1.0.tar.gz (26.8 kB view details)

Uploaded Source

Built Distribution

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

tokpulse-0.1.0-py3-none-any.whl (15.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tokpulse-0.1.0.tar.gz
  • Upload date:
  • Size: 26.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for tokpulse-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5704192f3edccb33f5b3d795081b457c4f86be092f32c2c0549942fe45cbdfb9
MD5 f9f1f6d678f06c6245b5df4f70c0c3d7
BLAKE2b-256 a3f8571cc8178fec0500ff2c15ee23fe615b9cec6e6f95910dff3c283efc592b

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tokpulse-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for tokpulse-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dd580ae32ce95f3388138f6e251b6277b09c2d10409fa4df8769fa8da46a40cf
MD5 8a65bf79f6964518ed4073f21c27d4a6
BLAKE2b-256 aeccfa8dfbe71167728932cdb4458404ee7ec0290c56cec5465f549efe07c206

See more details on using hashes here.

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