promptguard-sdk 1.8.0 → 1.10.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/README.md +116 -27
- package/dist/auto.d.ts +59 -1
- package/dist/auto.d.ts.map +1 -1
- package/dist/auto.js +84 -6
- package/dist/auto.js.map +1 -1
- package/dist/client.d.ts +120 -36
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +209 -26
- package/dist/client.js.map +1 -1
- package/dist/generated/api-types.d.ts +78 -0
- package/dist/generated/api-types.d.ts.map +1 -1
- package/dist/guard.d.ts +80 -0
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +147 -28
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/integrations/langchain.d.ts +46 -1
- package/dist/integrations/langchain.d.ts.map +1 -1
- package/dist/integrations/langchain.js +97 -10
- package/dist/integrations/langchain.js.map +1 -1
- package/dist/integrations/vercel-ai.d.ts +19 -1
- package/dist/integrations/vercel-ai.d.ts.map +1 -1
- package/dist/integrations/vercel-ai.js +158 -29
- package/dist/integrations/vercel-ai.js.map +1 -1
- package/dist/logger.d.ts +31 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +63 -0
- package/dist/logger.js.map +1 -0
- package/dist/patches/anthropic.d.ts +16 -0
- package/dist/patches/anthropic.d.ts.map +1 -1
- package/dist/patches/anthropic.js +69 -32
- package/dist/patches/anthropic.js.map +1 -1
- package/dist/patches/base.d.ts +17 -1
- package/dist/patches/base.d.ts.map +1 -1
- package/dist/patches/base.js +98 -7
- package/dist/patches/base.js.map +1 -1
- package/dist/patches/bedrock.d.ts +35 -2
- package/dist/patches/bedrock.d.ts.map +1 -1
- package/dist/patches/bedrock.js +195 -23
- package/dist/patches/bedrock.js.map +1 -1
- package/dist/patches/cohere.d.ts +11 -0
- package/dist/patches/cohere.d.ts.map +1 -1
- package/dist/patches/cohere.js +76 -2
- package/dist/patches/cohere.js.map +1 -1
- package/dist/patches/google.d.ts +8 -0
- package/dist/patches/google.d.ts.map +1 -1
- package/dist/patches/google.js +54 -0
- package/dist/patches/google.js.map +1 -1
- package/dist/patches/openai.d.ts +26 -5
- package/dist/patches/openai.d.ts.map +1 -1
- package/dist/patches/openai.js +225 -21
- package/dist/patches/openai.js.map +1 -1
- package/dist/resolve.d.ts.map +1 -1
- package/dist/resolve.js +6 -1
- package/dist/resolve.js.map +1 -1
- package/dist/retry.d.ts +54 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +99 -0
- package/dist/retry.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -1
- package/dist/version.js.map +1 -1
- package/package.json +33 -6
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[](https://www.npmjs.com/package/promptguard)
|
|
1
|
+
[](https://www.npmjs.com/package/promptguard-sdk)
|
|
2
2
|
[](https://github.com/acebot712/promptguard-node/actions/workflows/ci.yml)
|
|
3
3
|
[](https://github.com/acebot712/promptguard-node/blob/main/LICENSE)
|
|
4
4
|
[](https://www.typescriptlang.org/)
|
|
@@ -13,18 +13,36 @@ Drop-in security for AI applications. Secure any GenAI app - regardless of frame
|
|
|
13
13
|
npm install promptguard-sdk
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
> The npm package and the import specifier are both **`promptguard-sdk`** — no surprises:
|
|
17
|
+
> ```typescript
|
|
18
|
+
> import { init, PromptGuard } from 'promptguard-sdk';
|
|
19
|
+
> ```
|
|
20
|
+
|
|
21
|
+
Get a free API key at [app.promptguard.co](https://app.promptguard.co).
|
|
22
|
+
|
|
23
|
+
> **The SDK reads `PROMPTGUARD_API_KEY` from the environment; it does not auto-load `.env`.** Use [dotenv](https://www.npmjs.com/package/dotenv) (call `import 'dotenv/config'` first) if you keep secrets in a `.env` file.
|
|
24
|
+
|
|
25
|
+
> **PromptGuard fails open by default** — if the Guard API is unavailable, calls proceed *unscanned* so your app stays up. Set `failOpen: false` to block (fail closed) on a Guard outage instead.
|
|
26
|
+
|
|
27
|
+
> **Module format:** the package currently ships **CommonJS** (`require`) builds. It works in ESM projects via Node's CJS interop (`import { init } from 'promptguard-sdk'` transpiles to a `require`), and in plain CommonJS via `const { init } = require('promptguard-sdk')`.
|
|
28
|
+
>
|
|
29
|
+
> **Running a native-ESM app?** Auto-instrumentation (`init()`) may not cover your LLM calls — see [Limitations: ESM apps](#limitations) before relying on enforce mode.
|
|
30
|
+
|
|
16
31
|
## Option 1: Auto-Instrumentation (Recommended)
|
|
17
32
|
|
|
18
33
|
One line secures **every** LLM call in your application - no matter which framework you use.
|
|
19
34
|
|
|
20
35
|
```typescript
|
|
36
|
+
// All imports first — ES module imports are hoisted and always run before
|
|
37
|
+
// any other statement, regardless of their position in the file.
|
|
21
38
|
import { init } from 'promptguard-sdk';
|
|
39
|
+
import OpenAI from 'openai';
|
|
22
40
|
|
|
23
|
-
//
|
|
24
|
-
|
|
41
|
+
// init() runs as the first executed statement and patches the SDK prototypes.
|
|
42
|
+
// Patching works regardless of import order, so you don't need to worry about
|
|
43
|
+
// importing the LLM SDK "after" calling init().
|
|
44
|
+
init({ apiKey: 'pg_live_xxx' });
|
|
25
45
|
|
|
26
|
-
// Everything below is now secured transparently.
|
|
27
|
-
import OpenAI from 'openai';
|
|
28
46
|
const client = new OpenAI();
|
|
29
47
|
|
|
30
48
|
// This call is automatically scanned by PromptGuard.
|
|
@@ -34,35 +52,51 @@ const response = await client.chat.completions.create({
|
|
|
34
52
|
});
|
|
35
53
|
```
|
|
36
54
|
|
|
55
|
+
> `init()` is intentionally quiet on success (SDK logging defaults to `warn`). To confirm which provider SDKs are actually being protected, read `getAppliedPatches()` — the source of truth for what got patched:
|
|
56
|
+
> ```typescript
|
|
57
|
+
> import { init, getAppliedPatches } from 'promptguard-sdk';
|
|
58
|
+
> init({ apiKey: 'pg_live_xxx' });
|
|
59
|
+
> console.log('PromptGuard protecting:', getAppliedPatches()); // e.g. ['openai']
|
|
60
|
+
> ```
|
|
61
|
+
> If this list is empty, nothing is being scanned (see [Limitations: ESM apps](#limitations)). Set `logLevel: 'info'` on `init()` to also emit a one-line confirmation banner.
|
|
62
|
+
|
|
37
63
|
### Supported SDKs
|
|
38
64
|
|
|
39
65
|
Auto-instrumentation patches the `create` / `generateContent` / `chat` / `send` methods on:
|
|
40
66
|
|
|
41
67
|
| SDK | npm Package | What Gets Patched |
|
|
42
68
|
|-----|------------|-------------------|
|
|
43
|
-
| OpenAI | `openai` | `chat.completions.create` |
|
|
69
|
+
| OpenAI | `openai` | `chat.completions.create`, `responses.create` (string and message-item `input` forms) |
|
|
44
70
|
| Anthropic | `@anthropic-ai/sdk` | `messages.create` |
|
|
45
71
|
| Google Generative AI | `@google/generative-ai` | `generateContent` |
|
|
46
72
|
| Cohere | `cohere-ai` | `Client.chat` / `ClientV2.chat` |
|
|
47
|
-
| AWS Bedrock | `@aws-sdk/client-bedrock-runtime` | `BedrockRuntimeClient.send` (InvokeModel, Converse) |
|
|
73
|
+
| AWS Bedrock | `@aws-sdk/client-bedrock-runtime` | `BedrockRuntimeClient.send` (InvokeModel, InvokeModelWithResponseStream, Converse, ConverseStream) |
|
|
48
74
|
|
|
49
75
|
Any framework built on these SDKs is automatically covered: **LangChain.js**, **Vercel AI SDK**, **AutoGen**, **Semantic Kernel**, and more.
|
|
50
76
|
|
|
77
|
+
> Patches attach to the modules resolved via CommonJS `require()`. If `init()` finds **no** patchable SDK it logs a warning (nothing would be scanned). You can also verify at runtime with `getAppliedPatches()`:
|
|
78
|
+
> ```typescript
|
|
79
|
+
> import { init, getAppliedPatches } from 'promptguard-sdk';
|
|
80
|
+
> init({ apiKey: 'pg_live_xxx' });
|
|
81
|
+
> console.log(getAppliedPatches()); // e.g. ['openai', 'anthropic']
|
|
82
|
+
> ```
|
|
83
|
+
> Native-ESM apps: see [Limitations: ESM apps](#limitations).
|
|
84
|
+
|
|
51
85
|
### Modes
|
|
52
86
|
|
|
53
87
|
```typescript
|
|
54
88
|
// Enforce mode (default) - blocks policy violations.
|
|
55
|
-
init({ apiKey: '
|
|
89
|
+
init({ apiKey: 'pg_live_xxx', mode: 'enforce' });
|
|
56
90
|
|
|
57
91
|
// Monitor mode - logs threats but never blocks. Good for shadow deployment.
|
|
58
|
-
init({ apiKey: '
|
|
92
|
+
init({ apiKey: 'pg_live_xxx', mode: 'monitor' });
|
|
59
93
|
```
|
|
60
94
|
|
|
61
95
|
### Options
|
|
62
96
|
|
|
63
97
|
```typescript
|
|
64
98
|
init({
|
|
65
|
-
apiKey: '
|
|
99
|
+
apiKey: 'pg_live_xxx', // or set PROMPTGUARD_API_KEY env var
|
|
66
100
|
baseUrl: 'https://...', // or set PROMPTGUARD_BASE_URL env var
|
|
67
101
|
mode: 'enforce', // 'enforce' | 'monitor'
|
|
68
102
|
failOpen: true, // allow calls when Guard API is unreachable
|
|
@@ -87,7 +121,7 @@ Route LLM traffic through PromptGuard. Just swap your base URL.
|
|
|
87
121
|
```typescript
|
|
88
122
|
import { PromptGuard } from 'promptguard-sdk';
|
|
89
123
|
|
|
90
|
-
const pg = new PromptGuard({ apiKey: '
|
|
124
|
+
const pg = new PromptGuard({ apiKey: 'pg_live_xxx' });
|
|
91
125
|
|
|
92
126
|
// Use exactly like the OpenAI client.
|
|
93
127
|
const response = await pg.chat.completions.create({
|
|
@@ -123,7 +157,7 @@ import { PromptGuardCallbackHandler } from 'promptguard-sdk/integrations/langcha
|
|
|
123
157
|
import { ChatOpenAI } from '@langchain/openai';
|
|
124
158
|
|
|
125
159
|
const handler = new PromptGuardCallbackHandler({
|
|
126
|
-
apiKey: '
|
|
160
|
+
apiKey: 'pg_live_xxx',
|
|
127
161
|
mode: 'enforce',
|
|
128
162
|
scanResponses: true,
|
|
129
163
|
});
|
|
@@ -143,6 +177,10 @@ const result = await chain.invoke(
|
|
|
143
177
|
|
|
144
178
|
The callback handler provides rich context to PromptGuard - chain names, tool calls, agent steps - for more precise threat detection.
|
|
145
179
|
|
|
180
|
+
> **Redact decisions block in enforce mode:** LangChain callbacks observe calls but cannot rewrite the inputs of the in-flight LLM call, so a `redact` decision cannot be honored — in enforce mode it is escalated to a block (`PromptGuardBlockedError`) rather than silently sending the content the Guard API asked to redact. Use auto-instrumentation or explicit `GuardClient.scan()` calls if you need actual redaction.
|
|
181
|
+
>
|
|
182
|
+
> `scanResponses` defaults to `false` (consistent with `init()` and the Vercel AI middleware) — pass `scanResponses: true` to opt in to output scanning.
|
|
183
|
+
|
|
146
184
|
### Vercel AI SDK
|
|
147
185
|
|
|
148
186
|
```typescript
|
|
@@ -153,7 +191,7 @@ import { promptGuardMiddleware } from 'promptguard-sdk/integrations/vercel-ai';
|
|
|
153
191
|
const model = wrapLanguageModel({
|
|
154
192
|
model: openai('gpt-5-nano'),
|
|
155
193
|
middleware: promptGuardMiddleware({
|
|
156
|
-
apiKey: '
|
|
194
|
+
apiKey: 'pg_live_xxx',
|
|
157
195
|
mode: 'enforce',
|
|
158
196
|
scanResponses: true,
|
|
159
197
|
}),
|
|
@@ -172,13 +210,12 @@ Use the Guard client directly for maximum control:
|
|
|
172
210
|
```typescript
|
|
173
211
|
import { GuardClient } from 'promptguard-sdk';
|
|
174
212
|
|
|
175
|
-
const guard = new GuardClient({ apiKey: '
|
|
213
|
+
const guard = new GuardClient({ apiKey: 'pg_live_xxx' });
|
|
176
214
|
|
|
177
|
-
// Scan before sending to LLM
|
|
215
|
+
// Scan before sending to LLM (options-object form, preferred)
|
|
178
216
|
const decision = await guard.scan(
|
|
179
217
|
[{ role: 'user', content: userInput }],
|
|
180
|
-
'input',
|
|
181
|
-
'gpt-5-nano',
|
|
218
|
+
{ direction: 'input', model: 'gpt-5-nano' },
|
|
182
219
|
);
|
|
183
220
|
|
|
184
221
|
if (decision.blocked) {
|
|
@@ -191,23 +228,44 @@ if (decision.blocked) {
|
|
|
191
228
|
// Scan LLM response
|
|
192
229
|
const outputDecision = await guard.scan(
|
|
193
230
|
[{ role: 'assistant', content: llmOutput }],
|
|
194
|
-
'output',
|
|
231
|
+
{ direction: 'output' },
|
|
195
232
|
);
|
|
233
|
+
|
|
234
|
+
// The positional form still works for back-compat:
|
|
235
|
+
// await guard.scan(messages, 'input', 'gpt-5-nano');
|
|
196
236
|
```
|
|
197
237
|
|
|
198
238
|
## Retry Logic
|
|
199
239
|
|
|
200
|
-
Both `PromptGuard` and `GuardClient` support configurable retry behavior for transient failures:
|
|
240
|
+
Both the proxy client (`PromptGuard`) and the Guard client (`GuardClient`) support configurable retry behavior for transient failures:
|
|
201
241
|
|
|
202
242
|
```typescript
|
|
243
|
+
// Proxy client
|
|
203
244
|
const pg = new PromptGuard({
|
|
204
|
-
apiKey: '
|
|
205
|
-
maxRetries: 3, // Number of retry attempts (default:
|
|
206
|
-
retryDelay: 500, // Base delay in ms between retries (default:
|
|
245
|
+
apiKey: 'pg_live_xxx',
|
|
246
|
+
maxRetries: 3, // Number of retry attempts (default: 3)
|
|
247
|
+
retryDelay: 500, // Base delay in ms between retries (default: 1000)
|
|
207
248
|
});
|
|
249
|
+
|
|
250
|
+
// Guard client (standalone scanning)
|
|
251
|
+
const guard = new GuardClient({
|
|
252
|
+
apiKey: 'pg_live_xxx',
|
|
253
|
+
maxRetries: 3, // default: 3
|
|
254
|
+
retryDelay: 500, // default: 1000
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Because auto-instrumentation (`init()`) and the framework integrations all scan through `GuardClient`, they retry transient Guard API failures too — pass `maxRetries` / `retryDelay` to `init()`, `PromptGuardCallbackHandler`, or `promptGuardMiddleware`:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
init({ apiKey: 'pg_live_xxx', maxRetries: 3, retryDelay: 500 });
|
|
208
262
|
```
|
|
209
263
|
|
|
210
|
-
Retries use exponential backoff starting from `retryDelay
|
|
264
|
+
Retries use exponential backoff starting from `retryDelay`, with jitter so concurrent clients don't retry in lockstep. A server-provided `Retry-After` header is honored but clamped to 60 seconds. Only transient errors (network timeouts, 429/5xx responses) are retried; client errors (4xx) fail immediately.
|
|
265
|
+
|
|
266
|
+
> **Enforcement is unchanged by retries.** Retrying only affects *transient* Guard failures (network errors, 429/5xx). A real `block` / `redact` decision is terminal and is never retried, and once retries are exhausted a `GuardApiError` is raised so your existing `failOpen` policy governs — exactly as it would with `maxRetries: 0`. Retries never turn a would-be error into an allow.
|
|
267
|
+
|
|
268
|
+
> **Idempotency caveat:** all requests — including `POST`s — are retried on transient failure. If a request reached the server but the response was lost, the retry re-submits it. Chat/completion/scan calls are safe to re-submit, but each attempt may bill separately; set `maxRetries: 0` if you need strict at-most-once semantics.
|
|
211
269
|
|
|
212
270
|
## Embeddings
|
|
213
271
|
|
|
@@ -250,18 +308,18 @@ if (!validation.allowed) {
|
|
|
250
308
|
## Red Team Testing
|
|
251
309
|
|
|
252
310
|
```typescript
|
|
253
|
-
const pg = new PromptGuard({ apiKey: '
|
|
311
|
+
const pg = new PromptGuard({ apiKey: 'pg_live_xxx' });
|
|
254
312
|
|
|
255
313
|
// Run the autonomous red team agent (LLM-powered mutation)
|
|
256
314
|
const report = await pg.redteam.runAutonomous({
|
|
257
315
|
budget: 200,
|
|
258
|
-
|
|
316
|
+
targetPreset: 'support_bot:strict', // snake_case `target_preset` also accepted
|
|
259
317
|
});
|
|
260
|
-
console.log(`Grade: ${report.grade}, Bypass rate: ${(report.
|
|
318
|
+
console.log(`Grade: ${report.grade}, Bypass rate: ${(report.bypassRate * 100).toFixed(0)}%`);
|
|
261
319
|
|
|
262
320
|
// Get Attack Intelligence stats
|
|
263
321
|
const stats = await pg.redteam.intelligenceStats();
|
|
264
|
-
console.log(`Total patterns: ${stats.
|
|
322
|
+
console.log(`Total patterns: ${stats.totalPatterns}`);
|
|
265
323
|
```
|
|
266
324
|
|
|
267
325
|
## Configuration
|
|
@@ -274,6 +332,35 @@ console.log(`Total patterns: ${stats.total_patterns}`);
|
|
|
274
332
|
| `failOpen` | - | `true` | Allow calls when Guard API is unreachable |
|
|
275
333
|
| `scanResponses` | - | `false` | Also scan LLM responses |
|
|
276
334
|
| `timeout` | - | `10000` | HTTP timeout in milliseconds |
|
|
335
|
+
| `logLevel` | - | `"warn"` | SDK log verbosity: `"debug"`, `"info"`, `"warn"`, `"error"`, `"silent"` |
|
|
336
|
+
| `silent` | - | `false` | Shorthand for `logLevel: "silent"` |
|
|
337
|
+
|
|
338
|
+
> The proxy client (`PromptGuard`) talks to the `/api/v1/proxy` endpoints. If you set `baseUrl` / `PROMPTGUARD_BASE_URL` to `.../api/v1` (without `/proxy`), the SDK appends the `/proxy` suffix for you, so requests still land on the proxy.
|
|
339
|
+
>
|
|
340
|
+
> **Security:** the SDK sends your API key (and, in proxy mode, your prompt content) to whatever `PROMPTGUARD_BASE_URL` points at. Self-hosting is supported, so only point it at a host you trust.
|
|
341
|
+
>
|
|
342
|
+
> **Logging is process-global:** `logLevel` / `silent` set a single shared log level for the whole SDK. If several integrations or `init()` calls pass different values, the most recently constructed one wins. Use `setLogLevel()` directly for fine-grained control.
|
|
343
|
+
|
|
344
|
+
## Limitations
|
|
345
|
+
|
|
346
|
+
### ESM apps (auto-instrumentation)
|
|
347
|
+
|
|
348
|
+
Auto-instrumentation (`init()`) patches the provider modules that Node resolves via **CommonJS `require()`**. If your application runs as **native ESM** (`"type": "module"` in package.json, or `.mjs` files) and a provider ships separate ESM builds, the module instances your code `import`s can be *different objects* from the ones the SDK patched — the **dual-package hazard**. In that case your LLM calls bypass the patches entirely and **enforce mode silently protects nothing**.
|
|
349
|
+
|
|
350
|
+
What to do:
|
|
351
|
+
|
|
352
|
+
- **Verify at runtime** with `getAppliedPatches()` after `init()` — and note that a patch being listed proves the CJS build was patched, not that your ESM imports go through it. `init()` also logs a warning when it applies zero patches.
|
|
353
|
+
- **Prefer the ESM-safe APIs**, which don't rely on module patching:
|
|
354
|
+
- LangChain: `PromptGuardCallbackHandler` (`promptguard-sdk/integrations/langchain`)
|
|
355
|
+
- Vercel AI SDK: `promptGuardMiddleware` (`promptguard-sdk/integrations/vercel-ai`)
|
|
356
|
+
- Any framework: explicit `GuardClient.scan()` calls around your LLM invocations
|
|
357
|
+
- Transpiled-to-CJS TypeScript apps (the common `tsc`/`ts-node` default) are **not** affected — their `import`s compile to `require()` and hit the patched modules.
|
|
358
|
+
|
|
359
|
+
### Other limitations
|
|
360
|
+
|
|
361
|
+
- **Streaming responses are not output-scanned.** With auto-instrumentation and `scanResponses: true`, streaming calls (`stream: true`, Bedrock `ConverseStreamCommand`, etc.) skip the output scan — the stream is consumed incrementally by your code and cannot be buffered without breaking stream semantics. Input scanning still applies. A `debug`-level log is emitted when the output scan is skipped. The same applies to the Vercel AI SDK middleware: `streamText()` outputs are not scanned (`wrapStream` logs the skip); `generateText()` outputs are.
|
|
362
|
+
- **OpenAI `APIPromise` helpers are not preserved by auto-instrumentation.** Patched methods return a plain `Promise`, so `.withResponse()` / `.asResponse()` on `client.chat.completions.create(...)` are unavailable while `init()` is active. `await` the call and use the plain result instead.
|
|
363
|
+
- **Proxy client streaming:** `pg.chat.completions.create({ stream: true })` is rejected with a clear error — streaming is not yet supported by the proxy client.
|
|
277
364
|
|
|
278
365
|
## Error Handling
|
|
279
366
|
|
|
@@ -314,6 +401,8 @@ import type {
|
|
|
314
401
|
} from 'promptguard-sdk';
|
|
315
402
|
```
|
|
316
403
|
|
|
404
|
+
> **Response fields are camelCase — with one deliberate exception.** The SDK's normalized response objects (the `security`, `redteam`, `agent`, `scrape`, and `guard` namespaces) use camelCase field names (e.g. `report.bypassRate`, `stats.totalPatterns`, `validation.riskScore`, `result.threatsDetected`) regardless of the snake_case wire format — consistent with `GuardDecision` and `SecurityScanResult`. The **OpenAI-compatible** responses (`chat.completions`, `completions`, `embeddings`, i.e. `ChatCompletionResponse`) are the exception: they intentionally preserve OpenAI's snake_case shape (`choices[].finish_reason`, `usage.prompt_tokens`, …) so they stay drop-in compatible with the `openai` client. Request options are camelCase throughout (`maxTokens`, `targetPreset`), including `GuardContext` (`chainName`, `agentId`, `sessionId`, `toolCalls`).
|
|
405
|
+
|
|
317
406
|
## Links
|
|
318
407
|
|
|
319
408
|
- [Documentation](https://docs.promptguard.co)
|
package/dist/auto.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* ```ts
|
|
10
10
|
* import { init } from 'promptguard-sdk/auto';
|
|
11
11
|
*
|
|
12
|
-
* init({ apiKey: '
|
|
12
|
+
* init({ apiKey: 'pg_live_xxx' });
|
|
13
13
|
*
|
|
14
14
|
* // Everything below is now secured transparently.
|
|
15
15
|
* import OpenAI from 'openai';
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
import { GuardClient } from "./guard";
|
|
21
|
+
import { type LogLevel } from "./logger";
|
|
21
22
|
export interface InitOptions {
|
|
22
23
|
apiKey?: string;
|
|
23
24
|
baseUrl?: string;
|
|
@@ -29,11 +30,68 @@ export interface InitOptions {
|
|
|
29
30
|
scanResponses?: boolean;
|
|
30
31
|
/** HTTP timeout in ms for Guard API calls (default: `10000`). */
|
|
31
32
|
timeout?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Retry attempts for transient Guard API failures (network errors and
|
|
35
|
+
* 429/5xx responses) before the `failOpen` policy governs. Default: `3`.
|
|
36
|
+
* Retries never change the eventual decision — see {@link GuardClientConfig}.
|
|
37
|
+
*/
|
|
38
|
+
maxRetries?: number;
|
|
39
|
+
/** Base delay in ms between Guard API retries (exponential backoff). Default: `1000`. */
|
|
40
|
+
retryDelay?: number;
|
|
41
|
+
/**
|
|
42
|
+
* SDK log verbosity (default: `"warn"`). Set to `"silent"` to suppress all
|
|
43
|
+
* SDK logging, or `"info"`/`"debug"` for the init banner and more detail.
|
|
44
|
+
* NOTE: this sets the **process-global** SDK log level (shared with the
|
|
45
|
+
* framework integrations); the most recent setter wins.
|
|
46
|
+
*/
|
|
47
|
+
logLevel?: LogLevel;
|
|
48
|
+
/** Convenience shorthand for `logLevel: "silent"`. */
|
|
49
|
+
silent?: boolean;
|
|
32
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Initialise PromptGuard auto-instrumentation.
|
|
53
|
+
*
|
|
54
|
+
* Patches the provider SDKs (`openai`, `@anthropic-ai/sdk`,
|
|
55
|
+
* `@google/generative-ai`, `cohere-ai`, `@aws-sdk/client-bedrock-runtime`)
|
|
56
|
+
* so every LLM call they make is scanned by the Guard API.
|
|
57
|
+
*
|
|
58
|
+
* **Limitation — ESM apps (dual-package hazard):** patches are applied to the
|
|
59
|
+
* module instances resolved via CommonJS `require()`. If your application runs
|
|
60
|
+
* as native ESM (`"type": "module"` / `.mjs`) and the provider ships separate
|
|
61
|
+
* ESM builds, your `import`ed module instances can be *different objects* from
|
|
62
|
+
* the patched CJS ones — in that case auto-instrumentation silently protects
|
|
63
|
+
* nothing. Use {@link getAppliedPatches} as a runtime canary to verify the
|
|
64
|
+
* providers you use were actually patched, and prefer the ESM-safe
|
|
65
|
+
* alternatives: the LangChain callback handler
|
|
66
|
+
* (`promptguard-sdk/integrations/langchain`), the Vercel AI SDK middleware
|
|
67
|
+
* (`promptguard-sdk/integrations/vercel-ai`), or explicit `GuardClient.scan()`
|
|
68
|
+
* calls. See README "Limitations: ESM apps".
|
|
69
|
+
*
|
|
70
|
+
* When zero patches could be applied, a warning is logged so a
|
|
71
|
+
* misconfiguration never fails silently.
|
|
72
|
+
*/
|
|
33
73
|
export declare function init(options?: InitOptions): void;
|
|
34
74
|
export declare function shutdown(): void;
|
|
35
75
|
export declare function getGuardClient(): GuardClient | null;
|
|
36
76
|
export declare function getMode(): "enforce" | "monitor";
|
|
37
77
|
export declare function isFailOpen(): boolean;
|
|
38
78
|
export declare function shouldScanResponses(): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Runtime canary: the names of the auto-instrumentation patches that were
|
|
81
|
+
* actually applied by {@link init} (e.g. `["openai", "anthropic"]`).
|
|
82
|
+
*
|
|
83
|
+
* Because patches attach to the CJS-resolved provider modules, an ESM app can
|
|
84
|
+
* call `init()` successfully and still be unprotected (see the dual-package
|
|
85
|
+
* hazard note on {@link init}). Assert on this at startup when you rely on
|
|
86
|
+
* enforce mode:
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* init({ apiKey: "pg_live_xxx" });
|
|
91
|
+
* if (!getAppliedPatches().includes("openai")) {
|
|
92
|
+
* throw new Error("PromptGuard did not patch the openai SDK");
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export declare function getAppliedPatches(): string[];
|
|
39
97
|
//# sourceMappingURL=auto.d.ts.map
|
package/dist/auto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,OAAO,EAAE,KAAK,QAAQ,EAAuB,MAAM,UAAU,CAAA;AAkB7D,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mFAAmF;IACnF,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,kDAAkD;IAClD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,IAAI,CAqCpD;AAED,wBAAgB,QAAQ,IAAI,IAAI,CAU/B;AAMD,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAEnD;AAED,wBAAgB,OAAO,IAAI,SAAS,GAAG,SAAS,CAE/C;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAE5C"}
|
package/dist/auto.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* ```ts
|
|
11
11
|
* import { init } from 'promptguard-sdk/auto';
|
|
12
12
|
*
|
|
13
|
-
* init({ apiKey: '
|
|
13
|
+
* init({ apiKey: 'pg_live_xxx' });
|
|
14
14
|
*
|
|
15
15
|
* // Everything below is now secured transparently.
|
|
16
16
|
* import OpenAI from 'openai';
|
|
@@ -25,7 +25,9 @@ exports.getGuardClient = getGuardClient;
|
|
|
25
25
|
exports.getMode = getMode;
|
|
26
26
|
exports.isFailOpen = isFailOpen;
|
|
27
27
|
exports.shouldScanResponses = shouldScanResponses;
|
|
28
|
+
exports.getAppliedPatches = getAppliedPatches;
|
|
28
29
|
const guard_1 = require("./guard");
|
|
30
|
+
const logger_1 = require("./logger");
|
|
29
31
|
const resolve_1 = require("./resolve");
|
|
30
32
|
// ---------------------------------------------------------------------------
|
|
31
33
|
// Global state
|
|
@@ -35,22 +37,58 @@ let mode = "enforce";
|
|
|
35
37
|
let failOpen = true;
|
|
36
38
|
let scanResponses = false;
|
|
37
39
|
const appliedPatches = [];
|
|
40
|
+
/**
|
|
41
|
+
* Initialise PromptGuard auto-instrumentation.
|
|
42
|
+
*
|
|
43
|
+
* Patches the provider SDKs (`openai`, `@anthropic-ai/sdk`,
|
|
44
|
+
* `@google/generative-ai`, `cohere-ai`, `@aws-sdk/client-bedrock-runtime`)
|
|
45
|
+
* so every LLM call they make is scanned by the Guard API.
|
|
46
|
+
*
|
|
47
|
+
* **Limitation — ESM apps (dual-package hazard):** patches are applied to the
|
|
48
|
+
* module instances resolved via CommonJS `require()`. If your application runs
|
|
49
|
+
* as native ESM (`"type": "module"` / `.mjs`) and the provider ships separate
|
|
50
|
+
* ESM builds, your `import`ed module instances can be *different objects* from
|
|
51
|
+
* the patched CJS ones — in that case auto-instrumentation silently protects
|
|
52
|
+
* nothing. Use {@link getAppliedPatches} as a runtime canary to verify the
|
|
53
|
+
* providers you use were actually patched, and prefer the ESM-safe
|
|
54
|
+
* alternatives: the LangChain callback handler
|
|
55
|
+
* (`promptguard-sdk/integrations/langchain`), the Vercel AI SDK middleware
|
|
56
|
+
* (`promptguard-sdk/integrations/vercel-ai`), or explicit `GuardClient.scan()`
|
|
57
|
+
* calls. See README "Limitations: ESM apps".
|
|
58
|
+
*
|
|
59
|
+
* When zero patches could be applied, a warning is logged so a
|
|
60
|
+
* misconfiguration never fails silently.
|
|
61
|
+
*/
|
|
38
62
|
function init(options = {}) {
|
|
63
|
+
if (options.silent) {
|
|
64
|
+
(0, logger_1.setLogLevel)("silent");
|
|
65
|
+
}
|
|
66
|
+
else if (options.logLevel) {
|
|
67
|
+
(0, logger_1.setLogLevel)(options.logLevel);
|
|
68
|
+
}
|
|
39
69
|
const { apiKey, baseUrl } = (0, resolve_1.resolveCredentials)(options.apiKey, options.baseUrl);
|
|
40
70
|
const resolvedMode = options.mode ?? "enforce";
|
|
41
71
|
if (resolvedMode !== "enforce" && resolvedMode !== "monitor") {
|
|
42
|
-
throw new Error(
|
|
72
|
+
throw new Error(`mode must be 'enforce' or 'monitor', got '${resolvedMode}'`);
|
|
43
73
|
}
|
|
44
74
|
guardClient = new guard_1.GuardClient({
|
|
45
75
|
apiKey,
|
|
46
76
|
baseUrl,
|
|
47
77
|
timeout: options.timeout ?? 10000,
|
|
78
|
+
maxRetries: options.maxRetries,
|
|
79
|
+
retryDelay: options.retryDelay,
|
|
48
80
|
});
|
|
49
81
|
mode = resolvedMode;
|
|
50
82
|
failOpen = options.failOpen ?? true;
|
|
51
83
|
scanResponses = options.scanResponses ?? false;
|
|
52
84
|
applyPatches();
|
|
53
|
-
|
|
85
|
+
// One-line confirmation of which provider SDKs are actually protected.
|
|
86
|
+
// Suppressed by default (info level); set logLevel: "info" to see it, or
|
|
87
|
+
// read the same list programmatically via getAppliedPatches(). When ZERO
|
|
88
|
+
// patches applied, applyPatches() already logged a warn (visible by default).
|
|
89
|
+
const protecting = getAppliedPatches();
|
|
90
|
+
logger_1.logger.info(`auto-instrumentation initialised (mode=${mode}, fail_open=${failOpen}) — ` +
|
|
91
|
+
`protecting: ${protecting.length ? protecting.join(", ") : "none"}`);
|
|
54
92
|
}
|
|
55
93
|
function shutdown() {
|
|
56
94
|
for (const patch of appliedPatches) {
|
|
@@ -79,6 +117,26 @@ function isFailOpen() {
|
|
|
79
117
|
function shouldScanResponses() {
|
|
80
118
|
return scanResponses;
|
|
81
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Runtime canary: the names of the auto-instrumentation patches that were
|
|
122
|
+
* actually applied by {@link init} (e.g. `["openai", "anthropic"]`).
|
|
123
|
+
*
|
|
124
|
+
* Because patches attach to the CJS-resolved provider modules, an ESM app can
|
|
125
|
+
* call `init()` successfully and still be unprotected (see the dual-package
|
|
126
|
+
* hazard note on {@link init}). Assert on this at startup when you rely on
|
|
127
|
+
* enforce mode:
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* init({ apiKey: "pg_live_xxx" });
|
|
132
|
+
* if (!getAppliedPatches().includes("openai")) {
|
|
133
|
+
* throw new Error("PromptGuard did not patch the openai SDK");
|
|
134
|
+
* }
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
function getAppliedPatches() {
|
|
138
|
+
return appliedPatches.map((p) => p.name);
|
|
139
|
+
}
|
|
82
140
|
// ---------------------------------------------------------------------------
|
|
83
141
|
// Patch orchestration
|
|
84
142
|
// ---------------------------------------------------------------------------
|
|
@@ -94,12 +152,32 @@ function applyPatches() {
|
|
|
94
152
|
try {
|
|
95
153
|
const patchModule = require(mod.path);
|
|
96
154
|
if (patchModule.apply()) {
|
|
97
|
-
|
|
155
|
+
// apply() is idempotent per patch module, so a second init() without
|
|
156
|
+
// shutdown() reports "already patched" success. Skip the push when
|
|
157
|
+
// the patch is already tracked — a duplicate entry would make the
|
|
158
|
+
// getAppliedPatches() canary lie and call revert() twice on
|
|
159
|
+
// shutdown().
|
|
160
|
+
if (!appliedPatches.some((p) => p.name === mod.name)) {
|
|
161
|
+
appliedPatches.push({ name: mod.name, revert: patchModule.revert });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
logger_1.logger.debug(`${mod.name} patch not applied: SDK not resolvable via require() ` +
|
|
166
|
+
`(not installed, or only reachable as ESM)`);
|
|
98
167
|
}
|
|
99
168
|
}
|
|
100
|
-
catch {
|
|
101
|
-
|
|
169
|
+
catch (err) {
|
|
170
|
+
logger_1.logger.debug(`${mod.name} patch not applied: ${err instanceof Error ? err.message : String(err)}`);
|
|
102
171
|
}
|
|
103
172
|
}
|
|
173
|
+
if (appliedPatches.length === 0) {
|
|
174
|
+
logger_1.logger.warn("init() applied ZERO auto-instrumentation patches — no supported LLM SDK was " +
|
|
175
|
+
"resolvable via CommonJS require(), so NO calls are being scanned. If your app " +
|
|
176
|
+
'runs as native ESM ("type": "module" / .mjs), auto-instrumentation cannot see ' +
|
|
177
|
+
"the module instances your code imports (dual-package hazard). Use the ESM-safe " +
|
|
178
|
+
"integrations instead: the LangChain callback handler, the Vercel AI SDK " +
|
|
179
|
+
"middleware, or explicit GuardClient.scan() calls. " +
|
|
180
|
+
'See README "Limitations: ESM apps". Set logLevel: "debug" for per-SDK reasons.');
|
|
181
|
+
}
|
|
104
182
|
}
|
|
105
183
|
//# sourceMappingURL=auto.js.map
|
package/dist/auto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;
|
|
1
|
+
{"version":3,"file":"auto.js","sourceRoot":"","sources":["../src/auto.ts"],"names":[],"mappings":"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"}
|