gauntlet-ai 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gauntlet/__init__.py +20 -0
- gauntlet/cli.py +246 -0
- gauntlet/config.py +174 -0
- gauntlet/data/embeddings.npz +0 -0
- gauntlet/data/metadata.json +109 -0
- gauntlet/detector.py +274 -0
- gauntlet/exceptions.py +13 -0
- gauntlet/layers/__init__.py +1 -0
- gauntlet/layers/embeddings.py +269 -0
- gauntlet/layers/llm_judge.py +319 -0
- gauntlet/layers/rules.py +852 -0
- gauntlet/mcp_server.py +135 -0
- gauntlet/models.py +83 -0
- gauntlet_ai-0.1.0.dist-info/METADATA +281 -0
- gauntlet_ai-0.1.0.dist-info/RECORD +17 -0
- gauntlet_ai-0.1.0.dist-info/WHEEL +4 -0
- gauntlet_ai-0.1.0.dist-info/entry_points.txt +2 -0
gauntlet/mcp_server.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""MCP server for Gauntlet prompt injection detection.
|
|
2
|
+
|
|
3
|
+
Provides two tools for Claude Code integration:
|
|
4
|
+
- check_prompt: Run detection cascade on text
|
|
5
|
+
- scan_file: Read file and check for injections
|
|
6
|
+
|
|
7
|
+
Start with: gauntlet mcp-serve
|
|
8
|
+
|
|
9
|
+
Requires: pip install gauntlet-ai[mcp]
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def serve() -> None:
|
|
19
|
+
"""Start the MCP server."""
|
|
20
|
+
try:
|
|
21
|
+
from mcp.server import Server
|
|
22
|
+
from mcp.server.stdio import stdio_server
|
|
23
|
+
from mcp.types import TextContent, Tool
|
|
24
|
+
except ImportError:
|
|
25
|
+
raise ImportError(
|
|
26
|
+
"MCP server requires the mcp package. "
|
|
27
|
+
"Install with: pip install gauntlet-ai[mcp]"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
import asyncio
|
|
31
|
+
|
|
32
|
+
from gauntlet import Gauntlet
|
|
33
|
+
|
|
34
|
+
server = Server("gauntlet")
|
|
35
|
+
detector = Gauntlet()
|
|
36
|
+
|
|
37
|
+
@server.list_tools()
|
|
38
|
+
async def list_tools() -> list[Tool]:
|
|
39
|
+
return [
|
|
40
|
+
Tool(
|
|
41
|
+
name="check_prompt",
|
|
42
|
+
description="Check text for prompt injection attacks using Gauntlet's detection cascade.",
|
|
43
|
+
inputSchema={
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"text": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "The text to check for prompt injection",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
"required": ["text"],
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
Tool(
|
|
55
|
+
name="scan_file",
|
|
56
|
+
description="Read a file and check its contents for prompt injection attacks.",
|
|
57
|
+
inputSchema={
|
|
58
|
+
"type": "object",
|
|
59
|
+
"properties": {
|
|
60
|
+
"path": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"description": "Path to the file to scan",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
"required": ["path"],
|
|
66
|
+
},
|
|
67
|
+
),
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
@server.call_tool()
|
|
71
|
+
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
|
72
|
+
if name == "check_prompt":
|
|
73
|
+
text = arguments.get("text", "")
|
|
74
|
+
result = detector.detect(text)
|
|
75
|
+
return [TextContent(
|
|
76
|
+
type="text",
|
|
77
|
+
text=json.dumps(result.model_dump(), indent=2),
|
|
78
|
+
)]
|
|
79
|
+
|
|
80
|
+
elif name == "scan_file":
|
|
81
|
+
filepath = Path(arguments.get("path", "")).resolve()
|
|
82
|
+
cwd = Path.cwd().resolve()
|
|
83
|
+
|
|
84
|
+
# Security: only allow files within current working directory
|
|
85
|
+
try:
|
|
86
|
+
filepath.relative_to(cwd)
|
|
87
|
+
except ValueError:
|
|
88
|
+
return [TextContent(
|
|
89
|
+
type="text",
|
|
90
|
+
text=json.dumps({"error": f"Access denied: path must be within {cwd}"}),
|
|
91
|
+
)]
|
|
92
|
+
|
|
93
|
+
# Block hidden files
|
|
94
|
+
if any(part.startswith(".") for part in filepath.parts if part != "."):
|
|
95
|
+
return [TextContent(
|
|
96
|
+
type="text",
|
|
97
|
+
text=json.dumps({"error": "Access denied: cannot scan hidden files"}),
|
|
98
|
+
)]
|
|
99
|
+
|
|
100
|
+
if not filepath.exists():
|
|
101
|
+
return [TextContent(
|
|
102
|
+
type="text",
|
|
103
|
+
text=json.dumps({"error": f"File not found: {filepath}"}),
|
|
104
|
+
)]
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
text = filepath.read_text()
|
|
108
|
+
except Exception as e:
|
|
109
|
+
return [TextContent(
|
|
110
|
+
type="text",
|
|
111
|
+
text=json.dumps({"error": f"Failed to read file: {e}"}),
|
|
112
|
+
)]
|
|
113
|
+
|
|
114
|
+
result = detector.detect(text)
|
|
115
|
+
output = result.model_dump()
|
|
116
|
+
output["file"] = str(filepath)
|
|
117
|
+
return [TextContent(
|
|
118
|
+
type="text",
|
|
119
|
+
text=json.dumps(output, indent=2),
|
|
120
|
+
)]
|
|
121
|
+
|
|
122
|
+
return [TextContent(
|
|
123
|
+
type="text",
|
|
124
|
+
text=json.dumps({"error": f"Unknown tool: {name}"}),
|
|
125
|
+
)]
|
|
126
|
+
|
|
127
|
+
async def _run() -> None:
|
|
128
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
129
|
+
await server.run(read_stream, write_stream)
|
|
130
|
+
|
|
131
|
+
asyncio.run(_run())
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
if __name__ == "__main__":
|
|
135
|
+
serve()
|
gauntlet/models.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Pydantic models for Gauntlet detection results."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class LayerResult(BaseModel):
|
|
7
|
+
"""Result from a single detection layer."""
|
|
8
|
+
|
|
9
|
+
is_injection: bool = Field(
|
|
10
|
+
...,
|
|
11
|
+
description="Whether this layer detected an injection",
|
|
12
|
+
)
|
|
13
|
+
confidence: float = Field(
|
|
14
|
+
default=0.0,
|
|
15
|
+
ge=0.0,
|
|
16
|
+
le=1.0,
|
|
17
|
+
description="Confidence score from this layer",
|
|
18
|
+
)
|
|
19
|
+
attack_type: str | None = Field(
|
|
20
|
+
default=None,
|
|
21
|
+
description="Type of attack detected by this layer",
|
|
22
|
+
)
|
|
23
|
+
layer: int = Field(
|
|
24
|
+
...,
|
|
25
|
+
ge=1,
|
|
26
|
+
le=3,
|
|
27
|
+
description="Which layer produced this result (1, 2, or 3)",
|
|
28
|
+
)
|
|
29
|
+
latency_ms: float = Field(
|
|
30
|
+
default=0.0,
|
|
31
|
+
ge=0.0,
|
|
32
|
+
description="Time taken by this layer in milliseconds",
|
|
33
|
+
)
|
|
34
|
+
details: dict | None = Field(
|
|
35
|
+
default=None,
|
|
36
|
+
description="Layer-specific details",
|
|
37
|
+
)
|
|
38
|
+
error: str | None = Field(
|
|
39
|
+
default=None,
|
|
40
|
+
description="Error message if layer failed (fail-open)",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class DetectionResult(BaseModel):
|
|
45
|
+
"""Result from the Gauntlet detection pipeline."""
|
|
46
|
+
|
|
47
|
+
is_injection: bool = Field(
|
|
48
|
+
...,
|
|
49
|
+
description="Whether any layer detected a prompt injection",
|
|
50
|
+
)
|
|
51
|
+
confidence: float = Field(
|
|
52
|
+
default=0.0,
|
|
53
|
+
ge=0.0,
|
|
54
|
+
le=1.0,
|
|
55
|
+
description="Confidence from the detecting layer (or 0 if no detection)",
|
|
56
|
+
)
|
|
57
|
+
attack_type: str | None = Field(
|
|
58
|
+
default=None,
|
|
59
|
+
description="Type of attack detected (if any)",
|
|
60
|
+
)
|
|
61
|
+
detected_by_layer: int | None = Field(
|
|
62
|
+
default=None,
|
|
63
|
+
ge=1,
|
|
64
|
+
le=3,
|
|
65
|
+
description="Which layer made the detection (1, 2, or 3)",
|
|
66
|
+
)
|
|
67
|
+
layer_results: list[LayerResult] = Field(
|
|
68
|
+
default_factory=list,
|
|
69
|
+
description="Results from each layer that was executed",
|
|
70
|
+
)
|
|
71
|
+
total_latency_ms: float = Field(
|
|
72
|
+
default=0.0,
|
|
73
|
+
ge=0.0,
|
|
74
|
+
description="Total time taken across all layers in milliseconds",
|
|
75
|
+
)
|
|
76
|
+
errors: list[str] = Field(
|
|
77
|
+
default_factory=list,
|
|
78
|
+
description="Errors from layers that failed open (layer ran but errored)",
|
|
79
|
+
)
|
|
80
|
+
layers_skipped: list[int] = Field(
|
|
81
|
+
default_factory=list,
|
|
82
|
+
description="Layer numbers that were requested but unavailable (missing deps/keys)",
|
|
83
|
+
)
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gauntlet-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Prompt injection detection for LLM applications
|
|
5
|
+
Project-URL: Homepage, https://github.com/Ashwinash27/gauntlet-ai
|
|
6
|
+
Project-URL: Repository, https://github.com/Ashwinash27/gauntlet-ai
|
|
7
|
+
Project-URL: Documentation, https://github.com/Ashwinash27/gauntlet-ai#readme
|
|
8
|
+
Author: Gauntlet Contributors
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: ai-safety,llm,prompt-injection,security
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: pydantic>=2.0.0
|
|
21
|
+
Provides-Extra: all
|
|
22
|
+
Requires-Dist: anthropic>=0.18.0; extra == 'all'
|
|
23
|
+
Requires-Dist: mcp>=0.9.0; extra == 'all'
|
|
24
|
+
Requires-Dist: numpy>=1.24.0; extra == 'all'
|
|
25
|
+
Requires-Dist: openai>=1.12.0; extra == 'all'
|
|
26
|
+
Requires-Dist: rich>=13.0.0; extra == 'all'
|
|
27
|
+
Requires-Dist: typer[all]>=0.9.0; extra == 'all'
|
|
28
|
+
Provides-Extra: cli
|
|
29
|
+
Requires-Dist: rich>=13.0.0; extra == 'cli'
|
|
30
|
+
Requires-Dist: typer[all]>=0.9.0; extra == 'cli'
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
36
|
+
Provides-Extra: embeddings
|
|
37
|
+
Requires-Dist: numpy>=1.24.0; extra == 'embeddings'
|
|
38
|
+
Requires-Dist: openai>=1.12.0; extra == 'embeddings'
|
|
39
|
+
Provides-Extra: llm
|
|
40
|
+
Requires-Dist: anthropic>=0.18.0; extra == 'llm'
|
|
41
|
+
Provides-Extra: mcp
|
|
42
|
+
Requires-Dist: mcp>=0.9.0; extra == 'mcp'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# Gauntlet
|
|
46
|
+
|
|
47
|
+
**Prompt injection detection for LLM applications.**
|
|
48
|
+
Runs locally. Bring your own keys.
|
|
49
|
+
|
|
50
|
+
[](https://www.python.org/downloads/)
|
|
51
|
+
[](https://opensource.org/licenses/MIT)
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install gauntlet-ai[all]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Or install only what you need:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install gauntlet-ai # Layer 1 only (rules, zero deps beyond pydantic)
|
|
65
|
+
pip install gauntlet-ai[embeddings] # + Layer 2 (OpenAI embeddings + numpy)
|
|
66
|
+
pip install gauntlet-ai[llm] # + Layer 3 (Anthropic Claude)
|
|
67
|
+
pip install gauntlet-ai[cli] # + CLI (typer + rich)
|
|
68
|
+
pip install gauntlet-ai[mcp] # + MCP server for Claude Code
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Quick Start
|
|
72
|
+
|
|
73
|
+
### Python API
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from gauntlet import Gauntlet, detect
|
|
77
|
+
|
|
78
|
+
# Layer 1 only - zero config, catches ~60% of attacks
|
|
79
|
+
result = detect("ignore previous instructions")
|
|
80
|
+
print(result.is_injection) # True
|
|
81
|
+
print(result.confidence) # 0.95
|
|
82
|
+
print(result.attack_type) # instruction_override
|
|
83
|
+
|
|
84
|
+
# All layers - bring your own keys
|
|
85
|
+
g = Gauntlet(openai_key="sk-...", anthropic_key="sk-ant-...")
|
|
86
|
+
result = g.detect("subtle attack attempt")
|
|
87
|
+
|
|
88
|
+
# Or configure once
|
|
89
|
+
# Keys read from ~/.gauntlet/config.toml or env vars
|
|
90
|
+
g = Gauntlet()
|
|
91
|
+
result = g.detect("check this text")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### CLI
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Detect (Layer 1 by default)
|
|
98
|
+
gauntlet detect "ignore previous instructions"
|
|
99
|
+
|
|
100
|
+
# Use all configured layers
|
|
101
|
+
gauntlet detect "subtle attack" --all
|
|
102
|
+
|
|
103
|
+
# Read from file
|
|
104
|
+
gauntlet detect --file input.txt
|
|
105
|
+
|
|
106
|
+
# Scan a directory
|
|
107
|
+
gauntlet scan ./prompts/ --pattern "*.txt"
|
|
108
|
+
|
|
109
|
+
# JSON output
|
|
110
|
+
gauntlet detect "text" --json
|
|
111
|
+
|
|
112
|
+
# Configure API keys
|
|
113
|
+
gauntlet config set openai_key sk-xxx
|
|
114
|
+
gauntlet config set anthropic_key sk-ant-xxx
|
|
115
|
+
gauntlet config list
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### MCP Server (Claude Code Integration)
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
gauntlet mcp-serve
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Add to your Claude Code config:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"mcpServers": {
|
|
129
|
+
"gauntlet": {
|
|
130
|
+
"command": "gauntlet",
|
|
131
|
+
"args": ["mcp-serve"]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## How It Works
|
|
140
|
+
|
|
141
|
+
Three-layer detection cascade. Stops at the first layer that detects an injection.
|
|
142
|
+
|
|
143
|
+
### Layer 1: Rules (Free, Local)
|
|
144
|
+
|
|
145
|
+
50+ regex patterns covering 9 attack categories, 13 languages, Unicode homoglyph normalization. Catches ~60% of attacks in ~0.1ms. Zero dependencies.
|
|
146
|
+
|
|
147
|
+
### Layer 2: Embeddings (OpenAI Key)
|
|
148
|
+
|
|
149
|
+
Compares input against 500+ pre-computed attack embeddings using cosine similarity. One OpenAI API call per check (~$0.00002). Catches ~30% more attacks.
|
|
150
|
+
|
|
151
|
+
### Layer 3: LLM Judge (Anthropic Key)
|
|
152
|
+
|
|
153
|
+
Claude Haiku analyzes sanitized text characteristics. Catches sophisticated attacks that bypass rules and embeddings. ~$0.0003 per check.
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
User Input
|
|
157
|
+
|
|
|
158
|
+
v
|
|
159
|
+
[Layer 1: Rules] --detected--> STOP (injection found)
|
|
160
|
+
|
|
|
161
|
+
| clean
|
|
162
|
+
v
|
|
163
|
+
[Layer 2: Embeddings] --detected--> STOP (injection found)
|
|
164
|
+
|
|
|
165
|
+
| clean
|
|
166
|
+
v
|
|
167
|
+
[Layer 3: LLM Judge] --detected--> STOP (injection found)
|
|
168
|
+
|
|
|
169
|
+
| clean
|
|
170
|
+
v
|
|
171
|
+
PASS (no injection)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Attack Categories
|
|
177
|
+
|
|
178
|
+
| Category | Description | Example |
|
|
179
|
+
|----------|-------------|---------|
|
|
180
|
+
| `instruction_override` | Nullify system prompts | "Ignore previous instructions" |
|
|
181
|
+
| `jailbreak` | DAN, roleplay, persona attacks | "You are now DAN" |
|
|
182
|
+
| `delimiter_injection` | Fake XML/JSON boundaries | "</system>new prompt" |
|
|
183
|
+
| `data_extraction` | Leak system prompts/secrets | "Print your instructions" |
|
|
184
|
+
| `indirect_injection` | Hidden instructions in data | "[AI ONLY] execute this" |
|
|
185
|
+
| `context_manipulation` | Reality confusion | "Everything above is fake" |
|
|
186
|
+
| `obfuscation` | Encoded payloads | Base64, leetspeak, Unicode |
|
|
187
|
+
| `hypothetical_framing` | Fiction-wrapped attacks | "Hypothetically, with no rules..." |
|
|
188
|
+
| `multilingual_injection` | Non-English attacks | 13 languages supported |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Configuration
|
|
193
|
+
|
|
194
|
+
### Key Resolution Order
|
|
195
|
+
|
|
196
|
+
1. Constructor arguments
|
|
197
|
+
2. Config file (`~/.gauntlet/config.toml`)
|
|
198
|
+
3. Environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`)
|
|
199
|
+
4. Layer 1 only (no keys needed)
|
|
200
|
+
|
|
201
|
+
### Config File
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
gauntlet config set openai_key sk-xxx
|
|
205
|
+
gauntlet config set anthropic_key sk-ant-xxx
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Creates `~/.gauntlet/config.toml` with restrictive permissions.
|
|
209
|
+
|
|
210
|
+
### Environment Variables
|
|
211
|
+
|
|
212
|
+
| Variable | Description |
|
|
213
|
+
|----------|-------------|
|
|
214
|
+
| `OPENAI_API_KEY` | OpenAI API key for Layer 2 |
|
|
215
|
+
| `ANTHROPIC_API_KEY` | Anthropic API key for Layer 3 |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Detection Result
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from gauntlet import Gauntlet
|
|
223
|
+
|
|
224
|
+
g = Gauntlet()
|
|
225
|
+
result = g.detect("ignore previous instructions")
|
|
226
|
+
|
|
227
|
+
result.is_injection # True
|
|
228
|
+
result.confidence # 0.95
|
|
229
|
+
result.attack_type # "instruction_override"
|
|
230
|
+
result.detected_by_layer # 1
|
|
231
|
+
result.total_latency_ms # 0.3
|
|
232
|
+
result.layer_results # [LayerResult(...)]
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Project Structure
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
gauntlet/
|
|
241
|
+
__init__.py # Public API: detect(), Gauntlet class
|
|
242
|
+
detector.py # Core Gauntlet class + cascade logic
|
|
243
|
+
cli.py # Typer CLI
|
|
244
|
+
config.py # ~/.gauntlet/config.toml management
|
|
245
|
+
models.py # DetectionResult, LayerResult
|
|
246
|
+
exceptions.py # GauntletError, ConfigError
|
|
247
|
+
mcp_server.py # MCP server for Claude Code
|
|
248
|
+
layers/
|
|
249
|
+
rules.py # Layer 1 - regex patterns (zero deps)
|
|
250
|
+
embeddings.py # Layer 2 - OpenAI + local cosine similarity
|
|
251
|
+
llm_judge.py # Layer 3 - Anthropic Claude
|
|
252
|
+
data/
|
|
253
|
+
embeddings.npz # Pre-computed attack embeddings
|
|
254
|
+
metadata.json # Attack pattern metadata
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Published on PyPI as `gauntlet-ai`. Python import remains `from gauntlet import ...`.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Development
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Install dev dependencies
|
|
265
|
+
pip install -e ".[all,dev]" # From source
|
|
266
|
+
|
|
267
|
+
# Run tests
|
|
268
|
+
pytest -v
|
|
269
|
+
|
|
270
|
+
# Run tests with coverage
|
|
271
|
+
pytest --cov=gauntlet
|
|
272
|
+
|
|
273
|
+
# Format code
|
|
274
|
+
black .
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
gauntlet/__init__.py,sha256=4x-hcF5Y9mNy5o_eS32l2ChgdEN8V-dehZ7yqEehRAM,601
|
|
2
|
+
gauntlet/cli.py,sha256=ZiGBKo9RP7GuDTLH7SCLDBkErZKwv6Sj3lnJW7uXCE4,8790
|
|
3
|
+
gauntlet/config.py,sha256=tdYRIX5jOcCGpf5WEUiy2-9jlKwzR9sYS4gs77nTN_s,4839
|
|
4
|
+
gauntlet/detector.py,sha256=_7CjSXyWugma8tEBWa8nxP0pE4X5rRN3WYQRBXx_yJA,9387
|
|
5
|
+
gauntlet/exceptions.py,sha256=PlgQr5BD80yWG5nNTdPSfKlszGbOik0el-L_2WwTPeU,239
|
|
6
|
+
gauntlet/mcp_server.py,sha256=uWbuwFME4SrszgLwT119HkuLVtoDgmBS9IpI0JKUjYc,4236
|
|
7
|
+
gauntlet/models.py,sha256=_yXR1bFKqD1ghZszGoXzF8AKhO1DXt2_BwBvsCt8ulk,2361
|
|
8
|
+
gauntlet/data/embeddings.npz,sha256=ksinNBsk9q4ByIj8FdlbhsNh1MfXxECelnwWmuC_zhg,114409
|
|
9
|
+
gauntlet/data/metadata.json,sha256=MeujxED1-YAKZV055dD5ghtWjFVsUQJhiJiPgmNEAE0,2742
|
|
10
|
+
gauntlet/layers/__init__.py,sha256=tJLiUI_mJFv8zG5HuarGx8wVHBlfdYisBq6CqIRjReE,37
|
|
11
|
+
gauntlet/layers/embeddings.py,sha256=_8D6xlvHclUmRDau_KhcAopPwSBpq_3kXH1KvupWHqI,9120
|
|
12
|
+
gauntlet/layers/llm_judge.py,sha256=WaU3aML8hSYp-l6hNVVeaUuTiIfCZy5YYrxPvNnMuF4,10971
|
|
13
|
+
gauntlet/layers/rules.py,sha256=07HGTXlDOWoUOsKYwS6755yDgVGb7Ajx018tbKmr0IU,34016
|
|
14
|
+
gauntlet_ai-0.1.0.dist-info/METADATA,sha256=p0VSozJ1G3Dp0x1T0nTax1vjE7CXDLnfTPikUifwSac,7633
|
|
15
|
+
gauntlet_ai-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
+
gauntlet_ai-0.1.0.dist-info/entry_points.txt,sha256=tFzR6arHWGXdJSDa2jcMQWcwjuVkZYtklXbCvevXdxY,47
|
|
17
|
+
gauntlet_ai-0.1.0.dist-info/RECORD,,
|