Metadata-Version: 2.5
Name: ganivra
Version: 0.3.0
Summary: AI execution cost and unit-economics telemetry for the OpenAI Python SDK
Project-URL: Homepage, https://ganivra.com
Project-URL: Documentation, https://ganivra.com
Author: Ganivra
License-Expression: MIT
License-File: LICENSE
Keywords: ai,cost,llm,observability,openai,unit-economics
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
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
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: openai>=1.50
Provides-Extra: release
Requires-Dist: build>=1.2; extra == 'release'
Requires-Dist: twine>=6.0; extra == 'release'
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == 'test'
Description-Content-Type: text/markdown

# Ganivra Python SDK

Ganivra measures the cost and unit economics of AI executions without proxying
model traffic or storing prompts and responses.

## Install

```bash
python -m pip install ganivra
```

Ganivra requires Python 3.10 or newer and currently instruments synchronous
OpenAI Responses API calls.

## Initialize before creating the OpenAI client

```python
import ganivra
from openai import OpenAI

ganivra.init(api_key="gv_live_your_workspace_key")
client = OpenAI()

response = client.responses.create(
    model="gpt-5",
    input="Explain our refund policy simply.",
)
```

Initialization automatically captures model, input/output/cached token counts,
latency, status, error type, and environment. Ganivra creates execution and step
IDs and exports the metadata on a bounded background queue.

## Add business context for unit economics

```python
def answer_for(account, credits_debited):
    with ganivra.trace(
        metadata={"channel": "voice"},
        application="support-platform",
        workflow="support_answer",
        feature="support_chatbot",
        customer_id=account.id,
        plan_key=account.plan_key,
        credits=credits_debited,
        prompt_id="support_answer",
        prompt_version="v1",
    ):
        return client.responses.create(
            model="gpt-5",
            input="Explain our refund policy simply.",
        )
```

Trace metadata is optional. It enables customer, feature, workflow, prompt, and
margin reporting. Use the stable tenant or account identifier already present
in each authenticated request as `customer_id`; customers do not need to be
registered with Ganivra first. Configure customer pricing plans separately when
you want revenue derived automatically instead of sending `revenue_usd` per
execution. The `metadata` mapping accepts additional bounded JSON
dimensions and is inherited by nested traces. Use pseudonymous identifiers
where appropriate.

Pricing rates stay in Ganivra; never look up a rate or calculate revenue in the
instrumented request. Subscription and per-execution plans only need
`customer_id`. Token and hybrid plans use the token/execution telemetry captured
by the SDK. For a credit/unit plan, also pass the exact usage attribute
configured on the plan—`credits` above—with the units your application already
deducted. Send `plan_key` when customers can choose or change plans without a
manual Ganivra assignment. Ganivra combines the plan and credit usage with the
rate configured in your workspace.

Treat one billable customer action as one trace/execution. If one trace produces
multiple model calls, the inherited transaction-level credit value is repeated
on the resulting events and counted once. Start a separate trace for each
independently billed action.

## Record confirmed purchases (optional)

Event telemetry works on its own and provides AI cost plus plan-rated unit
economics. After your payment webhook is verified and your application has
updated the customer's credits or subscription, optionally send the confirmed
financial transaction so Ganivra can reconcile plan-rated revenue with payment
actuals.

```python
result = ganivra.transactions.create(
    external_id=payment_intent.id,  # stable provider ID; retries are idempotent
    customer_id=account.id,
    transaction_type="credit_purchase",
    amount=3999,
    currency="INR",
    amount_usd=47,
    units_purchased=1000,
    plan_key="growth",
    source="stripe",
)
```

For USD, send only `amount` and `currency="USD"`. For another currency, send
exactly one of `amount_usd` or `exchange_rate_to_usd`. Use
`transaction_type="subscription_payment"` for renewals and
`transaction_type="refund"` for refunds. Include `execution_id` when a payment
belongs to a particular AI result. This call is synchronous and raises
`ganivra.GanivraAPIError` if Ganivra does not accept the transaction; only call
it after your own payment and entitlement updates succeed.

## Configuration

```python
ganivra.init(
    api_key="gv_live_your_workspace_key",
    endpoint="https://api.ganivra.com",
    environment="production",
    queue_size=1000,
    batch_size=20,
    timeout_seconds=2.0,
    max_retries=2,
)
```

For local Ganivra development, set `endpoint="http://127.0.0.1:8000"`.
Call `ganivra.flush()` in short-lived scripts when you want to wait briefly for
queued telemetry.

## Privacy and failure behavior

- OpenAI requests continue going directly from your application to OpenAI.
- Prompt and response bodies are never collected.
- OpenAI API keys are never collected.
- Telemetry failures never change the OpenAI result or exception your code sees.
- A full queue or unavailable Ganivra endpoint drops telemetry instead of
  blocking the application.

Prefer the language-independent REST API when automatic Python instrumentation
is not appropriate. The canonical API contract is documented at
`docs/API.md` in the Ganivra project.
