Metadata-Version: 2.5
Name: llm-argus
Version: 0.1.0
Summary: Auto-instrumenting observability SDK for LLM inference.
Project-URL: Homepage, https://github.com/shrirang3/argus
Project-URL: Repository, https://github.com/shrirang3/argus
Project-URL: Issues, https://github.com/shrirang3/argus/issues
Author: Shrirang Mahankaliwar
License-Expression: MIT
License-File: LICENSE
Keywords: groq,instrumentation,llm,observability,openai,opentelemetry
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.9
Provides-Extra: all
Requires-Dist: groq>=0.11; extra == 'all'
Requires-Dist: openai>=1.40; extra == 'all'
Provides-Extra: groq
Requires-Dist: groq>=0.11; extra == 'groq'
Provides-Extra: openai
Requires-Dist: openai>=1.40; extra == 'openai'
Description-Content-Type: text/markdown

# argus

Auto-instrumenting observability SDK for LLM inference.

One call at startup. Every provider call in the process is captured after that —
including calls made by code you did not write, because the instrumentation
replaces the method on the provider's *class*, not on a client instance you hand
it.

## Install

```bash
uv add llm-argus
pip install llm-argus
```

The distribution is `llm-argus`; the import is `argus`.

Provider packages are extras, not dependencies — instrumenting Groq should not
require OpenAI's package to be installed:

```bash
uv add "llm-argus[groq]"     # or llm-argus[openai], llm-argus[all]
```

Unreleased work can be installed straight from the repository:

```bash
uv add "llm-argus @ git+https://github.com/shrirang3/argus@main#subdirectory=packages/argus"
```

## Use

```python
import argus

argus.init(endpoint="http://ingestion:8001/v1/events", service="chat-app")

# ...unchanged application code...
resp = await client.chat.completions.create(model=..., messages=...)   # logged

await argus.shutdown()   # in your shutdown hook — drains the buffer
```

Both arguments fall back to environment variables (`ARGUS_ENDPOINT`,
`ARGUS_SERVICE`), so in a container the integration is `import argus` plus
`argus.init()`.

To correlate calls into conversations:

```python
with argus.conversation(conversation_id):
    ...
```

Without it, events are still recorded — `conversation_id` is simply `NULL`.

## What it guarantees

The wrapper is a decorator, never a replacement:

1. **Calls through.** The provider's own method does the work.
2. **Re-raises the original exception.** Never swallowed, never re-wrapped.
3. **Returns the response untouched.** The application cannot tell it is there.

The transport is non-blocking: `emit()` appends to a bounded in-memory buffer
and returns. A background task batches and POSTs. On failure it degrades in
stages — retry with backoff, then spill to disk, and only then drop, oldest
first, counted. `argus.stats()` exposes those counters so data loss is visible
rather than silent.

## What it instruments today

| | |
|---|---|
| Providers | Groq, and anything on the OpenAI wire format (OpenAI, Cerebras, …) |
| Method | `chat.completions.create`, sync and async |
| Streaming | async fully (including time-to-first-token); sync emits without output or usage |

Anthropic and the OpenAI Responses API are not wired yet.

## Configuration

Every field of `argus.Config` is overridable by environment variable, because
the knobs that need turning — buffer size, flush interval — are the ones you
discover under load, in a deployed container, without a code change:

`ARGUS_ENDPOINT`, `ARGUS_SERVICE`, `ARGUS_ENABLED`, `ARGUS_QUEUE_MAXSIZE`,
`ARGUS_BATCH_SIZE`, `ARGUS_FLUSH_INTERVAL`, `ARGUS_TIMEOUT`,
`ARGUS_MAX_RETRIES`, `ARGUS_SPILL_PATH`.

## Requirements

Python 3.11+, and a collector listening at `endpoint` that accepts the
`EventBatch` payload in `argus.schema`. The reference collector, worker,
Postgres schema and dashboard live in the
[argus repository](https://github.com/shrirang3/argus).

The drain task starts on the running asyncio event loop, so a fully synchronous
(WSGI) application will buffer events without sending them.

## License

MIT