Metadata-Version: 2.4
Name: grepture
Version: 0.1.0
Summary: Grepture SDK — AI gateway client with PII redaction, tracing, and prompt management
Project-URL: Homepage, https://grepture.com
Project-URL: Repository, https://github.com/grepture/sdk-python
License-Expression: AGPL-3.0-only
License-File: LICENSE
Keywords: ai,grepture,llm,observability,openai,pii,proxy,redaction
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Description-Content-Type: text/markdown

# grepture

Python SDK for [Grepture](https://grepture.com) — AI gateway with PII redaction, tracing, cost tracking, and prompt management. Works with any OpenAI-compatible SDK.

## Install

```bash
pip install grepture
```

## Quick start

```python
from openai import OpenAI
from grepture import Grepture

grepture = Grepture(api_key="gpt_abc123", proxy_url="https://proxy.grepture.com")

client = OpenAI(**grepture.client_options(
    api_key="sk-openai-key",
    base_url="https://api.openai.com/v1",
))

# Works exactly like normal — requests flow through Grepture
completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "hi"}],
)
```

## Trace mode (zero-latency observability)

```python
grepture = Grepture(api_key="gpt_abc123", proxy_url="https://proxy.grepture.com", mode="trace")
client = OpenAI(**grepture.client_options(api_key="sk-openai-key", base_url="https://api.openai.com/v1"))
# Requests go DIRECT to the provider; traces are sent async in the background.
grepture.flush()  # call before exit in serverless environments
```

## Modes

| Mode | Default | Traffic flow | Use case |
|------|---------|---------------|----------|
| `"proxy"` | Yes | App → Grepture → Provider | PII redaction, blocking, prompt management |
| `"trace"` | No | App → Provider (direct) | Observability and cost tracking without latency overhead |

In **proxy mode** (default), requests route through the Grepture proxy where detection rules are applied. In **trace mode**, requests go directly to the provider — the SDK captures metadata (tokens, model, latency, cost) asynchronously and sends it to the dashboard in the background.

## Async usage

Every feature has an async counterpart via `AsyncGrepture`, built on `httpx.AsyncClient`.

```python
from openai import AsyncOpenAI
from grepture import AsyncGrepture

grepture = AsyncGrepture(api_key="gpt_abc123", proxy_url="https://proxy.grepture.com")

client = AsyncOpenAI(**grepture.client_options(
    api_key="sk-openai-key",
    base_url="https://api.openai.com/v1",
))

completion = await client.chat.completions.create(
    model="gpt-5.5",
    messages=[{"role": "user", "content": "hi"}],
)

await grepture.flush()  # call before exit in serverless environments
```

## Raw requests

Use `grepture.request()` when you don't have (or don't want) a provider SDK in the loop. It routes through the same proxy/trace logic as `client_options()` and returns a `GreptureResponse`.

```python
response = grepture.request(
    "https://api.openai.com/v1/chat/completions",
    headers={"Authorization": "Bearer sk-openai-key"},
    json={"model": "gpt-5.5", "messages": [{"role": "user", "content": "hi"}]},
)

print(response.status_code)    # 200
print(response.request_id)     # proxy-assigned request id
print(response.rules_applied)  # ["rule-uuid-1"]
print(response.json())         # parsed response body
```

## Tracing

Group related requests into a trace, label each step, attach metadata, and log custom events.

```python
grepture = Grepture(api_key="gpt_abc123", proxy_url="https://proxy.grepture.com", trace_id="agent-run-42")
client = OpenAI(**grepture.client_options(api_key="sk-openai-key", base_url="https://api.openai.com/v1"))

# Attach metadata to all requests in this trace
grepture.set_metadata({"user_id": "u_123", "environment": "prod"})

# Label each step
grepture.set_label("extract-facts")
client.chat.completions.create(model="gpt-5.5", messages=[...])

# Log a custom event between AI calls
grepture.log("extract-facts-done", {"tokens": 174})

grepture.set_label("draft-response")
client.chat.completions.create(model="gpt-5.5", messages=[...])

grepture.flush()  # call before exit in serverless environments
```

All trace data (labels, metadata, log events) is visible in the Grepture dashboard under Traffic Log > Traces, and on the dedicated trace detail page.

## Prompt management

Fetch and resolve prompt templates managed in the Grepture dashboard.

```python
# Proxy mode: attach a prompt reference to a request; the proxy resolves it server-side
messages = grepture.prompt.use("greeting", variables={"name": "Ada"})
client.chat.completions.create(model="gpt-5.5", messages=messages)

# Fetch a prompt template directly
template = grepture.prompt.get("greeting", version=3)

# Fetch + resolve variables locally (works in both proxy and trace mode)
assembled = grepture.prompt.assemble("greeting", variables={"name": "Ada"})
client.chat.completions.create(model="gpt-5.5", messages=assembled["messages"])

# Resolve a set of messages against variables without a network call
resolved = grepture.prompt.resolve(
    [{"role": "system", "content": "Hi {{name}}"}], {"name": "Ada"}
)

# List all prompts
prompts = grepture.prompt.list()
```

