promptguard-sdk 2.2.1 → 2.4.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 +63 -13
- package/dist/auto.d.ts +1 -0
- package/dist/auto.d.ts.map +1 -1
- package/dist/auto.js +5 -0
- package/dist/auto.js.map +1 -1
- package/dist/generated/api-types.d.ts +175 -0
- package/dist/generated/api-types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/langchain.d.ts +37 -1
- package/dist/integrations/langchain.d.ts.map +1 -1
- package/dist/integrations/langchain.js +64 -5
- package/dist/integrations/langchain.js.map +1 -1
- package/dist/integrations/session.d.ts +33 -0
- package/dist/integrations/session.d.ts.map +1 -0
- package/dist/integrations/session.js +71 -0
- package/dist/integrations/session.js.map +1 -0
- package/dist/integrations/vercel-ai-output.d.ts +109 -0
- package/dist/integrations/vercel-ai-output.d.ts.map +1 -0
- package/dist/integrations/vercel-ai-output.js +330 -0
- package/dist/integrations/vercel-ai-output.js.map +1 -0
- package/dist/integrations/vercel-ai.d.ts +114 -24
- package/dist/integrations/vercel-ai.d.ts.map +1 -1
- package/dist/integrations/vercel-ai.js +200 -82
- package/dist/integrations/vercel-ai.js.map +1 -1
- package/dist/patches/base.d.ts +5 -0
- package/dist/patches/base.d.ts.map +1 -1
- package/dist/patches/base.js +5 -2
- package/dist/patches/base.js.map +1 -1
- package/dist/patches/openai.d.ts +8 -0
- package/dist/patches/openai.d.ts.map +1 -1
- package/dist/patches/openai.js +16 -0
- package/dist/patches/openai.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -4
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Output scanning for the Vercel AI SDK integration — generated and streamed.
|
|
4
|
+
*
|
|
5
|
+
* This is the one place that decides what a model's output is scanned as and
|
|
6
|
+
* what a decision about it does. The middleware (`vercel-ai.ts`) calls it from
|
|
7
|
+
* both `wrapGenerate` and `wrapStream`, and anything else that wraps an AI SDK
|
|
8
|
+
* model should call it too rather than restating the policy.
|
|
9
|
+
*
|
|
10
|
+
* Streams are scanned in windows, then once more in full:
|
|
11
|
+
*
|
|
12
|
+
* - **Enforce mode** holds back the stream parts that carry scanned text
|
|
13
|
+
* (text deltas, tool-call input) until that text has been scanned. Each
|
|
14
|
+
* time `windowChars` new characters have arrived, everything held is
|
|
15
|
+
* scanned and, if clean, released — except the last `WINDOW_OVERLAP_CHARS`
|
|
16
|
+
* characters, which stay held and are scanned again at the head of the next
|
|
17
|
+
* window. So every released character has been scanned together with at
|
|
18
|
+
* least the overlap that follows it, and a secret up to the overlap long is
|
|
19
|
+
* always seen whole before any of it is released. When the model finishes,
|
|
20
|
+
* the complete text is scanned — the same text, built the same way, as a
|
|
21
|
+
* generated result — before the held tail and the `finish` part are
|
|
22
|
+
* released. Parts that carry no scanned text (stream start, reasoning,
|
|
23
|
+
* sources, files, metadata) are delivered at once when nothing is held
|
|
24
|
+
* ahead of them, and queue behind held text otherwise, so order is kept.
|
|
25
|
+
* - **Monitor mode** never stops a call, so it never holds one: parts pass
|
|
26
|
+
* through as they arrive and only the complete text is scanned, at the end.
|
|
27
|
+
*
|
|
28
|
+
* Reasoning is not scanned, here or on the generated path.
|
|
29
|
+
*
|
|
30
|
+
* A block (or an output redact, which cannot be honoured on text already
|
|
31
|
+
* generated) stops the stream with `PromptGuardBlockedError`. A Guard API
|
|
32
|
+
* outage fails open by default (ADR-0002): the held parts are released and the
|
|
33
|
+
* rest of that stream passes unscanned, so an outage costs one failed scan per
|
|
34
|
+
* stream rather than one per window. With `failOpen: false` it stops the
|
|
35
|
+
* stream with the `GuardApiError` instead.
|
|
36
|
+
*
|
|
37
|
+
* How a stopped stream ends is the caller's choice (`StreamStop`): by
|
|
38
|
+
* erroring it, so whatever reads it throws, or by delivering one AI SDK
|
|
39
|
+
* `error` part and closing it. Either way nothing held is released and the
|
|
40
|
+
* `finish` part never arrives.
|
|
41
|
+
*/
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.WINDOW_OVERLAP_CHARS = exports.DEFAULT_STREAM_WINDOW_CHARS = void 0;
|
|
44
|
+
exports.validateWindowChars = validateWindowChars;
|
|
45
|
+
exports.scanOutputText = scanOutputText;
|
|
46
|
+
exports.extractGeneratedText = extractGeneratedText;
|
|
47
|
+
exports.scanOutputStream = scanOutputStream;
|
|
48
|
+
const guard_1 = require("../guard");
|
|
49
|
+
const logger_1 = require("../logger");
|
|
50
|
+
/**
|
|
51
|
+
* Default new characters of streamed output per scanned window.
|
|
52
|
+
*
|
|
53
|
+
* Not taken from any external standard: 1000 was chosen as a balance between
|
|
54
|
+
* how long output is held and how many Guard API round trips a stream costs.
|
|
55
|
+
* At roughly four characters a token it is about 250 tokens, which takes a
|
|
56
|
+
* model a few seconds to generate — about as long as one Guard round trip, so
|
|
57
|
+
* scanning a window roughly keeps pace with generating the next. A smaller
|
|
58
|
+
* window delivers the first text sooner and costs more scans; a larger one
|
|
59
|
+
* costs fewer scans and holds output longer.
|
|
60
|
+
*/
|
|
61
|
+
exports.DEFAULT_STREAM_WINDOW_CHARS = 1000;
|
|
62
|
+
/**
|
|
63
|
+
* Characters of scanned text always kept held and rescanned at the head of
|
|
64
|
+
* the next window — the longest secret guaranteed to be seen whole across a
|
|
65
|
+
* window boundary.
|
|
66
|
+
*/
|
|
67
|
+
exports.WINDOW_OVERLAP_CHARS = 200;
|
|
68
|
+
/** Throws `RangeError` unless `value` is a usable `streamWindowChars`. */
|
|
69
|
+
function validateWindowChars(value) {
|
|
70
|
+
if (!(value > 0)) {
|
|
71
|
+
throw new RangeError(`streamWindowChars must be a positive number, got ${value}`);
|
|
72
|
+
}
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Scan one piece of model output. Resolves when the output may be delivered;
|
|
77
|
+
* throws `PromptGuardBlockedError` when enforce mode stops it, and rethrows
|
|
78
|
+
* the `GuardApiError` when failing closed. `sessionId` names the
|
|
79
|
+
* Conversation the call belongs to, when it names one.
|
|
80
|
+
*/
|
|
81
|
+
async function scanOutputText(text, policy, modelId, what = "response", sessionId) {
|
|
82
|
+
try {
|
|
83
|
+
const decision = await policy.guard.scan([{ role: "assistant", content: text }], "output", modelId, { framework: "vercel-ai-sdk", ...(sessionId !== undefined ? { sessionId } : {}) });
|
|
84
|
+
if (decision.blocked || decision.redacted) {
|
|
85
|
+
// Generated output cannot be rewritten after the fact, so an
|
|
86
|
+
// output-direction redact decision is enforced as a block.
|
|
87
|
+
if (policy.mode === "enforce")
|
|
88
|
+
throw new guard_1.PromptGuardBlockedError(decision);
|
|
89
|
+
logger_1.logger.warn(`[monitor] would ${decision.redacted ? "redact" : "block"} ${what}: ${decision.threatType}`);
|
|
90
|
+
}
|
|
91
|
+
return "scanned";
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
if (err instanceof guard_1.PromptGuardBlockedError)
|
|
95
|
+
throw err;
|
|
96
|
+
// Only a Guard API outage is eligible for fail-open; rethrow the
|
|
97
|
+
// original error when failing closed.
|
|
98
|
+
if (!(err instanceof guard_1.GuardApiError))
|
|
99
|
+
throw err;
|
|
100
|
+
if (!policy.failOpen)
|
|
101
|
+
throw err;
|
|
102
|
+
logger_1.logger.warn(`Guard API unavailable, ${what} left unscanned (failOpen=true): ${(0, guard_1.safeErrorLabel)(err)}`);
|
|
103
|
+
return "failed-open";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Extract all scannable text from a `doGenerate` result.
|
|
108
|
+
*
|
|
109
|
+
* Handles both AI SDK result shapes:
|
|
110
|
+
* - **v4 (LanguageModelV1):** top-level `text` string plus `toolCalls`
|
|
111
|
+
* (every tool call's `args`/`input` is scanned — not just the first).
|
|
112
|
+
* - **v5+ (LanguageModelV2/V3):** `content` is an array of parts
|
|
113
|
+
* (`{ type: "text", text }` / `{ type: "tool-call", input }`) with no
|
|
114
|
+
* top-level `text`.
|
|
115
|
+
*
|
|
116
|
+
* Returns the concatenated text, or `null` when nothing scannable exists.
|
|
117
|
+
*/
|
|
118
|
+
function extractGeneratedText(result) {
|
|
119
|
+
const texts = [];
|
|
120
|
+
const stringify = (value) => typeof value === "string" ? value : JSON.stringify(value);
|
|
121
|
+
if (typeof result?.text === "string" && result.text)
|
|
122
|
+
texts.push(result.text);
|
|
123
|
+
if (Array.isArray(result?.content)) {
|
|
124
|
+
for (const raw of result.content) {
|
|
125
|
+
if (!raw || typeof raw !== "object")
|
|
126
|
+
continue;
|
|
127
|
+
const part = raw;
|
|
128
|
+
if (part.type === "text" && typeof part.text === "string" && part.text) {
|
|
129
|
+
texts.push(part.text);
|
|
130
|
+
}
|
|
131
|
+
else if (part.type === "tool-call") {
|
|
132
|
+
const args = part.input ?? part.args;
|
|
133
|
+
if (args != null)
|
|
134
|
+
texts.push(stringify(args));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (Array.isArray(result?.toolCalls)) {
|
|
139
|
+
for (const raw of result.toolCalls) {
|
|
140
|
+
if (!raw || typeof raw !== "object")
|
|
141
|
+
continue;
|
|
142
|
+
const call = raw;
|
|
143
|
+
const args = call.args ?? call.input;
|
|
144
|
+
if (args != null)
|
|
145
|
+
texts.push(stringify(args));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const combined = texts.join("\n");
|
|
149
|
+
return combined || null;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Accumulates a model stream's parts into the result `doGenerate` would have
|
|
153
|
+
* returned, so the final scan sees exactly what a generated response would.
|
|
154
|
+
*
|
|
155
|
+
* Recognises both stream-part vocabularies:
|
|
156
|
+
* - **LanguageModelV1:** `text-delta { textDelta }`,
|
|
157
|
+
* `tool-call-delta { toolCallId, argsTextDelta }`, `tool-call { args }`.
|
|
158
|
+
* - **LanguageModelV2/V3:** `text-delta { id, delta }`,
|
|
159
|
+
* `tool-input-delta { id, delta }`, `tool-call { toolCallId, input }`.
|
|
160
|
+
*/
|
|
161
|
+
class StreamedOutput {
|
|
162
|
+
constructor() {
|
|
163
|
+
/** Text blocks by id, in first-seen order (V1 parts have no id). */
|
|
164
|
+
this.texts = new Map();
|
|
165
|
+
/** Tool input assembled from deltas, by call id. */
|
|
166
|
+
this.toolDeltas = new Map();
|
|
167
|
+
/** Complete tool calls, by call id. */
|
|
168
|
+
this.toolCalls = new Map();
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Record one part. Returns the newly visible text it carries (for the
|
|
172
|
+
* window), or `""` when it carries none.
|
|
173
|
+
*/
|
|
174
|
+
add(part) {
|
|
175
|
+
switch (part.type) {
|
|
176
|
+
case "text-delta": {
|
|
177
|
+
const delta = str(part.delta) ?? str(part.textDelta) ?? "";
|
|
178
|
+
const id = str(part.id) ?? "";
|
|
179
|
+
this.texts.set(id, (this.texts.get(id) ?? "") + delta);
|
|
180
|
+
return delta;
|
|
181
|
+
}
|
|
182
|
+
case "tool-input-delta":
|
|
183
|
+
case "tool-call-delta": {
|
|
184
|
+
const delta = str(part.delta) ?? str(part.argsTextDelta) ?? "";
|
|
185
|
+
const id = str(part.id) ?? str(part.toolCallId) ?? "";
|
|
186
|
+
this.toolDeltas.set(id, (this.toolDeltas.get(id) ?? "") + delta);
|
|
187
|
+
return delta;
|
|
188
|
+
}
|
|
189
|
+
case "tool-call": {
|
|
190
|
+
const id = str(part.toolCallId) ?? "";
|
|
191
|
+
const input = part.input ?? part.args;
|
|
192
|
+
if (input == null)
|
|
193
|
+
return "";
|
|
194
|
+
this.toolCalls.set(id, input);
|
|
195
|
+
// Input already streamed as deltas was counted when it arrived.
|
|
196
|
+
if (this.toolDeltas.has(id))
|
|
197
|
+
return "";
|
|
198
|
+
return typeof input === "string" ? input : JSON.stringify(input);
|
|
199
|
+
}
|
|
200
|
+
default:
|
|
201
|
+
return "";
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/** The complete output, as `extractGeneratedText` renders a generated result. */
|
|
205
|
+
text() {
|
|
206
|
+
const content = [];
|
|
207
|
+
for (const text of this.texts.values())
|
|
208
|
+
content.push({ type: "text", text });
|
|
209
|
+
for (const [id, input] of this.toolCalls)
|
|
210
|
+
content.push({ type: "tool-call", toolCallId: id, input });
|
|
211
|
+
// A call streamed as deltas whose closing `tool-call` part never came.
|
|
212
|
+
for (const [id, input] of this.toolDeltas) {
|
|
213
|
+
if (!this.toolCalls.has(id))
|
|
214
|
+
content.push({ type: "tool-call", toolCallId: id, input });
|
|
215
|
+
}
|
|
216
|
+
return extractGeneratedText({ content });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function str(value) {
|
|
220
|
+
return typeof value === "string" ? value : undefined;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Wrap a model stream so its output is scanned as described in this module's
|
|
224
|
+
* header. Parts are passed through unchanged and in order; where the policy
|
|
225
|
+
* stops the output, the stream ends with `PromptGuardBlockedError` (or the
|
|
226
|
+
* `GuardApiError` when failing closed), as `stopAs` says.
|
|
227
|
+
*/
|
|
228
|
+
function scanOutputStream(stream, policy, modelId, stopAs = "error", sessionId) {
|
|
229
|
+
const output = new StreamedOutput();
|
|
230
|
+
const enforce = policy.mode === "enforce";
|
|
231
|
+
const what = "streamed response";
|
|
232
|
+
/** Held parts, in order, with the scanned text each carries. */
|
|
233
|
+
let held = [];
|
|
234
|
+
/** Total length of the text carried by `held`. */
|
|
235
|
+
let heldChars = 0;
|
|
236
|
+
/** Characters that have arrived since the last window scan. */
|
|
237
|
+
let unscannedChars = 0;
|
|
238
|
+
/** Set once fail-open has given up on this stream. */
|
|
239
|
+
let unscanned = false;
|
|
240
|
+
/** Set once the stream has ended with an error part. */
|
|
241
|
+
let stopped = false;
|
|
242
|
+
const releaseAll = (controller) => {
|
|
243
|
+
for (const { part } of held)
|
|
244
|
+
controller.enqueue(part);
|
|
245
|
+
held = [];
|
|
246
|
+
heldChars = 0;
|
|
247
|
+
};
|
|
248
|
+
/** Release from the front while the overlap stays held behind it. */
|
|
249
|
+
const releaseScanned = (controller) => {
|
|
250
|
+
while (held.length && heldChars - held[0].text.length >= exports.WINDOW_OVERLAP_CHARS) {
|
|
251
|
+
const front = held.shift();
|
|
252
|
+
heldChars -= front.text.length;
|
|
253
|
+
controller.enqueue(front.part);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
return stream.pipeThrough(new TransformStream({
|
|
257
|
+
async transform(part, controller) {
|
|
258
|
+
if (stopped)
|
|
259
|
+
return;
|
|
260
|
+
try {
|
|
261
|
+
await step(part, controller);
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
stop(err, controller);
|
|
265
|
+
// Ends the stream here, and cancels the model's stream behind it.
|
|
266
|
+
controller.terminate();
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
async flush(controller) {
|
|
270
|
+
if (stopped)
|
|
271
|
+
return;
|
|
272
|
+
try {
|
|
273
|
+
await finish(controller);
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
stop(err, controller);
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
}));
|
|
280
|
+
/** End a stopped stream as `stopAs` says. */
|
|
281
|
+
function stop(err, controller) {
|
|
282
|
+
if (stopAs === "error")
|
|
283
|
+
throw err;
|
|
284
|
+
stopped = true;
|
|
285
|
+
controller.enqueue({ type: "error", error: err });
|
|
286
|
+
}
|
|
287
|
+
async function step(part, controller) {
|
|
288
|
+
if (unscanned) {
|
|
289
|
+
controller.enqueue(part);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const record = part && typeof part === "object" ? part : null;
|
|
293
|
+
const text = record ? output.add(record) : "";
|
|
294
|
+
if (!enforce) {
|
|
295
|
+
controller.enqueue(part);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
// Nothing to scan and nothing ahead of it: no reason to hold it. The
|
|
299
|
+
// finish part always waits for the final scan.
|
|
300
|
+
if (!text && !held.length && record?.type !== "finish") {
|
|
301
|
+
controller.enqueue(part);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
held.push({ part, text });
|
|
305
|
+
heldChars += text.length;
|
|
306
|
+
unscannedChars += text.length;
|
|
307
|
+
if (unscannedChars < policy.windowChars)
|
|
308
|
+
return;
|
|
309
|
+
unscannedChars = 0;
|
|
310
|
+
const window = held.map((h) => h.text).join("");
|
|
311
|
+
// Throws on block or fail-closed, which stops the stream (see `stop`).
|
|
312
|
+
const outcome = await scanOutputText(window, policy, modelId, `rest of ${what}`, sessionId);
|
|
313
|
+
if (outcome === "scanned") {
|
|
314
|
+
releaseScanned(controller);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
unscanned = true;
|
|
318
|
+
releaseAll(controller);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async function finish(controller) {
|
|
322
|
+
if (unscanned)
|
|
323
|
+
return;
|
|
324
|
+
const text = output.text();
|
|
325
|
+
if (text)
|
|
326
|
+
await scanOutputText(text, policy, modelId, what, sessionId);
|
|
327
|
+
releaseAll(controller);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=vercel-ai-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-ai-output.js","sourceRoot":"","sources":["../../src/integrations/vercel-ai-output.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,29 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Vercel AI SDK integration
|
|
2
|
+
* Vercel AI SDK integration for the `ai` package.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Two ways in, with the same scanning:
|
|
5
|
+
*
|
|
6
|
+
* - `promptguard(model)` wraps a language model of ai 5, 6 or 7
|
|
7
|
+
* (specification v2, v3 or v4), and the wrapped model is used wherever the
|
|
8
|
+
* original was:
|
|
6
9
|
*
|
|
7
|
-
* @example
|
|
8
10
|
* ```ts
|
|
9
11
|
* import { openai } from '@ai-sdk/openai';
|
|
10
|
-
* import {
|
|
12
|
+
* import { generateText } from 'ai';
|
|
13
|
+
* import { promptguard } from 'promptguard-sdk/integrations/vercel-ai';
|
|
14
|
+
*
|
|
15
|
+
* const model = promptguard(openai('gpt-5-nano'), { scanResponses: true });
|
|
16
|
+
* const { text } = await generateText({ model, prompt: 'Hello!' });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* - `promptGuardMiddleware(options)` is the same scanning as AI SDK
|
|
20
|
+
* middleware, for `wrapLanguageModel` — useful alongside other middleware,
|
|
21
|
+
* and the way in on ai 4:
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { wrapLanguageModel } from 'ai';
|
|
11
25
|
* import { promptGuardMiddleware } from 'promptguard-sdk/integrations/vercel-ai';
|
|
12
26
|
*
|
|
13
27
|
* const model = wrapLanguageModel({
|
|
14
28
|
* model: openai('gpt-5-nano'),
|
|
15
29
|
* middleware: promptGuardMiddleware({ apiKey: 'pg_live_xxx' }),
|
|
16
30
|
* });
|
|
17
|
-
*
|
|
18
|
-
* const { text } = await generateText({ model, prompt: 'Hello!' });
|
|
19
31
|
* ```
|
|
32
|
+
*
|
|
33
|
+
* With `scanResponses: true`, `streamText()` output is scanned too: in
|
|
34
|
+
* enforce mode text is held until it has been scanned, a window
|
|
35
|
+
* (`streamWindowChars`, default 1000 characters) at a time, and the complete
|
|
36
|
+
* text is scanned before the stream finishes. See `vercel-ai-output.ts` for
|
|
37
|
+
* the full behaviour and its guarantee.
|
|
38
|
+
*
|
|
39
|
+
* Neither imports `ai`: the wrapper calls the middleware's hooks the way
|
|
40
|
+
* `wrapLanguageModel` does, so it works with whichever `ai` the application
|
|
41
|
+
* has (ai@7 is ESM-only; this package is CommonJS) and loads without one.
|
|
20
42
|
*/
|
|
21
43
|
import { type GuardClientConfig } from "../guard";
|
|
22
44
|
import { type LogLevel } from "../logger";
|
|
23
|
-
export interface PromptGuardMiddlewareOptions extends GuardClientConfig {
|
|
45
|
+
export interface PromptGuardMiddlewareOptions extends Omit<GuardClientConfig, "apiKey"> {
|
|
46
|
+
/** PromptGuard API key (default: the `PROMPTGUARD_API_KEY` environment variable). */
|
|
47
|
+
apiKey?: string;
|
|
24
48
|
mode?: "enforce" | "monitor";
|
|
49
|
+
/**
|
|
50
|
+
* Scan the model's output as well as its input (default: `false`). Applies
|
|
51
|
+
* to `generateText()` and to `streamText()` alike.
|
|
52
|
+
*/
|
|
25
53
|
scanResponses?: boolean;
|
|
26
54
|
failOpen?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* With `scanResponses` in enforce mode, how many new characters of
|
|
57
|
+
* streamed output are collected before the held text is scanned (default:
|
|
58
|
+
* `1000`; see `DEFAULT_STREAM_WINDOW_CHARS` for why). A smaller window
|
|
59
|
+
* delivers the first text sooner at the cost of more scans; `Infinity`
|
|
60
|
+
* holds the whole response for a single scan at the end.
|
|
61
|
+
*/
|
|
62
|
+
streamWindowChars?: number;
|
|
27
63
|
/**
|
|
28
64
|
* SDK log verbosity (default: `"warn"`). Set to `"silent"` to suppress logs.
|
|
29
65
|
* NOTE: this sets the **process-global** SDK log level (shared with `init()`
|
|
@@ -32,36 +68,90 @@ export interface PromptGuardMiddlewareOptions extends GuardClientConfig {
|
|
|
32
68
|
logLevel?: LogLevel;
|
|
33
69
|
/** Convenience shorthand for `logLevel: "silent"`. */
|
|
34
70
|
silent?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* The Conversation to name when a call names none of its own. The AI SDK
|
|
73
|
+
* has no conversation id, so a call names one with
|
|
74
|
+
* `providerOptions: { promptguard: { sessionId } }` (`providerMetadata` on
|
|
75
|
+
* `ai@4`), which wins over this. Without either, no session id is sent.
|
|
76
|
+
*/
|
|
77
|
+
sessionId?: string;
|
|
35
78
|
}
|
|
36
79
|
/**
|
|
37
80
|
* Create a Vercel AI SDK middleware that scans inputs/outputs via
|
|
38
81
|
* the PromptGuard Guard API.
|
|
39
82
|
*
|
|
40
83
|
* Returns an object conforming to the Vercel AI SDK `LanguageModelMiddleware`
|
|
41
|
-
* interface (`transformParams
|
|
84
|
+
* interface (`transformParams`, plus `wrapGenerate` and `wrapStream` when
|
|
85
|
+
* `scanResponses` is set). The hooks are generic over the call options and
|
|
86
|
+
* results they pass through, so the object is assignable to the middleware
|
|
87
|
+
* type of each supported `ai` major (checked by `tests/vercel-ai-types.test.ts`).
|
|
42
88
|
*/
|
|
43
|
-
export declare function promptGuardMiddleware(options
|
|
89
|
+
export declare function promptGuardMiddleware(options?: PromptGuardMiddlewareOptions): {
|
|
90
|
+
/**
|
|
91
|
+
* The middleware specification version. ai@6 requires `"v3"` here;
|
|
92
|
+
* ai@7 accepts any version string, and ai@5 does not read it. The hooks
|
|
93
|
+
* themselves read every call shape from V2 to V4.
|
|
94
|
+
*/
|
|
95
|
+
specificationVersion: "v3";
|
|
44
96
|
/**
|
|
45
97
|
* transformParams - scans the input messages before they reach the model.
|
|
46
98
|
*/
|
|
47
|
-
transformParams: ({ params }: {
|
|
48
|
-
params:
|
|
49
|
-
|
|
99
|
+
transformParams: <P extends object>({ params, model, }: {
|
|
100
|
+
params: P;
|
|
101
|
+
model?: ModelIdentity;
|
|
102
|
+
}) => Promise<P>;
|
|
50
103
|
/**
|
|
51
104
|
* wrapGenerate - optionally scans the model's response.
|
|
52
105
|
*/
|
|
53
|
-
wrapGenerate: (({ doGenerate, params, }: {
|
|
54
|
-
doGenerate: () =>
|
|
55
|
-
params
|
|
56
|
-
|
|
106
|
+
wrapGenerate: (<R>({ doGenerate, params, model, }: {
|
|
107
|
+
doGenerate: () => PromiseLike<R>;
|
|
108
|
+
params?: object;
|
|
109
|
+
model?: ModelIdentity;
|
|
110
|
+
}) => Promise<R>) | undefined;
|
|
57
111
|
/**
|
|
58
|
-
* wrapStream -
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* silent; input scanning via transformParams still applies.
|
|
112
|
+
* wrapStream - optionally scans the model's streamed response, in
|
|
113
|
+
* windows and then in full, with the same decisions and the same
|
|
114
|
+
* fail-open policy as wrapGenerate (see `vercel-ai-output.ts`).
|
|
62
115
|
*/
|
|
63
|
-
wrapStream: (({ doStream }: {
|
|
64
|
-
doStream: () =>
|
|
65
|
-
|
|
116
|
+
wrapStream: (<R>({ doStream, params, model, }: {
|
|
117
|
+
doStream: () => PromiseLike<R>;
|
|
118
|
+
params?: object;
|
|
119
|
+
model?: ModelIdentity;
|
|
120
|
+
}) => Promise<R>) | undefined;
|
|
66
121
|
};
|
|
122
|
+
/**
|
|
123
|
+
* What every AI SDK language model specification (V1 to V4) has in common,
|
|
124
|
+
* which is all `promptguard()` relies on.
|
|
125
|
+
*/
|
|
126
|
+
export interface LanguageModelLike {
|
|
127
|
+
readonly specificationVersion: string;
|
|
128
|
+
readonly modelId: string;
|
|
129
|
+
doGenerate(options: never): PromiseLike<unknown>;
|
|
130
|
+
doStream(options: never): PromiseLike<unknown>;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Wrap an AI SDK language model so every call it makes is scanned by
|
|
134
|
+
* PromptGuard, exactly as `promptGuardMiddleware(options)` would scan it
|
|
135
|
+
* under `wrapLanguageModel` — generated and streamed alike.
|
|
136
|
+
*
|
|
137
|
+
* The wrapped model is the model it wraps in every other respect: its own
|
|
138
|
+
* specification version, provider, model id, supported URLs and any other
|
|
139
|
+
* property, so it is accepted wherever the original was and has the same
|
|
140
|
+
* type. Options are the middleware's; the API key defaults to
|
|
141
|
+
* `PROMPTGUARD_API_KEY`, and a missing one throws here rather than on the
|
|
142
|
+
* first call.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* const model = promptguard(openai('gpt-5-nano'), { scanResponses: true });
|
|
147
|
+
* const result = streamText({ model, prompt: 'Hello!' });
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export declare function promptguard<M extends LanguageModelLike>(model: M, options?: PromptGuardMiddlewareOptions): M;
|
|
151
|
+
/** What the hooks read from the model `wrapLanguageModel` hands them. */
|
|
152
|
+
interface ModelIdentity {
|
|
153
|
+
readonly specificationVersion?: unknown;
|
|
154
|
+
readonly modelId?: unknown;
|
|
155
|
+
}
|
|
156
|
+
export {};
|
|
67
157
|
//# sourceMappingURL=vercel-ai.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../src/integrations/vercel-ai.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vercel-ai.d.ts","sourceRoot":"","sources":["../../src/integrations/vercel-ai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAGL,KAAK,iBAAiB,EAIvB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,KAAK,QAAQ,EAAuB,MAAM,WAAW,CAAA;AAiB9D,MAAM,WAAW,4BAA6B,SAAQ,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC;IACrF,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,sDAAsD;IACtD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAMD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,4BAAiC;IA2B5E;;;;OAIG;;IAGH;;OAEG;sBACqB,CAAC,SAAS,MAAM,sBAGrC;QACD,MAAM,EAAE,CAAC,CAAA;QACT,KAAK,CAAC,EAAE,aAAa,CAAA;KACtB,KAAG,OAAO,CAAC,CAAC,CAAC;IAoEd;;OAEG;oBAEQ,CAAC,kCAIL;QACD,UAAU,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,CAAA;QAChC,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,aAAa,CAAA;KACtB,KAAG,OAAO,CAAC,CAAC,CAAC;IAiBlB;;;;OAIG;kBAEQ,CAAC,gCAIL;QACD,QAAQ,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,CAAA;QAC9B,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,aAAa,CAAA;KACtB,KAAG,OAAO,CAAC,CAAC,CAAC;EAuBrB;AAyBD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAA;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,OAAO,EAAE,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IAChD,QAAQ,CAAC,OAAO,EAAE,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;CAC/C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,iBAAiB,EACrD,KAAK,EAAE,CAAC,EACR,OAAO,GAAE,4BAAiC,GACzC,CAAC,CA+DH;AAgBD,yEAAyE;AACzE,UAAU,aAAa;IACrB,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B"}
|