@devilsdev/rag-pipeline-utils 2.2.0 → 2.3.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.
Files changed (46) hide show
  1. package/README.md +323 -722
  2. package/contracts/contract-schema.json +189 -0
  3. package/contracts/reranker-contract.json +69 -0
  4. package/dist/index.cjs +19 -0
  5. package/dist/index.mjs +15 -1
  6. package/package.json +4 -2
  7. package/src/cli/commands/wizard.js +627 -0
  8. package/src/cli/input-sanitization-middleware.js +356 -0
  9. package/src/cli/templates/development.template.json +64 -0
  10. package/src/cli/templates/minimal.template.json +33 -0
  11. package/src/cli/templates/production.template.json +104 -0
  12. package/src/cli/templates/testing.template.json +45 -0
  13. package/src/config/connection-pool-config.json +259 -0
  14. package/src/config/memory-optimization.js +414 -0
  15. package/src/config/redaction-patterns.json +150 -0
  16. package/src/config/sanitization-rules.json +323 -0
  17. package/src/core/performance/streaming-safeguards.js +10 -1
  18. package/src/core/plugin-registry.js +275 -0
  19. package/src/dag/core/error-context.js +391 -0
  20. package/src/dag/core/execution-scheduler.js +350 -0
  21. package/src/dag/core/topology-validation.js +415 -0
  22. package/src/dag/dag-engine.js +26 -424
  23. package/src/dev/README.md +126 -0
  24. package/src/dev/dev-server.js +356 -0
  25. package/src/dev/examples/hot-reload-usage.js +355 -0
  26. package/src/dev/hot-reload.js +807 -0
  27. package/src/enterprise/sso-integration.js +184 -15
  28. package/src/index.js +19 -0
  29. package/src/observability/event-logger.js +62 -2
  30. package/src/plugins/optimized-openai-embedder.js +169 -0
  31. package/src/reranker/llm-reranker.js +30 -4
  32. package/src/security/jwt-config.js +431 -0
  33. package/src/security/jwt-validator.js +701 -0
  34. package/src/utils/api-client-pool-integration.js +436 -0
  35. package/src/utils/batch-processor.js +710 -0
  36. package/src/utils/connection-pool.js +605 -0
  37. package/src/utils/contract-validator.js +460 -0
  38. package/src/utils/error-formatter.js +603 -0
  39. package/src/utils/examples/error-formatter-usage.js +351 -0
  40. package/src/utils/input-sanitizer.js +967 -0
  41. package/src/utils/memory-monitor.js +800 -0
  42. package/src/utils/rate-limiter.js +244 -0
  43. package/src/utils/secure-logger-config.js +352 -0
  44. package/src/utils/secure-logger.js +576 -0
  45. package/src/utils/secure-logging.js +338 -0
  46. package/src/utils/structured-logger.js +32 -5
