Metadata-Version: 2.4
Name: kostlens
Version: 0.1.4
Summary: KostLens SDK - track every AI/LLM call's cost, tokens and latency with 3 lines
Project-URL: Homepage, https://kostlens.com
Project-URL: Documentation, https://kostlens.com/docs/
Project-URL: Source, https://github.com/kostlens/sdks
Project-URL: Issues, https://github.com/kostlens/sdks/issues
Author: KostLens
License: MIT License
        
        Copyright (c) 2026 KostLens (kostlens.com)
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: ai-cost,anthropic,gemini,llm,observability,openai,tokens
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# kostlens

Track every AI call your app makes: tokens, cost, latency, and the tags that
matter (feature, end user), with wrappers designed so they can't break your app.

Part of [KostLens](https://kostlens.com), the financial layer for your AI usage.

## Install

```bash
pip install kostlens
```

## Quickstart (2 lines)

```python
from openai import OpenAI
from kostlens import KostLens, wrap_openai

kl = KostLens(os.environ["KOSTLENS_KEY"])
client = wrap_openai(OpenAI(), kl, tags={"feature": "chat"})

# Use `client` exactly as before. Streaming included.
```

Also available: `wrap_anthropic` and `wrap_google_genai`. Any OpenAI-compatible
API (Groq, OpenRouter, DeepSeek, Kimi...) works through `wrap_openai`. Pass
`provider="deepseek"` so pricing uses the honest vendor label.

Async clients (`AsyncOpenAI`, `AsyncAnthropic`) are supported too. Wrap them
the same way and `await` as usual.

## Tag what matters

```python
# inside your request handler, where you know the customer
client = wrap_openai(OpenAI(), kl, tags={
    "feature": "summarize",
    "endUserId": user.id,   # camelCase, a reserved key
})
```

Tag keys travel verbatim: use `endUserId`, **not** `end_user_id`. A snake_case
key is stored as a plain custom tag and the Customers page stays empty.

`endUserId` powers cost-per-customer; `feature` powers cost-per-feature.

Tags belong to the client you wrap. `feature` rarely changes, so wrapping once
per module is fine. `endUserId` changes per request, so that wrap goes in the
handler.

**Give each set of tags its own client.** Wrapping replaces
`client.chat.completions.create` in place and marks the client, so handing the
same client over again returns it untouched and every call keeps the tags it
started with. The mark is what stops a second wrap from layering over the first
and reporting every call once per layer. It also means one shared module-level
client cannot carry a different `endUserId` per request: construct the client
inside the handler.

## The sacred rule

The SDK is designed so it can never break the host app. `track()` is
non-blocking (the HTTP call happens on a background daemon thread), network
failures and KostLens outages degrade to "some events are lost", never to an
exception in your request path. Events are batched, retried with backoff on
transient failures, and dropped (with an `on_error` callback) when permanently
rejected.

## Options

```python
KostLens(
    "kl_live_...",
    "https://ingest.kostlens.com",  # default; override for self-hosted
    flush_interval=5.0,
    max_batch_size=100,
    max_buffer_size=5000,
    on_error=lambda err: logging.warning("kostlens: %s", err),
)
```

Call `kl.shutdown()` before a short-lived process exits to flush what is
buffered (it is also registered with `atexit`).

## Docs

Full documentation: [kostlens.com/docs](https://kostlens.com/docs/)

MIT © KostLens
