@devilsdev/rag-pipeline-utils 2.1.12 → 2.2.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/.ragrc.schema.json +218 -0
- package/README.md +292 -32
- package/bin/cli.js +8 -14
- package/dist/index.d.ts +237 -0
- package/dist/index.mjs +22 -7
- package/package.json +17 -13
- package/src/ai/adaptive-retrieval.js +191 -0
- package/src/ai/federated-learning.js +214 -0
- package/src/ai/federation/federated-learning-coordinator.js +345 -0
- package/src/ai/federation.js +76 -0
- package/src/ai/index-fixed.js +509 -0
- package/src/ai/index.js +33 -0
- package/src/ai/model-trainer.js +217 -0
- package/src/ai/model-training.js +205 -0
- package/src/ai/module-template.js +37 -0
- package/src/ai/multimodal/multi-modal-processor.js +345 -0
- package/src/ai/multimodal-processing.js +268 -0
- package/src/ai/multimodal.js +71 -0
- package/src/ai/orchestrator.js +70 -0
- package/src/ai/retrieval/adaptive-retrieval-engine.js +398 -0
- package/src/ai/retrieval-engine.js +69 -0
- package/src/ai/training/model-training-orchestrator.js +228 -0
- package/src/ci/workflow-analyzer.js +71 -0
- package/src/cli/commands/ai-ml.js +967 -0
- package/src/cli/commands/docs.js +228 -0
- package/src/cli/commands/dx.js +538 -0
- package/src/cli/commands/plugin-hub.js +875 -0
- package/src/cli/doctor-command.js +639 -0
- package/src/cli/enhanced-cli-commands.js +945 -0
- package/src/cli/interactive-wizard.js +997 -0
- package/src/cli/plugin-marketplace-commands.js +869 -0
- package/src/cli.js +17 -0
- package/src/config/enhanced-ragrc-schema.js +632 -0
- package/src/config/load-config.js +54 -0
- package/src/config/load-plugin-config.js +69 -0
- package/src/config/normalize-config.js +105 -0
- package/src/config/schema-validator.js +315 -0
- package/src/config/validate-plugin-schema.js +35 -0
- package/src/config/validate-schema.js +113 -0
- package/src/contracts/plugin-contracts.js +33 -0
- package/src/core/create-pipeline.js +99 -0
- package/src/core/dependency-injection.js +242 -0
- package/src/core/observability/event-logger.js +241 -0
- package/src/core/observability/instrumented-pipeline.js +609 -0
- package/src/core/observability/metrics.js +497 -0
- package/src/core/observability/tracing.js +781 -0
- package/src/core/performance/benchmark.js +516 -0
- package/src/core/performance/parallel-processor.js +240 -0
- package/src/core/performance/streaming-safeguards.js +611 -0
- package/src/core/pipeline-factory.js +60 -0
- package/src/core/plugin-contracts.js +89 -0
- package/src/core/plugin-marketplace/plugin-metadata.js +485 -0
- package/src/core/plugin-marketplace/plugin-publisher.js +574 -0
- package/src/core/plugin-marketplace/plugin-registry-format.js +419 -0
- package/src/core/plugin-marketplace/version-resolver.js +61 -0
- package/src/core/plugin-registry.js +265 -0
- package/src/dag/dag-engine.js +954 -0
- package/src/dag/dag-node.js +76 -0
- package/src/dag/enhanced-dag-engine.js +822 -0
- package/src/dx/index.js +40 -0
- package/src/dx/integration-templates.js +665 -0
- package/src/dx/performance-profiler.js +968 -0
- package/src/dx/realtime-debugger.js +755 -0
- package/src/dx/visual-pipeline-builder.js +578 -0
- package/src/ecosystem/plugin-analytics-dashboard.js +778 -0
- package/src/ecosystem/plugin-certification.js +580 -0
- package/src/ecosystem/plugin-hub.js +821 -0
- package/src/enterprise/audit-logging.js +967 -0
- package/src/enterprise/data-governance.js +408 -0
- package/src/enterprise/multi-tenancy.js +496 -0
- package/src/enterprise/sso-integration.js +903 -0
- package/src/evaluate/evaluator.js +52 -0
- package/src/evaluate/scoring.js +91 -0
- package/src/index.js +67 -0
- package/src/ingest.js +59 -0
- package/src/loader/csv-loader.js +38 -0
- package/src/loader/directory-loader.js +48 -0
- package/src/loader/html-loader.js +62 -0
- package/src/loader/markdown-loader.js +67 -0
- package/src/mocks/openai-embedder.js +119 -0
- package/src/mocks/openai-llm.js +77 -0
- package/src/mocks/pdf-loader.js +21 -0
- package/src/mocks/pinecone-retriever.js +106 -0
- package/src/observability/audit-logger.js +758 -0
- package/src/observability/dashboard-config.json +675 -0
- package/src/observability/event-logger.js +310 -0
- package/src/observability/metrics.js +691 -0
- package/src/observability/security-monitor.js +830 -0
- package/src/observability/slo-monitor.js +319 -0
- package/src/observability/telemetry.js +686 -0
- package/src/pipeline/pipeline.js +18 -0
- package/src/plugins/sample-embedder.js +53 -0
- package/src/plugins/sample-llm.js +30 -0
- package/src/plugins/sample-loader.js +24 -0
- package/src/plugins/sample-retriever.js +84 -0
- package/src/query.js +117 -0
- package/src/reranker/llm-reranker.js +85 -0
- package/src/scripts/script-utilities.js +36 -0
- package/src/security/plugin-sandbox.js +586 -0
- package/src/security/plugin-signature-verifier.js +414 -0
- package/src/security/validator.js +36 -0
- package/src/utils/ci/diagnostic-reporter.js +57 -0
- package/src/utils/logger.js +211 -0
- package/src/utils/performance-monitor.js +561 -0
- package/src/utils/plugin-scaffolder.js +447 -0
- package/src/utils/retry-policy.js +443 -0
- package/src/utils/retry.js +107 -0
- package/src/utils/safe-expression-evaluator.js +579 -0
- package/src/utils/structured-logger.js +361 -0
- package/src/utils/validate-plugin-contract.js +42 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sample Embedder Plugin
|
|
3
|
+
* Minimal fixture for test compatibility with configurable batch processing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class SampleEmbedder {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.name = 'sample-embedder';
|
|
9
|
+
this.version = '1.0.0';
|
|
10
|
+
this.options = options;
|
|
11
|
+
|
|
12
|
+
// Configurable batch size from environment or options
|
|
13
|
+
this.batchSize =
|
|
14
|
+
parseInt(process.env.RAG_EMBEDDER_BATCH_SIZE) || options.batchSize || 50; // Default batch size
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async embed(texts) {
|
|
18
|
+
if (!Array.isArray(texts)) {
|
|
19
|
+
return new Array(384).fill(0).map(() => Math.random());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Process in batches for better memory management
|
|
23
|
+
const results = [];
|
|
24
|
+
for (let i = 0; i < texts.length; i += this.batchSize) {
|
|
25
|
+
const batch = texts.slice(i, i + this.batchSize);
|
|
26
|
+
const batchResults = await this._processBatch(batch);
|
|
27
|
+
results.push(...batchResults);
|
|
28
|
+
|
|
29
|
+
// Optional progress callback
|
|
30
|
+
if (this.options.onProgress) {
|
|
31
|
+
this.options.onProgress({
|
|
32
|
+
processed: Math.min(i + this.batchSize, texts.length),
|
|
33
|
+
total: texts.length,
|
|
34
|
+
stage: 'embedding',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return results;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async _processBatch(batch) {
|
|
43
|
+
// Simulate batch processing with slight delay for realism
|
|
44
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
45
|
+
return batch.map(() => new Array(384).fill(0).map(() => Math.random()));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async embedQuery(query) {
|
|
49
|
+
return new Array(384).fill(0).map(() => Math.random());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { SampleEmbedder };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sample LLM Plugin
|
|
3
|
+
* Minimal fixture for test compatibility
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class SampleLLM {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.name = 'sample-llm';
|
|
9
|
+
this.version = '1.0.0';
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async generate(prompt, context = []) {
|
|
14
|
+
// Mock response for testing
|
|
15
|
+
return `Generated response for: ${prompt.substring(0, 50)}...`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async *generateStream(prompt, context = []) {
|
|
19
|
+
const response = `Generated response for: ${prompt.substring(0, 50)}...`;
|
|
20
|
+
const tokens = response.split(' ');
|
|
21
|
+
|
|
22
|
+
for (const token of tokens) {
|
|
23
|
+
yield token + ' ';
|
|
24
|
+
// Small delay for realistic streaming
|
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { SampleLLM };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sample Loader Plugin
|
|
3
|
+
* Minimal fixture for test compatibility
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class SampleLoader {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.name = 'sample-loader';
|
|
9
|
+
this.version = '1.0.0';
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async load(filePath) {
|
|
14
|
+
return [
|
|
15
|
+
{
|
|
16
|
+
content: 'Sample document content',
|
|
17
|
+
metadata: { source: filePath },
|
|
18
|
+
chunk: () => ['Sample chunk 1', 'Sample chunk 2'],
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { SampleLoader };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sample Retriever Plugin
|
|
3
|
+
* Minimal fixture for test compatibility with configurable batch processing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class SampleRetriever {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.name = 'sample-retriever';
|
|
9
|
+
this.version = '1.0.0';
|
|
10
|
+
this.options = options;
|
|
11
|
+
this.storage = [];
|
|
12
|
+
|
|
13
|
+
// Configurable batch size from environment or options
|
|
14
|
+
this.batchSize =
|
|
15
|
+
parseInt(process.env.RAG_RETRIEVER_BATCH_SIZE) ||
|
|
16
|
+
options.batchSize ||
|
|
17
|
+
100; // Default batch size for retrieval
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async store(vectors) {
|
|
21
|
+
// Process storage in batches for better memory management
|
|
22
|
+
if (Array.isArray(vectors) && vectors.length > this.batchSize) {
|
|
23
|
+
for (let i = 0; i < vectors.length; i += this.batchSize) {
|
|
24
|
+
const batch = vectors.slice(i, i + this.batchSize);
|
|
25
|
+
this.storage.push(...batch);
|
|
26
|
+
|
|
27
|
+
// Optional progress callback
|
|
28
|
+
if (this.options.onProgress) {
|
|
29
|
+
this.options.onProgress({
|
|
30
|
+
processed: Math.min(i + this.batchSize, vectors.length),
|
|
31
|
+
total: vectors.length,
|
|
32
|
+
stage: 'storing',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Small delay to prevent overwhelming memory
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
this.storage.push(...vectors);
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async retrieve(queryVector, k = 5) {
|
|
46
|
+
// For large result sets, process in batches
|
|
47
|
+
const totalResults = Math.min(k, this.storage.length);
|
|
48
|
+
const results = [];
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < totalResults; i += this.batchSize) {
|
|
51
|
+
const batchSize = Math.min(this.batchSize, totalResults - i);
|
|
52
|
+
const batchResults = this._retrieveBatch(i, batchSize);
|
|
53
|
+
results.push(...batchResults);
|
|
54
|
+
|
|
55
|
+
// Optional progress callback
|
|
56
|
+
if (this.options.onProgress && totalResults > this.batchSize) {
|
|
57
|
+
this.options.onProgress({
|
|
58
|
+
processed: Math.min(i + this.batchSize, totalResults),
|
|
59
|
+
total: totalResults,
|
|
60
|
+
stage: 'retrieving',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Small delay for large retrievals
|
|
65
|
+
if (totalResults > this.batchSize) {
|
|
66
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_retrieveBatch(startIndex, batchSize) {
|
|
74
|
+
return this.storage
|
|
75
|
+
.slice(startIndex, startIndex + batchSize)
|
|
76
|
+
.map((vector, index) => ({
|
|
77
|
+
content: `Retrieved document ${startIndex + index + 1}`,
|
|
78
|
+
score: Math.random(),
|
|
79
|
+
metadata: { index: startIndex + index },
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { SampleRetriever };
|
package/src/query.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version: 2.0.0
|
|
3
|
+
* Path: /src/query.js
|
|
4
|
+
* Description: Handles querying the RAG pipeline with streaming support
|
|
5
|
+
* Author: Ali Kahwaji
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { createRagPipeline } = require('./core/create-pipeline.js');
|
|
9
|
+
// eslint-disable-line global-require
|
|
10
|
+
const { loadPluginsFromJson } = require('./config/load-plugin-config.js');
|
|
11
|
+
// eslint-disable-line global-require
|
|
12
|
+
const { logger } = require('./utils/logger.js');
|
|
13
|
+
// eslint-disable-line global-require
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Query the RAG pipeline with a standard response
|
|
17
|
+
* @param {string} prompt - Query prompt
|
|
18
|
+
* @param {object} config - Pipeline configuration
|
|
19
|
+
* @returns {Promise<string>} Generated response
|
|
20
|
+
*/
|
|
21
|
+
async function queryPipeline(_prompt, config) {
|
|
22
|
+
if (!prompt) {
|
|
23
|
+
throw new Error('No prompt provided for query.');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!config) {
|
|
27
|
+
throw new Error('No configuration provided for query.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Load plugins from configuration
|
|
32
|
+
await loadPluginsFromJson(process.cwd());
|
|
33
|
+
|
|
34
|
+
// Extract plugin names from config
|
|
35
|
+
const loaderName = Object.keys(config.loader)[0];
|
|
36
|
+
const embedderName = Object.keys(config.embedder)[0];
|
|
37
|
+
const retrieverName = Object.keys(config.retriever)[0];
|
|
38
|
+
const llmName = Object.keys(config.llm)[0];
|
|
39
|
+
|
|
40
|
+
// Create pipeline instance
|
|
41
|
+
const pipeline = createRagPipeline(
|
|
42
|
+
{
|
|
43
|
+
loader: loaderName,
|
|
44
|
+
embedder: embedderName,
|
|
45
|
+
retriever: retrieverName,
|
|
46
|
+
llm: llmName,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
useRetry: true,
|
|
50
|
+
useLogging: true,
|
|
51
|
+
useReranker: config.useReranker || false,
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Execute query
|
|
56
|
+
return await pipeline.query(prompt);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
logger.error('Query pipeline failed', { error: error.message, prompt });
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Query the RAG pipeline with streaming response
|
|
65
|
+
* @param {string} prompt - Query prompt
|
|
66
|
+
* @param {object} config - Pipeline configuration
|
|
67
|
+
* @returns {AsyncIterable<string>} Stream of response tokens
|
|
68
|
+
*/
|
|
69
|
+
async function* queryPipelineStream(prompt, config) {
|
|
70
|
+
if (!prompt) {
|
|
71
|
+
throw new Error('No prompt provided for streaming query.');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!config) {
|
|
75
|
+
throw new Error('No configuration provided for streaming query.');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
// Load plugins from configuration
|
|
80
|
+
await loadPluginsFromJson(process.cwd());
|
|
81
|
+
|
|
82
|
+
// Extract plugin names from config
|
|
83
|
+
const loaderName = Object.keys(config.loader)[0];
|
|
84
|
+
const embedderName = Object.keys(config.embedder)[0];
|
|
85
|
+
const retrieverName = Object.keys(config.retriever)[0];
|
|
86
|
+
const llmName = Object.keys(config.llm)[0];
|
|
87
|
+
|
|
88
|
+
// Create pipeline instance
|
|
89
|
+
const pipeline = createRagPipeline(
|
|
90
|
+
{
|
|
91
|
+
loader: loaderName,
|
|
92
|
+
embedder: embedderName,
|
|
93
|
+
retriever: retrieverName,
|
|
94
|
+
llm: llmName,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
useRetry: true,
|
|
98
|
+
useLogging: true,
|
|
99
|
+
useReranker: config.useReranker || false,
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Execute streaming query
|
|
104
|
+
yield* pipeline.queryStream(prompt);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
logger.error('Streaming query pipeline failed', {
|
|
107
|
+
error: error.message,
|
|
108
|
+
prompt,
|
|
109
|
+
});
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
queryPipeline,
|
|
116
|
+
queryPipelineStream,
|
|
117
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class LLMReranker {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.llm = options.llm;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async rerank(query, docs, opts = {}) {
|
|
9
|
+
if (!Array.isArray(docs)) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// If no LLM provided, fall back to simple overlap scoring
|
|
14
|
+
if (!this.llm) {
|
|
15
|
+
return this._fallbackRerank(query, docs);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
// Use LLM to get reranking order
|
|
20
|
+
const response = await this.llm.generate(query, docs, opts);
|
|
21
|
+
const rankingOrder = JSON.parse(response);
|
|
22
|
+
|
|
23
|
+
// Apply the ranking order
|
|
24
|
+
const reranked = [];
|
|
25
|
+
for (const index of rankingOrder) {
|
|
26
|
+
if (index >= 0 && index < docs.length) {
|
|
27
|
+
reranked.push(docs[index]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return reranked;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
// Fall back to simple overlap scoring on LLM error
|
|
34
|
+
return this._fallbackRerank(query, docs);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_fallbackRerank(query, docs) {
|
|
39
|
+
// Simple bag-of-words overlap scoring
|
|
40
|
+
const queryTokens = query
|
|
41
|
+
.toLowerCase()
|
|
42
|
+
.split(/\s+/)
|
|
43
|
+
.filter((t) => t.length > 0);
|
|
44
|
+
|
|
45
|
+
const scored = docs.map((doc, originalIndex) => {
|
|
46
|
+
const text = doc.text || doc.content || '';
|
|
47
|
+
const docTokens = text
|
|
48
|
+
.toLowerCase()
|
|
49
|
+
.split(/\s+/)
|
|
50
|
+
.filter((t) => t.length > 0);
|
|
51
|
+
|
|
52
|
+
// Count overlapping tokens
|
|
53
|
+
let overlap = 0;
|
|
54
|
+
for (const qToken of queryTokens) {
|
|
55
|
+
if (docTokens.includes(qToken)) {
|
|
56
|
+
overlap++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Add tiny length-based tie-break
|
|
61
|
+
const lengthTieBreak = text.length * 0.0001;
|
|
62
|
+
const score = overlap + lengthTieBreak;
|
|
63
|
+
|
|
64
|
+
return { doc, score, originalIndex };
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Sort by score (descending), then by original index (ascending) for stable sort
|
|
68
|
+
scored.sort((a, b) => {
|
|
69
|
+
if (b.score !== a.score) {
|
|
70
|
+
return b.score - a.score;
|
|
71
|
+
}
|
|
72
|
+
return a.originalIndex - b.originalIndex;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return scored.map((item) => item.doc);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Create default instance
|
|
80
|
+
const defaultInstance = new LLMReranker();
|
|
81
|
+
|
|
82
|
+
// CJS+ESM interop pattern
|
|
83
|
+
module.exports = defaultInstance;
|
|
84
|
+
module.exports.LLMReranker = LLMReranker;
|
|
85
|
+
module.exports.default = module.exports;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/scripts/script-utilities.js
|
|
2
|
+
|
|
3
|
+
// default sleeper uses real timers (prod) - unref'd to not block process exit
|
|
4
|
+
const sleep = (ms) =>
|
|
5
|
+
new Promise((resolve) => {
|
|
6
|
+
const t = setTimeout(resolve, ms);
|
|
7
|
+
// critical: don't keep the process alive just because of a timer
|
|
8
|
+
if (typeof t.unref === "function") t.unref();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
async function retry(fn, opts = {}) {
|
|
12
|
+
const {
|
|
13
|
+
retries = 3,
|
|
14
|
+
baseDelay = 100,
|
|
15
|
+
factor = 2,
|
|
16
|
+
onDelay = () => {},
|
|
17
|
+
sleeper = sleep, // <-- inject for tests
|
|
18
|
+
} = opts;
|
|
19
|
+
|
|
20
|
+
let lastErr;
|
|
21
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
22
|
+
try {
|
|
23
|
+
// pass attempt if useful for logging/logic
|
|
24
|
+
return await fn(attempt);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
lastErr = err;
|
|
27
|
+
if (attempt === retries) break;
|
|
28
|
+
const delay = baseDelay * Math.pow(factor, attempt);
|
|
29
|
+
onDelay(delay, attempt);
|
|
30
|
+
await sleeper(delay); // <-- no-op in tests
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw lastErr;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = { retry, sleep };
|