@@ -0,0 +1,627 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Interactive Configuration Wizard CLI Command
5
+ *
6
+ * Provides an interactive CLI wizard for creating and managing
7
+ * RAG pipeline configurations with:
8
+ * - Template-based configuration
9
+ * - Real-time validation
10
+ * - Plugin discovery and selection
11
+ * - Configuration preview and testing
12
+ *
13
+ * @module cli/commands/wizard
14
+ * @since 2.4.0
15
+ */
16
+
17
+ const { Command } = require("commander");
18
+ const chalk = require("chalk");
19
+ const inquirer = require("inquirer");
20
+ const fs = require("fs/promises");
21
+ const path = require("path");
22
+ const {
23
+ InteractiveWizard,
24
+ runInteractiveWizard,
25
+ } = require("../interactive-wizard");
26
+ const { validateRagrc } = require("../../config/enhanced-ragrc-schema");
27
+
28
+ /**
29
+ * Configuration Templates
30
+ *
31
+ * Pre-defined templates for common use cases
32
+ */
33
+ const TEMPLATES = {
34
+ minimal: {
35
+ name: "Minimal Setup",
36
+ description: "Basic RAG pipeline with essential plugins",
37
+ config: {
38
+ metadata: {
39
+ name: "minimal-rag-pipeline",
40
+ version: "1.0.0",
41
+ description: "Minimal RAG pipeline configuration",
42
+ environment: "development",
43
+ },
44
+ plugins: {
45
+ loader: {
46
+ "file-loader": "latest",
47
+ },
48
+ embedder: {
49
+ "openai-embedder": "latest",
50
+ },
51
+ retriever: {
52
+ "vector-retriever": "latest",
53
+ },
54
+ llm: {
55
+ "openai-llm": "latest",
56
+ },
57
+ },
58
+ pipeline: {
59
+ stages: ["loader", "embedder", "retriever", "llm"],
60
+ },
61
+ observability: {
62
+ logging: {
63
+ level: "info",
64
+ structured: true,
65
+ },
66
+ },
67
+ },
68
+ },
69
+
70
+ production: {
71
+ name: "Production Ready",
72
+ description:
73
+ "Production-grade configuration with monitoring and optimization",
74
+ config: {
75
+ metadata: {
76
+ name: "production-rag-pipeline",
77
+ version: "1.0.0",
78
+ description: "Production RAG pipeline with full observability",
79
+ environment: "production",
80
+ },
81
+ plugins: {
82
+ loader: {
83
+ "file-loader": "latest",
84
+ },
85
+ embedder: {
86
+ "openai-embedder": {
87
+ name: "openai-embedder",
88
+ version: "latest",
89
+ config: {
90
+ batchSize: 100,
91
+ timeout: 30000,
92
+ },
93
+ },
94
+ },
95
+ retriever: {
96
+ "vector-retriever": {
97
+ name: "vector-retriever",
98
+ version: "latest",
99
+ config: {
100
+ topK: 10,
101
+ threshold: 0.7,
102
+ },
103
+ },
104
+ },
105
+ llm: {
106
+ "openai-llm": "latest",
107
+ },
108
+ reranker: {
109
+ "similarity-reranker": "latest",
110
+ },
111
+ },
112
+ pipeline: {
113
+ stages: ["loader", "embedder", "retriever", "reranker", "llm"],
114
+ retries: {
115
+ enabled: true,
116
+ maxAttempts: 3,
117
+ backoff: "exponential",
118
+ },
119
+ timeout: 60000,
120
+ },
121
+ performance: {
122
+ caching: {
123
+ enabled: true,
124
+ maxSize: 1000,
125
+ ttl: 3600,
126
+ },
127
+ parallel: {
128
+ enabled: true,
129
+ maxConcurrency: 5,
130
+ batchSize: 10,
131
+ },
132
+ streaming: {
133
+ enabled: true,
134
+ maxMemoryMB: 512,
135
+ bufferSize: 100,
136
+ },
137
+ },
138
+ observability: {
139
+ logging: {
140
+ level: "info",
141
+ structured: true,
142
+ events: true,
143
+ },
144
+ tracing: {
145
+ enabled: true,
146
+ sampleRate: 0.1,
147
+ },
148
+ metrics: {
149
+ enabled: true,
150
+ interval: 60000,
151
+ },
152
+ },
153
+ },
154
+ },
155
+
156
+ development: {
157
+ name: "Development",
158
+ description: "Development environment with verbose logging",
159
+ config: {
160
+ metadata: {
161
+ name: "dev-rag-pipeline",
162
+ version: "1.0.0",
163
+ description: "Development RAG pipeline",
164
+ environment: "development",
165
+ },
166
+ plugins: {
167
+ loader: {
168
+ "file-loader": "latest",
169
+ },
170
+ embedder: {
171
+ "local-embedder": "latest",
172
+ },
173
+ retriever: {
174
+ "vector-retriever": "latest",
175
+ },
176
+ llm: {
177
+ "local-llm": "latest",
178
+ },
179
+ },
180
+ pipeline: {
181
+ stages: ["loader", "embedder", "retriever", "llm"],
182
+ timeout: 30000,
183
+ },
184
+ observability: {
185
+ logging: {
186
+ level: "debug",
187
+ structured: true,
188
+ events: true,
189
+ },
190
+ },
191
+ },
192
+ },
193
+
194
+ testing: {
195
+ name: "Testing",
196
+ description: "Testing configuration with mock plugins",
197
+ config: {
198
+ metadata: {
199
+ name: "test-rag-pipeline",
200
+ version: "1.0.0",
201
+ description: "Testing RAG pipeline",
202
+ environment: "testing",
203
+ },
204
+ plugins: {
205
+ loader: {
206
+ "mock-loader": "./src/mocks/pdf-loader.js",
207
+ },
208
+ embedder: {
209
+ "mock-embedder": "./src/mocks/openai-embedder.js",
210
+ },
211
+ retriever: {
212
+ "mock-retriever": "./src/mocks/pinecone-retriever.js",
213
+ },
214
+ llm: {
215
+ "mock-llm": "./src/mocks/openai-llm.js",
216
+ },
217
+ },
218
+ pipeline: {
219
+ stages: ["loader", "embedder", "retriever", "llm"],
220
+ },
221
+ observability: {
222
+ logging: {
223
+ level: "warn",
224
+ structured: true,
225
+ },
226
+ },
227
+ },
228
+ },
229
+
230
+ custom: {
231
+ name: "Custom (Interactive)",
232
+ description: "Build your configuration from scratch",
233
+ config: null, // Will use interactive wizard
234
+ },
235
+ };
236
+
237
+ /**
238
+ * Create wizard command
239
+ *
240
+ * @returns {Command} Wizard command
241
+ */
242
+ function createWizardCommand() {
243
+ const wizardCmd = new Command("wizard");
244
+
245
+ wizardCmd
246
+ .description("Interactive configuration wizard for RAG pipeline setup")
247
+ .option(
248
+ "-o, --output <path>",
249
+ "Output configuration file path",
250
+ ".ragrc.json",
251
+ )
252
+ .option(
253
+ "-t, --template <name>",
254
+ "Use a configuration template (minimal|production|development|testing|custom)",
255
+ )
256
+ .option("--list-templates", "List available templates")
257
+ .option("--validate <file>", "Validate an existing configuration file")
258
+ .option("--no-save", "Don't save configuration (preview only)")
259
+ .option("--quiet", "Minimal output")
260
+ .action(async (options) => {
261
+ try {
262
+ // List templates
263
+ if (options.listTemplates) {
264
+ await listTemplates();
265
+ return;
266
+ }
267
+
268
+ // Validate existing configuration
269
+ if (options.validate) {
270
+ await validateConfiguration(options.validate);
271
+ return;
272
+ }
273
+
274
+ // Run interactive wizard
275
+ if (!options.quiet) {
276
+ console.log(
277
+ chalk.cyan("╔════════════════════════════════════════════╗"),
278
+ );
279
+ console.log(
280
+ chalk.cyan("ā•‘ RAG Pipeline Configuration Wizard ā•‘"),
281
+ );
282
+ console.log(
283
+ chalk.cyan("ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•\n"),
284
+ );
285
+ }
286
+
287
+ // Template selection or custom
288
+ let config;
289
+ if (options.template) {
290
+ config = await useTemplate(options.template, options);
291
+ } else {
292
+ // Ask user to select template or custom
293
+ const templateChoice = await inquirer.prompt([
294
+ {
295
+ type: "list",
296
+ name: "template",
297
+ message: "Choose a starting point:",
298
+ choices: Object.entries(TEMPLATES).map(([key, template]) => ({
299
+ name: `${template.name} - ${template.description}`,
300
+ value: key,
301
+ short: template.name,
302
+ })),
303
+ default: "custom",
304
+ },
305
+ ]);
306
+
307
+ if (templateChoice.template === "custom") {
308
+ config = await runCustomWizard(options);
309
+ } else {
310
+ config = await useTemplate(templateChoice.template, options);
311
+ }
312
+ }
313
+
314
+ // Display success message
315
+ if (!options.quiet && config) {
316
+ console.log(
317
+ chalk.green("\nāœ… Configuration wizard completed successfully!"),
318
+ );
319
+ console.log(
320
+ chalk.gray(` Configuration saved to: ${options.output}`),
321
+ );
322
+ console.log(chalk.gray("\n Next steps:"));
323
+ console.log(
324
+ chalk.gray(
325
+ " 1. Review your configuration: cat " + options.output,
326
+ ),
327
+ );
328
+ console.log(
329
+ chalk.gray(" 2. Test your pipeline: npm run pipeline:test"),
330
+ );
331
+ console.log(chalk.gray(" 3. Start your pipeline: npm start"));
332
+ }
333
+ } catch (error) {
334
+ console.error(chalk.red("\nāŒ Wizard failed:"), error.message);
335
+ if (error.stack && !options.quiet) {
336
+ console.error(chalk.gray(error.stack));
337
+ }
338
+ process.exit(1);
339
+ }
340
+ });
341
+
342
+ return wizardCmd;
343
+ }
344
+
345
+ /**
346
+ * List available templates
347
+ */
348
+ async function listTemplates() {
349
+ console.log(chalk.cyan("\nšŸ“‹ Available Configuration Templates:\n"));
350
+
351
+ for (const [key, template] of Object.entries(TEMPLATES)) {
352
+ console.log(chalk.bold(` ${key}:`));
353
+ console.log(` ${template.description}`);
354
+
355
+ if (template.config) {
356
+ const pluginCount = Object.values(template.config.plugins || {}).reduce(
357
+ (sum, plugins) => sum + Object.keys(plugins).length,
358
+ 0,
359
+ );
360
+ const features = [];
361
+
362
+ if (template.config.performance?.caching?.enabled)
363
+ features.push("caching");
364
+ if (template.config.performance?.parallel?.enabled)
365
+ features.push("parallel");
366
+ if (template.config.observability?.tracing?.enabled)
367
+ features.push("tracing");
368
+
369
+ console.log(chalk.gray(` • ${pluginCount} plugins configured`));
370
+ if (features.length > 0) {
371
+ console.log(chalk.gray(` • Features: ${features.join(", ")}`));
372
+ }
373
+ }
374
+ console.log();
375
+ }
376
+
377
+ console.log(chalk.gray("Usage: npm run wizard -- --template <name>"));
378
+ console.log(
379
+ chalk.gray(" or: npm run wizard (for interactive selection)\n"),
380
+ );
381
+ }
382
+
383
+ /**
384
+ * Use configuration template
385
+ *
386
+ * @param {string} templateName - Template name
387
+ * @param {object} options - Command options
388
+ * @returns {Promise<object>} Configuration
389
+ */
390
+ async function useTemplate(templateName, options) {
391
+ const template = TEMPLATES[templateName];
392
+
393
+ if (!template) {
394
+ throw new Error(
395
+ `Unknown template: ${templateName}. Use --list-templates to see available options.`,
396
+ );
397
+ }
398
+
399
+ if (templateName === "custom") {
400
+ return await runCustomWizard(options);
401
+ }
402
+
403
+ console.log(chalk.blue(`\nšŸŽÆ Using template: ${template.name}\n`));
404
+ console.log(chalk.gray(` ${template.description}\n`));
405
+
406
+ // Clone template configuration
407
+ const config = JSON.parse(JSON.stringify(template.config));
408
+
409
+ // Allow customization
410
+ const customizeAnswer = await inquirer.prompt([
411
+ {
412
+ type: "confirm",
413
+ name: "customize",
414
+ message: "Would you like to customize this template?",
415
+ default: false,
416
+ },
417
+ ]);
418
+
419
+ if (customizeAnswer.customize) {
420
+ // Customize project metadata
421
+ const metadata = await inquirer.prompt([
422
+ {
423
+ type: "input",
424
+ name: "name",
425
+ message: "Project name:",
426
+ default: config.metadata.name,
427
+ validate: (input) => input.length > 0 || "Project name is required",
428
+ },
429
+ {
430
+ type: "input",
431
+ name: "description",
432
+ message: "Project description:",
433
+ default: config.metadata.description,
434
+ },
435
+ ]);
436
+
437
+ config.metadata.name = metadata.name;
438
+ config.metadata.description = metadata.description;
439
+ config.metadata.createdAt = new Date().toISOString();
440
+ }
441
+
442
+ // Validate configuration
443
+ const validation = validateRagrc(config);
444
+ if (!validation.valid) {
445
+ console.error(chalk.red("\nāŒ Template configuration is invalid:"));
446
+ validation.errors?.forEach((error) => {
447
+ console.error(chalk.red(` ${error.instancePath}: ${error.message}`));
448
+ });
449
+ throw new Error("Invalid template configuration");
450
+ }
451
+
452
+ // Use normalized configuration
453
+ const finalConfig = validation.normalized;
454
+
455
+ // Save configuration if not disabled
456
+ if (options.save !== false) {
457
+ await fs.writeFile(
458
+ options.output,
459
+ JSON.stringify(finalConfig, null, 2),
460
+ "utf-8",
461
+ );
462
+ }
463
+
464
+ // Display configuration summary
465
+ displayConfigSummary(finalConfig);
466
+
467
+ return finalConfig;
468
+ }
469
+
470
+ /**
471
+ * Run custom wizard
472
+ *
473
+ * @param {object} options - Command options
474
+ * @returns {Promise<object>} Configuration
475
+ */
476
+ async function runCustomWizard(options) {
477
+ console.log(
478
+ chalk.blue("\nšŸ§™ Starting interactive configuration wizard...\n"),
479
+ );
480
+
481
+ const wizard = new InteractiveWizard({
482
+ outputPath: options.output,
483
+ });
484
+
485
+ const config = await wizard.run();
486
+
487
+ return config;
488
+ }
489
+
490
+ /**
491
+ * Validate configuration file
492
+ *
493
+ * @param {string} filePath - Configuration file path
494
+ */
495
+ async function validateConfiguration(filePath) {
496
+ console.log(chalk.blue(`\nšŸ” Validating configuration: ${filePath}\n`));
497
+
498
+ try {
499
+ // Read configuration file
500
+ const content = await fs.readFile(filePath, "utf-8");
501
+ const config = JSON.parse(content);
502
+
503
+ // Validate against schema
504
+ const validation = validateRagrc(config);
505
+
506
+ if (validation.valid) {
507
+ console.log(chalk.green("āœ… Configuration is valid!\n"));
508
+
509
+ // Display summary
510
+ displayConfigSummary(config);
511
+
512
+ // Display warnings if any
513
+ if (validation.warnings && validation.warnings.length > 0) {
514
+ console.log(chalk.yellow("\nāš ļø Warnings:"));
515
+ validation.warnings.forEach((warning) => {
516
+ console.log(chalk.yellow(` • ${warning}`));
517
+ });
518
+ }
519
+ } else {
520
+ console.log(chalk.red("āŒ Configuration is invalid:\n"));
521
+
522
+ validation.errors?.forEach((error) => {
523
+ console.log(chalk.red(` • ${error.instancePath}: ${error.message}`));
524
+ });
525
+
526
+ process.exit(1);
527
+ }
528
+ } catch (error) {
529
+ if (error.code === "ENOENT") {
530
+ console.error(chalk.red(`āŒ File not found: ${filePath}`));
531
+ } else if (error instanceof SyntaxError) {
532
+ console.error(chalk.red(`āŒ Invalid JSON: ${error.message}`));
533
+ } else {
534
+ console.error(chalk.red(`āŒ Validation failed: ${error.message}`));
535
+ }
536
+ process.exit(1);
537
+ }
538
+ }
539
+
540
+ /**
541
+ * Display configuration summary
542
+ *
543
+ * @param {object} config - Configuration object
544
+ */
545
+ function displayConfigSummary(config) {
546
+ console.log(chalk.cyan("\nšŸ“‹ Configuration Summary:\n"));
547
+
548
+ // Project metadata
549
+ if (config.metadata) {
550
+ console.log(chalk.bold(" Project:"));
551
+ console.log(` Name: ${config.metadata.name || "Unnamed"}`);
552
+ console.log(
553
+ ` Environment: ${config.metadata.environment || "development"}`,
554
+ );
555
+ if (config.metadata.description) {
556
+ console.log(` Description: ${config.metadata.description}`);
557
+ }
558
+ console.log();
559
+ }
560
+
561
+ // Plugins
562
+ if (config.plugins) {
563
+ console.log(chalk.bold(" Plugins:"));
564
+ for (const [type, plugins] of Object.entries(config.plugins)) {
565
+ const pluginNames = Object.keys(plugins);
566
+ if (pluginNames.length > 0) {
567
+ console.log(` ${type}: ${pluginNames.join(", ")}`);
568
+ }
569
+ }
570
+ console.log();
571
+ }
572
+
573
+ // Pipeline
574
+ if (config.pipeline) {
575
+ console.log(chalk.bold(" Pipeline:"));
576
+ if (config.pipeline.stages) {
577
+ console.log(` Stages: ${config.pipeline.stages.join(" → ")}`);
578
+ }
579
+ if (config.pipeline.retries?.enabled) {
580
+ console.log(
581
+ ` Retries: ${config.pipeline.retries.maxAttempts} attempts (${config.pipeline.retries.backoff})`,
582
+ );
583
+ }
584
+ console.log();
585
+ }
586
+
587
+ // Performance
588
+ if (config.performance) {
589
+ const features = [];
590
+ if (config.performance.caching?.enabled) features.push("caching");
591
+ if (config.performance.parallel?.enabled)
592
+ features.push("parallel processing");
593
+ if (config.performance.streaming?.enabled) features.push("streaming");
594
+
595
+ if (features.length > 0) {
596
+ console.log(chalk.bold(" Performance:"));
597
+ features.forEach((feature) => console.log(` • ${feature}`));
598
+ console.log();
599
+ }
600
+ }
601
+
602
+ // Observability
603
+ if (config.observability) {
604
+ const features = [];
605
+ if (config.observability.logging)
606
+ features.push(`logging (${config.observability.logging.level})`);
607
+ if (config.observability.tracing?.enabled) features.push("tracing");
608
+ if (config.observability.metrics?.enabled) features.push("metrics");
609
+
610
+ if (features.length > 0) {
611
+ console.log(chalk.bold(" Observability:"));
612
+ features.forEach((feature) => console.log(` • ${feature}`));
613
+ console.log();
614
+ }
615
+ }
616
+ }
617
+
618
+ /**
619
+ * Export command and utilities
620
+ */
621
+ module.exports = {
622
+ createWizardCommand,
623
+ TEMPLATES,
624
+ useTemplate,
625
+ validateConfiguration,
626
+ displayConfigSummary,
627
+ };