Metadata-Version: 2.4
Name: anvaya-sdk
Version: 0.1.0
Summary: Anvaya Python SDK — AI failure observability instrumentation
Author: Ashutosh Siddh
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/siddhashutosh/anvaya
Project-URL: Documentation, https://github.com/siddhashutosh/anvaya#readme
Project-URL: Repository, https://github.com/siddhashutosh/anvaya
Project-URL: Issues, https://github.com/siddhashutosh/anvaya/issues
Keywords: observability,llm,ai,tracing,agents,rag,failure-analysis
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
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 :: System :: Monitoring
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Provides-Extra: dev
Dynamic: license-file

# anvaya-sdk — Python SDK

Standard library only. No `requests`, no `pydantic`, no transitive anything.

```
pip install anvaya-sdk
```

⚠️ **Install `anvaya-sdk`, import `anvaya`.** The bare name `anvaya` on PyPI
belongs to an unrelated mathematics library by another author — `pip install
anvaya` fetches that, not this. The import name is unchanged.

```python
import anvaya

anvaya.init(api_key="anv_prod_...", endpoint="https://api.anvaya.dev")

with anvaya.span("llm.answer", kind="llm", provider="anthropic") as s:
    response = model.complete(prompt)
    s["outputTokens"] = response.usage.output_tokens
```

The handle yielded by `span()` is a plain dict; assigning to it before the
block exits adds fields to the span. The block is timed automatically, and an
exception raised inside it is recorded as `status="error"` with the exception's
type name — and then **re-raised**. The SDK never swallows your exception.

## Configuration

| Argument | Environment | Default |
|---|---|---|
| `api_key` | `ANVAYA_API_KEY` | `""` |
| `endpoint` | `ANVAYA_ENDPOINT` | `http://localhost:4318` |
| `environment` | — | `production` |
| `batch_size` | — | 256 |
| `flush_interval` | — | 5.0 s |
| `queue_limit` | — | 10 000 spans |

With no API key the client is inert: no thread, no timer, no network, no
exception. A developer running your test suite without credentials gets
silence rather than a stack trace or a hang.

## What it will not do

Each of these has a test in `tests/`, and a matching test in the Node SDK.

- **Raise into your request path.** The only exception this package raises is
  `SpanError`, and only for genuinely malformed instrumentation — at the line
  that wrote it, not from a background thread pointing somewhere else.
  Transport failures, 5xx responses, a dead endpoint and a full queue are all
  counters, readable via `client.stats()`.
- **Send your prompts.** `content=` is hashed to a `sha256:` reference. The
  payload goes only where you configure it to.
- **Hold your process open.** The flush thread is a daemon and `close()` is
  registered with `atexit`.
- **Retry a 402, 401 or 403.** A quota rejection means the fix is commercial; a
  bad credential does not fix itself. The client disables itself and says so
  once.
- **Requeue a failed batch.** A queue that refills faster than it drains during
  an outage is how an SDK causes the memory exhaustion it was meant to observe.
- **Drop the newest span under pressure.** It drops the oldest. During an
  incident the newest spans are the ones describing it.
- **Guess a timestamp.** An unparseable `startedAt` raises rather than
  defaulting to `now()` — a span stamped with its arrival time looks correct
  and poisons every latency number derived from it.

## Tests

No test dependencies either:

```
py -m unittest discover -s tests      # Windows
python3 -m unittest discover -s tests # everywhere else
```

`tests/test_conformance.py` reads `../conformance/corpus.json`, the same file
the Node SDK and the control plane are tested against. See
[`../README.md`](../README.md) for why that file exists.
