Skip to main content

Metadata-only collector for measuring the energy, carbon, water and land of AI inference.

Project description

tetrameter (Python)

Metadata-only collector for measuring the energy, carbon, water and land of AI inference. Sibling of @kumokodo/tetrameter-sdk; both produce identical rows, so a company running Python and TypeScript sees one shape of data.

pip install tetrameter
import tetrameter

tetrameter.configure()   # reads TETRAMETER_ENDPOINT and TETRAMETER_KEY

with tetrameter.trace(outcome="debate judged", customer=org_id):
    response = client.messages.create(...)
    tetrameter.record_anthropic_message(response, feature="reviewer")
    tetrameter.flush()

What it will not do

Carry prompt or completion text. sanitize keeps a fixed allow-list of fields and drops everything else, so record(**response.__dict__) cannot leak a completion even by accident. A deny-list would need updating every time a provider added a field, and the first time somebody forgot, prompt text would be stored.

Raise. record() swallows its own failures. Instrumentation that can break the application it observes gets removed rather than fixed.

Run without credentials. configure() returns None when TETRAMETER_ENDPOINT and TETRAMETER_KEY are not both set, and the collector stays inert. A developer machine records nothing instead of writing production-shaped rows into a production organisation.

Block your request path. All I/O happens on a background thread. A full batch is handed over and record() returns immediately -- it used to send inline, which stalled an asyncio event loop for the length of an HTTP round trip every hundredth call. flush() is still synchronous, deliberately: it is what you call at a trace boundary, and on a serverless platform the process can be frozen the moment your handler returns, so a flush that only queued would lose exactly the records you asked to send. From async code, call it off the loop:

await asyncio.to_thread(tetrameter.flush)

Retry a failed batch. A retry queue inside a telemetry client is a memory leak waiting for an outage. Ingest is idempotent on (org, id) so a retry made elsewhere — a proxy, a load balancer — is harmless instead.

Traces

A trace is one thing your business asked for. Wrap the outer function and every call beneath it joins, with no id threaded through call sites.

Exactly one trace per delivered outcome: a nested one opens a second trace id and splits one piece of work into two, understating the cost of both.

For runtimes that re-enter the same logical trace in separate invocations — Inngest steps, for instance — pass trace_id explicitly. A fresh random id per step turns eight steps into eight traces each claiming a whole outcome, which multiplies the outcome count and divides the per-outcome footprint. Both flattering.

Adapters

Function For
record_anthropic_message anthropic.messages.create
record_openai_completion OpenAI chat completions
record_embedding Any embedding call
record_failure A call that raised

Each reads usage counters and nothing else.

A counter reported as 0 is recorded as 0; a counter the provider never mentions stays absent. Those are different facts and conflating them hides under-measurement -- the calls explicitly reporting zero cache reads are the denominator of any hit-rate figure, so discarding them makes hit rate read 100%.

Any usage counter an adapter does not read is logged once per provider per process, at INFO, names only. "This provider does not report X" and "we are reading the wrong key for X" are otherwise the same observation, and a library that silently reads a fixed list of names can be wrong forever and look complete doing it.

Two are worth explaining, because the TypeScript SDK shipped both bugs and this package pins them from the start:

  • Anthropic's three token buckets stay apart. input_tokens is ordinary, cache_read_input_tokens is the cheap one, and cache_creation_input_tokens is the expensive one — 1.25× input on the five-minute TTL, 2× on the one-hour. Folding writes into input under-prices the write turn while the reads after it stay exact, so any measured caching saving reads high.
  • Embeddings report usage under a different name, and reading only one of them records every embedding as 0/0 with no error — indistinguishable from a call that genuinely cost nothing.

Licence

Apache-2.0. The methodology is meant to be checked, so the code that produces the numbers is readable.

Releasing

Tagged, like the npm packages, and published by GitHub Actions through PyPI Trusted Publishing — no API token exists anywhere.

git tag python-v0.1.0 && git push --tags

The workflow refuses to publish a tag whose version does not match pyproject.toml, and verifies the built wheel actually contains the package and its licence before uploading. Both checks are cheap, and a version on PyPI cannot be replaced or yanked into non-existence afterwards — unlike npm there is not even a 72-hour window.

One-time setup on PyPI, under the project's Publishing settings. Because tetrameter has never been uploaded, this is added as a pending publisher, which is how a project is bootstrapped without a token:

Field Value
PyPI project name tetrameter
Owner samwsimpson
Repository name tetrameter-core
Workflow name publish-python.yml
Environment name pypi-publish

Then create a GitHub environment called pypi-publish and restrict its deployment branches and tags to python-v*, so the workflow can only run from a release tag.

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

tetrameter-0.1.2.tar.gz (23.3 kB view details)

Uploaded Source

Built Distribution

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

tetrameter-0.1.2-py3-none-any.whl (21.8 kB view details)

Uploaded Python 3

File details

Details for the file tetrameter-0.1.2.tar.gz.

File metadata

  • Download URL: tetrameter-0.1.2.tar.gz
  • Upload date:
  • Size: 23.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tetrameter-0.1.2.tar.gz
Algorithm Hash digest
SHA256 121039fc2a7edb472368234c7fae65414fc069beea691785f65ea8623f638d2a
MD5 98ad5b7d3766a4b5c34a2453475ba962
BLAKE2b-256 c161ed5435cd58303416aec766378e3b69c1c7048de4dfe8dd7dd28037788aad

See more details on using hashes here.

Provenance

The following attestation bundles were made for tetrameter-0.1.2.tar.gz:

Publisher: publish-python.yml on samwsimpson/tetrameter-core

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

File details

Details for the file tetrameter-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: tetrameter-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 21.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tetrameter-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 8a469a8e54c23c4da0ebd4eabf2266f3ef35e9af27ba0297b7a6be3e9140fdd4
MD5 398907f85f15074dc11bdc2d9b400fc2
BLAKE2b-256 0299ed131809d14b9f1c8cf1564be473145ae6329084ef55335cb346b4023117

See more details on using hashes here.

Provenance

The following attestation bundles were made for tetrameter-0.1.2-py3-none-any.whl:

Publisher: publish-python.yml on samwsimpson/tetrameter-core

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