Metadata-Version: 2.4
Name: kalmia
Version: 0.2.0
Summary: Kalmia Tracing SDK — automatic LLM call tracing for OpenAI and Anthropic
Author: Kalmia
License: MIT
Project-URL: Homepage, https://github.com/xuandy05/trace-analyzer/tree/main/sdk/python#readme
Project-URL: Repository, https://github.com/xuandy05/trace-analyzer
Project-URL: Issues, https://github.com/xuandy05/trace-analyzer/issues
Keywords: llm,tracing,observability,openai,anthropic,claude,agents
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == "openai"
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.10.0; extra == "anthropic"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Dynamic: license-file

# Kalmia SDK (Python)

Automatic LLM call tracing for OpenAI and Anthropic. Wrap your client once and
every call — prompts, responses, tool calls, errors — is captured and sent to
your Kalmia dashboard. Zero external dependencies.

## Installation

```sh
pip install kalmia
```

## Usage

Get your API key from the Kalmia dashboard, then initialize the logger once at
startup and wrap your LLM client:

```python
from kalmia import init_logger, wrap_anthropic
import anthropic

init_logger(
    project_name="my-agent",
    api_key="kal_live_your_api_key",  # required — or set KALMIA_API_KEY
)

client = wrap_anthropic(anthropic.Anthropic())
# use `client` exactly like the Anthropic SDK — traces are captured automatically
```

OpenAI works the same way:

```python
from kalmia import init_logger, wrap_openai
import openai

init_logger(project_name="my-agent", api_key="kal_live_your_api_key")
client = wrap_openai(openai.OpenAI())
```

## Configuration

| Argument / env var | Purpose |
| --- | --- |
| `project_name` (required) | Groups traces under a project. |
| `api_key` / `KALMIA_API_KEY` | Workspace API key. Without it, traces are rejected and dropped. |
| `base_url` / `KALMIA_BASE_URL` | Where traces are sent. Defaults to the hosted Kalmia backend (`https://www.kalmia.dev`). Set this for local development or self-hosting. |

## Decorators and spans

Use `@traced` to capture custom steps, and `current_span()` to log inside them:

```python
from kalmia import traced, current_span

@traced(name="run")
def run(message):
    current_span().log(input={"message": message})
    ...
```

## Delivery reliability

Trace delivery is **at-least-once with no silent loss**:

- **Retry with backoff.** Transient failures (transport errors, timeouts, `429`,
  `5xx`) are retried with bounded backoff. Permanent `4xx` responses (`400`,
  `401`, `413`) are not retried.
- **Buffering.** A trace that still fails after its retries is held in a bounded
  in-memory buffer and re-attempted on the next send, then once more at
  interpreter exit (`atexit`).
- **No silent loss.** Every drop path — a non-retryable response, a full buffer
  evicting its oldest entry, a server-side rejection, or a trace still
  undelivered at exit — emits a warning naming the trace `id`, so a lost run is
  always visible in your logs.
- **Safe retries.** The backend de-duplicates by trace `id`, so a retried
  delivery never creates a duplicate.

The buffer lives in memory, so a hard kill (`SIGKILL`, OOM) can still lose
buffered traces — those are warned when the trace is first buffered. Durable
on-disk buffering is planned. Delivery never raises into your application.
