Metadata-Version: 2.4
Name: useprism
Version: 0.5.0
Summary: LLM Observability in 3 lines — cost, latency, errors and quality, automatically
Author: Sérgio Cardoso
License-Expression: MIT
Project-URL: Homepage, https://prism-landing-six.vercel.app
Keywords: llm,observability,monitoring,anthropic,openai,tracing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27
Dynamic: license-file

# prism-sdk

LLM Observability in 3 lines. Costs, latency, errors and quality — automatically.

```bash
pip install useprism
```

---

## Quick Start

### With Anthropic

```python
import anthropic
import prism

# 1. Init
prism.init("prism_your_api_key")

# 2. Wrap your client
client = prism.wrap(anthropic.Anthropic())

# 3. Use exactly as before — everything is traced automatically
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Summarise this contract..."}],
)
```

### With OpenAI

```python
import openai
import prism

prism.init("prism_your_api_key")
client = prism.wrap(openai.OpenAI())

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello!"}],
)
```

### Streaming

```python
# Anthropic — as an iterator
stream = client.messages.create(model="claude-sonnet-4-6", stream=True,
                                max_tokens=1024, messages=[...])
for event in stream:
    ...

# Anthropic — as a context manager
with client.messages.stream(model="claude-sonnet-4-6", max_tokens=1024, messages=[...]) as s:
    for text in s.text_stream:
        print(text, end="")
```

The trace is recorded when the stream finishes — output tokens are only known at that
point. Events pass through untouched.

**Streaming with OpenAI:** their API only returns `usage` if you ask for it. Without it,
token counts and cost come back as zero, and the trace is flagged with
`metadata.usage_missing` so you can tell "it was free" apart from "it could not be
measured":

```python
stream = client.chat.completions.create(
    model="gpt-4o", stream=True, messages=[...],
    stream_options={"include_usage": True},   # without this, no cost data
)
```

### Async clients

```python
import anthropic, prism

prism.init("prism_your_api_key")
client = prism.wrap(anthropic.AsyncAnthropic())

response = await client.messages.create(model="claude-sonnet-4-6",
                                        max_tokens=1024, messages=[...])

# async streaming
stream = await client.messages.create(stream=True, ...)
async for event in stream:
    ...
```

`AsyncOpenAI` works the same way.

### When a call fails

Your exception is re-raised untouched — Prism never swallows errors. The trace is recorded
first, with the provider's status code and the exception type and message under
`metadata.error`, so a failed call shows *why* rather than just `500`.

```python
try:
    client.messages.create(...)
except anthropic.RateLimitError:
    ...   # your handling is unaffected; the trace already recorded 429
```

### With a custom / unsupported client

```python
import prism
from prism.manual import trace

prism.init("prism_your_api_key")

with trace("my-model", endpoint="/api/summarize") as t:
    result = my_custom_llm_call(prompt)
    t.set_tokens(prompt=500, completion=200)
    t.set_response(result.text)
```

---

## Configuration

```python
prism.init(
    api_key="prism_your_api_key",
    debug=True,           # print logs to stdout
    flush_interval=2.0,   # seconds between batch flushes
    batch_size=20,        # traces per batch request
)
```

---

## What gets tracked automatically

| Field            | Description                            |
|------------------|----------------------------------------|
| `model`          | Model name (e.g. claude-sonnet-4-6)   |
| `provider`       | anthropic / openai / google            |
| `prompt_tokens`  | Input tokens used                      |
| `completion_tokens` | Output tokens used                  |
| `cost_usd`       | Calculated cost (server-side)          |
| `latency_ms`     | End-to-end response time               |
| `status_code`    | 200 on success, 4xx/5xx on errors      |
| `prompt_preview` | First 500 chars of the prompt          |
| `response_preview` | First 500 chars of the response      |
| `metadata.error`   | On failure: exception type and message |
| `session_id`     | Conversation grouping (if provided)    |
| `prompt_name` / `prompt_version` | Via `extra_headers`    |

---

## FastAPI integration example

```python
from fastapi import FastAPI, Request
import anthropic
import prism

prism.init("prism_your_api_key")
app = FastAPI()

@app.post("/api/chat")
async def chat(request: Request):
    body = await request.json()

    # Wrap per-request for endpoint-level tracking
    client = prism.wrap(
        anthropic.Anthropic(),
        endpoint="/api/chat",
    )

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        messages=[{"role": "user", "content": body["message"]}],
    )
    return {"reply": response.content[0].text}
```

---

## Overhead

- **~0ms** added to your LLM calls (async background thread)
- Traces are batched and sent every 2 seconds
- On shutdown, all pending traces are flushed

---

## License

MIT
