Metadata-Version: 2.4
Name: proxyprompt
Version: 0.1.0
Summary: A Python SDK for Masking Sensitive Data Before Sending to LLMs
Author-email: DeepMind Partner <partner@google.com>
License: MIT
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: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: cryptography>=41.0.0
Requires-Dist: click>=8.0.0
Provides-Extra: redis
Requires-Dist: redis>=4.5.0; extra == "redis"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"

﻿# ProxyPrompt

`ProxyPrompt` is a production-quality, high-performance Python SDK designed to detect, mask, and unmask sensitive or proprietary information (PII, credentials, database schemas, IP addresses, etc.) in text prompts before sending them to Large Language Models (LLMs). It works transparently with any LLM provider (OpenAI, Anthropic, Gemini, Ollama, Groq, etc.) and integrates smoothly into popular web frameworks (FastAPI, Flask) and agent orchestrators (LangChain, LlamaIndex).

---

## Features

- **30+ Built-in Detectors**: URL, Email, Phone, API Keys, JWT, UUID, IPv4/v6, MAC Addresses, Credit Cards, Windows/Linux Paths, Database/Table/Column Names, SQL Queries, JSON, XML, HTML, Markdown Links, and Cloud Secrets (AWS, Azure, Google).
- **Secure Key Management**: Supports industry-standard **AES-256-GCM** encryption for storing original values. Features a pluggable `KeyProvider` interface (env variables, static keys, Vault, AWS KMS, etc.) to keep secrets out of repositories.
- **Streaming Unmasking**: Includes a high-efficiency sliding-buffer stream unmasker that reconstructs placeholder tokens split across arbitrary stream chunks and unmasks them on-the-fly.
- **Pluggable Storage Backends**: Memory, JSON file, SQLite, and Redis stores.
- **Extensible Plugin System**: Register custom regex detectors dynamically in one line.
- **Middlewares & Callback Handlers**: Pre-built wrappers for OpenAI, Anthropic, Gemini, Ollama, Groq, LangChain, LlamaIndex, FastAPI, and Flask.
- **Production CLI**: CLI commands to mask, unmask, inspect, clean, and monitor stats.

---

## Installation

Install using pip:

```bash
pip install proxyprompt
```

To install optional dependencies (like Redis):

```bash
pip install proxyprompt[redis]
```

---

## Quick Start

### Basic Usage

```python
from proxyprompt import mask, unmask

# 1. Mask sensitive details in a prompt
prompt = "Please send database dump of customer_prod to admin@company.com immediately."
req = mask(prompt)

print(req.text)
# Output: "Please send database dump of <DATABASE_1> to <EMAIL_1> immediately."
print(req.thread_id)
# Output: "8e91af92" (Secure random hex ID)

# 2. Send masked prompt to LLM...
# response = client.chat(req.text)
response_text = "Sent database dump of <DATABASE_1> to <EMAIL_1> successfully."

# 3. Unmask original values in the LLM response
final = unmask(response_text, thread_id=req.thread_id)
print(final)
# Output: "Sent database dump of customer_prod to admin@company.com successfully."
```

### Configured Mask Instance

```python
from proxyprompt import Mask

mask_engine = Mask(
    store="sqlite",             # Persistence backend
    db_path="my_mappings.db",
    encryption=True,            # Enable AES-256-GCM
    sensitive_schema_names=["payments_tbl", "ssn_col"]  # Database Schema overrides
)

res = mask_engine.mask("Query tables payments_tbl or ssn_col.")
print(res.text)
# Output: "Query tables <DATABASE_1> or <DATABASE_2>."
```

---

## Streaming Response Unmasking

When LLM responses stream token-by-token, placeholder brackets can be split across chunks (e.g. `<LI` in Chunk 1, and `NK_1>` in Chunk 2). `ProxyPrompt` implements a sliding-window buffer that guarantees correct restoration with $O(N)$ time complexity and minimal memory footprint:

```python
# Synchronous Stream
stream_generator = client.chat.completions.create(prompt=req.text, stream=True)
unmasked_stream = mask_engine.unmask_stream(stream_generator, thread_id=req.thread_id)

for chunk in unmasked_stream:
    print(chunk, end="")
```

---

## Middleware Integrations

### OpenAI SDK Wrapper

Wrap the OpenAI client to automatically mask prompts and unmask completions (supports streaming and async):

```python
import openai
from proxyprompt.middleware import wrap_openai

client = wrap_openai(openai.OpenAI())

# Prompts are masked outgoing, responses are unmasked incoming automatically
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "My private API key is AIzaSyA1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q"}]
)
```

### FastAPI Middleware

Apply ASGI middleware to intercept, mask, and unmask HTTP payloads:

```python
from fastapi import FastAPI
from proxyprompt.middleware import FastAPIMaskMiddleware

app = FastAPI()
app.add_middleware(FastAPIMaskMiddleware, target_keys=["prompt", "query"])
```

### LangChain Callbacks

```python
from langchain.chat_models import ChatOpenAI
from proxyprompt.middleware import LangChainMaskCallbackHandler

handler = LangChainMaskCallbackHandler()
chat = ChatOpenAI(callbacks=[handler])
```

---

## Advanced: Dynamic Plugins

```python
mask_engine = Mask()
mask_engine.register_detector(
    name="Employee ID",
    regex=r"EMP\d{5}",
    placeholder="EMPLOYEE"
)

res = mask_engine.mask("EMP12345 joined the channel.")
print(res.text)  # Output: "<EMPLOYEE_1> joined the channel."
```

---

## Storage Backends

| Backend | Initialization | Persistence | Note |
|---|---|---|---|
| `MemoryStore` | `Mask(store="memory")` | Ephemeral | Default, thread-safe |
| `JsonStore` | `Mask(store="json", db_path="store.json")` | File-based | Lightweight file persistence |
| `SQLiteStore` | `Mask(store="sqlite", db_path="store.db")` | DB-based | Robust concurrency (WAL mode) |
| `RedisStore` | `Mask(store="redis", redis_url="redis://...")` | Distributed | Perfect for clustered LLM apps |

---

## Security Guidelines

- **Zero Keys in Config**: Never save AES keys inside git repositories or YAML configs.
- **Key Providers**: Supply keys programmatically via `StaticKeyProvider` or load them automatically from environment variables using `EnvKeyProvider` (looks for `PROXYPROMPT_ENCRYPTION_KEY` by default). Interface stubs are provided for HashiCorp Vault, AWS KMS, Google Secret Manager, and Azure Key Vault.
- **Tamper Protection**: `EncryptedStore` binds the AES-256-GCM ciphertext with the unique `placeholder` name as Authenticated Associated Data (AAD). If a database record is swapped or altered, decryption fails immediately with `DecryptionError`.

---

## CLI Usage

`ProxyPrompt` includes a Click-powered CLI:

```bash
# Mask file contents (defaults to persistent JSON file store for CLI runs)
proxyprompt mask input.txt -o masked.txt

# Unmask file contents
proxyprompt unmask masked.txt -t <thread_id> -o unmasked.txt

# Inspect mappings
proxyprompt inspect <thread_id>

# Run cleanup of expired mappings (older than 24h)
proxyprompt clean --expiration 86400

# View statistics
proxyprompt stats
```

---

## License

This project is licensed under the MIT License.

