Metadata-Version: 2.4
Name: instructeer
Version: 1.0.0
Summary: Python client for the Instructeer API
Author: Instructeer
License: MIT
Project-URL: Homepage, https://instructeer.com
Project-URL: Documentation, https://instructeer.com/docs
Project-URL: Repository, https://github.com/Instructeer/instructeer-python
Project-URL: Bug Tracker, https://github.com/Instructeer/instructeer-python/issues
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-cov>=5; extra == "dev"
Requires-Dist: respx>=0.21; extra == "dev"
Requires-Dist: black>=24; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"

# instructeer

Python client library for the [Instructeer](https://instructeer.com) API.

Instructeer provides deterministic guardrails for LLM pipelines — no ML inference, no probabilistic scores. The same input always produces the same output.

- **SQLGuard** — AST-based SQL risk analysis. Blocks dangerous or expensive queries before they reach your database.
- **PIIGuard** — Regex and lexicon-based PII detection. Prevents sensitive data from leaking through prompts or logs.

[Quick Start](https://instructeer.com/docs/quickstart) · [Documentation](https://instructeer.com/docs)

## Installation

```bash
pip install instructeer
```

Requires Python 3.11+.

## Authentication

All requests require an API key. Get yours at [instructeer.com](https://instructeer.com).

Pass the key directly or set the `INSTRUCTEER_API_KEY` environment variable:

```bash
# macOS / Linux
export INSTRUCTEER_API_KEY=rg_...

# Windows (cmd)
set INSTRUCTEER_API_KEY=rg_...

# Windows (PowerShell)
$env:INSTRUCTEER_API_KEY="rg_..."
```

## SQLGuard

```python
from instructeer.guards import SQLGuard

# api_key = os.environ["INSTRUCTEER_API_KEY"]
sql = SQLGuard(api_key="rg_...")
result = sql.analyze("SELECT * FROM users WHERE id = 1")

print(result.allowed)    # True
print(result.severity)   # "low"
print(result.issues)     # []
```

```json
{ "allowed": true, "severity": "low", "issues": [] }
```

Block a query based on the result:

```python
generated_sql = "DELETE FROM users"
result = sql.analyze(generated_sql)

# Block on high severity only (allowed is False)
if not result.allowed:
    raise ValueError(f"Query blocked: {result.issues[0].message}")

# Block on medium and high severity
if result.severity in ("medium", "high"):
    raise ValueError(f"Query blocked: {result.issues[0].message}")
```

```json
{
  "allowed": false,
  "severity": "high",
  "issues": [
    { "rule": "dml_no_where", "severity": "high", "message": "DELETE without WHERE clause would affect all rows" }
  ]
}
```

### Dialects

Defaults to `"postgres"`. Supported values:

`postgres` · `mysql` · `bigquery` · `tsql` · `oracle` · `snowflake` · `databricks` · `sqlite` · `hive` · `mariadb`

```python
result = sql.analyze("SELECT TOP 10 * FROM users", dialect="tsql")
```

### Response

| Field | Type | Description |
|---|---|---|
| `allowed` | `bool` | `False` when a high-severity issue is detected |
| `severity` | `str` | `"low"`, `"medium"`, or `"high"` |
| `issues` | `list[Issue]` | Each issue has `rule`, `message`, and `severity` |

## PIIGuard

```python
from instructeer.guards import PIIGuard

# api_key = os.environ["INSTRUCTEER_API_KEY"]
pii = PIIGuard(api_key="rg_...")
result = pii.detect_all("Alice's SSN is 123-45-6789 and email is alice@example.com")

print(result.summary.total)           # 2
print(result.summary.max_severity)    # "high"
for d in result.detections:
    print(d.entity_type, d.value, d.confidence)
```

```json
{
  "detections": [
    { "entity_type": "US_SSN",        "value": "123-45-6789",        "confidence": 1.0, "severity": "high" },
    { "entity_type": "EMAIL_ADDRESS", "value": "alice@example.com",  "confidence": 1.0, "severity": "medium" }
  ],
  "summary": {
    "total": 2,
    "max_severity": "high",
    "by_type": { "US_SSN": 1, "EMAIL_ADDRESS": 1 },
    "by_severity": { "high": 1, "medium": 1 }
  }
}
```

### Single detectors

```python
result = pii.detect_email("Reach out at hello@example.com")
result = pii.detect_ssn("Patient SSN: 078-05-1120")
result = pii.detect_card("Charged to 4111 1111 1111 1111")
result = pii.detect_phone("Call 800-555-0199")
```

Available: `detect_email` · `detect_phone` · `detect_ssn` · `detect_card` · `detect_bank` · `detect_iban` · `detect_ip` · `detect_url` · `detect_date` · `detect_dob` · `detect_dl` · `detect_passport` · `detect_address` · `detect_person`

### Filtering

```python
result = pii.detect_all(
    text,
    detectors=["email", "ssn", "card"],
    min_confidence=0.8,
    confidence_thresholds={"US_ADDRESS": 0.7},
    use_default_thresholds=True,
)
```

### Response

**`DetectResponse`**

| Field | Type | Description |
|---|---|---|
| `detections` | `list[Detection]` | All detected PII spans |
| `summary` | `DetectionSummary` | Aggregate counts and severity roll-up |

**`Detection`**

| Field | Type | Description |
|---|---|---|
| `entity_type` | `str` | e.g. `"EMAIL_ADDRESS"`, `"US_SSN"`, `"CREDIT_CARD"` |
| `value` | `str` | The matched text |
| `start` | `int` | Start character offset |
| `end` | `int` | End character offset |
| `severity` | `str` | `"low"`, `"medium"`, or `"high"` |
| `confidence` | `float` | Detection confidence (0.0–1.0) |
| `extra` | `dict \| None` | Additional detector-specific metadata |

**`DetectionSummary`**

| Field | Type | Description |
|---|---|---|
| `total` | `int` | Total number of detections |
| `max_severity` | `str \| None` | Highest severity with confidence ≥ 0.3 |
| `by_type` | `dict[str, int]` | Count per entity type |
| `by_severity` | `dict[str, int]` | Count per severity level |

## Async

```python
import asyncio
from instructeer.guards import AsyncSQLGuard, AsyncPIIGuard

async def main():
    async with AsyncSQLGuard(api_key="rg_...") as sql:
        result = await sql.analyze("DROP TABLE users")

    async with AsyncPIIGuard(api_key="rg_...") as pii:
        result = await pii.detect_all("SSN: 123-45-6789")

asyncio.run(main())
```

## Error handling

```python
from instructeer import (
    AuthenticationError,
    PermissionDeniedError,
    RateLimitError,
    UnprocessableEntityError,
    APIConnectionError,
    APITimeoutError,
    APIError,
)

try:
    result = sql.analyze(query)
except AuthenticationError:
    ...
except PermissionDeniedError:
    ...
except RateLimitError as e:
    print(e.status_code)   # 429
except UnprocessableEntityError:
    ...
except APITimeoutError:
    ...
except APIConnectionError:
    ...
except APIError as e:
    print(e.status_code)
```

## Configuration

| Parameter | Default | Description |
|---|---|---|
| `api_key` | env `INSTRUCTEER_API_KEY` | Your Instructeer API key |
| `base_url` | `https://api.instructeer.com` | Override for self-hosted deployments |
| `timeout` | `30.0` | Request timeout in seconds |
| `max_retries` | `2` | Retries on 429 and 5xx with exponential backoff |

## License

MIT
