Metadata-Version: 2.4
Name: gjallarhorn-hq-sdk
Version: 1.0.0
Summary: Official Python SDK for the Gjallarhorn prompt-injection detection API
Project-URL: Homepage, https://gjallarhorn.watch
Project-URL: Repository, https://github.com/gjallarhorn-hq/gjallarhorn
Project-URL: Documentation, https://gjallarhorn.watch/docs
Project-URL: Issues, https://github.com/gjallarhorn-hq/gjallarhorn/issues
License: MIT
License-File: LICENSE
Keywords: ai-security,gjallarhorn,llm-security,prompt-injection
Classifier: Development Status :: 4 - Beta
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: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24.0
Description-Content-Type: text/markdown

# gjallarhorn-sdk

Official Python SDK for the [Gjallarhorn](https://gjallarhorn.watch) prompt-injection detection API.

[![PyPI version](https://badge.fury.io/py/gjallarhorn-sdk.svg)](https://pypi.org/project/gjallarhorn-sdk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

## Installation

```bash
pip install gjallarhorn-sdk
```

**Requirements**: Python 3.9+, `httpx`.

## Quickstart

```python
from gjallarhorn_sdk import GjallarhornClient

client = GjallarhornClient("gjh_your_api_key_here")

result = client.scan(user_input)
if result.should_block:
    raise ValueError("Injection detected")
# safe to send to LLM
```

## Async usage

```python
import asyncio
from gjallarhorn_sdk import AsyncGjallarhornClient

async def main():
    async with AsyncGjallarhornClient("gjh_...") as client:
        result = await client.scan("some user input")
        print(result.risk_level, result.detection_layers)

asyncio.run(main())
```

## Methods

| Method | Description |
|--------|-------------|
| `scan(content, *, use_classifier?, context?)` | Scan text for prompt injection |
| `scan_multimodal(file, mime_type, filename?)` | Scan PDF/image/QR code |
| `get_quota()` | Get credit usage and quota |
| `check_canary(llm_response)` | Check for canary token in LLM output |

Both `GjallarhornClient` (sync) and `AsyncGjallarhornClient` (async) have the same methods.

## Scan a PDF

```python
from pathlib import Path

result = client.scan_multimodal(Path("user-upload.pdf"), "application/pdf")
print(result.risk_level, result.billed_pages)
```

## Error handling

```python
from gjallarhorn_sdk import (
    AuthError, QuotaExceededError, RateLimitError,
    ExtractionFailedError, ServiceUnavailableError,
)

try:
    result = client.scan(user_input)
except AuthError:
    print("Invalid API key")
except QuotaExceededError:
    print("Monthly quota hit")
except RateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after}s")
except ExtractionFailedError:
    print("Could not extract file content")
```

The SDK retries automatically on rate-limit (respecting `retry_after`) and 503 errors (exponential backoff, max 3 retries).

## License

MIT
