prompt-lock 0.2.0
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.
- package/CHANGELOG.md +65 -0
- package/LICENSE +21 -0
- package/README.md +351 -0
- package/dist/assertions/builtin.d.ts +5 -0
- package/dist/assertions/builtin.d.ts.map +1 -0
- package/dist/assertions/builtin.js +172 -0
- package/dist/assertions/builtin.js.map +1 -0
- package/dist/assertions/custom.d.ts +3 -0
- package/dist/assertions/custom.d.ts.map +1 -0
- package/dist/assertions/custom.js +26 -0
- package/dist/assertions/custom.js.map +1 -0
- package/dist/assertions/index.d.ts +3 -0
- package/dist/assertions/index.d.ts.map +1 -0
- package/dist/assertions/index.js +32 -0
- package/dist/assertions/index.js.map +1 -0
- package/dist/assertions/json-schema.d.ts +3 -0
- package/dist/assertions/json-schema.d.ts.map +1 -0
- package/dist/assertions/json-schema.js +35 -0
- package/dist/assertions/json-schema.js.map +1 -0
- package/dist/cache.d.ts +14 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +114 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +434 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-validation.d.ts +6 -0
- package/dist/config-validation.d.ts.map +1 -0
- package/dist/config-validation.js +133 -0
- package/dist/config-validation.js.map +1 -0
- package/dist/github.d.ts +11 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +138 -0
- package/dist/github.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/anthropic.d.ts +3 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +46 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/custom.d.ts +3 -0
- package/dist/providers/custom.d.ts.map +1 -0
- package/dist/providers/custom.js +178 -0
- package/dist/providers/custom.js.map +1 -0
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +34 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai.d.ts +3 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +45 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/reporter.d.ts +6 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +251 -0
- package/dist/reporter.js.map +1 -0
- package/dist/retry.d.ts +8 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +51 -0
- package/dist/retry.js.map +1 -0
- package/dist/runner.d.ts +16 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +203 -0
- package/dist/runner.js.map +1 -0
- package/dist/snapshot.d.ts +7 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +146 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/types.d.ts +138 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +9 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +83 -0
- package/dist/utils.js.map +1 -0
- package/package.json +82 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-04-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Custom HTTP provider** — connect to Ollama, LM Studio, Azure OpenAI, or any REST endpoint with `{ type: 'custom', url: '...' }`
|
|
13
|
+
- **Output caching** (`--cache`) — skip LLM calls for unchanged prompts, with `prompt-lock cache stats` and `cache clear` commands
|
|
14
|
+
- **Retry with exponential backoff** — automatic retry on rate limits (429), server errors (5xx), and network errors (3 retries by default)
|
|
15
|
+
- **GitHub PR comments** (`--github-pr owner/repo#123`) — post markdown results table to pull requests, updates existing comment in place
|
|
16
|
+
- **Dataset testing** — test prompts against multiple inputs via `dataset` config field
|
|
17
|
+
- **Parallel execution** (`--parallel`, `--concurrency`) — run prompts concurrently
|
|
18
|
+
- **Snapshot history** — timestamped snapshots with `prompt-lock history <id>` command
|
|
19
|
+
- **CLI flags** — `--dry-run`, `--verbose`, `--parallel`, `--concurrency`, `--cache`, `--github-pr`
|
|
20
|
+
- **Progress spinner** — visual feedback during LLM calls
|
|
21
|
+
- 3 new assertions: `contains-all`, `no-duplicates`, `max-latency`
|
|
22
|
+
- Config validation module with schema checks for all assertion types and provider configs
|
|
23
|
+
- Dataset results in JSON and HTML reports
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Template variables now support dashes and dots: `{{user-name}}`, `{{api.version}}`
|
|
28
|
+
- Provider model passing is now type-safe (removed `as any` casts)
|
|
29
|
+
- Snapshots use `{id}/latest.json` format (backward compatible with old `{id}.json`)
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Invalid regex in `matches-regex` assertion no longer crashes the run
|
|
34
|
+
- Missing API keys now throw clear actionable error messages
|
|
35
|
+
- HTML report escapes all user-controlled fields (XSS fix)
|
|
36
|
+
- `PromptLock` class defaults `snapshotDir` and `reportDir` properly
|
|
37
|
+
|
|
38
|
+
### Security
|
|
39
|
+
|
|
40
|
+
- Snapshot and cache file paths sanitize prompt IDs to prevent path traversal
|
|
41
|
+
- Custom provider URLs validated to require `http://` or `https://` protocol
|
|
42
|
+
- Parallel runner uses queue-based dispatch to prevent race conditions
|
|
43
|
+
|
|
44
|
+
### Packaging
|
|
45
|
+
|
|
46
|
+
- Added `files` field — npm tarball now ships only `dist/`, `README.md`, `CHANGELOG.md`, `LICENSE`
|
|
47
|
+
- Added `exports` field for ESM/CJS resolution
|
|
48
|
+
- Added `declarationMap` for IDE go-to-definition
|
|
49
|
+
- Added `LICENSE` (MIT)
|
|
50
|
+
- `prepublishOnly` now runs both build and tests
|
|
51
|
+
- Added `repository`, `homepage`, `author`, `bugs` to package.json
|
|
52
|
+
|
|
53
|
+
## [0.1.0] - 2026-04-05
|
|
54
|
+
|
|
55
|
+
### Added
|
|
56
|
+
|
|
57
|
+
- `PromptLock` class for defining prompts with behavioral assertions and snapshot baselines
|
|
58
|
+
- CLI commands: `init`, `run`, `snapshot`, `diff`
|
|
59
|
+
- 11 built-in assertion types: `contains`, `not-contains`, `starts-with`, `ends-with`, `matches-regex`, `max-length`, `min-length`, `json-valid`, `json-schema`, `no-hallucination-words`, `custom`
|
|
60
|
+
- OpenAI and Anthropic provider adapters with lazy SDK loading
|
|
61
|
+
- Snapshot capture, storage, and diff (file-based in `.promptlock/snapshots/`)
|
|
62
|
+
- Report generation: console (colorized), JSON, and self-contained HTML
|
|
63
|
+
- CI mode with exit code 1 on assertion failure (`--ci` flag)
|
|
64
|
+
- Project scaffolding via `prompt-lock init`
|
|
65
|
+
- GitHub Actions example in README
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shmuli David
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# prompt-lock
|
|
2
|
+
|
|
3
|
+
**Version control and behavioral regression testing for LLM prompts.**
|
|
4
|
+
|
|
5
|
+
prompt-lock wraps your prompts with behavioral assertions and snapshot baselines. On every change, it runs the assertion suite and flags regressions — like Jest for LLM behavior.
|
|
6
|
+
The lightweight, code-first alternative to promptfoo. TypeScript-native. Works with any LLM endpoint. Zero cloud dependencies.
|
|
7
|
+
|
|
8
|
+
## Demo
|
|
9
|
+
|
|
10
|
+
Run the demo without any API keys to see prompt-lock in action:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
git clone https://github.com/shmulikdav/Promptlock.git
|
|
14
|
+
cd Promptlock
|
|
15
|
+
npm install && npm run build
|
|
16
|
+
node demo.js
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This runs 4 simulated prompts (2 passing, 2 failing), saves snapshots, shows diffs, and generates an HTML report — all with mock LLM outputs.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install prompt-lock
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
You'll also need at least one LLM provider SDK (or use a custom endpoint):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# For Anthropic
|
|
31
|
+
npm install @anthropic-ai/sdk
|
|
32
|
+
|
|
33
|
+
# For OpenAI
|
|
34
|
+
npm install openai
|
|
35
|
+
|
|
36
|
+
# For Ollama, LM Studio, Azure, etc. — no extra install needed!
|
|
37
|
+
# Use the custom provider: { type: 'custom', url: 'http://localhost:11434/api/generate' }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 1. Initialize prompt-lock in your project
|
|
44
|
+
npx prompt-lock init
|
|
45
|
+
|
|
46
|
+
# 2. Edit the example prompt in prompts/example.js
|
|
47
|
+
|
|
48
|
+
# 3. Set your API key
|
|
49
|
+
export ANTHROPIC_API_KEY=your-key-here
|
|
50
|
+
|
|
51
|
+
# 4. Run assertions
|
|
52
|
+
npx prompt-lock run
|
|
53
|
+
|
|
54
|
+
# 5. Save a snapshot baseline
|
|
55
|
+
npx prompt-lock snapshot
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Defining Prompts
|
|
59
|
+
|
|
60
|
+
Create `.js` files in your `prompts/` directory. You can export a single config or an array of configs:
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
// prompts/summarizer.js
|
|
64
|
+
module.exports = {
|
|
65
|
+
id: 'article-summarizer',
|
|
66
|
+
version: '1.0.0',
|
|
67
|
+
provider: 'anthropic',
|
|
68
|
+
model: 'claude-sonnet-4-20250514',
|
|
69
|
+
|
|
70
|
+
prompt: `You are a professional summarizer.
|
|
71
|
+
Summarize the following article in 3 bullet points.
|
|
72
|
+
Article: {{article}}`,
|
|
73
|
+
|
|
74
|
+
defaultVars: {
|
|
75
|
+
article: 'The quick brown fox jumped over the lazy dog.'
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
assertions: [
|
|
79
|
+
{ type: 'contains', value: '•' },
|
|
80
|
+
{ type: 'max-length', chars: 500 },
|
|
81
|
+
{ type: 'not-contains', value: 'I cannot' },
|
|
82
|
+
{ type: 'max-latency', ms: 10000 },
|
|
83
|
+
]
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Using Custom Providers (Ollama, LM Studio, Azure, etc.)
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
module.exports = {
|
|
91
|
+
id: 'local-test',
|
|
92
|
+
provider: {
|
|
93
|
+
type: 'custom',
|
|
94
|
+
url: 'http://localhost:11434/api/generate',
|
|
95
|
+
// Optional: custom headers for auth
|
|
96
|
+
headers: { 'Authorization': 'Bearer ...' },
|
|
97
|
+
// Optional: custom response path (auto-detects OpenAI, Anthropic, Ollama formats)
|
|
98
|
+
responsePath: 'response',
|
|
99
|
+
},
|
|
100
|
+
model: 'llama3',
|
|
101
|
+
prompt: 'Hello {{name}}',
|
|
102
|
+
defaultVars: { name: 'world' },
|
|
103
|
+
assertions: [
|
|
104
|
+
{ type: 'min-length', chars: 5 },
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Testing with Datasets
|
|
110
|
+
|
|
111
|
+
Test a prompt against multiple inputs:
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
module.exports = {
|
|
115
|
+
id: 'classifier',
|
|
116
|
+
provider: 'openai',
|
|
117
|
+
model: 'gpt-4o-mini',
|
|
118
|
+
prompt: 'Classify this ticket: {{ticket}}',
|
|
119
|
+
defaultVars: { ticket: 'My payment failed' },
|
|
120
|
+
dataset: [
|
|
121
|
+
{ ticket: 'My payment failed' },
|
|
122
|
+
{ ticket: 'How do I reset my password?' },
|
|
123
|
+
{ ticket: 'Your product is great!' },
|
|
124
|
+
],
|
|
125
|
+
assertions: [
|
|
126
|
+
{ type: 'json-valid' },
|
|
127
|
+
{ type: 'max-latency', ms: 5000 },
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Programmatic Usage
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { PromptLock } from 'prompt-lock';
|
|
136
|
+
|
|
137
|
+
const lock = new PromptLock({
|
|
138
|
+
id: 'my-prompt',
|
|
139
|
+
version: '1.0.0',
|
|
140
|
+
provider: 'anthropic',
|
|
141
|
+
model: 'claude-sonnet-4-20250514',
|
|
142
|
+
prompt: 'Translate to French: {{text}}',
|
|
143
|
+
defaultVars: { text: 'Hello world' },
|
|
144
|
+
assertions: [
|
|
145
|
+
{ type: 'not-contains', value: 'Hello' },
|
|
146
|
+
{ type: 'min-length', chars: 5 },
|
|
147
|
+
],
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const results = await lock.run();
|
|
151
|
+
console.log(results[0].passed); // true or false
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## CLI Reference
|
|
155
|
+
|
|
156
|
+
### `prompt-lock init`
|
|
157
|
+
|
|
158
|
+
Scaffold a `.promptlock/` folder with config and an example prompt.
|
|
159
|
+
|
|
160
|
+
### `prompt-lock run`
|
|
161
|
+
|
|
162
|
+
Run all assertions against current prompts.
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
prompt-lock run # Run all prompts
|
|
166
|
+
prompt-lock run --id my-prompt # Run a specific prompt
|
|
167
|
+
prompt-lock run --ci # Exit code 1 on failure
|
|
168
|
+
prompt-lock run --report html # Generate HTML report
|
|
169
|
+
prompt-lock run --report json # Generate JSON report
|
|
170
|
+
prompt-lock run --report both # Generate both reports
|
|
171
|
+
prompt-lock run --dry-run # Show what would be tested without calling LLMs
|
|
172
|
+
prompt-lock run --verbose # Show detailed output per prompt
|
|
173
|
+
prompt-lock run --parallel # Run prompts in parallel
|
|
174
|
+
prompt-lock run --concurrency 10 # Max concurrent runs (default: 5)
|
|
175
|
+
prompt-lock run --cache # Cache LLM outputs (skip unchanged prompts)
|
|
176
|
+
prompt-lock run --github-pr owner/repo#123 # Post results as PR comment
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `prompt-lock snapshot`
|
|
180
|
+
|
|
181
|
+
Capture and save the current output as a baseline. Snapshots are versioned — previous snapshots are kept as history.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
prompt-lock snapshot # Snapshot all prompts
|
|
185
|
+
prompt-lock snapshot --id my-prompt
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `prompt-lock diff`
|
|
189
|
+
|
|
190
|
+
Compare current LLM output against the last saved snapshot.
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
prompt-lock diff # Diff all prompts
|
|
194
|
+
prompt-lock diff --id my-prompt
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### `prompt-lock history`
|
|
198
|
+
|
|
199
|
+
View snapshot history for a prompt.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
prompt-lock history my-prompt
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `prompt-lock cache`
|
|
206
|
+
|
|
207
|
+
Manage the output cache (used with `--cache` flag).
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
prompt-lock cache stats # Show cache size and entries
|
|
211
|
+
prompt-lock cache clear # Clear all cached outputs
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Output Caching
|
|
215
|
+
|
|
216
|
+
Use `--cache` to skip LLM calls for prompts that haven't changed. Cached outputs are stored in `.promptlock/cache/` and keyed by prompt text + model name.
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# First run: calls the LLM, saves to cache
|
|
220
|
+
prompt-lock run --cache
|
|
221
|
+
|
|
222
|
+
# Second run: uses cache, instant results
|
|
223
|
+
prompt-lock run --cache
|
|
224
|
+
|
|
225
|
+
# Clear cache when you want fresh results
|
|
226
|
+
prompt-lock cache clear
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Retry Logic
|
|
230
|
+
|
|
231
|
+
All LLM provider calls automatically retry on transient errors (rate limits, timeouts, network errors) with exponential backoff. Default: 3 retries. Use `--verbose` to see retry activity.
|
|
232
|
+
|
|
233
|
+
### GitHub PR Comments
|
|
234
|
+
|
|
235
|
+
Post test results directly to a GitHub pull request:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
export GITHUB_TOKEN=your-token
|
|
239
|
+
prompt-lock run --ci --github-pr owner/repo#123
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
This posts a markdown table with pass/fail results and failure details. If a comment already exists, it updates in place.
|
|
243
|
+
|
|
244
|
+
## Assertion Reference
|
|
245
|
+
|
|
246
|
+
| Assertion | Config | What it checks |
|
|
247
|
+
|-----------|--------|---------------|
|
|
248
|
+
| `contains` | `value: string` | Output contains the string |
|
|
249
|
+
| `not-contains` | `value: string` | Output does NOT contain the string |
|
|
250
|
+
| `contains-all` | `values: string[]` | Output contains ALL listed strings |
|
|
251
|
+
| `starts-with` | `value: string` | Output starts with the string |
|
|
252
|
+
| `ends-with` | `value: string` | Output ends with the string |
|
|
253
|
+
| `matches-regex` | `pattern: string` | Output matches regex pattern |
|
|
254
|
+
| `max-length` | `chars: number` | Output is under N characters |
|
|
255
|
+
| `min-length` | `chars: number` | Output is over N characters |
|
|
256
|
+
| `json-valid` | — | Output is valid JSON |
|
|
257
|
+
| `json-schema` | `schema: object` | Output JSON matches a JSON Schema |
|
|
258
|
+
| `no-hallucination-words` | `words?: string[]` | Output does NOT contain hallucination indicators |
|
|
259
|
+
| `no-duplicates` | `separator?: string` | Output has no duplicate items (split by separator, default `\n`) |
|
|
260
|
+
| `max-latency` | `ms: number` | LLM response time is under N milliseconds |
|
|
261
|
+
| `custom` | `name: string, fn: (output) => boolean` | User-provided function returning boolean |
|
|
262
|
+
|
|
263
|
+
## Provider Setup
|
|
264
|
+
|
|
265
|
+
### Anthropic
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
export ANTHROPIC_API_KEY=your-key-here
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### OpenAI
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
export OPENAI_API_KEY=your-key-here
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Custom Provider (Ollama, LM Studio, Azure, any HTTP endpoint)
|
|
278
|
+
|
|
279
|
+
No API key needed for local models. Just set the URL:
|
|
280
|
+
|
|
281
|
+
```javascript
|
|
282
|
+
provider: {
|
|
283
|
+
type: 'custom',
|
|
284
|
+
url: 'http://localhost:11434/api/generate', // Ollama
|
|
285
|
+
// url: 'http://localhost:1234/v1/chat/completions', // LM Studio
|
|
286
|
+
// url: 'https://your-resource.openai.azure.com/openai/deployments/gpt-4/chat/completions?api-version=2024-02-01', // Azure
|
|
287
|
+
headers: { 'api-key': process.env.AZURE_API_KEY }, // Optional auth
|
|
288
|
+
responsePath: 'choices[0].message.content', // Optional: path to extract response
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Auto-detects response format for OpenAI, Anthropic, and Ollama APIs. Use `responsePath` for custom APIs.
|
|
293
|
+
|
|
294
|
+
## CI/CD Integration
|
|
295
|
+
|
|
296
|
+
### GitHub Actions
|
|
297
|
+
|
|
298
|
+
```yaml
|
|
299
|
+
name: Prompt Regression Tests
|
|
300
|
+
on: [push, pull_request]
|
|
301
|
+
jobs:
|
|
302
|
+
prompt-lock:
|
|
303
|
+
runs-on: ubuntu-latest
|
|
304
|
+
steps:
|
|
305
|
+
- uses: actions/checkout@v4
|
|
306
|
+
- uses: actions/setup-node@v4
|
|
307
|
+
with:
|
|
308
|
+
node-version: '20'
|
|
309
|
+
- run: npm install
|
|
310
|
+
- run: npx prompt-lock run --ci --cache --github-pr ${{ github.repository }}#${{ github.event.pull_request.number }}
|
|
311
|
+
env:
|
|
312
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
313
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
- `--ci` ensures exit code 1 when any assertion fails
|
|
317
|
+
- `--cache` skips LLM calls for unchanged prompts (faster, cheaper)
|
|
318
|
+
- `--github-pr` posts results as a PR comment with pass/fail table
|
|
319
|
+
- Automatic retry on rate limits and transient errors (3 retries with exponential backoff)
|
|
320
|
+
|
|
321
|
+
## Configuration
|
|
322
|
+
|
|
323
|
+
`.promptlock/config.json` (created by `init`):
|
|
324
|
+
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"promptsDir": "./prompts",
|
|
328
|
+
"snapshotDir": "./.promptlock/snapshots",
|
|
329
|
+
"reportDir": "./.promptlock/reports",
|
|
330
|
+
"defaultProvider": "anthropic",
|
|
331
|
+
"ci": {
|
|
332
|
+
"failOnRegression": true,
|
|
333
|
+
"reportFormat": ["json", "html"]
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Template Variables
|
|
339
|
+
|
|
340
|
+
Use `{{variableName}}` in your prompts. Supports alphanumeric, dashes, dots, and underscores:
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
{{article}} ✅
|
|
344
|
+
{{user-name}} ✅
|
|
345
|
+
{{api.version}} ✅
|
|
346
|
+
{{my_var}} ✅
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## License
|
|
350
|
+
|
|
351
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin.d.ts","sourceRoot":"","sources":["../../src/assertions/builtin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAc3C,KAAK,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,eAAe,CAAC;AAE7F,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAoK9D,CAAC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.builtinAssertions = void 0;
|
|
4
|
+
const DEFAULT_HALLUCINATION_WORDS = [
|
|
5
|
+
'As an AI',
|
|
6
|
+
'as an AI',
|
|
7
|
+
'I cannot',
|
|
8
|
+
'I can\'t',
|
|
9
|
+
'I don\'t have access',
|
|
10
|
+
'I\'m not able to',
|
|
11
|
+
'I am not able to',
|
|
12
|
+
'As a language model',
|
|
13
|
+
'as a language model',
|
|
14
|
+
];
|
|
15
|
+
exports.builtinAssertions = {
|
|
16
|
+
'contains': (output, config) => {
|
|
17
|
+
const value = config.value;
|
|
18
|
+
return {
|
|
19
|
+
type: 'contains',
|
|
20
|
+
name: `contains "${value}"`,
|
|
21
|
+
passed: output.includes(value),
|
|
22
|
+
expected: `output to contain "${value}"`,
|
|
23
|
+
actual: output.includes(value) ? 'found' : 'not found',
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
'not-contains': (output, config) => {
|
|
27
|
+
const value = config.value;
|
|
28
|
+
return {
|
|
29
|
+
type: 'not-contains',
|
|
30
|
+
name: `not-contains "${value}"`,
|
|
31
|
+
passed: !output.includes(value),
|
|
32
|
+
expected: `output to NOT contain "${value}"`,
|
|
33
|
+
actual: output.includes(value) ? 'found' : 'not found',
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
'starts-with': (output, config) => {
|
|
37
|
+
const value = config.value;
|
|
38
|
+
return {
|
|
39
|
+
type: 'starts-with',
|
|
40
|
+
name: `starts-with "${value}"`,
|
|
41
|
+
passed: output.startsWith(value),
|
|
42
|
+
expected: `output to start with "${value}"`,
|
|
43
|
+
actual: `starts with "${output.slice(0, value.length)}"`,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
'ends-with': (output, config) => {
|
|
47
|
+
const value = config.value;
|
|
48
|
+
return {
|
|
49
|
+
type: 'ends-with',
|
|
50
|
+
name: `ends-with "${value}"`,
|
|
51
|
+
passed: output.endsWith(value),
|
|
52
|
+
expected: `output to end with "${value}"`,
|
|
53
|
+
actual: `ends with "${output.slice(-value.length)}"`,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
'matches-regex': (output, config) => {
|
|
57
|
+
const pattern = config.pattern;
|
|
58
|
+
let regex;
|
|
59
|
+
try {
|
|
60
|
+
regex = new RegExp(pattern);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
return {
|
|
64
|
+
type: 'matches-regex',
|
|
65
|
+
name: `matches-regex /${pattern}/`,
|
|
66
|
+
passed: false,
|
|
67
|
+
expected: `output to match /${pattern}/`,
|
|
68
|
+
actual: `invalid regex: ${e.message}`,
|
|
69
|
+
message: `Invalid regex pattern: ${e.message}`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const matched = regex.test(output);
|
|
73
|
+
return {
|
|
74
|
+
type: 'matches-regex',
|
|
75
|
+
name: `matches-regex /${pattern}/`,
|
|
76
|
+
passed: matched,
|
|
77
|
+
expected: `output to match /${pattern}/`,
|
|
78
|
+
actual: matched ? 'matched' : 'no match',
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
'max-length': (output, config) => {
|
|
82
|
+
const chars = config.chars;
|
|
83
|
+
return {
|
|
84
|
+
type: 'max-length',
|
|
85
|
+
name: `max-length ${chars}`,
|
|
86
|
+
passed: output.length <= chars,
|
|
87
|
+
expected: `<= ${chars} chars`,
|
|
88
|
+
actual: `${output.length} chars`,
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
'min-length': (output, config) => {
|
|
92
|
+
const chars = config.chars;
|
|
93
|
+
return {
|
|
94
|
+
type: 'min-length',
|
|
95
|
+
name: `min-length ${chars}`,
|
|
96
|
+
passed: output.length >= chars,
|
|
97
|
+
expected: `>= ${chars} chars`,
|
|
98
|
+
actual: `${output.length} chars`,
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
'json-valid': (output) => {
|
|
102
|
+
let passed = false;
|
|
103
|
+
let message;
|
|
104
|
+
try {
|
|
105
|
+
JSON.parse(output);
|
|
106
|
+
passed = true;
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
message = e.message;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
type: 'json-valid',
|
|
113
|
+
name: 'json-valid',
|
|
114
|
+
passed,
|
|
115
|
+
expected: 'valid JSON',
|
|
116
|
+
actual: passed ? 'valid JSON' : `invalid JSON: ${message}`,
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
'contains-all': (output, config) => {
|
|
120
|
+
const values = config.values;
|
|
121
|
+
const missing = values.filter(v => !output.includes(v));
|
|
122
|
+
return {
|
|
123
|
+
type: 'contains-all',
|
|
124
|
+
name: `contains-all [${values.length} items]`,
|
|
125
|
+
passed: missing.length === 0,
|
|
126
|
+
expected: `output to contain all of: ${values.join(', ')}`,
|
|
127
|
+
actual: missing.length > 0 ? `missing: ${missing.join(', ')}` : 'all found',
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
'no-duplicates': (output, config) => {
|
|
131
|
+
const separator = config.separator ?? '\n';
|
|
132
|
+
const items = output.split(separator).map(s => s.trim()).filter(Boolean);
|
|
133
|
+
const seen = new Set();
|
|
134
|
+
const duplicates = [];
|
|
135
|
+
for (const item of items) {
|
|
136
|
+
if (seen.has(item))
|
|
137
|
+
duplicates.push(item);
|
|
138
|
+
seen.add(item);
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
type: 'no-duplicates',
|
|
142
|
+
name: 'no-duplicates',
|
|
143
|
+
passed: duplicates.length === 0,
|
|
144
|
+
expected: 'no duplicate items',
|
|
145
|
+
actual: duplicates.length > 0 ? `duplicates: ${duplicates.join(', ')}` : 'no duplicates',
|
|
146
|
+
};
|
|
147
|
+
},
|
|
148
|
+
'max-latency': (output, config) => {
|
|
149
|
+
// This is evaluated by the runner which injects __duration into config
|
|
150
|
+
const ms = config.ms;
|
|
151
|
+
const actual = config.__duration ?? 0;
|
|
152
|
+
return {
|
|
153
|
+
type: 'max-latency',
|
|
154
|
+
name: `max-latency ${ms}ms`,
|
|
155
|
+
passed: actual <= ms,
|
|
156
|
+
expected: `<= ${ms}ms`,
|
|
157
|
+
actual: `${actual}ms`,
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
'no-hallucination-words': (output, config) => {
|
|
161
|
+
const words = config.words ?? DEFAULT_HALLUCINATION_WORDS;
|
|
162
|
+
const found = words.filter(w => output.includes(w));
|
|
163
|
+
return {
|
|
164
|
+
type: 'no-hallucination-words',
|
|
165
|
+
name: 'no-hallucination-words',
|
|
166
|
+
passed: found.length === 0,
|
|
167
|
+
expected: 'no hallucination words',
|
|
168
|
+
actual: found.length > 0 ? `found: ${found.join(', ')}` : 'none found',
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=builtin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin.js","sourceRoot":"","sources":["../../src/assertions/builtin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/assertions/custom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACjD,OAAO,CAAC,eAAe,CAAC,CAoB1B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertCustom = assertCustom;
|
|
4
|
+
async function assertCustom(output, name, fn) {
|
|
5
|
+
try {
|
|
6
|
+
const result = await fn(output);
|
|
7
|
+
return {
|
|
8
|
+
type: 'custom',
|
|
9
|
+
name: `custom: ${name}`,
|
|
10
|
+
passed: !!result,
|
|
11
|
+
expected: `custom assertion "${name}" to pass`,
|
|
12
|
+
actual: result ? 'passed' : 'failed',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
return {
|
|
17
|
+
type: 'custom',
|
|
18
|
+
name: `custom: ${name}`,
|
|
19
|
+
passed: false,
|
|
20
|
+
expected: `custom assertion "${name}" to pass`,
|
|
21
|
+
actual: `threw error: ${e.message}`,
|
|
22
|
+
message: e.message,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=custom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom.js","sourceRoot":"","sources":["../../src/assertions/custom.ts"],"names":[],"mappings":";;AAEA,oCAwBC;AAxBM,KAAK,UAAU,YAAY,CAChC,MAAc,EACd,IAAY,EACZ,EAAkD;IAElD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;QAChC,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,WAAW,IAAI,EAAE;YACvB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,QAAQ,EAAE,qBAAqB,IAAI,WAAW;YAC9C,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ;SACrC,CAAC;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,WAAW,IAAI,EAAE;YACvB,MAAM,EAAE,KAAK;YACb,QAAQ,EAAE,qBAAqB,IAAI,WAAW;YAC9C,MAAM,EAAE,gBAAiB,CAAW,CAAC,OAAO,EAAE;YAC9C,OAAO,EAAG,CAAW,CAAC,OAAO;SAC9B,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/assertions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK5D,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,eAAe,EAAE,GAC5B,OAAO,CAAC,eAAe,EAAE,CAAC,CA6B5B"}
|