`grepture.prompt.use()` raises `RuntimeError` in trace mode, since it depends on the proxy to resolve the prompt server-side — use `grepture.prompt.assemble()` instead, which fetches the template and resolves it locally.

## Embeddings

```python
result = grepture.embeddings.create(
    model="text-embedding-3-small",
    input="hello world",
    openai_key="sk-openai-key",
)
print(result["data"][0]["embedding"])
print(result["redactions"])  # {"count": 0, "categories": []}
```

## Error handling

The SDK raises typed errors on non-OK responses from the proxy:

```python
from grepture import Grepture, AuthError, BlockedError

try:
    response = grepture.request(url, json=payload)
except BlockedError:
    ...  # Request blocked by a Grepture rule (403)
except AuthError:
    ...  # Invalid Grepture API key (401)
```

| Error Class | Status | When |
|-------------|--------|------|
| `BadRequestError` | 400 | Malformed request |
| `AuthError` | 401 | Invalid Grepture API key |
| `BlockedError` | 403 | Request blocked by a rule |
| `ProxyError` | 502/504 | Target unreachable or timed out |
| `GreptureError` | other | Any other non-OK status (base class for all of the above) |

## API

### `Grepture(api_key, proxy_url, *, mode="proxy", trace_id=None)` / `AsyncGrepture(...)`

| Parameter | Type | Description |
|-----------|------|--------------|
| `api_key` | `str` | Your Grepture API key (`gpt_xxx`) |
| `proxy_url` | `str` | Grepture proxy URL (e.g. `https://proxy.grepture.com`) |
| `mode` | `"proxy" \| "trace"` | Operating mode (default: `"proxy"`) |
| `trace_id` | `str \| None` | Default trace ID for conversation tracing |

### `grepture.client_options(*, base_url, api_key=None, debug=False)`

Returns `{"base_url", "api_key", "http_client"}` for use with OpenAI-shaped SDK constructors (`OpenAI(**client_options(...))`).

| Parameter | Type | Description |
|-----------|------|--------------|
| `base_url` | `str` | Target base URL (e.g. `https://api.openai.com/v1`) |
| `api_key` | `str \| None` | Target API key (e.g. `sk-openai-key`); omit to use a key stored in the Grepture dashboard (proxy mode only) |
| `debug` | `bool` | Attach debug headers to proxied requests |

### `grepture.request(target_url, *, method="POST", headers=None, json=None, content=None, trace_id=None, label=None, metadata=None, debug=False)`

Issues a single request through the proxy (or direct, in trace mode) and returns a `GreptureResponse`. Pass either `json` (auto-serialized) or raw `content` bytes, not both.

### `grepture.set_trace_id(trace_id)` / `grepture.get_trace_id()`

Set or clear the default trace ID for all subsequent requests.

### `grepture.set_label(label)` / `grepture.get_label()`

Set or clear the default label for all subsequent requests. Override per-request via `request(..., label=...)`.

### `grepture.set_metadata(metadata)` / `grepture.get_metadata()`

Set or clear default metadata (`dict[str, str]`) for all subsequent requests. Override per-request via `request(..., metadata=...)` (values merge, per-request wins on conflicts).

### `grepture.log(event, data=None)`

Log a custom event into the current trace. `event` is the event name (string), `data` is an optional payload (dict). Events appear in the trace timeline alongside AI calls.

### `grepture.flush()` (`await grepture.flush()` on `AsyncGrepture`)

Flushes any pending trace and log data. Call before process exit in serverless or short-lived environments.

### `grepture.prompt.use(slug, *, variables=None, version=None)`

Attach a prompt reference to a message list; the proxy resolves it server-side. Raises `RuntimeError` in trace mode.

### `grepture.prompt.get(slug, *, version=None)` / `grepture.prompt.assemble(slug, *, variables=None, version=None)`

Fetch a raw prompt template, or fetch and resolve it against `variables` locally.

### `grepture.prompt.resolve(messages, variables)`

Resolve `{{var}}`, `{{#if}}`, and `{{#each}}` template syntax against a variable dict, without a network call.

### `grepture.prompt.list()`

List all prompts available to your API key.

### `grepture.embeddings.create(*, model, input, dimensions=None, encoding_format=None, user=None, on_pii=None, strategy=None, openai_key=None, trace_id=None)`

Create embeddings through the Grepture proxy, with PII redaction applied to `input` before it reaches the provider.

## Requirements

Python 3.9+. Depends on [`httpx`](https://www.python-httpx.org/) only.
