Metadata-Version: 2.4
Name: pylva-sdk
Version: 1.0.1
Summary: Pylva SDK — cost infrastructure for AI agent businesses
Project-URL: Homepage, https://pylva.com
Project-URL: Repository, https://github.com/SpaceGravity/pylva
Author: Pylva
License: MIT
License-File: LICENSE
Keywords: anthropic,cost,llm,openai,telemetry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.5
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: tomli>=2; (python_version < '3.11') and extra == 'dev'
Description-Content-Type: text/markdown

# pylva (Python)

Cost infrastructure for AI agent businesses. Auto-instruments `openai` and
`anthropic` clients; emits server-priced telemetry to your Pylva backend.

Version `1.0.1` is the first public SDK foundation release. Public APIs follow
SemVer from this release onward; the telemetry wire format remains schema
`1.6`.

```bash
pip install pylva-sdk
```

```python
import pylva

pylva.init(api_key="pv_live_...", endpoint="https://api.pylva.com")
# every subsequent openai / anthropic call emits a telemetry event.
```

## Package name

The PyPI distribution package is `pylva-sdk`; the runtime import package is
`pylva`:

```bash
pip install pylva-sdk
```

```python
import pylva
```

### Failover (reliability_failover rules)

If you use `reliability_failover` rules, switch to the explicit-client
constructor so Pylva has a handle to the backup provider:

```python
from openai import OpenAI
from anthropic import Anthropic
from pylva import Pylva

Pylva(
    api_key="pv_live_...",
    openai=OpenAI(),
    anthropic=Anthropic(),
)
```

`pylva.init(api_key)` keeps working for telemetry-only deployments. In
v1.0, the SDK detects active cross-provider failover states and surfaces
warnings; actual backup-provider dispatch is still beta/internal and is not
part of the stable launch promise.

## Auto-instrumentation

Importing `pylva` monkey-patches any `openai` / `anthropic` modules
already loaded. Patches are isolated per R1 — SDK errors never propagate to
your agent.

Calls are captured with: `model`, `provider`, `tokens_in`, `tokens_out`,
`latency_ms`, `status`, `step_name` (if set), `customer_id`. Cost is computed
server-side.

## Tracking context

```python
from pylva import track_context

with track_context(customer_id="acme-corp", step="authentication"):
    resp = openai_client.chat.completions.create(...)
```

`track_context` is a `contextvars`-backed context manager; it threads
metadata across async + threaded boundaries correctly.

## Non-LLM costs

```python
from pylva import report_usage

report_usage(
    customer_id="acme-corp",
    tool="translation",
    metric="characters",
    value=4200,
    step="translation",
)
```

## Reactive budget enforcement (B2a)

When a builder configures a `budget_limit` rule with `hard_stop=True`, the
SDK enforces **pre-call**:

```python
import pylva
from pylva import PylvaBudgetExceeded, BudgetExceededSource

pylva.init(api_key="pv_live_...")

try:
    resp = openai_client.chat.completions.create(...)
except PylvaBudgetExceeded as err:
    print(f"Budget hit for {err.customer_id}: ${err.accumulated_usd:.2f} / ${err.limit_usd:.2f}")
    print(f"Source: {err.source.value}")  # 'sdk_precall' or 'backend_ingest_flag'
    # graceful degradation
```

### How it works

- **Pre-call accumulator (per-process `dict` + `threading.Lock`).** Keyed on
  `(rule_id, scope_token, period_start)`. Local enforcement is driven by
  backend budget flags and `/budget/sync` reconciliation. If the local view
  is already over a hard-stop limit, the SDK raises
  `PylvaBudgetExceeded(source=BudgetExceededSource.SDK_PRECALL)`.

- **Backend-authoritative flag.** Every ingest response may carry
  `budget_exceeded[]`. The SDK bumps local accumulators to `limit + 1`
  on receipt; next pre-call raises `source=BACKEND_INGEST_FLAG`.

- **5-min sync loop.** A `threading.Timer` re-POSTs accumulator state to
  `/api/v1/budget/sync` and overwrites local with the server truth
  (not additive).

- **Passthrough.** Cold rules cache or unreachable backend → pre-call is a
  no-op. Your agent never blocks due to Pylva being degraded (R5).

### Rule behavior matrix

| `type` | `hard_stop` | Behavior |
|---|---|---|
| `budget_limit` | `True` | Pre-call raises `PylvaBudgetExceeded`; LLM call skipped. |
| `budget_limit` | `False` | Pre-call prints an advisory warning (1/min per rule); LLM call proceeds. |
| `cost_threshold` | n/a | Post-call evaluation only (backend); no SDK-side enforcement. |

## Webhook verification

```python
import os
from pylva import verify_webhook, InvalidSignatureFormat

def handle_webhook(body: str, signature: str, timestamp: str):
    try:
        ok = verify_webhook(body, signature, os.environ["WEBHOOK_SECRET"], timestamp)
    except InvalidSignatureFormat:
        return 400
    if not ok:
        return 400
    # ...
```

`verify_webhook` accepts both raw hex and the `sha256=<hex>` GitHub-style
prefix (B2a D34 parity with TS SDK). Default timestamp tolerance is 300 s.

## Privacy & PII

Pylva **does not redact** `step_name`, `customer_id`, or `metadata`
values. Do **not** pass raw user message content, email addresses, or phone
numbers into these fields. The charset-regex input validation rejects HTML
and most control characters — it does not protect against free-form PII.

See `specs.md` Appendix D14 for the rationale. PII handling is the builder's
responsibility; we provide exportable data and deletion endpoints.

## License

MIT.
