qvunex
Measure what your AI inference actually costs.
Most teams running AI in production know exactly one number: the monthly cloud bill. They can't tell you cost per endpoint, per feature, or per prediction. When the bill doubles, they can't say why.
This tells you.
pip install git+https://github.com/qaisermehdi3-coder/qvunex
from qvunex import meter
@meter("checkout-classifier")
def predict(batch):
return model(batch)
qvunex report
qvunex checklist
That's the whole integration.
What you get
====================================================================
QVUNEX METER REPORT
====================================================================
window 3600.0 s
calls 41,209
inferences 329,672
gpu rate $0.7500/hr x 1 device(s)
window cost $0.75
CPKI $0.0023 per 1,000 inferences
--------------------------------------------------------------------
COST BY ENDPOINT
--------------------------------------------------------------------
endpoint inf share cost CPKI
image-classifier 251,104 78.1% $0.59 $0.0023
text-embedder 62,336 18.6% $0.14 $0.0022
thumbnail-scorer 16,232 3.3% $0.02 $0.0015
--------------------------------------------------------------------
WASTE
--------------------------------------------------------------------
mean GPU utilisation 31.4%
time below 5% util 38.2%
cost of that idle time $0.29 <-- paid for, not used
duty cycle 44.1% time actually inside inference
batch efficiency 19.0% mean batch vs max seen (32)
Plus latency percentiles per endpoint, memory headroom, and a short list of things worth looking at — fired only on defensible thresholds, because a meter that cries wolf stops being read.
The checklist
Two cost figures are only comparable if they were produced under the same conditions,
and those conditions almost never get published. qvunex checklist fills in what the
meter observed and prints everything else as MISSING, loudly:
04 WORKLOAD
------------------------------------------------------------------
4.1 batch_size / concurrency [observed]
batch 1-32, mean 6.2 (6 distinct sizes)
4.2 input_tokens / output_tokens
>> MISSING - qvunex cannot observe this. State it yourself.
Tell it what it cannot see, and those fields fill in too:
qvunex.configure(rate_usd_hour=0.35, context={
"engine": "vLLM 0.27.1",
"cuda_graphs": True,
"model_id": "Qwen/Qwen2.5-1.5B-Instruct",
"dtype": "fp16 weights, fp16 compute",
})
Why this is in the tool rather than in a document: I published a benchmark showing 4-bit quantization was 24.8% more expensive than fp16, then re-ran it with CUDA graphs enabled and measured 48.3% cheaper. One unreported flag, 73 percentage points, same card, same afternoon. A checklist nobody fills in is not a standard.
Full field list and the evidence behind each one: https://gist.github.com/qaisermehdi3-coder/b00f296641681695daf90e5a500d0d23
Design choices, and why
Local only. Every measurement is appended to a JSONL file on your own disk. This package contains no network code — grep it. That isn't a limitation, it's the point: a read-only tool that never phones home gets adopted in an afternoon instead of surviving a six-month security review.
No dependencies. Pure standard library. GPU stats come from shelling out to
nvidia-smi, which exists wherever an NVIDIA GPU does. Nothing for anyone's
platform team to approve.
Never breaks the caller. Any failure inside the meter is swallowed. Your
function's exceptions propagate untouched, and a call that raises is still
recorded with ok=False. A measurement tool that can take down production is a
tool nobody installs twice.
Cheap. The hot path is a perf_counter pair, a dict, and an append to a
buffered list. No I/O per call.
Honest. Costs are attributed, not measured: we know total GPU-hours and each endpoint's share of busy time, and we divide. Under concurrency, summed call duration exceeds wall time — so duty cycle is capped and the overlap is flagged in the output rather than quietly normalised. Without a GPU price, cost is reported as unavailable, never estimated from a default.
Configuration
Set your GPU price to get costs. Everything else has a working default.
import qvunex
qvunex.configure(
path="~/.qvunex/events.jsonl", # where the corpus lives
rate_usd_hour=0.75, # what you pay per GPU-hour
sample_interval=1.0, # device polling, seconds
)
Or by environment, which is usually easier in a container:
QVUNEX_PATH=/data/qvunex.jsonl
QVUNEX_RATE_USD_HOUR=0.75
QVUNEX_DISABLED=1 # hard off, decorator becomes a passthrough
For code that isn't shaped like a function:
with meter.span("batch-job", batch=len(items)):
process(items)
Batch size is inferred from the first argument (.shape[0], then len(),
then 1). Override it when that guess is wrong:
@meter("ranker", batch=lambda args, kwargs: len(kwargs["docs"]))
def rank(*, query, docs): ...
CLI
qvunex report # default corpus
qvunex report /data/events.jsonl # a specific file
qvunex report --rate 0.75 # override the recorded price
qvunex report --json # raw analysis for your own tooling
qvunex checklist # comparability fields, gaps marked MISSING
qvunex demo # synthetic workload + report, no setup
The corpus
Events are JSONL, one object per line, schema-versioned in schema.py. Three
record types: session, call, gpu.
Keep these files. The corpus is the asset — cross-workload measurement data can't be collected retroactively, and no cloud vendor can assemble it, because no cloud vendor is neutral enough to be allowed to measure its competitors.
Status
v0.2. Measures what it claims to measure and nothing more. Precision headroom and input-triviality analysis are deliberately not here yet — those change your model, and this release only observes.
qvunex checklist exits non-zero when fields are missing, so you can wire it into
CI and fail a build that would publish an uncomparable number.
Apache-2.0.
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 qvunex-0.2.1.tar.gz.
File metadata
- Download URL: qvunex-0.2.1.tar.gz
- Upload date:
- Size: 26.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9abd91ea2a76d02ad73b8b67e7a1c339237fc7f90b844a5cbedf2bc2cd9f020b
|
|
| MD5 |
b3cef7a97bb312e51dc72c58227a1f20
|
|
| BLAKE2b-256 |
7f17058bc5e279fac5a91fa40c7698920e040adb3144ff5f4d009370cd9dd03f
|
File details
Details for the file qvunex-0.2.1-py3-none-any.whl.
File metadata
- Download URL: qvunex-0.2.1-py3-none-any.whl
- Upload date:
- Size: 24.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f12a70f1263b9de3616d0d5f8210d77eec5b0cfd3edf23a4e821403508c58686
|
|
| MD5 |
c2a600038e37d0699dc3686f02f11cd0
|
|
| BLAKE2b-256 |
151d8a6996d6a17488c63bc79e2bd6d16101292532a67b13b46c8d71fcff228c
|