ai-sdk-guardrails 1.0.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 ADDED
@@ -0,0 +1,729 @@
1
+ # AI SDK Guardrails
2
+
3
+ The safest way to build production AI applications with the Vercel AI SDK. A comprehensive TypeScript library that protects your AI systems with intelligent input and output validation, streaming safety, and enterprise-grade reliability.
4
+
5
+ [![npm version](https://badge.fury.io/js/ai-sdk-guardrails.svg)](https://www.npmjs.com/package/ai-sdk-guardrails)
6
+ [![Downloads](https://img.shields.io/npm/dm/ai-sdk-guardrails.svg)](https://www.npmjs.com/package/ai-sdk-guardrails)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ ## Why AI SDK Guardrails?
11
+
12
+ Building AI applications is exciting, but deploying them safely in production requires careful consideration. This library provides the safety net you need without sacrificing developer experience or performance.
13
+
14
+ ### 🛡️ Complete Protection
15
+
16
+ Validate inputs before they reach your model and outputs before they reach users. Catch harmful content, PII, prompt injections, and more.
17
+
18
+ ### ⚡ Real-time Streaming Safety
19
+
20
+ The only guardrails library with true streaming support. Monitor and stop streams in real-time when safety violations occur.
21
+
22
+ ### 🎯 Built for Vercel AI SDK
23
+
24
+ First-class support for all AI SDK functions: `generateText`, `generateObject`, `streamText`, `streamObject`, and `embed`.
25
+
26
+ ### 🔧 Developer Friendly
27
+
28
+ Simple API with TypeScript autocompletion, helpful error messages, and sensible defaults. Start safe in minutes, not hours.
29
+
30
+ ## How It Works
31
+
32
+ ```mermaid
33
+ graph TB
34
+ A[User Input] --> B{Input Guardrails}
35
+ B -->|✅ Pass| C[AI Model]
36
+ B -->|❌ Block| D[GuardrailError]
37
+
38
+ C --> E[AI Response]
39
+ E --> F{Output Guardrails}
40
+ F -->|✅ Pass| G[Safe Response]
41
+ F -->|❌ Block| H[GuardrailError]
42
+
43
+ subgraph "Input Guardrails"
44
+ I1[Length Limit]
45
+ I2[Blocked Keywords]
46
+ I3[PII Detection]
47
+ I4[Prompt Injection]
48
+ I5[Rate Limiting]
49
+ end
50
+
51
+ subgraph "Output Guardrails"
52
+ O1[Quality Check]
53
+ O2[Toxicity Filter]
54
+ O3[Privacy Leakage]
55
+ O4[Schema Validation]
56
+ O5[Confidence Threshold]
57
+ end
58
+
59
+ B -.-> I1
60
+ B -.-> I2
61
+ B -.-> I3
62
+ B -.-> I4
63
+ B -.-> I5
64
+
65
+ F -.-> O1
66
+ F -.-> O2
67
+ F -.-> O3
68
+ F -.-> O4
69
+ F -.-> O5
70
+
71
+ classDef inputNode fill:#e1f5fe
72
+ classDef outputNode fill:#f3e5f5
73
+ classDef blockNode fill:#ffebee
74
+ classDef passNode fill:#e8f5e8
75
+
76
+ class I1,I2,I3,I4,I5 inputNode
77
+ class O1,O2,O3,O4,O5 outputNode
78
+ class D,H blockNode
79
+ class G passNode
80
+ ```
81
+
82
+ ## Installation
83
+
84
+ ```bash
85
+ npm install ai-sdk-guardrails
86
+ # or
87
+ pnpm add ai-sdk-guardrails
88
+ # or
89
+ yarn add ai-sdk-guardrails
90
+ ```
91
+
92
+ ## Quick Start
93
+
94
+ Get started with production-ready guardrails in just a few lines:
95
+
96
+ ```typescript
97
+ import { generateTextWithGuardrails } from 'ai-sdk-guardrails';
98
+ import { blockedKeywords } from 'ai-sdk-guardrails/guardrails/input';
99
+ import { outputLengthLimit } from 'ai-sdk-guardrails/guardrails/output';
100
+ import { openai } from '@ai-sdk/openai';
101
+
102
+ const result = await generateTextWithGuardrails(
103
+ {
104
+ model: openai('gpt-4-turbo'),
105
+ prompt: 'Write a helpful response about web security',
106
+ },
107
+ {
108
+ inputGuardrails: [blockedKeywords(['hack', 'exploit', 'vulnerability'])],
109
+ outputGuardrails: [outputLengthLimit(500)],
110
+ },
111
+ );
112
+
113
+ console.log(result.text);
114
+ ```
115
+
116
+ ## Core Concepts
117
+
118
+ ### Input Guardrails
119
+
120
+ Input guardrails validate prompts before they reach your AI model. Use them to:
121
+
122
+ - Block harmful or inappropriate content
123
+ - Enforce length limits
124
+ - Detect prompt injection attempts
125
+ - Remove personally identifiable information (PII)
126
+ - Implement rate limiting
127
+
128
+ ```typescript
129
+ import { createInputGuardrail } from 'ai-sdk-guardrails';
130
+
131
+ const mathHomeworkDetector = createInputGuardrail(
132
+ 'math-homework-detector',
133
+ 'Prevents direct homework solving requests',
134
+ (context) => {
135
+ const { prompt } = context;
136
+ const homeworkPatterns = [
137
+ /solve this equation/i,
138
+ /what is \d+ [\+\-\*\/] \d+/i,
139
+ /calculate the answer/i,
140
+ ];
141
+
142
+ const isHomework = homeworkPatterns.some((pattern) =>
143
+ pattern.test(prompt || ''),
144
+ );
145
+
146
+ return {
147
+ tripwireTriggered: isHomework,
148
+ message: isHomework ? 'Direct homework solving detected' : undefined,
149
+ suggestion: isHomework
150
+ ? 'Try asking about the concepts instead'
151
+ : undefined,
152
+ };
153
+ },
154
+ );
155
+ ```
156
+
157
+ ### Output Guardrails
158
+
159
+ Output guardrails validate AI responses before they reach users. Use them to:
160
+
161
+ - Ensure response quality
162
+ - Block sensitive information
163
+ - Enforce formatting requirements
164
+ - Validate against schemas
165
+ - Check confidence levels
166
+
167
+ ```typescript
168
+ import { createOutputGuardrail } from 'ai-sdk-guardrails';
169
+
170
+ const sensitiveInfoFilter = createOutputGuardrail(
171
+ 'sensitive-info-filter',
172
+ (context) => {
173
+ const { text } = context.result;
174
+ const sensitivePatterns = [
175
+ /password:\s*\w+/i,
176
+ /api[_-]?key:\s*\w+/i,
177
+ /\b\d{3}-\d{2}-\d{4}\b/, // SSN format
178
+ ];
179
+
180
+ const hasSensitive = sensitivePatterns.some((pattern) =>
181
+ pattern.test(text || ''),
182
+ );
183
+
184
+ return {
185
+ tripwireTriggered: hasSensitive,
186
+ message: hasSensitive
187
+ ? 'Response contains sensitive information'
188
+ : undefined,
189
+ severity: hasSensitive ? 'critical' : 'low',
190
+ };
191
+ },
192
+ );
193
+ ```
194
+
195
+ ### Streaming Guardrails
196
+
197
+ Protect streaming responses in real-time. The stream automatically stops if guardrails detect violations:
198
+
199
+ ```typescript
200
+ import { streamTextWithGuardrails } from 'ai-sdk-guardrails';
201
+
202
+ const stream = await streamTextWithGuardrails(
203
+ {
204
+ model: openai('gpt-4-turbo'),
205
+ prompt: 'Generate a long story...',
206
+ },
207
+ {
208
+ outputGuardrails: [
209
+ outputLengthLimit(1000),
210
+ blockedOutputContent(['inappropriate', 'offensive']),
211
+ ],
212
+ onOutputBlocked: (error) => {
213
+ console.error('Stream blocked:', error.reason);
214
+ },
215
+ },
216
+ );
217
+
218
+ // The stream will automatically stop if guardrails trigger
219
+ for await (const chunk of stream.textStream) {
220
+ process.stdout.write(chunk);
221
+ }
222
+ ```
223
+
224
+ ## Built-in Guardrails
225
+
226
+ ### Input Guardrails
227
+
228
+ ```typescript
229
+ import {
230
+ lengthLimit,
231
+ blockedWords,
232
+ blockedKeywords,
233
+ profanityFilter,
234
+ promptInjectionDetector,
235
+ piiDetector,
236
+ toxicityDetector,
237
+ mathHomeworkDetector,
238
+ codeGenerationLimiter,
239
+ } from 'ai-sdk-guardrails/guardrails/input';
240
+
241
+ // Content filters
242
+ const contentGuardrails = [
243
+ lengthLimit(1000),
244
+ blockedWords(['spam', 'hack']),
245
+ profanityFilter(['custom', 'words']),
246
+ toxicityDetector(0.7), // threshold
247
+ ];
248
+
249
+ // Security guardrails
250
+ const securityGuardrails = [promptInjectionDetector(), piiDetector()];
251
+
252
+ // Educational guardrails
253
+ const educationGuardrails = [
254
+ mathHomeworkDetector(),
255
+ codeGenerationLimiter(['javascript', 'python']),
256
+ ];
257
+ ```
258
+
259
+ ### Output Guardrails
260
+
261
+ ```typescript
262
+ import {
263
+ lengthLimit,
264
+ blockedContent,
265
+ jsonValidation,
266
+ confidenceThreshold,
267
+ toxicityFilter,
268
+ schemaValidation,
269
+ tokenUsageLimit,
270
+ performanceMonitor,
271
+ hallucinationDetector,
272
+ biasDetector,
273
+ factualAccuracyChecker,
274
+ privacyLeakageDetector,
275
+ contentConsistencyChecker,
276
+ complianceChecker,
277
+ } from 'ai-sdk-guardrails/guardrails/output';
278
+
279
+ // Quality guardrails
280
+ const qualityGuardrails = [
281
+ lengthLimit(500),
282
+ confidenceThreshold(0.8),
283
+ hallucinationDetector(0.7),
284
+ factualAccuracyChecker(true), // require sources
285
+ ];
286
+
287
+ // Safety guardrails
288
+ const safetyGuardrails = [
289
+ toxicityFilter(0.5),
290
+ biasDetector(),
291
+ privacyLeakageDetector(),
292
+ complianceChecker(['GDPR', 'HIPAA']),
293
+ ];
294
+
295
+ // Performance guardrails
296
+ const performanceGuardrails = [
297
+ tokenUsageLimit(1000),
298
+ performanceMonitor(5000), // max 5 seconds
299
+ ];
300
+ ```
301
+
302
+ ## Integration with Autoevals
303
+
304
+ Use [Autoevals](https://github.com/braintrust-data/autoevals) for sophisticated AI quality evaluation as guardrails:
305
+
306
+ ```typescript
307
+ import { Factuality, init } from 'autoevals';
308
+ import { createOutputGuardrail } from 'ai-sdk-guardrails';
309
+
310
+ function createFactualityGuardrail({
311
+ expected,
312
+ minScore,
313
+ }: {
314
+ expected: string;
315
+ minScore: number;
316
+ }) {
317
+ return createOutputGuardrail('factuality-check', async (context) => {
318
+ const { text } = extractContent(context.result);
319
+ const { prompt } = extractTextContent(context.input);
320
+
321
+ const evalResult = await Factuality({
322
+ output: text,
323
+ expected,
324
+ input: prompt || '',
325
+ model: MODEL_NAME,
326
+ });
327
+
328
+ const isFactual = (evalResult.score || 0) >= minScore;
329
+
330
+ return {
331
+ tripwireTriggered: !isFactual,
332
+ message: isFactual
333
+ ? `Factual content (score: ${evalResult.score})`
334
+ : `Factual accuracy too low (score: ${evalResult.score}, required: ${minScore})`,
335
+ severity: isFactual ? 'low' : 'high',
336
+ metadata: {
337
+ factualityScore: evalResult.score,
338
+ rationale: evalResult.metadata?.rationale,
339
+ expected,
340
+ minScore,
341
+ },
342
+ suggestion: isFactual
343
+ ? undefined
344
+ : 'Please provide more accurate information',
345
+ };
346
+ });
347
+ }
348
+
349
+ // Usage
350
+ const result = await generateTextWithGuardrails(
351
+ {
352
+ model: openai('gpt-4-turbo'),
353
+ prompt: 'Which country has the highest population?',
354
+ },
355
+ {
356
+ outputGuardrails: [
357
+ createFactualityGuardrail({
358
+ expected: 'China',
359
+ minScore: 0.7,
360
+ }),
361
+ ],
362
+ },
363
+ );
364
+ ```
365
+
366
+ ## Error Handling
367
+
368
+ Guardrails provide rich error information to help you handle violations gracefully:
369
+
370
+ ```typescript
371
+ import { GuardrailError } from 'ai-sdk-guardrails';
372
+
373
+ try {
374
+ const result = await generateTextWithGuardrails(params, guardrails);
375
+ } catch (error) {
376
+ if (error instanceof GuardrailError) {
377
+ // Access detailed error information
378
+ console.log('Guardrail triggered:', error.guardrailName);
379
+ console.log('Reason:', error.reason);
380
+ console.log('Type:', error.type); // 'input' or 'output'
381
+
382
+ // Get all issues
383
+ error.issues.forEach((issue) => {
384
+ console.log(`${issue.guardrail}: ${issue.message}`);
385
+ console.log('Severity:', issue.severity);
386
+ console.log('Suggestion:', issue.suggestion);
387
+ });
388
+
389
+ // Use helper methods
390
+ const summary = error.getSummary();
391
+ console.log(`Total issues: ${summary.totalIssues}`);
392
+ console.log(
393
+ `Guardrails triggered: ${summary.guardrailsTriggered.join(', ')}`,
394
+ );
395
+ }
396
+ }
397
+ ```
398
+
399
+ ## Advanced Usage
400
+
401
+ ### Custom Validation Logic
402
+
403
+ Create sophisticated guardrails for your specific needs:
404
+
405
+ ```typescript
406
+ const businessLogicGuardrail = createInputGuardrail(
407
+ 'business-logic-validator',
408
+ 'Ensures requests meet business requirements',
409
+ async (context) => {
410
+ const { prompt, messages } = context;
411
+
412
+ // Implement your custom logic
413
+ const validationResult = await validateBusinessRules({
414
+ content: prompt,
415
+ history: messages,
416
+ });
417
+
418
+ return {
419
+ tripwireTriggered: !validationResult.isValid,
420
+ message: validationResult.error,
421
+ metadata: validationResult.details,
422
+ severity: validationResult.severity,
423
+ suggestion: validationResult.suggestion,
424
+ };
425
+ },
426
+ );
427
+ ```
428
+
429
+ ### Composing Guardrails
430
+
431
+ Combine multiple guardrail configurations for different scenarios:
432
+
433
+ ```typescript
434
+ // Development guardrails (more lenient)
435
+ const devGuardrails = {
436
+ inputGuardrails: [lengthLimit(2000)],
437
+ throwOnBlocked: false,
438
+ onInputBlocked: (error) => console.warn('Dev warning:', error),
439
+ };
440
+
441
+ // Production guardrails (strict)
442
+ const prodGuardrails = {
443
+ inputGuardrails: [lengthLimit(500), blockedKeywords(['test', 'debug'])],
444
+ outputGuardrails: [confidenceThreshold(0.9), complianceChecker(['GDPR'])],
445
+ throwOnBlocked: true,
446
+ };
447
+
448
+ // Use based on environment
449
+ const guardrails =
450
+ process.env.NODE_ENV === 'production' ? prodGuardrails : devGuardrails;
451
+ ```
452
+
453
+ ### Object Generation with Schema Validation
454
+
455
+ Validate structured outputs with custom business logic:
456
+
457
+ ```typescript
458
+ import { z } from 'zod';
459
+
460
+ const userSchema = z.object({
461
+ name: z.string(),
462
+ age: z.number().min(0).max(120),
463
+ email: z.string().email(),
464
+ });
465
+
466
+ const schemaValidator = createOutputGuardrail(
467
+ 'schema-validator',
468
+ async (context) => {
469
+ const { object } = extractContent(context.result);
470
+ try {
471
+ userSchema.parse(object);
472
+ return { tripwireTriggered: false };
473
+ } catch (error) {
474
+ return {
475
+ tripwireTriggered: true,
476
+ message: 'Schema validation failed',
477
+ severity: 'high',
478
+ metadata: { validationError: error.message },
479
+ };
480
+ }
481
+ },
482
+ );
483
+
484
+ const result = await generateObjectWithGuardrails(
485
+ {
486
+ model: openai('gpt-4-turbo'),
487
+ prompt: 'Create a user profile for John Doe, age 30',
488
+ schema: userSchema,
489
+ },
490
+ {
491
+ outputGuardrails: [schemaValidator],
492
+ },
493
+ );
494
+ ```
495
+
496
+ ## Best Practices
497
+
498
+ ### 1. Layer Your Defence
499
+
500
+ Use multiple guardrails for comprehensive protection:
501
+
502
+ ```typescript
503
+ const guardrails = {
504
+ inputGuardrails: [
505
+ // First line: Block obvious threats
506
+ promptInjectionDetector(),
507
+ blockedKeywords(['malicious', 'exploit']),
508
+
509
+ // Second line: Quality control
510
+ lengthLimit(1000),
511
+
512
+ // Third line: Business logic
513
+ customBusinessRules(),
514
+ ],
515
+ outputGuardrails: [
516
+ // Ensure quality
517
+ confidenceThreshold(0.7),
518
+
519
+ // Ensure safety
520
+ toxicityFilter(),
521
+ privacyLeakageDetector(),
522
+ ],
523
+ };
524
+ ```
525
+
526
+ ### 2. Handle Errors Gracefully
527
+
528
+ Provide helpful feedback when guardrails trigger:
529
+
530
+ ```typescript
531
+ onInputBlocked: (error) => {
532
+ // Map technical errors to user-friendly messages
533
+ const userMessage =
534
+ {
535
+ 'content-length-limit': 'Your message is too long. Please shorten it.',
536
+ 'blocked-keywords': 'Your request contains restricted content.',
537
+ 'pii-detector': 'Please remove personal information from your request.',
538
+ }[error.guardrailName] || 'Your request could not be processed.';
539
+
540
+ return userMessage;
541
+ };
542
+ ```
543
+
544
+ ### 3. **Publishing Process (Using Changesets)**
545
+
546
+ You're already set up with **Changesets** which is the recommended approach. Here's the process:
547
+
548
+ #### **Step 1: Create a Changeset**
549
+
550
+ ```bash
551
+ npx changeset
552
+ ```
553
+
554
+ This will:
555
+
556
+ - Ask you what type of change (patch/minor/major)
557
+ - Let you write a summary of changes
558
+ - Create a changeset file
559
+
560
+ #### **Step 2: Version and Publish**
561
+
562
+ You have two options:
563
+
564
+ **Option A: Manual Control**
565
+
566
+ ```bash
567
+ # 1. Run CI to ensure everything passes
568
+ npm run ci
569
+
570
+ # 2. Version the package (reads changesets and updates version)
571
+ npx changeset version
572
+
573
+ # 3. Publish to npm
574
+ npx changeset publish
575
+ ```
576
+
577
+ **Option B: Use Your Local Release Script**
578
+
579
+ ```bash
580
+ npm run local-release
581
+ ```
582
+
583
+ This runs: `npm run ci && changeset version && changeset publish`
584
+
585
+ ### 4. **First Release Steps**
586
+
587
+ For your first release, I recommend:
588
+
589
+ 1. **Fix any remaining issues** (like the prettier formatting):
590
+
591
+ ```bash
592
+ npm run format
593
+ ```
594
+
595
+ 2. **Create your first changeset**:
596
+
597
+ ```bash
598
+ npx changeset
599
+ ```
600
+
601
+ - Select "major" (since this is 0.0.1 → 1.0.0)
602
+ - Write: "Initial release of AI SDK Guardrails"
603
+
604
+ 3. **Release it**:
605
+
606
+ ```bash
607
+ npm run local-release
608
+ ```
609
+
610
+ ### 5. **Package Name Assessment**
611
+
612
+ **✅ `ai-sdk-guardrails` is excellent because:**
613
+
614
+ - Available on npm
615
+ - Descriptive and searchable
616
+ - Follows naming conventions
617
+ - Clearly indicates it's for AI SDK
618
+ - Professional sounding
619
+
620
+ ### 6. **Additional Recommendations**
621
+
622
+ Consider these npm badges for your README:
623
+
624
+ ```markdown
625
+ <code_block_to_apply_changes_from>
626
+ [![npm version](https://badge.fury.io/js/ai-sdk-guardrails.svg)](https://www.npmjs.com/package/ai-sdk-guardrails)
627
+ [![Downloads](https://img.shields.io/npm/dm/ai-sdk-guardrails.svg)](https://www.npmjs.com/package/ai-sdk-guardrails)
628
+ ```
629
+
630
+ ### 7. **GitHub Release Integration**
631
+
632
+ After publishing to npm, you might want to:
633
+
634
+ - Create GitHub releases that match your npm versions
635
+ - Set up GitHub Actions for automated publishing (optional)
636
+
637
+ **Would you like me to help you run through the first release process now?**
638
+
639
+ ## All AI SDK Functions Supported
640
+
641
+ The library provides guarded versions of all AI SDK functions:
642
+
643
+ ```typescript
644
+ import {
645
+ generateTextWithGuardrails,
646
+ generateObjectWithGuardrails,
647
+ streamTextWithGuardrails,
648
+ streamObjectWithGuardrails,
649
+ embedWithGuardrails,
650
+ } from 'ai-sdk-guardrails';
651
+
652
+ // Generate text with guardrails
653
+ const textResult = await generateTextWithGuardrails(
654
+ { model, prompt: 'Hello' },
655
+ { inputGuardrails: [lengthLimit(100)] },
656
+ );
657
+
658
+ // Generate structured objects with guardrails
659
+ const objectResult = await generateObjectWithGuardrails(
660
+ { model, prompt: 'Create user', schema: userSchema },
661
+ { outputGuardrails: [schemaValidation(userSchema)] },
662
+ );
663
+
664
+ // Stream text with real-time guardrails
665
+ const textStream = await streamTextWithGuardrails(
666
+ { model, prompt: 'Long response' },
667
+ { outputGuardrails: [outputLengthLimit(1000)] },
668
+ );
669
+
670
+ // Stream objects with guardrails
671
+ const objectStream = await streamObjectWithGuardrails(
672
+ { model, prompt: 'Stream data', schema: dataSchema },
673
+ { outputGuardrails: [schemaValidation(dataSchema)] },
674
+ );
675
+
676
+ // Embed with input validation
677
+ const embedResult = await embedWithGuardrails(
678
+ { model, value: 'Text to embed' },
679
+ { inputGuardrails: [piiDetector()] },
680
+ );
681
+ ```
682
+
683
+ ## TypeScript Support
684
+
685
+ Full TypeScript support with intelligent type inference:
686
+
687
+ ```typescript
688
+ // Types are automatically inferred
689
+ const result = await generateTextWithGuardrails(
690
+ {
691
+ model: openai('gpt-4-turbo'),
692
+ prompt: 'Hello',
693
+ },
694
+ {
695
+ inputGuardrails: [
696
+ /* your guardrails */
697
+ ],
698
+ },
699
+ );
700
+
701
+ // result is fully typed as GenerateTextResult
702
+ console.log(result.text);
703
+ console.log(result.usage);
704
+ console.log(result.finishReason);
705
+ ```
706
+
707
+ ## Examples
708
+
709
+ Explore our comprehensive examples that demonstrate real-world usage patterns:
710
+
711
+ - **[Basic Guardrails](examples/basic-guardrails.ts)** - Simple input/output validation with math homework detection
712
+ - **[Streaming Guardrails](examples/streaming-guardrails.ts)** - Real-time stream protection with content filtering
713
+ - **[Object Guardrails](examples/object-guardrails.ts)** - Schema validation and custom object validation
714
+ - **[Autoevals Integration](examples/autoevals-guardrails.ts)** - AI quality evaluation with factuality checking
715
+ - **[Kitchen Sink](examples/kitchen-sink.ts)** - Advanced patterns, composition, and LLM-as-judge
716
+
717
+ Each example is fully functional and demonstrates different aspects of the library with practical use cases.
718
+
719
+ ## Contributing
720
+
721
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
722
+
723
+ ## License
724
+
725
+ MIT © [Jag Reehal](https://github.com/jagreehal)
726
+
727
+ ---
728
+
729
+ Built with ❤️ for the AI community. Star the repo if you find it helpful!