Metadata-Version: 2.4
Name: tokengrip
Version: 0.1.0
Summary: Track and optimize your AI spending with Tokengrip
License-Expression: MIT
Keywords: ai,anthropic,cost,llm,openai,tokengrip,tracking
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
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 :: Software Development :: Libraries
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# tokengrip

Track and optimize your AI spending with [Tokengrip](https://tokengrip.com).

## Installation

```bash
pip install tokengrip
```

## Quick Start

```python
from tokengrip import Tokengrip, wrap_openai
import openai

tg = Tokengrip(api_key="tq_live_...")
client = wrap_openai(openai.OpenAI(), tg)

# Usage is automatically tracked
response = client.chat.completions.create(
    model="gpt-4.1",
    messages=[{"role": "user", "content": "Hello!"}],
)

# Don't forget to flush on exit
tg.shutdown()
```

## Wrappers

### OpenAI

```python
from tokengrip import Tokengrip, wrap_openai
import openai

tg = Tokengrip(api_key="tq_live_...", project_id="my-project")
client = wrap_openai(openai.OpenAI(), tg)

# Chat Completions API
response = client.chat.completions.create(
    model="gpt-4.1",
    messages=[{"role": "user", "content": "Hello!"}],
)

# Responses API
response = client.responses.create(
    model="gpt-4.1",
    input="Hello!",
)

# Streaming works too
stream = client.chat.completions.create(
    model="gpt-4.1",
    messages=[{"role": "user", "content": "Hello!"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")
```

### Anthropic

```python
from tokengrip import Tokengrip, wrap_anthropic
import anthropic

tg = Tokengrip(api_key="tq_live_...", project_id="my-project")
client = wrap_anthropic(anthropic.Anthropic(), tg)

response = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
)
```

### Async Clients

Both sync and async clients are supported transparently:

```python
import openai
from tokengrip import Tokengrip, wrap_openai

tg = Tokengrip(api_key="tq_live_...")
client = wrap_openai(openai.AsyncOpenAI(), tg)

response = await client.chat.completions.create(
    model="gpt-4.1",
    messages=[{"role": "user", "content": "Hello!"}],
)
```

## Options

```python
tg = Tokengrip(
    api_key="tq_live_...",       # Required
    base_url="https://...",       # Default: https://tokengrip.com
    project_id="my-project",      # Applied to all records
    agent_name="my-agent",        # Applied to all records
    debug=True,                   # Log tracking activity
    batch_size=10,                # Records per batch
    flush_interval_ms=5000,       # Auto-flush interval
)

# Wrapper-specific options
client = wrap_openai(openai.OpenAI(), tg, {
    "project_id": "override",     # Override per-wrapper
    "agent_name": "override",
    "task_type": "summarization",
    "capture_content": True,      # Capture truncated prompt/response
})
```

## Manual Tracking

```python
tg.track(
    provider="openai",
    model="gpt-4.1",
    inputTokens=500,
    outputTokens=150,
    latencyMs=1200,
    projectId="my-project",
)
```
