Getting Started

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.

Source is open — cognocient-python-wrapper on GitHub, published as cognocient on PyPI.

Supported providers: OpenAI and Anthropic only

This wrapper covers two of the seven providers Cognocient supports. If your API key is for Google Gemini, Mistral, Groq, Together AI, or Azure OpenAI, this package has no wrapper class for you — see below for what to do instead.

pip install cognocient[openai]
# Before
from openai import OpenAI
client = OpenAI(api_key="sk-...")
 
# After
from cognocient import CognocientOpenAI as OpenAI
client = OpenAI(
    api_key="sk-...",              # your own real OpenAI key, unchanged
    cognocient_key="sk-cog-...",   # auths the async usage report only — no traffic routes through Cognocient
)
 
client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "hello"}],
    cognocient_feature="support-bot",  # optional attribution
)

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.

Which providers does this wrapper support?

Only two: OpenAI (CognocientOpenAI) and Anthropic (CognocientAnthropic). That's it — there is no CognocientGemini, CognocientMistral, CognocientGroq, CognocientTogether, or CognocientAzureOpenAI class in this package today.

This matters because the proxy supports seven providers — OpenAI, Anthropic, Google Gemini, Mistral, Groq, Together AI, and Azure OpenAI — through one base_url. The wrapper's coverage is narrower because it works by wrapping each provider's Python SDK object directly, and that has to be built one SDK at a time.

I have a Gemini, Mistral, Groq, Together AI, or Azure OpenAI key — what do I do?

Two real options, no third option that "sort of" works:

  1. Use the proxy instead. It's provider-agnostic — it works at the HTTP layer, not the SDK layer — so all seven providers get live attribution and pre-call budget enforcement, which this wrapper never provides for any provider anyway. This is the only path that covers your provider today.
  2. Use the CSV/OTel importer if you don't want any live network call to Cognocient from your runtime at all — works for any provider, since it reads logs you already have, but is historical-only (no live dashboard, no budgets).

Don't try to force it through CognocientOpenAI

Groq and Together AI expose OpenAI-compatible endpoints, so passing their base_url into CognocientOpenAI will technically run without an error. Don't do this: the wrapper hardcodes the reported provider field to "openai", so your dashboard would mislabel every one of those calls as OpenAI spend. This isn't a supported path — it just fails silently instead of loudly, which is worse.

What does cognocient_key actually do?

This is a fair question, especially if you picked the wrapper because you don't want to connect to the proxy — doesn't passing a Cognocient key mean you're connecting to Cognocient after all?

In plain terms: no. cognocient_key is only a credential for reporting, not a connection for your traffic. Here's the full path a call takes:

  1. You call client.chat.completions.create(...).
  2. The wrapper forwards that call straight to OpenAI or Anthropic, using your own real api_key — same URL, same request, as if Cognocient didn't exist.
  3. Once the real response comes back, the wrapper starts a separate background task: it packages up metadata (model, tokens, cost, latency, your attribution tags) and sends it to Cognocient's ingestion endpoint, authenticated with cognocient_key.
  4. That reporting call is fire-and-forget. If it's slow, fails, or never happens, your real API call already completed and returned — nothing about step 1 or 2 depends on it.

So cognocient_key never touches your prompts, your responses, or your request path. It only tells Cognocient's ingestion API "this usage report belongs to this account." It's the same kind of key you'd use with the proxy, reused here for a completely different job — proving whose report this is, not routing anything.

If you want zero dependency on Cognocient at all

Even the reporting call is optional in the sense that it never blocks or affects your app — but if you'd rather not have your process talk to Cognocient's servers under any circumstances, the CSV/OTel importer is a fully offline alternative: no live network call to Cognocient from your runtime, ever — you export logs and upload them on your own schedule.

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).

p50p95p99mean
Raw SDK call0.7675ms0.8537ms1.1672ms0.7807ms
Wrapped call0.7827ms0.8701ms1.1897ms0.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 kwargProxy header
cognocient_featureX-Cost-Feature
cognocient_departmentX-Cost-Department
cognocient_userX-Cost-User
cognocient_sessionX-Cost-Session
cognocient_tierX-Cost-Tier
cognocient_projectX-Cost-Project
cognocient_gl_accountX-Cost-GL-Account
cognocient_workloadX-Cost-Workload
cognocient_outcomeX-Cost-Outcome
cognocient_run_idX-Cost-Run-ID

Frequently asked questions