@devilsdev/rag-pipeline-utils 1.0.1 → 2.0.1

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 (59) hide show
  1. package/.codecov.yml +21 -0
  2. package/.github/workflows/ci.yml +18 -0
  3. package/.github/workflows/docs-deploy.yml +36 -0
  4. package/.github/workflows/publish-release-blog.yml +51 -0
  5. package/.husky/pre-commit +6 -0
  6. package/CHANGELOG.md +40 -0
  7. package/README.md +101 -72
  8. package/USE_CASES.md +134 -0
  9. package/__tests__/fixtures/.ragrc.json +8 -0
  10. package/__tests__/fixtures/invalid-ragrc.json +4 -0
  11. package/__tests__/fixtures/src/mocks/openai-embedder.js +16 -0
  12. package/__tests__/fixtures/src/mocks/openai-llm.js +9 -0
  13. package/__tests__/fixtures/src/mocks/pdf-loader.js +17 -0
  14. package/__tests__/fixtures/src/mocks/pinecone-retriever.js +26 -0
  15. package/__tests__/fixtures/valid-ragrc.json +16 -0
  16. package/__tests__/integration/cli/config-flow.test.js +84 -15
  17. package/__tests__/integration/config/load-config.test.js +45 -30
  18. package/__tests__/unit/config/validate-schema.test.js +121 -0
  19. package/__tests__/unit/reranker/llm-reranker.test.js +9 -33
  20. package/__tests__/unit/reranker/reranker.enriched.test.js +19 -0
  21. package/__tests__/unit/reranker/reranker.fallback.test.js +15 -0
  22. package/__tests__/unit/reranker/reranker.snapshot.test.js +9 -11
  23. package/__tests__/unit/reranker/reranker.structured-output.test.js +17 -0
  24. package/bin/cli.js +49 -155
  25. package/docs-site/blog/2021-08-26-welcome/index.md +0 -1
  26. package/docs-site/blog/2025-04-17-release-2.0.md +67 -0
  27. package/docs-site/blog/2025-04-18-release-2.0.0.md +21 -0
  28. package/docs-site/blog/authors.yml +5 -23
  29. package/docs-site/blog/tags.yml +9 -0
  30. package/docs-site/docs/USE_CASES.md +0 -0
  31. package/docs-site/docusaurus.config.js +13 -0
  32. package/docs-site/sidebars.js +21 -9
  33. package/package.json +25 -2
  34. package/plugin.config.json +14 -0
  35. package/scripts/banner-injector.js +89 -0
  36. package/scripts/ci-runner.js +51 -0
  37. package/scripts/repair-fixtures.js +30 -0
  38. package/scripts/setup.js +26 -2
  39. package/scripts/validate-changelog-version.js +48 -0
  40. package/scripts/verify-fixture-mocks.js +45 -0
  41. package/scripts/verify-fixtures.js +36 -0
  42. package/src/config/load-config.js +19 -21
  43. package/src/config/load-plugin-config.js +56 -0
  44. package/src/config/validate-plugin-schema.js +28 -0
  45. package/src/config/validate-schema.js +88 -30
  46. package/src/core/create-pipeline.js +40 -26
  47. package/src/core/plugin-contracts.js +21 -0
  48. package/src/core/plugin-registry.js +4 -17
  49. package/src/mocks/openai-embedder.js +30 -0
  50. package/src/mocks/openai-llm.js +18 -0
  51. package/src/mocks/pdf-loader.js +17 -0
  52. package/src/mocks/pinecone-retriever.js +25 -0
  53. package/src/reranker/llm-reranker.js +28 -43
  54. package/src/utils/ci/diagnostic-reporter.js +36 -0
  55. package/src/utils/validate-plugin-contract.js +36 -0
  56. package/.github/workflows/release.yml +0 -20
  57. package/docs-site/blog/2019-05-28-first-blog-post.md +0 -12
  58. package/docs-site/blog/2019-05-29-long-blog-post.md +0 -44
  59. package/docs-site/blog/2021-08-01-mdx-blog-post.mdx +0 -24
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Version: 0.1.0
3
+ * File: /src/mocks/openai-embedder.js
4
+ * Description: Mock implementation of an OpenAI embedder
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ export default class OpenAIEmbedder {
9
+ /**
10
+ * Returns static vector embeddings for input chunks
11
+ * @param {string[]} chunks
12
+ * @returns {Promise<Array<{ id: string, values: number[], metadata: { text: string } }>>}
13
+ */
14
+ async embed(chunks) {
15
+ return chunks.map((text, i) => ({
16
+ id: `v${i}`,
17
+ values: [0.1, 0.2, 0.3],
18
+ metadata: { text },
19
+ }));
20
+ }
21
+
22
+ /**
23
+ * Returns a static vector for the given query prompt
24
+ * @param {string} prompt
25
+ * @returns {Promise<number[]>}
26
+ */
27
+ async embedQuery(prompt) {
28
+ return [0.1, 0.2, 0.3];
29
+ }
30
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Version: 0.1.0
3
+ * File: /src/mocks/openai-llm.js
4
+ * Description: Mock implementation of an OpenAI LLM
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ export default class OpenAILLM {
9
+ /**
10
+ * Generates a static answer from input prompt and context
11
+ * @param {string} prompt
12
+ * @param {Array<{ text: string }>} context
13
+ * @returns {Promise<string>}
14
+ */
15
+ async generate(prompt, context) {
16
+ return `Generated answer using: ${context.map(d => d.text).join(', ')}`;
17
+ }
18
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Version: 0.1.0
3
+ * File: /src/mocks/pdf-loader.js
4
+ * Description: Mock implementation of a PDF document loader
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ export default class PDFLoader {
9
+ /**
10
+ * Loads a mock PDF file and returns an array of chunked content.
11
+ * @param {string} filePath - Path to the PDF file
12
+ * @returns {Promise<Array<{ chunk: () => string[] }>>}
13
+ */
14
+ async load(filePath) {
15
+ return [{ chunk: () => [`Test loaded: ${filePath}`] }];
16
+ }
17
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Version: 0.1.0
3
+ * File: /src/mocks/pinecone-retriever.js
4
+ * Description: Mock implementation of a Pinecone retriever
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ export default class PineconeRetriever {
9
+ /**
10
+ * Mocks vector storage (no-op).
11
+ */
12
+ async store() {}
13
+
14
+ /**
15
+ * Returns mock retrieved documents
16
+ * @returns {Promise<Array<{ id: string, text: string, metadata: object }>>}
17
+ */
18
+ async retrieve() {
19
+ return [
20
+ { id: 'a', text: 'Chunk about pine trees', metadata: {} },
21
+ { id: 'b', text: 'Chunk about vectors', metadata: {} },
22
+ { id: 'c', text: 'Chunk about databases', metadata: {} },
23
+ ];
24
+ }
25
+ }
@@ -1,49 +1,34 @@
1
1
  /**
2
- * Version: 0.1.0
3
- * Path: /src/reranker/llm-reranker.js
4
- * Description: LLM-based reranker that reorders retrieved context for relevance
5
- * Author: Ali Kahwaji
2
+ * Version: 1.0.4
3
+ * Description: LLM reranker using LLM to reorder documents based on relevance
4
+ * Author: devilsdev
5
+ * File: src/reranker/llm-reranker.js
6
6
  */
7
7
 
8
- /**
9
- * LLMReranker ranks context chunks by relevance to a given prompt.
10
- * @interface
11
- */
12
8
  export class LLMReranker {
13
- constructor({ llm }) {
14
- if (!llm || typeof llm.generate !== 'function') {
15
- throw new Error('LLM instance must implement generate(prompt, context)');
16
- }
17
- this.llm = llm;
18
- }
19
-
20
- /**
21
- * Reranks retrieved context chunks using the LLM.
22
- * @param {string} prompt - The user's question or query
23
- * @param {Array<{ text: string, metadata?: object }>} documents - Retrieved context chunks
24
- * @param {number} [topK=5] - Number of top-ranked documents to return
25
- * @returns {Promise<Array>} - Reranked top K chunks
26
- */
27
- async rerank(prompt, documents, topK = 5) {
28
- const contextText = documents.map((d, i) => `Chunk ${i + 1}: ${d.text}`).join('\n');
29
-
30
- const instruction = `Rank the following document chunks by how well they answer the prompt.\n
31
- Prompt: ${prompt}\n
32
- Chunks:\n${contextText}\n
33
- Respond with a JSON array of chunk indexes in descending order of relevance.`;
34
-
35
- const result = await this.llm.generate(instruction, []);
36
-
37
- let indexOrder = [];
38
- try {
39
- indexOrder = JSON.parse(result.match(/\[.*\]/s)?.[0] || '[]');
40
- } catch (err) {
41
- console.warn('Failed to parse reranker output:', result);
42
- return documents.slice(0, topK); // fallback
43
- }
44
-
45
- const ranked = indexOrder.map(i => documents[i]).filter(Boolean);
46
- return ranked.slice(0, topK);
9
+ /**
10
+ * @param {Object} options
11
+ * @param {{ generate(prompt: string): Promise<string> }} options.llm - An LLM client with a `generate` method
12
+ */
13
+ constructor({ llm }) {
14
+ this.llm = llm;
15
+ }
16
+
17
+ /**
18
+ * Rerank documents based on the LLM output
19
+ * @param {string} query
20
+ * @param {Array<{ text: string }>} docs
21
+ * @returns {Promise<Array<{ text: string }>>}
22
+ */
23
+ async rerank(query, docs) {
24
+ const prompt = `Given the query: "${query}", rank the following documents by relevance: \n${docs.map((d, i) => `[${i}]: ${d.text}`).join('\n')}\nReturn an array of indices like [2, 0, 1]`;
25
+
26
+ try {
27
+ const json = await this.llm.generate(prompt);
28
+ const indices = JSON.parse(json);
29
+ return indices.map(i => docs[i]).filter(Boolean);
30
+ } catch (err) {
31
+ return docs; // Fallback if LLM response is not parsable
47
32
  }
48
33
  }
49
-
34
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Version: 1.0.0
3
+ * Description: Enhanced CI diagnostics for plugin mock verification
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ import path from 'path';
8
+ import chalk from 'chalk';
9
+
10
+ /**
11
+ * Pretty prints CI validation errors and guidance
12
+ * @param {Array<{ file: string, missing: string[] }>} failures
13
+ */
14
+ export function reportPluginValidationFailures(failures) {
15
+ if (!failures.length) return;
16
+
17
+ console.log(`\n${chalk.redBright.bold('❌ Plugin Mock Validation Failed')}`);
18
+
19
+ failures.forEach(({ file, missing }) => {
20
+ const fileName = path.basename(file);
21
+ console.log(
22
+ `${chalk.red(` ✖ ${fileName}`)} ${chalk.gray(`missing: ${missing.join(', ')}`)}`
23
+ );
24
+
25
+ // Add method-level fix hints
26
+ missing.forEach(method => {
27
+ const pluginType = fileName.split('.')[0].split('-')[1] || 'unknown';
28
+ console.error(` → Fix: implement method ${chalk.cyan(`${method}()`)} for plugin type ${chalk.yellow(pluginType)}`);
29
+ });
30
+ });
31
+
32
+ console.log(`\n${chalk.yellowBright('Recommended Fixes:')}`);
33
+ console.log(' • Implement all required methods defined in /src/core/plugin-contracts.js');
34
+ console.log(` • Re-run: ${chalk.cyan('npm run ci')} to confirm validation`);
35
+ console.log(` • Optionally use: ${chalk.cyan('scripts/repair-fixtures.js')} to auto-repair mocks\n`);
36
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Version: 1.0.0
3
+ * Description: Type-safe plugin contract validator for runtime safety
4
+ * Author: Ali Kahwaji
5
+ * File: /src/utils/validate-plugin-contract.js
6
+ */
7
+
8
+ import { pluginContracts } from '../core/plugin-contracts.js';
9
+
10
+ /**
11
+ * Validates that a plugin instance implements all required methods.
12
+ * @param {string} type - Plugin type (e.g. 'loader', 'embedder')
13
+ * @param {object} instance - Instantiated plugin module
14
+ * @param {string} filePath - Path to plugin (used in error reporting)
15
+ * @throws {Error} if any required method is missing or not a function
16
+ */
17
+ export function validatePluginContract(type, instance, filePath) {
18
+ const expectedMethods = pluginContracts[type];
19
+ if (!expectedMethods) {
20
+ throw new Error(`[validatePluginContract] Unknown plugin type: ${type}`);
21
+ }
22
+
23
+ const missing = expectedMethods.filter(method =>
24
+ typeof instance[method] !== 'function'
25
+ );
26
+
27
+ if (missing.length > 0) {
28
+ throw new Error(
29
+ `[validatePluginContract] Plugin '${filePath}' is missing required methods for '${type}': ${missing.join(', ')}`
30
+ );
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Optionally wraps plugin instance with a Proxy for method usage auditing (see enforcePluginProxy)
36
+ */
@@ -1,20 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- branches: [main]
6
-
7
- jobs:
8
- release:
9
- runs-on: ubuntu-latest
10
- steps:
11
- - uses: actions/checkout@v4
12
- - uses: actions/setup-node@v3
13
- with:
14
- node-version: 18
15
- registry-url: 'https://registry.npmjs.org/'
16
- - run: npm ci
17
- - run: npx semantic-release
18
- env:
19
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
20
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1,12 +0,0 @@
1
- ---
2
- slug: first-blog-post
3
- title: First Blog Post
4
- authors: [slorber, yangshun]
5
- tags: [hola, docusaurus]
6
- ---
7
-
8
- Lorem ipsum dolor sit amet...
9
-
10
- <!-- truncate -->
11
-
12
- ...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
@@ -1,44 +0,0 @@
1
- ---
2
- slug: long-blog-post
3
- title: Long Blog Post
4
- authors: yangshun
5
- tags: [hello, docusaurus]
6
- ---
7
-
8
- This is the summary of a very long blog post,
9
-
10
- Use a `<!--` `truncate` `-->` comment to limit blog post size in the list view.
11
-
12
- <!-- truncate -->
13
-
14
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
15
-
16
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
17
-
18
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
19
-
20
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
21
-
22
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
23
-
24
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
25
-
26
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
27
-
28
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
29
-
30
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
31
-
32
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
33
-
34
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
35
-
36
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
37
-
38
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
39
-
40
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
41
-
42
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
43
-
44
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
@@ -1,24 +0,0 @@
1
- ---
2
- slug: mdx-blog-post
3
- title: MDX Blog Post
4
- authors: [slorber]
5
- tags: [docusaurus]
6
- ---
7
-
8
- Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
9
-
10
- :::tip
11
-
12
- Use the power of React to create interactive blog posts.
13
-
14
- :::
15
-
16
- {/* truncate */}
17
-
18
- For example, use JSX to create an interactive button:
19
-
20
- ```js
21
- <button onClick={() => alert('button clicked!')}>Click me!</button>
22
- ```
23
-
24
- <button onClick={() => alert('button clicked!')}>Click me!</button>