Metadata-Version: 2.4
Name: cognocient
Version: 0.1.3
Summary: Drop-in async-reporting wrapper for the OpenAI and Anthropic Python SDKs — live cost attribution without changing your base_url.
Project-URL: Homepage, https://github.com/mandarvshinde/cognocient-python-wrapper
Project-URL: Repository, https://github.com/mandarvshinde/cognocient-python-wrapper
Project-URL: Documentation, https://cognocient.com/docs/python-wrapper
Project-URL: Issues, https://github.com/mandarvshinde/cognocient-python-wrapper/issues
License: MIT
License-File: LICENSE
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.25; extra == 'anthropic'
Provides-Extra: dev
Requires-Dist: anthropic>=0.25; extra == 'dev'
Requires-Dist: openai>=1.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == 'openai'
Description-Content-Type: text/markdown

# cognocient

A drop-in wrapper around the OpenAI and Anthropic Python SDKs that reports
usage to Cognocient asynchronously, so you get live cost attribution
without changing your `base_url` or routing traffic through a proxy.

**Supported providers: OpenAI and Anthropic only** (`CognocientOpenAI` and
`CognocientAnthropic`). Have a Gemini, Mistral, Groq, Together AI, or Azure
OpenAI key instead? See [Which providers does this cover?](#which-providers-does-this-cover)
below before you install — this package doesn't have a wrapper class for
you yet.

```bash
pip install cognocient[openai]      # or cognocient[anthropic], or both
```

```python
# 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, used exactly as before
    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 — same field names the proxy accepts as X-Cost-* headers
)
```

Every method the real SDK exposes still works unchanged. This wrapper only
intercepts `chat.completions.create()` (`messages.create()` for Anthropic)
to time the call and report its usage after the fact; everything else is
forwarded to the real client untouched.

### What is `cognocient_key`, exactly?

It's just an authentication credential — the same kind of API key you'd
generate for the proxy — but here it does one thing only: it lets the
wrapper's background reporting call prove to Cognocient's ingestion API
which account a usage report belongs to. It has nothing to do with your
actual OpenAI/Anthropic request. That request still goes straight to
`api.openai.com` / `api.anthropic.com` with your own real provider key,
exactly like it would with no Cognocient wrapper installed at all — no
`base_url` change, no network hop, no dependency on Cognocient being up.
The wrapper just fires a second, separate, best-effort call afterward to
log what happened, off the critical path, on a background thread. Losing
that reporting call (Cognocient down, key wrong, network blip) never
affects your real API call — see "Reliability" below.

### Which providers does this cover?

Only OpenAI and Anthropic. There is no `CognocientGemini`,
`CognocientMistral`, `CognocientGroq`, `CognocientTogether`, or
`CognocientAzureOpenAI` class in this package today — the [Cognocient
proxy](https://cognocient.com/docs/providers) covers all seven
(OpenAI, Anthropic, Google Gemini, Mistral, Groq, Together AI, Azure
OpenAI) because it works at the HTTP layer, not by wrapping each
provider's SDK object one at a time.

If your key is for one of the five providers this wrapper doesn't
cover:

1. **Use [the proxy](https://cognocient.com/docs/quickstart) instead** —
   it's provider-agnostic, covers all seven providers, and adds pre-call
   budget enforcement this wrapper never provides for any provider.
2. **Use the [CSV/OTel importer](https://cognocient.com/docs/attribution)**
   if you want zero live network calls to Cognocient from your runtime —
   works for any provider, historical-only.

Don't try passing Groq's or Together AI's OpenAI-compatible `base_url`
into `CognocientOpenAI` — it will run without erroring, but the reported
`provider` field is hardcoded to `"openai"`, so your dashboard will
mislabel that spend. Not a supported path.

## This is one of three ways to see your Cognocient dashboard

| | Live attribution | Pre-call enforcement (block/degrade) | Code change |
|---|---|---|---|
| **Proxy** (`base_url` swap) | Yes | Yes | One line |
| **This wrapper** | Yes | No — see below | Swap the import, add a key |
| **CSV/OTel import** | No (historical only) | No | None |

## Security — read this before you decide

**This wrapper is not more secure than the proxy. It is a different
tradeoff, not a strictly better one.**

With the proxy, your real provider API key lives server-side, under
Cognocient's control, in one place. With this wrapper, your real provider
key stays in your own application process, exactly as it does today
without Cognocient at all — the wrapper calls the provider directly,
using your key, inside your runtime. 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. We're not going to tell you
this "removes a security roadblock" — it trades one shape of exposure
for a different one.

What this wrapper honestly gives you over the proxy:
- **Zero added request latency.** 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 or unreachable, your call to
  OpenAI/Anthropic still completes normally — see "Reliability" below.

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. The dashboard will say so explicitly for
any account using this path.

## 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 at `DEBUG` level via the `cognocient`
  logger, not retried.

See `tests/test_reporter_failure_isolation.py` for a test that simulates
an unreachable ingestion endpoint and asserts the real call still
completes normally.

## 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, this wrapper will under-report your usage today. Use
the proxy or the CSV/OTel importer if that matters for your evaluation.

## Attribution fields

Same field names the proxy accepts as `X-Cost-*` headers, passed as
keyword arguments instead:

| 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` |

## Development

```bash
pip install -e ".[dev]"
pytest
python benchmark/benchmark_wrapper_overhead.py
```
