ai-sdk-guardrails 5.0.1 → 5.1.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 +210 -2
- package/dist/chunk-CB2WX5S7.js +393 -0
- package/dist/chunk-OP5XB2DJ.js +1067 -0
- package/dist/chunk-XWDH2NCM.js +1209 -0
- package/dist/chunk-ZZFNRQAT.js +355 -0
- package/dist/guardrails/input.cjs +1109 -0
- package/dist/guardrails/input.d.cts +133 -0
- package/dist/guardrails/input.d.ts +133 -0
- package/dist/guardrails/input.js +37 -0
- package/dist/guardrails/output.cjs +1260 -0
- package/dist/guardrails/output.d.cts +100 -0
- package/dist/guardrails/output.d.ts +100 -0
- package/dist/guardrails/output.js +55 -0
- package/dist/guardrails/tools.cjs +658 -0
- package/dist/guardrails/tools.d.cts +60 -0
- package/dist/guardrails/tools.d.ts +60 -0
- package/dist/guardrails/tools.js +10 -0
- package/dist/index.cjs +6755 -0
- package/dist/index.d.cts +1887 -0
- package/dist/index.d.ts +1887 -0
- package/dist/index.js +3760 -0
- package/dist/types-WcM_8Gvq.d.cts +743 -0
- package/dist/types-WcM_8Gvq.d.ts +743 -0
- package/package.json +30 -21
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AI SDK Guardrails
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Safety and quality controls for Vercel AI SDK
|
|
4
4
|
|
|
5
5
|
Add guardrails to your AI applications in one line of code. Block PII, prevent prompt injection, enforce output quality - while keeping your existing telemetry and observability stack intact.
|
|
6
6
|
|
|
@@ -35,6 +35,25 @@ await generateText({ model: safeModel, prompt: '...' });
|
|
|
35
35
|
npm install ai-sdk-guardrails
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
## 🧙♂️ No-Code Wizard (New!)
|
|
39
|
+
|
|
40
|
+
**Don't want to write code?** Use our visual wizard to configure guardrails:
|
|
41
|
+
|
|
42
|
+
1. **Open the wizard**: [wizard-prototype/index.html](./wizard-prototype/index.html)
|
|
43
|
+
2. **Choose your use case**: Content moderation, data protection, quality assurance, or security
|
|
44
|
+
3. **Select guardrails**: Pick from 40+ built-in guardrails
|
|
45
|
+
4. **Configure settings**: Adjust thresholds and parameters with sliders and toggles
|
|
46
|
+
5. **Copy generated code**: Get production-ready TypeScript code instantly
|
|
47
|
+
|
|
48
|
+
**Perfect for:**
|
|
49
|
+
|
|
50
|
+
- 🎯 **Non-technical users** who need AI safety
|
|
51
|
+
- 🚀 **Quick prototyping** of guardrail configurations
|
|
52
|
+
- 📚 **Learning** how to use the library
|
|
53
|
+
- 👥 **Team onboarding** and training
|
|
54
|
+
|
|
55
|
+
The wizard generates code that works out of the box - just copy, paste, and run!
|
|
56
|
+
|
|
38
57
|
## Why Guardrails Matter
|
|
39
58
|
|
|
40
59
|
Real problems that guardrails solve:
|
|
@@ -367,6 +386,191 @@ const agent = withAgentGuardrails(
|
|
|
367
386
|
const result = await agent.generate({ prompt: '...' });
|
|
368
387
|
```
|
|
369
388
|
|
|
389
|
+
## Advanced Stopping Mechanisms
|
|
390
|
+
|
|
391
|
+
Control exactly **when and how** guardrails stop execution with powerful, composable stopping mechanisms:
|
|
392
|
+
|
|
393
|
+
### 1. AbortSignal-Based Stopping
|
|
394
|
+
|
|
395
|
+
Clean, standard API for canceling AI operations:
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
import { createGuardrailAbortController } from 'ai-sdk-guardrails';
|
|
399
|
+
|
|
400
|
+
const { signal, abortOnViolation } = createGuardrailAbortController();
|
|
401
|
+
|
|
402
|
+
const model = withGuardrails(openai('gpt-4o'), {
|
|
403
|
+
outputGuardrails: [toxicityFilter()],
|
|
404
|
+
onOutputBlocked: abortOnViolation('critical'), // Abort on critical violations
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Signal will be aborted if critical violation detected
|
|
408
|
+
await streamText({ model, prompt: '...', abortSignal: signal });
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Features:**
|
|
412
|
+
|
|
413
|
+
- Standard AbortController API
|
|
414
|
+
- Severity-based abortion (`'low' | 'medium' | 'high' | 'critical'`)
|
|
415
|
+
- Custom abort conditions
|
|
416
|
+
- Manual abortion support
|
|
417
|
+
|
|
418
|
+
### 2. Stream Transform with Source-Level Stopping
|
|
419
|
+
|
|
420
|
+
Stop streaming at the **source** (most efficient):
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
import { createGuardrailStreamTransform } from 'ai-sdk-guardrails';
|
|
424
|
+
|
|
425
|
+
const result = streamText({
|
|
426
|
+
model,
|
|
427
|
+
prompt: 'Tell me a story',
|
|
428
|
+
experimental_transform: createGuardrailStreamTransform(
|
|
429
|
+
[toxicityFilter(), piiDetector()],
|
|
430
|
+
{
|
|
431
|
+
stopOnSeverity: 'high', // Stop on high/critical
|
|
432
|
+
checkInterval: 1, // Check every chunk
|
|
433
|
+
onViolation: (summary) => {
|
|
434
|
+
// Violation callback
|
|
435
|
+
console.log('Stopped:', summary);
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
),
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Modes:**
|
|
443
|
+
|
|
444
|
+
- `createGuardrailStreamTransform` - Progressive checking (each chunk)
|
|
445
|
+
- `createGuardrailStreamTransformBuffered` - Buffered checking (on flush)
|
|
446
|
+
|
|
447
|
+
### 3. Token-Level Control
|
|
448
|
+
|
|
449
|
+
Reduce overhead with smart token-based checking:
|
|
450
|
+
|
|
451
|
+
```ts
|
|
452
|
+
import {
|
|
453
|
+
createTokenBudgetTransform,
|
|
454
|
+
createTokenAwareGuardrailTransform,
|
|
455
|
+
} from 'ai-sdk-guardrails';
|
|
456
|
+
|
|
457
|
+
experimental_transform: [
|
|
458
|
+
// Hard token limit
|
|
459
|
+
createTokenBudgetTransform({
|
|
460
|
+
maxTokens: 1000,
|
|
461
|
+
onBudgetExceeded: (info) => console.log(info),
|
|
462
|
+
}),
|
|
463
|
+
|
|
464
|
+
// Check guardrails every N tokens (not every chunk!)
|
|
465
|
+
createTokenAwareGuardrailTransform([toxicityFilter()], {
|
|
466
|
+
checkEveryTokens: 50, // Check every 50 tokens
|
|
467
|
+
maxTokens: 1000, // Combined with budget
|
|
468
|
+
stopOnSeverity: 'high',
|
|
469
|
+
}),
|
|
470
|
+
];
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Benefits:**
|
|
474
|
+
|
|
475
|
+
- Reduce guardrail overhead by 80%+
|
|
476
|
+
- Cost control with token budgets
|
|
477
|
+
- Custom tokenizer support
|
|
478
|
+
|
|
479
|
+
### 4. Adaptive Multi-Step Execution
|
|
480
|
+
|
|
481
|
+
Self-correcting behavior across multi-step agent execution:
|
|
482
|
+
|
|
483
|
+
```ts
|
|
484
|
+
import {
|
|
485
|
+
createAdaptivePrepareStep,
|
|
486
|
+
type GuardrailViolation,
|
|
487
|
+
} from 'ai-sdk-guardrails';
|
|
488
|
+
|
|
489
|
+
const violations: GuardrailViolation[] = [];
|
|
490
|
+
|
|
491
|
+
const agent = new Agent({
|
|
492
|
+
model,
|
|
493
|
+
tools: { search: searchTool },
|
|
494
|
+
prepareStep: createAdaptivePrepareStep({
|
|
495
|
+
violations,
|
|
496
|
+
escalateAfter: 3, // Stop after 3 violations
|
|
497
|
+
strategy: (violations) => ({
|
|
498
|
+
temperature: Math.max(0.1, 0.7 - violations.length * 0.1),
|
|
499
|
+
system: `${violations.length} violations detected. Be careful.`,
|
|
500
|
+
}),
|
|
501
|
+
}),
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
// Track violations
|
|
505
|
+
withAgentGuardrails(agent, {
|
|
506
|
+
outputGuardrails: [toxicityFilter()],
|
|
507
|
+
onOutputBlocked: (summary, context, step) => {
|
|
508
|
+
violations.push({ step, summary });
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
**Features:**
|
|
514
|
+
|
|
515
|
+
- Progressive temperature reduction
|
|
516
|
+
- Custom adaptive strategies
|
|
517
|
+
- Escalation to auto-stop
|
|
518
|
+
- Lookback window configuration
|
|
519
|
+
|
|
520
|
+
### 5. Tool Execution Abortion
|
|
521
|
+
|
|
522
|
+
Prevent dangerous tool execution before or during execution:
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
import { wrapToolWithAbortion } from 'ai-sdk-guardrails';
|
|
526
|
+
|
|
527
|
+
const safeTool = wrapToolWithAbortion(
|
|
528
|
+
dangerousApiTool,
|
|
529
|
+
[urlValidator, paramValidator],
|
|
530
|
+
{
|
|
531
|
+
checkBefore: true, // Validate before execution
|
|
532
|
+
monitorDuring: true, // Monitor during execution
|
|
533
|
+
monitorInterval: 1000, // Check every second
|
|
534
|
+
checkInputDelta: true, // Monitor streaming inputs
|
|
535
|
+
abortOnSeverity: 'critical',
|
|
536
|
+
},
|
|
537
|
+
);
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**Protection:**
|
|
541
|
+
|
|
542
|
+
- Pre-execution validation
|
|
543
|
+
- Real-time monitoring
|
|
544
|
+
- Streaming input checking
|
|
545
|
+
- Manual abortion control
|
|
546
|
+
|
|
547
|
+
### 6. Finish Reason & Metadata
|
|
548
|
+
|
|
549
|
+
Better observability with proper finish reasons and metadata:
|
|
550
|
+
|
|
551
|
+
```ts
|
|
552
|
+
import { createFinishReasonEnhancement } from 'ai-sdk-guardrails';
|
|
553
|
+
|
|
554
|
+
// Automatically set in middleware, or manually:
|
|
555
|
+
const enhanced = createFinishReasonEnhancement(summary, result);
|
|
556
|
+
|
|
557
|
+
console.log(enhanced.finishReason); // 'content_filter' for blocks
|
|
558
|
+
console.log(enhanced.providerMetadata.guardrails);
|
|
559
|
+
// {
|
|
560
|
+
// blocked: true,
|
|
561
|
+
// violations: [{ message: '...', severity: 'high', ... }],
|
|
562
|
+
// executionTime: 50,
|
|
563
|
+
// stats: { passed: 2, blocked: 1, failed: 0 }
|
|
564
|
+
// }
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
**Features:**
|
|
568
|
+
|
|
569
|
+
- Standard `content_filter` finish reason
|
|
570
|
+
- Structured violation metadata
|
|
571
|
+
- Execution statistics
|
|
572
|
+
- Custom metadata preservation
|
|
573
|
+
|
|
370
574
|
## MCP Security Guardrails (Advanced)
|
|
371
575
|
|
|
372
576
|
**Production-Ready**: Protect against the ["lethal trifecta" vulnerability](https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/) when using Model Context Protocol (MCP) tools.
|
|
@@ -540,7 +744,7 @@ See source for all built-in guardrails:
|
|
|
540
744
|
|
|
541
745
|
## Examples
|
|
542
746
|
|
|
543
|
-
Browse 48+ runnable examples: [examples/README.md](./examples/README.md)
|
|
747
|
+
Browse 48+ runnable examples: [examples/README.md](./examples/README.md) |
|
|
544
748
|
|
|
545
749
|
### Quick Starts
|
|
546
750
|
|
|
@@ -623,6 +827,10 @@ Changes:
|
|
|
623
827
|
|
|
624
828
|
**Type-safe**: Rich TypeScript types and inference throughout.
|
|
625
829
|
|
|
830
|
+
**Comprehensive**: 40+ built-in guardrails covering security, quality, compliance, and performance.
|
|
831
|
+
|
|
832
|
+
**Advanced features**: Early detection, parallel execution, enhanced prompt injection detection, MCP security, and more.
|
|
833
|
+
|
|
626
834
|
## Contributing
|
|
627
835
|
|
|
628
836
|
Issues and PRs are welcome.
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/errors.ts
|
|
9
|
+
var GuardrailsError = class extends Error {
|
|
10
|
+
timestamp;
|
|
11
|
+
metadata;
|
|
12
|
+
constructor(message, metadata = {}) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.timestamp = /* @__PURE__ */ new Date();
|
|
15
|
+
this.metadata = metadata;
|
|
16
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Convert the error to a serializable object for logging/reporting
|
|
20
|
+
*/
|
|
21
|
+
toJSON() {
|
|
22
|
+
return {
|
|
23
|
+
name: this.name,
|
|
24
|
+
code: this.code,
|
|
25
|
+
message: this.message,
|
|
26
|
+
timestamp: this.timestamp.toISOString(),
|
|
27
|
+
metadata: this.metadata,
|
|
28
|
+
stack: this.stack
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check if this error is of a specific type
|
|
33
|
+
*/
|
|
34
|
+
is(errorClass) {
|
|
35
|
+
return this instanceof errorClass;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var GuardrailValidationError = class extends GuardrailsError {
|
|
39
|
+
name = "GuardrailValidationError";
|
|
40
|
+
code = "GUARDRAIL_VALIDATION_FAILED";
|
|
41
|
+
guardrailName;
|
|
42
|
+
validationErrors;
|
|
43
|
+
constructor(guardrailName, validationErrors, metadata = {}) {
|
|
44
|
+
const message = `Guardrail "${guardrailName}" validation failed: ${validationErrors.map((e) => e.message).join(", ")}`;
|
|
45
|
+
super(message, { ...metadata, guardrailName, validationErrors });
|
|
46
|
+
this.guardrailName = guardrailName;
|
|
47
|
+
this.validationErrors = validationErrors;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var GuardrailExecutionError = class extends GuardrailsError {
|
|
51
|
+
name = "GuardrailExecutionError";
|
|
52
|
+
code = "GUARDRAIL_EXECUTION_FAILED";
|
|
53
|
+
guardrailName;
|
|
54
|
+
originalError;
|
|
55
|
+
constructor(guardrailName, originalError, metadata = {}) {
|
|
56
|
+
const message = originalError ? `Guardrail "${guardrailName}" execution failed: ${originalError.message}` : `Guardrail "${guardrailName}" execution failed`;
|
|
57
|
+
super(message, {
|
|
58
|
+
...metadata,
|
|
59
|
+
guardrailName,
|
|
60
|
+
originalError: originalError?.message
|
|
61
|
+
});
|
|
62
|
+
this.guardrailName = guardrailName;
|
|
63
|
+
this.originalError = originalError;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var GuardrailTimeoutError = class extends GuardrailsError {
|
|
67
|
+
name = "GuardrailTimeoutError";
|
|
68
|
+
code = "GUARDRAIL_TIMEOUT";
|
|
69
|
+
guardrailName;
|
|
70
|
+
timeoutMs;
|
|
71
|
+
constructor(guardrailName, timeoutMs, metadata = {}) {
|
|
72
|
+
const message = `Guardrail "${guardrailName}" timed out after ${timeoutMs}ms`;
|
|
73
|
+
super(message, { ...metadata, guardrailName, timeoutMs });
|
|
74
|
+
this.guardrailName = guardrailName;
|
|
75
|
+
this.timeoutMs = timeoutMs;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
var GuardrailConfigurationError = class extends GuardrailsError {
|
|
79
|
+
name = "GuardrailConfigurationError";
|
|
80
|
+
code = "GUARDRAIL_CONFIG_INVALID";
|
|
81
|
+
configPath;
|
|
82
|
+
configErrors;
|
|
83
|
+
constructor(configErrors, configPath, metadata = {}) {
|
|
84
|
+
const message = `Guardrail configuration error${configPath ? ` in ${configPath}` : ""}: ${configErrors.join(", ")}`;
|
|
85
|
+
super(message, { ...metadata, configPath, configErrors });
|
|
86
|
+
this.configPath = configPath;
|
|
87
|
+
this.configErrors = configErrors;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var GuardrailsInputError = class extends GuardrailsError {
|
|
91
|
+
name = "GuardrailsInputError";
|
|
92
|
+
code = "INPUT_BLOCKED";
|
|
93
|
+
blockedGuardrails;
|
|
94
|
+
constructor(blockedGuardrails, metadata = {}) {
|
|
95
|
+
const guardrailNames = blockedGuardrails.map((g) => g.name).join(", ");
|
|
96
|
+
const message = `Input blocked by guardrail${blockedGuardrails.length > 1 ? "s" : ""}: ${guardrailNames}`;
|
|
97
|
+
super(message, { ...metadata, blockedGuardrails });
|
|
98
|
+
this.blockedGuardrails = blockedGuardrails;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
var GuardrailsOutputError = class extends GuardrailsError {
|
|
102
|
+
name = "GuardrailsOutputError";
|
|
103
|
+
code = "OUTPUT_BLOCKED";
|
|
104
|
+
blockedGuardrails;
|
|
105
|
+
constructor(blockedGuardrails, metadata = {}) {
|
|
106
|
+
const guardrailNames = blockedGuardrails.map((g) => g.name).join(", ");
|
|
107
|
+
const message = `Output blocked by guardrail${blockedGuardrails.length > 1 ? "s" : ""}: ${guardrailNames}`;
|
|
108
|
+
super(message, { ...metadata, blockedGuardrails });
|
|
109
|
+
this.blockedGuardrails = blockedGuardrails;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
var MiddlewareError = class extends GuardrailsError {
|
|
113
|
+
name = "MiddlewareError";
|
|
114
|
+
code = "MIDDLEWARE_ERROR";
|
|
115
|
+
middlewareType;
|
|
116
|
+
phase;
|
|
117
|
+
originalError;
|
|
118
|
+
constructor(middlewareType, phase, originalError, metadata = {}) {
|
|
119
|
+
const message = originalError ? `${middlewareType} middleware ${phase} error: ${originalError.message}` : `${middlewareType} middleware ${phase} error`;
|
|
120
|
+
super(message, {
|
|
121
|
+
...metadata,
|
|
122
|
+
middlewareType,
|
|
123
|
+
phase,
|
|
124
|
+
originalError: originalError?.message
|
|
125
|
+
});
|
|
126
|
+
this.middlewareType = middlewareType;
|
|
127
|
+
this.phase = phase;
|
|
128
|
+
this.originalError = originalError;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
function isGuardrailsError(error) {
|
|
132
|
+
return error instanceof GuardrailsError;
|
|
133
|
+
}
|
|
134
|
+
function extractErrorInfo(error) {
|
|
135
|
+
if (isGuardrailsError(error)) {
|
|
136
|
+
return {
|
|
137
|
+
name: error.name,
|
|
138
|
+
message: error.message,
|
|
139
|
+
code: error.code,
|
|
140
|
+
metadata: error.metadata
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (error instanceof Error) {
|
|
144
|
+
return {
|
|
145
|
+
name: error.name,
|
|
146
|
+
message: error.message
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
name: "UnknownError",
|
|
151
|
+
message: String(error)
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/core.ts
|
|
156
|
+
function createInputGuardrail(name, description, execute) {
|
|
157
|
+
return { name, description, execute };
|
|
158
|
+
}
|
|
159
|
+
function createOutputGuardrail(name, execute) {
|
|
160
|
+
return { name, execute };
|
|
161
|
+
}
|
|
162
|
+
function createGenerateWithErrorHandling(generate, signal, onError, retryOnError, maxRetries) {
|
|
163
|
+
return async (params, attemptNum) => {
|
|
164
|
+
try {
|
|
165
|
+
return signal ? await generate(params, signal) : await generate(params);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
onError?.(error, attemptNum);
|
|
168
|
+
if (retryOnError?.(error, attemptNum) && attemptNum <= maxRetries) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function buildRetrySummary(attemptHistory, validationResult, isUsingEnhancedFeatures, maxRetries) {
|
|
176
|
+
const blockedResults = attemptHistory.filter((historyAttempt) => historyAttempt.blocked).map(() => ({
|
|
177
|
+
message: validationResult.message,
|
|
178
|
+
metadata: validationResult.metadata
|
|
179
|
+
}));
|
|
180
|
+
const summary = { blockedResults };
|
|
181
|
+
if (isUsingEnhancedFeatures) {
|
|
182
|
+
summary.totalAttempts = maxRetries + 1;
|
|
183
|
+
summary.attempts = [...attemptHistory];
|
|
184
|
+
}
|
|
185
|
+
return summary;
|
|
186
|
+
}
|
|
187
|
+
async function performInitialAttempt(params, generateFn, validate, onAttempt, maxRetries, retryOnError) {
|
|
188
|
+
const attemptHistory = [];
|
|
189
|
+
onAttempt?.({
|
|
190
|
+
attempt: 0,
|
|
191
|
+
totalAttempts: maxRetries + 1,
|
|
192
|
+
isRetry: false
|
|
193
|
+
});
|
|
194
|
+
try {
|
|
195
|
+
const result = await generateFn(params, 0);
|
|
196
|
+
const validationResult = await Promise.resolve(validate(result));
|
|
197
|
+
attemptHistory.push({
|
|
198
|
+
attempt: 0,
|
|
199
|
+
result,
|
|
200
|
+
blocked: validationResult.blocked
|
|
201
|
+
});
|
|
202
|
+
return { result, validationResult, attemptHistory };
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (!retryOnError?.(error, 0)) {
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
result: void 0,
|
|
209
|
+
validationResult: {
|
|
210
|
+
blocked: true,
|
|
211
|
+
message: `Generation error: ${error}`
|
|
212
|
+
},
|
|
213
|
+
attemptHistory
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function performBackoffWait(backoffMs, attempt, signal) {
|
|
218
|
+
const wait = typeof backoffMs === "function" ? backoffMs(attempt) : backoffMs ?? 0;
|
|
219
|
+
if (wait && wait > 0) {
|
|
220
|
+
await new Promise((resolve, reject) => {
|
|
221
|
+
const timeout = setTimeout(resolve, wait);
|
|
222
|
+
signal?.addEventListener("abort", () => {
|
|
223
|
+
clearTimeout(timeout);
|
|
224
|
+
reject(new Error("Aborted during retry backoff"));
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
async function retry(options) {
|
|
231
|
+
const {
|
|
232
|
+
generate,
|
|
233
|
+
params,
|
|
234
|
+
validate,
|
|
235
|
+
buildRetryParams,
|
|
236
|
+
maxRetries = 1,
|
|
237
|
+
backoffMs,
|
|
238
|
+
signal,
|
|
239
|
+
onAttempt,
|
|
240
|
+
retryOnError,
|
|
241
|
+
onError,
|
|
242
|
+
onExhausted = "return-last"
|
|
243
|
+
} = options;
|
|
244
|
+
signal?.throwIfAborted();
|
|
245
|
+
const generateFn = createGenerateWithErrorHandling(
|
|
246
|
+
generate,
|
|
247
|
+
signal,
|
|
248
|
+
onError,
|
|
249
|
+
retryOnError,
|
|
250
|
+
maxRetries
|
|
251
|
+
);
|
|
252
|
+
const {
|
|
253
|
+
result: initialResult,
|
|
254
|
+
validationResult,
|
|
255
|
+
attemptHistory
|
|
256
|
+
} = await performInitialAttempt(
|
|
257
|
+
params,
|
|
258
|
+
generateFn,
|
|
259
|
+
validate,
|
|
260
|
+
onAttempt,
|
|
261
|
+
maxRetries,
|
|
262
|
+
retryOnError
|
|
263
|
+
);
|
|
264
|
+
let result = initialResult;
|
|
265
|
+
let v = validationResult;
|
|
266
|
+
let lastParams = params;
|
|
267
|
+
let attempt = 0;
|
|
268
|
+
while (v.blocked && attempt < maxRetries) {
|
|
269
|
+
attempt++;
|
|
270
|
+
signal?.throwIfAborted();
|
|
271
|
+
const enhancedFeatures = !!(signal || onAttempt || retryOnError || onError || onExhausted !== "return-last");
|
|
272
|
+
const summary = buildRetrySummary(
|
|
273
|
+
attemptHistory,
|
|
274
|
+
v,
|
|
275
|
+
enhancedFeatures,
|
|
276
|
+
maxRetries
|
|
277
|
+
);
|
|
278
|
+
const nextParams = buildRetryParams({
|
|
279
|
+
summary,
|
|
280
|
+
originalParams: params,
|
|
281
|
+
lastParams,
|
|
282
|
+
lastResult: result
|
|
283
|
+
});
|
|
284
|
+
const wait = typeof backoffMs === "function" ? backoffMs(attempt) : backoffMs ?? 0;
|
|
285
|
+
onAttempt?.({
|
|
286
|
+
attempt,
|
|
287
|
+
totalAttempts: maxRetries + 1,
|
|
288
|
+
lastResult: result,
|
|
289
|
+
waitMs: wait,
|
|
290
|
+
isRetry: true
|
|
291
|
+
});
|
|
292
|
+
await performBackoffWait(backoffMs, attempt, signal);
|
|
293
|
+
lastParams = nextParams;
|
|
294
|
+
try {
|
|
295
|
+
result = await generateFn(lastParams, attempt);
|
|
296
|
+
v = await Promise.resolve(validate(result));
|
|
297
|
+
attemptHistory.push({
|
|
298
|
+
attempt,
|
|
299
|
+
result,
|
|
300
|
+
blocked: v.blocked,
|
|
301
|
+
waitMs: wait
|
|
302
|
+
});
|
|
303
|
+
} catch (error) {
|
|
304
|
+
if (!retryOnError?.(error, attempt)) {
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
v = { blocked: true, message: `Generation error: ${error}` };
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (v.blocked && onExhausted === "throw") {
|
|
311
|
+
throw new Error(
|
|
312
|
+
`Retry exhausted after ${maxRetries} attempts: ${v.message}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
var retryHelpers = {
|
|
318
|
+
/**
|
|
319
|
+
* Increases max output tokens for retry attempts
|
|
320
|
+
*/
|
|
321
|
+
increaseTokens: (increase = 200) => ({
|
|
322
|
+
lastParams
|
|
323
|
+
}) => ({
|
|
324
|
+
...lastParams,
|
|
325
|
+
maxOutputTokens: Math.max(
|
|
326
|
+
400,
|
|
327
|
+
(lastParams.maxOutputTokens ?? 400) + increase
|
|
328
|
+
)
|
|
329
|
+
}),
|
|
330
|
+
/**
|
|
331
|
+
* Adds encouraging prompt for retry attempts
|
|
332
|
+
*/
|
|
333
|
+
addEncouragingPrompt: (encouragement = "Please provide a more detailed and comprehensive response.") => ({
|
|
334
|
+
lastParams,
|
|
335
|
+
summary
|
|
336
|
+
}) => {
|
|
337
|
+
const basePrompt = Array.isArray(lastParams.prompt) ? lastParams.prompt : [
|
|
338
|
+
{
|
|
339
|
+
role: "user",
|
|
340
|
+
content: [
|
|
341
|
+
{ type: "text", text: String(lastParams.prompt || "") }
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
];
|
|
345
|
+
return {
|
|
346
|
+
...lastParams,
|
|
347
|
+
prompt: [
|
|
348
|
+
...basePrompt,
|
|
349
|
+
{
|
|
350
|
+
role: "user",
|
|
351
|
+
content: [
|
|
352
|
+
{
|
|
353
|
+
type: "text",
|
|
354
|
+
text: `${summary.blockedResults[0]?.message ? `Note: ${summary.blockedResults[0].message}.` : ""} ${encouragement}`
|
|
355
|
+
}
|
|
356
|
+
]
|
|
357
|
+
}
|
|
358
|
+
]
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
/**
|
|
362
|
+
* Combines token increase with encouraging prompt
|
|
363
|
+
*/
|
|
364
|
+
improveResponse: (tokenIncrease = 200, encouragement) => (args) => {
|
|
365
|
+
const withTokens = retryHelpers.increaseTokens(tokenIncrease)(args);
|
|
366
|
+
const withEncouragement = retryHelpers.addEncouragingPrompt(
|
|
367
|
+
encouragement
|
|
368
|
+
)({ ...args, lastParams: withTokens });
|
|
369
|
+
return { ...withTokens, ...withEncouragement };
|
|
370
|
+
},
|
|
371
|
+
/**
|
|
372
|
+
* Simple parameter passthrough (no changes)
|
|
373
|
+
*/
|
|
374
|
+
noChange: () => ({ lastParams }) => lastParams
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
export {
|
|
378
|
+
__require,
|
|
379
|
+
GuardrailsError,
|
|
380
|
+
GuardrailValidationError,
|
|
381
|
+
GuardrailExecutionError,
|
|
382
|
+
GuardrailTimeoutError,
|
|
383
|
+
GuardrailConfigurationError,
|
|
384
|
+
GuardrailsInputError,
|
|
385
|
+
GuardrailsOutputError,
|
|
386
|
+
MiddlewareError,
|
|
387
|
+
isGuardrailsError,
|
|
388
|
+
extractErrorInfo,
|
|
389
|
+
createInputGuardrail,
|
|
390
|
+
createOutputGuardrail,
|
|
391
|
+
retry,
|
|
392
|
+
retryHelpers
|
|
393
|
+
};
|