@devilsdev/rag-pipeline-utils 1.0.0 → 2.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.
Files changed (72) hide show
  1. package/.codecov.yml +21 -0
  2. package/.dockerignore +14 -14
  3. package/.eslintignore +5 -0
  4. package/.eslintrc.json +21 -0
  5. package/.github/workflows/ci.yml +64 -42
  6. package/.husky/pre-commit +5 -0
  7. package/CHANGELOG.md +46 -0
  8. package/Dockerfile +22 -22
  9. package/LICENSE +674 -674
  10. package/README.md +117 -116
  11. package/__tests__/fixtures/.ragrc.json +8 -0
  12. package/__tests__/fixtures/invalid-ragrc.json +4 -0
  13. package/__tests__/fixtures/sample-eval-dataset.json +14 -14
  14. package/__tests__/fixtures/sample.pdf +1 -0
  15. package/__tests__/fixtures/src/mocks/openai-embedder.js +16 -0
  16. package/__tests__/fixtures/src/mocks/openai-llm.js +9 -0
  17. package/__tests__/fixtures/src/mocks/pdf-loader.js +17 -0
  18. package/__tests__/fixtures/src/mocks/pinecone-retriever.js +26 -0
  19. package/__tests__/fixtures/valid-ragrc.json +16 -0
  20. package/__tests__/integration/cli/config-flow.test.js +98 -44
  21. package/__tests__/integration/config/load-config.test.js +67 -52
  22. package/__tests__/unit/config/validate-schema.test.js +121 -0
  23. package/__tests__/unit/core/plugin-registry.test.js +55 -55
  24. package/__tests__/unit/reranker/llm-reranker.test.js +18 -42
  25. package/__tests__/unit/reranker/reranker.enriched.test.js +19 -0
  26. package/__tests__/unit/reranker/reranker.fallback.test.js +15 -0
  27. package/__tests__/unit/reranker/reranker.snapshot.test.js +15 -17
  28. package/__tests__/unit/reranker/reranker.structured-output.test.js +17 -0
  29. package/bin/cli.js +62 -168
  30. package/docs-site/docs/Architecture.md +76 -76
  31. package/docs-site/docs/CLI.md +69 -69
  32. package/docs-site/docs/Evaluation.md +61 -61
  33. package/docs-site/docs/FAQ.md +50 -50
  34. package/docs-site/docs/Plugins.md +61 -61
  35. package/docs-site/docs/Usage.md +72 -72
  36. package/jest.config.js +20 -13
  37. package/package.json +20 -3
  38. package/plugin.config.json +14 -0
  39. package/public/src/App.jsx +57 -57
  40. package/release.config.js +36 -23
  41. package/scripts/banner-injector.js +89 -0
  42. package/scripts/ci-runner.js +51 -0
  43. package/scripts/repair-fixtures.js +30 -0
  44. package/scripts/setup.js +31 -0
  45. package/scripts/test-global-setup.js +18 -0
  46. package/scripts/verify-fixture-mocks.js +45 -0
  47. package/scripts/verify-fixtures.js +36 -0
  48. package/server.js +31 -31
  49. package/src/config/load-config.js +33 -35
  50. package/src/config/load-plugin-config.js +56 -0
  51. package/src/config/validate-plugin-schema.js +28 -0
  52. package/src/config/validate-schema.js +107 -49
  53. package/src/core/create-pipeline.js +96 -82
  54. package/src/core/plugin-contracts.js +21 -0
  55. package/src/core/plugin-registry.js +48 -61
  56. package/src/dag/dag-engine.js +69 -69
  57. package/src/evaluate/evaluator.js +44 -44
  58. package/src/evaluate/scoring.js +73 -73
  59. package/src/loader/csv-loader.js +30 -30
  60. package/src/loader/directory-loader.js +37 -37
  61. package/src/loader/html-loader.js +57 -57
  62. package/src/loader/markdown-loader.js +58 -58
  63. package/src/mocks/openai-embedder.js +30 -0
  64. package/src/mocks/openai-llm.js +18 -0
  65. package/src/mocks/pdf-loader.js +17 -0
  66. package/src/mocks/pinecone-retriever.js +25 -0
  67. package/src/reranker/llm-reranker.js +34 -49
  68. package/src/utils/ci/diagnostic-reporter.js +36 -0
  69. package/src/utils/logger.js +29 -29
  70. package/src/utils/retry.js +42 -42
  71. package/src/utils/validate-plugin-contract.js +36 -0
  72. package/.github/workflows/release.yml +0 -21
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Version: 1.0.0
3
+ * Description: Verifies that plugin mock implementations expose required methods
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ import { readdirSync, statSync } from 'fs';
8
+ import { resolve, join, extname } from 'path';
9
+ import { pathToFileURL } from 'url';
10
+
11
+ const MOCKS_DIR = resolve('__tests__/fixtures/src/mocks');
12
+
13
+ const pluginContracts = {
14
+ 'openai-embedder.js': ['embed', 'embedQuery'],
15
+ 'openai-llm.js': ['ask'],
16
+ 'pdf-loader.js': ['load'],
17
+ 'pinecone-retriever.js': ['store', 'search'],
18
+ };
19
+
20
+ const failures = [];
21
+
22
+ for (const file of readdirSync(MOCKS_DIR)) {
23
+ const fullPath = join(MOCKS_DIR, file);
24
+ const isFile = statSync(fullPath).isFile();
25
+ if (!isFile || extname(file) !== '.js') continue;
26
+
27
+ const expectedMethods = pluginContracts[file];
28
+ if (!expectedMethods) continue;
29
+
30
+ const mod = await import(pathToFileURL(fullPath).href);
31
+ const plugin = new mod.default();
32
+
33
+ for (const method of expectedMethods) {
34
+ if (typeof plugin[method] !== 'function') {
35
+ failures.push(`❌ ${file} failed: [${file}] missing method: ${method}`);
36
+ }
37
+ }
38
+ }
39
+
40
+ if (failures.length > 0) {
41
+ failures.forEach(msg => console.error(msg));
42
+ process.exit(1);
43
+ } else {
44
+ console.log('✅ All plugin mocks verified.');
45
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Version: 1.0.0
3
+ * Description: Script to verify presence of all expected fixture plugins before running integration tests.
4
+ * Author: Ali Kahwaji
5
+ * File: scripts/verify-fixtures.js
6
+ */
7
+
8
+ import { existsSync } from 'fs';
9
+ import { resolve } from 'path';
10
+
11
+ const REQUIRED_FIXTURES = [
12
+ '__tests__/fixtures/sample.pdf',
13
+ '__tests__/fixtures/.ragrc.json',
14
+ '__tests__/fixtures/src/mocks/pdf-loader.js',
15
+ '__tests__/fixtures/src/mocks/openai-embedder.js',
16
+ '__tests__/fixtures/src/mocks/pinecone-retriever.js',
17
+ '__tests__/fixtures/src/mocks/openai-llm.js'
18
+ ];
19
+
20
+ let missing = [];
21
+ for (const file of REQUIRED_FIXTURES) {
22
+ const fullPath = resolve(file);
23
+ if (!existsSync(fullPath)) {
24
+ missing.push(fullPath);
25
+ }
26
+ }
27
+
28
+ if (missing.length) {
29
+ console.error('[ERROR] Missing test fixture files:');
30
+ for (const f of missing) {
31
+ console.error(' -', f);
32
+ }
33
+ process.exit(1);
34
+ } else {
35
+ console.log('[OK] All fixture files are present.');
36
+ }
package/server.js CHANGED
@@ -1,31 +1,31 @@
1
- /**
2
- * Version: 0.1.0
3
- * Path: /server.js
4
- * Description: Local static server for serving evaluation dashboard
5
- * Author: Ali Kahwaji
6
- */
7
-
8
- import express from 'express';
9
- import path from 'path';
10
- import { fileURLToPath } from 'url';
11
-
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = path.dirname(__filename);
14
-
15
- const app = express();
16
- const port = process.env.PORT || 3000;
17
-
18
- // Serve evaluation results statically
19
- app.use('/results', express.static(path.join(__dirname, '__tests__/fixtures')));
20
-
21
- // Serve public frontend assets
22
- app.use(express.static(path.join(__dirname, 'public')));
23
-
24
- // Fallback to index.html
25
- app.get('*', (req, res) => {
26
- res.sendFile(path.join(__dirname, 'public/index.html'));
27
- });
28
-
29
- app.listen(port, () => {
30
- console.log(` Dashboard running at http://localhost:${port}`);
31
- });
1
+ /**
2
+ * Version: 0.1.0
3
+ * Path: /server.js
4
+ * Description: Local static server for serving evaluation dashboard
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ import express from 'express';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ const app = express();
16
+ const port = process.env.PORT || 3000;
17
+
18
+ // Serve evaluation results statically
19
+ app.use('/results', express.static(path.join(__dirname, '__tests__/fixtures')));
20
+
21
+ // Serve public frontend assets
22
+ app.use(express.static(path.join(__dirname, 'public')));
23
+
24
+ // Fallback to index.html
25
+ app.get('*', (req, res) => {
26
+ res.sendFile(path.join(__dirname, 'public/index.html'));
27
+ });
28
+
29
+ app.listen(port, () => {
30
+ console.log(` Dashboard running at http://localhost:${port}`);
31
+ });
@@ -1,35 +1,33 @@
1
- /**
2
- * Version: 0.1.0
3
- * Path: /src/config/load-config.js
4
- * Description: Loader and validator for `.ragrc.json` configuration files
5
- * Author: Ali Kahwaji
6
- */
7
-
8
- import fs from 'fs';
9
- import path from 'path';
10
- import { validate } from './validate-schema.js';
11
-
12
- const DEFAULT_CONFIG_FILE = '.ragrc.json';
13
-
14
- /**
15
- * Load and validate the RAG configuration from a file.
16
- *
17
- * @param {string} [configPath] - Optional custom config path
18
- * @returns {object} - Validated config object
19
- * @throws {Error} - If file not found or validation fails
20
- */
21
- export function loadRagConfig(configPath = DEFAULT_CONFIG_FILE) {
22
- const fullPath = path.resolve(process.cwd(), configPath);
23
-
24
- if (!fs.existsSync(fullPath)) {
25
- throw new Error(`Config file not found: ${fullPath}`);
26
- }
27
-
28
- const raw = fs.readFileSync(fullPath, 'utf-8');
29
- const config = JSON.parse(raw);
30
-
31
- validate(config); // Throws if invalid
32
-
33
- return config;
34
- }
35
-
1
+ /**
2
+ * Version: 1.1.0
3
+ * Description: Loads validated .ragrc.json config from disk
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { validateRagrcSchema } from './validate-schema.js';
10
+ import { logger } from '../utils/logger.js';
11
+
12
+ /**
13
+ * Loads and validates a RAG config file.
14
+ * @param {string} configPath - Absolute path to .ragrc.json
15
+ * @returns {object} Parsed config object
16
+ * @throws {Error} If file is missing or invalid
17
+ */
18
+ export function loadRagConfig(configPath) {
19
+ if (!fs.existsSync(configPath)) {
20
+ throw new Error(`Config file not found: ${configPath}`);
21
+ }
22
+
23
+ const raw = fs.readFileSync(configPath, 'utf-8');
24
+ const parsed = JSON.parse(raw);
25
+
26
+ const result = validateRagrcSchema(parsed);
27
+ if (!result.valid) {
28
+ logger.error({ errors: result.errors }, 'Invalid .ragrc.json schema');
29
+ throw new Error('Config validation failed');
30
+ }
31
+
32
+ return parsed;
33
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Version: 1.1.1
3
+ * Description: Dynamic plugin loader with direct type:name registry mapping
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { pathToFileURL } from 'url';
10
+ import registry from '../core/plugin-registry.js';
11
+ import { validatePluginSchema } from './validate-schema.js';
12
+ import { logger } from '../utils/logger.js';
13
+
14
+ /**
15
+ * Loads plugins from validated config path and registers them.
16
+ * @param {string} configPath
17
+ */
18
+ export async function loadPluginsFromJson(configPath) {
19
+ if (!fs.existsSync(configPath)) {
20
+ throw new Error(`Plugin config missing: ${configPath}`);
21
+ }
22
+
23
+ const raw = fs.readFileSync(configPath, 'utf-8');
24
+ const config = JSON.parse(raw);
25
+
26
+ const validation = validatePluginSchema(config);
27
+ if (!validation.valid) {
28
+ logger.error({ errors: validation.errors }, 'Invalid plugin config');
29
+ throw new Error('Plugin config validation failed');
30
+ }
31
+
32
+ for (const [type, namedPlugins] of Object.entries(config)) {
33
+ if (!['loader', 'embedder', 'retriever', 'llm'].includes(type)) continue;
34
+
35
+ for (const [name, relPath] of Object.entries(namedPlugins)) {
36
+ const absPath = path.resolve(path.dirname(configPath), relPath);
37
+ const fileUrl = pathToFileURL(absPath).href;
38
+
39
+ try {
40
+ const mod = await import(fileUrl);
41
+ const PluginClass = mod?.default;
42
+ if (typeof PluginClass !== 'function') {
43
+ throw new Error(`Invalid export in plugin [${type}:${name}] at ${absPath}`);
44
+ }
45
+
46
+ const instance = new PluginClass();
47
+ registry.register(type, name, instance);
48
+
49
+ logger.info({ type, name }, `Registered plugin: ${type}:${name}`);
50
+ } catch (err) {
51
+ logger.error({ type, name, err }, `Failed to register plugin [${type}:${name}]`);
52
+ throw err;
53
+ }
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Version: 0.1.0
3
+ * Description: Schema validator for plugin.config.json
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ export function validatePluginSchema(config) {
8
+ const validTypes = ['loader', 'embedder', 'retriever', 'llm'];
9
+ const errors = [];
10
+
11
+ for (const type of Object.keys(config)) {
12
+ if (!validTypes.includes(type)) {
13
+ errors.push(`Unknown plugin type: ${type}`);
14
+ continue;
15
+ }
16
+ for (const [name, modulePath] of Object.entries(config[type])) {
17
+ if (typeof name !== 'string' || typeof modulePath !== 'string') {
18
+ errors.push(`Invalid plugin definition for type "${type}" and name "${name}"`);
19
+ }
20
+ }
21
+ }
22
+
23
+ return {
24
+ valid: errors.length === 0,
25
+ errors
26
+ };
27
+ }
28
+
@@ -1,49 +1,107 @@
1
- /**
2
- * Version: 0.1.0
3
- * Path: /src/config/validate-schema.js
4
- * Description: JSON schema validation for `.ragrc.json` config using AJV
5
- * Author: Ali Kahwaji
6
- */
7
-
8
- import Ajv from 'ajv';
9
-
10
- // Define allowed config structure
11
- const schema = {
12
- type: 'object',
13
- properties: {
14
- loader: { type: 'string' },
15
- embedder: { type: 'string' },
16
- retriever: { type: 'string' },
17
- llm: { type: 'string' },
18
- chunk: {
19
- type: 'object',
20
- properties: {
21
- strategy: { type: 'string' },
22
- size: { type: 'number' }
23
- },
24
- required: ['strategy', 'size'],
25
- additionalProperties: false
26
- }
27
- },
28
- required: ['loader', 'embedder', 'retriever', 'llm'],
29
- additionalProperties: false
30
- };
31
-
32
- const ajv = new Ajv();
33
- const validateSchema = ajv.compile(schema);
34
-
35
- /**
36
- * Validates a given config object against the `.ragrc.json` schema.
37
- * Throws if the schema is invalid.
38
- *
39
- * @param {object} config - Configuration object
40
- * @throws {Error} - Validation errors, if any
41
- */
42
- export function validate(config) {
43
- const valid = validateSchema(config);
44
- if (!valid) {
45
- const messages = validateSchema.errors.map(e => `${e.instancePath} ${e.message}`).join(', ');
46
- throw new Error(`Invalid config: ${messages}`);
47
- }
48
- }
49
-
1
+ /**
2
+ * Version: 1.3.0
3
+ * Description: AJV-powered validators for .ragrc.json full config and plugin-only structure
4
+ * Author: Ali Kahwaji
5
+ * File: /src/config/validate-schema.js
6
+ */
7
+
8
+ import Ajv from 'ajv';
9
+
10
+ /**
11
+ * Full .ragrc.json schema (used in load-config.js)
12
+ */
13
+ const ragrcSchema = {
14
+ type: 'object',
15
+ required: ['loader', 'embedder', 'retriever', 'llm', 'namespace', 'pipeline'],
16
+ properties: {
17
+ loader: {
18
+ type: 'object',
19
+ minProperties: 1,
20
+ additionalProperties: { type: 'string' }
21
+ },
22
+ embedder: {
23
+ type: 'object',
24
+ minProperties: 1,
25
+ additionalProperties: { type: 'string' }
26
+ },
27
+ retriever: {
28
+ type: 'object',
29
+ minProperties: 1,
30
+ additionalProperties: { type: 'string' }
31
+ },
32
+ llm: {
33
+ type: 'object',
34
+ minProperties: 1,
35
+ additionalProperties: { type: 'string' }
36
+ },
37
+ namespace: {
38
+ type: 'string',
39
+ minLength: 1
40
+ },
41
+ pipeline: {
42
+ type: 'array',
43
+ items: {
44
+ type: 'string',
45
+ enum: ['loader', 'embedder', 'retriever']
46
+ },
47
+ minItems: 1,
48
+ uniqueItems: true
49
+ }
50
+ },
51
+ additionalProperties: false
52
+ };
53
+
54
+ /**
55
+ * Minimal plugin-only schema (used in load-plugin-config.js)
56
+ */
57
+ const pluginSchema = {
58
+ type: 'object',
59
+ required: ['loader', 'embedder', 'retriever', 'llm'],
60
+ properties: {
61
+ loader: {
62
+ type: 'object',
63
+ minProperties: 1,
64
+ additionalProperties: { type: 'string' }
65
+ },
66
+ embedder: {
67
+ type: 'object',
68
+ minProperties: 1,
69
+ additionalProperties: { type: 'string' }
70
+ },
71
+ retriever: {
72
+ type: 'object',
73
+ minProperties: 1,
74
+ additionalProperties: { type: 'string' }
75
+ },
76
+ llm: {
77
+ type: 'object',
78
+ minProperties: 1,
79
+ additionalProperties: { type: 'string' }
80
+ }
81
+ },
82
+ additionalProperties: true // allow namespace, pipeline, etc.
83
+ };
84
+
85
+ /**
86
+ * Validates the full .ragrc.json config structure
87
+ * @param {object} config
88
+ * @returns {{ valid: boolean, errors?: any[] }}
89
+ */
90
+ export function validateRagrcSchema(config) {
91
+ const ajv = new Ajv({ allErrors: true });
92
+ const validate = ajv.compile(ragrcSchema);
93
+ const valid = validate(config);
94
+ return valid ? { valid: true } : { valid: false, errors: validate.errors };
95
+ }
96
+
97
+ /**
98
+ * Validates a plugin-only structure (for runtime plugin loading)
99
+ * @param {object} config
100
+ * @returns {{ valid: boolean, errors?: any[] }}
101
+ */
102
+ export function validatePluginSchema(config) {
103
+ const ajv = new Ajv({ allErrors: true });
104
+ const validate = ajv.compile(pluginSchema);
105
+ const valid = validate(config);
106
+ return valid ? { valid: true } : { valid: false, errors: validate.errors };
107
+ }
@@ -1,82 +1,96 @@
1
- /**
2
- * Version: 0.1.3
3
- * Path: /src/core/create-pipeline.js
4
- * Description: RAG pipeline factory with optional LLM reranker integration
5
- * Author: Ali Kahwaji
6
- */
7
-
8
- import { PluginRegistry } from './plugin-registry.js';
9
- import { logger } from '../utils/logger.js';
10
- import { withRetry } from '../utils/retry.js';
11
- import { LLMReranker } from '../reranker/llm-reranker.js';
12
-
13
- const registry = new PluginRegistry();
14
-
15
- /**
16
- * Create a RAG pipeline instance using registered plugin components
17
- * @param {object} config
18
- * @param {string} config.loader
19
- * @param {string} config.embedder
20
- * @param {string} config.retriever
21
- * @param {string} config.llm
22
- * @param {boolean} [config.useReranker=false]
23
- * @returns {object} RAG pipeline instance
24
- */
25
- export function createRagPipeline({ loader, embedder, retriever, llm, useReranker = false }) {
26
- const rerankerInstance = useReranker ? new LLMReranker({ llm: registry.get('llm', llm) }) : null;
27
-
28
- return {
29
- async ingest(path) {
30
- logger.info({ loader, embedder, retriever }, 'Pipeline ingest start');
31
- const loaderInstance = registry.get('loader', loader);
32
- const embedderInstance = registry.get('embedder', embedder);
33
- const retrieverInstance = registry.get('retriever', retriever);
34
-
35
- const documents = await loaderInstance.load(path);
36
- const chunks = documents.flatMap(doc => doc.chunk());
37
- const vectors = await embedderInstance.embed(chunks);
38
-
39
- await withRetry(() => retrieverInstance.store(vectors), {
40
- label: 'vector-store',
41
- retries: 3,
42
- initialDelay: 500
43
- });
44
-
45
- logger.info('Ingestion pipeline completed');
46
- },
47
-
48
- async query(prompt) {
49
- logger.info({ prompt, embedder, retriever, llm, useReranker }, 'Pipeline query start');
50
- const embedderInstance = registry.get('embedder', embedder);
51
- const retrieverInstance = registry.get('retriever', retriever);
52
- const llmInstance = registry.get('llm', llm);
53
-
54
- const queryVector = await embedderInstance.embedQuery(prompt);
55
- let retrieved = await withRetry(() => retrieverInstance.retrieve(queryVector), {
56
- label: 'vector-retrieve',
57
- retries: 3,
58
- initialDelay: 500
59
- });
60
-
61
- if (rerankerInstance) {
62
- logger.info('Applying LLM reranker to retrieved chunks');
63
- retrieved = await withRetry(() => rerankerInstance.rerank(prompt, retrieved), {
64
- label: 'rerank',
65
- retries: 2,
66
- initialDelay: 400
67
- });
68
- }
69
-
70
- const result = await withRetry(() => llmInstance.generate(prompt, retrieved), {
71
- label: 'llm-generate',
72
- retries: 3,
73
- initialDelay: 500
74
- });
75
-
76
- return result;
77
- }
78
- };
79
- }
80
-
81
- export { registry };
82
-
1
+ /**
2
+ * Version: 0.2.0
3
+ * Path: /src/core/create-pipeline.js
4
+ * Description: RAG pipeline factory with modular retry, logging, and reranker middleware
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ import registry from './plugin-registry.js';
9
+ import { logger } from '../utils/logger.js';
10
+ import { withRetry } from '../utils/retry.js';
11
+ import { LLMReranker } from '../reranker/llm-reranker.js';
12
+
13
+ /**
14
+ * Pipeline options to toggle middleware.
15
+ * @typedef {Object} PipelineOptions
16
+ * @property {boolean} [useRetry=true] - Enable retry logic on network/storage.
17
+ * @property {boolean} [useLogging=true] - Enable structured logging.
18
+ * @property {boolean} [useReranker=false] - Use reranker for query refinement.
19
+ */
20
+
21
+ /**
22
+ * Creates a RAG pipeline instance.
23
+ * @param {object} config - Pipeline configuration.
24
+ * @param {string} config.loader
25
+ * @param {string} config.embedder
26
+ * @param {string} config.retriever
27
+ * @param {string} config.llm
28
+ * @param {PipelineOptions} [options]
29
+ * @returns {object} RAG pipeline instance
30
+ */
31
+ export function createRagPipeline(
32
+ { loader, embedder, retriever, llm },
33
+ { useRetry = true, useLogging = true, useReranker = false } = {}
34
+ ) {
35
+ const loaderInstance = registry.get('loader', loader);
36
+ const embedderInstance = registry.get('embedder', embedder);
37
+ const retrieverInstance = registry.get('retriever', retriever);
38
+ const llmInstance = registry.get('llm', llm);
39
+ const rerankerInstance = useReranker ? new LLMReranker({ llm: llmInstance }) : null;
40
+
41
+ const log = (level, msg, data = {}) => {
42
+ if (useLogging) logger[level](data, msg);
43
+ };
44
+
45
+ const wrap = async (fn, context) => {
46
+ return useRetry ? withRetry(fn, context) : fn();
47
+ };
48
+
49
+ return {
50
+ async ingest(docPath) {
51
+ log('info', 'Pipeline ingest start', { loader, embedder, retriever });
52
+
53
+ const documents = await loaderInstance.load(docPath);
54
+ const chunks = documents.flatMap(doc => doc.chunk());
55
+ const vectors = await embedderInstance.embed(chunks);
56
+
57
+ await wrap(() => retrieverInstance.store(vectors), {
58
+ label: 'vector-store',
59
+ retries: 3,
60
+ initialDelay: 500
61
+ });
62
+
63
+ log('info', 'Ingestion pipeline completed');
64
+ },
65
+
66
+ async query(prompt) {
67
+ log('info', 'Pipeline query start', { prompt, embedder, retriever, llm, useReranker });
68
+
69
+ const queryVector = await embedderInstance.embedQuery(prompt);
70
+ let retrieved = await wrap(() => retrieverInstance.retrieve(queryVector), {
71
+ label: 'vector-retrieve',
72
+ retries: 3,
73
+ initialDelay: 500
74
+ });
75
+
76
+ if (rerankerInstance) {
77
+ log('info', 'Applying LLM reranker to retrieved chunks');
78
+ retrieved = await wrap(() => rerankerInstance.rerank(prompt, retrieved), {
79
+ label: 'rerank',
80
+ retries: 2,
81
+ initialDelay: 400
82
+ });
83
+ }
84
+
85
+ const result = await wrap(() => llmInstance.generate(prompt, retrieved), {
86
+ label: 'llm-generate',
87
+ retries: 3,
88
+ initialDelay: 500
89
+ });
90
+
91
+ return result;
92
+ }
93
+ };
94
+ }
95
+
96
+ export { registry };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Version: 1.0.0
3
+ * Description: Shared plugin interface contracts for runtime + CI mock validation
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ export const pluginContracts = {
8
+ loader: {
9
+ requiredMethods: ['load'],
10
+ },
11
+ embedder: {
12
+ requiredMethods: ['embed', 'embedQuery'],
13
+ },
14
+ retriever: {
15
+ requiredMethods: ['store', 'search'],
16
+ },
17
+ llm: {
18
+ requiredMethods: ['ask'],
19
+ },
20
+ };
21
+