Metadata-Version: 2.4
Name: rankigi-litellm
Version: 1.0.0
Summary: LiteLLM CustomLogger that adds RANKIGI cryptographic receipts to every model call routed through LiteLLM.
Project-URL: Homepage, https://rankigi.com/docs/connectors/litellm
Project-URL: Repository, https://github.com/Rankigi-Inc/rankigi
Author-email: Rankigi <hello@rankigi.com>
License-Expression: MIT
Requires-Python: >=3.9
Requires-Dist: litellm>=1.40.0
Requires-Dist: rankigi>=4.0.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# rankigi-litellm

RANKIGI integration for [LiteLLM](https://github.com/BerriAI/litellm).
Every model call routed through LiteLLM gets a cryptographic receipt.

Works with direct `litellm.completion()` usage and with the LiteLLM
proxy (`config.yaml`).

## Install

```bash
pip install rankigi-litellm
```

## Setup

First, enroll a connector passport in your RANKIGI dashboard (Agents → New,
type "connector"). Copy the credentials into your environment:

```bash
export RANKIGI_API_KEY=rnk_...
export RANKIGI_AGENT_ID=<uuid>
export RANKIGI_PASSPORT_ID=<uuid>
export RANKIGI_SIGNING_KEY=<base64 ed25519 seed>
```

### Direct litellm usage

```python
import litellm
from rankigi_litellm import RankigiLogger

litellm.callbacks = [RankigiLogger(agent_tag="checkout-service-prod")]

response = litellm.completion(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "hi"}],
)
```

Every `litellm.completion(...)` call now writes a receipt to the RANKIGI
chain identified by the connector passport.

### LiteLLM proxy

In `config.yaml`:

```yaml
litellm_settings:
  callbacks: ["rankigi_litellm.RankigiLogger"]
```

The proxy will instantiate the logger and credentials will be picked up
from `RANKIGI_*` env vars.

## What gets a receipt

| LiteLLM event | RANKIGI action |
|---|---|
| `async_log_success_event` / `log_success_event` | `llm.<provider>.chat` |
| `async_log_stream_event` (on stream completion) | same, with `meta.stream = true` |
| `async_log_failure_event` / `log_failure_event` | `llm_error` (`severity: "warn"`) |

## What gets hashed

`messages` (the full prompt history) and `response` (the model output) are
SHA-256-hashed locally by the SDK before any network call. Only the
hashes plus safe metadata reach RANKIGI:

- `model_id` (`gen_ai.request.model` equivalent)
- `provider_request_id` (provider completion ID)
- `provider`, `call_type` (`completion`, `acompletion`, etc.)
- `token_usage` (full StandardLoggingPayload usage block)
- `latency_ms`
- `agent_meta` from `agent_tag`

## Safety

- Every callback is wrapped in try/except. SDK or network failures are
  absorbed silently. **The logger never raises into LiteLLM.**
- Logging callbacks are post-hoc (after the model call), so they cannot
  affect the response your application sees.
- We do **not** implement `async_pre_call_hook`. That hook can raise and
  block LiteLLM's request — incompatible with the RANKIGI non-blocking
  principle. The proof layer must never interfere with the agent it
  observes.

## Explicit credentials

If you can't use env vars, pass the credentials directly:

```python
RankigiLogger(
    api_key=os.environ["RANKIGI_API_KEY"],
    agent_id=os.environ["RANKIGI_AGENT_ID"],
    passport_id=os.environ["RANKIGI_PASSPORT_ID"],
    signing_private_key=os.environ["RANKIGI_SIGNING_KEY"],
    agent_tag="checkout-service-prod",
)
```

## Test injection

For unit tests, pass a mock `client`:

```python
class FakeClient:
    def track(self, **kwargs): ...

RankigiLogger(client=FakeClient())
```

## License

MIT
