Python async wrapper — live attribution without a proxy
A drop-in wrapper around the OpenAI and Anthropic Python SDKs that reports usage to Cognocient asynchronously. Zero added latency, but no pre-call enforcement — the honest tradeoff explained.
A drop-in wrapper import for the OpenAI and Anthropic Python SDKs. Swap
one import, add a key, and get live cost attribution — without changing
your base_url or routing traffic through a proxy.
Every method the real SDK exposes still works unchanged. This wrapper
only intercepts chat.completions.create() (messages.create() for
Anthropic) to report usage after the fact.
Security — read this before you decide
This is not more secure than the proxy
It's a different tradeoff, not a strictly better one — we're not going to tell you otherwise.
With the proxy, your real provider API key lives server-side, under Cognocient's control, in one place. With this wrapper, your key stays in your own application process and calls the provider directly — exactly as it does today without Cognocient at all. Some security teams prefer that (no third-party network hop in the request path); others are less comfortable with third-party code executing inside their process with key access. Both are reasonable positions.
What this wrapper honestly gives you over the proxy:
- Zero added request latency, in the practical sense — see the measured benchmark below. Reporting happens after your real call already returned, on a background thread, off the critical path.
- Zero risk of a Cognocient outage affecting your production call. If Cognocient's ingestion API is down, your call to OpenAI or Anthropic still completes normally.
What you give up versus the proxy: pre-call enforcement. Because Cognocient only hears about a call after it already happened, budgets configured in Cognocient cannot block or degrade a call made through this wrapper before it fires. Your dashboard will say so explicitly for any account using this path — see Budget Enforcement.
Measured latency overhead
200 warm-up calls excluded, 2,000 timed iterations per leg, both legs hitting the same in-process mock transport (no real network call to OpenAI or to Cognocient — see methodology below).
| p50 | p95 | p99 | mean | |
|---|---|---|---|---|
| Raw SDK call | 0.7675ms | 0.8537ms | 1.1672ms | 0.7807ms |
| Wrapped call | 0.7827ms | 0.8701ms | 1.1897ms | 0.7961ms |
| Added overhead | +0.0152ms | — | — | +0.0153ms |
What this number does and doesn't include
This measures only the wrapper's own interception code on your request path — timing the call, stripping cognocient_* kwargs, and queuing a report. It does not include real network latency to OpenAI/Anthropic or to Cognocient's ingestion API, because reporting runs on a background thread your request never waits on — that's the architectural claim this benchmark exists to check, not assume. Reproduce it yourself: cognocient-python-wrapper/benchmark/benchmark_wrapper_overhead.py, no real API keys required.
Reliability
Reporting is fire-and-forget on a background thread with a bounded local queue, flushed every few seconds or every 50 calls, whichever comes first. If the ingestion API is slow, down, or unreachable:
- Your real provider call is completely unaffected — it already happened before reporting was attempted.
- No exception is ever raised into your code from a reporting failure.
- No retry loop that could pile up work in your process — a failed batch is dropped and logged locally, not retried.
This is verified by an actual test that simulates an unreachable
ingestion endpoint and asserts the real call still completes normally —
cognocient-python-wrapper/tests/test_reporter_failure_isolation.py,
not just claimed in this paragraph.
Known limitation: streaming isn't reported yet
stream=True calls are passed through to the real SDK completely
unmodified — your application behaves identically — but are not
currently reported to Cognocient. Usage totals aren't available until a
stream completes, and reliably capturing them requires wrapping the
stream iterator itself, which this version doesn't do. If most of your
traffic streams, use the proxy or the
CSV/OTel importer instead for now.
Attribution fields
Same field names the proxy accepts as X-Cost-* headers, passed as
keyword arguments instead — see Attribution Headers.
| Wrapper kwarg | Proxy header |
|---|---|
cognocient_feature | X-Cost-Feature |
cognocient_department | X-Cost-Department |
cognocient_user | X-Cost-User |
cognocient_session | X-Cost-Session |
cognocient_tier | X-Cost-Tier |
cognocient_project | X-Cost-Project |
cognocient_gl_account | X-Cost-GL-Account |
cognocient_workload | X-Cost-Workload |
cognocient_outcome | X-Cost-Outcome |
cognocient_run_id | X-Cost-Run-ID |
Frequently asked questions
Related articles