@devilsdev/rag-pipeline-utils 2.4.5 → 2.6.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 +83 -192
- package/CHANGELOG.md +431 -0
- package/README.md +418 -117
- package/SECURITY.md +5 -5
- package/dist/experimental.cjs +2 -0
- package/dist/experimental.d.ts +1 -0
- package/dist/experimental.mjs +5 -0
- package/dist/index.cjs +6442 -1355
- package/dist/index.d.ts +129 -2
- package/dist/index.mjs +1 -1
- package/dist/testing.cjs +2 -0
- package/dist/testing.d.ts +1 -0
- package/dist/testing.mjs +5 -0
- package/dist/types/adapters/ai-sdk.d.ts +53 -0
- package/dist/types/agentic/agentic-pipeline.d.ts +103 -0
- package/dist/types/agentic/iterative-retriever.d.ts +85 -0
- package/dist/types/agentic/query-planner.d.ts +145 -0
- package/dist/types/agentic/self-critique.d.ts +45 -0
- package/dist/types/ai/adaptive-retrieval.d.ts +44 -0
- package/dist/types/ai/federation/federated-learning-coordinator.d.ts +62 -0
- package/dist/types/ai/multimodal-processing.d.ts +32 -0
- package/dist/types/ai/multimodal.d.ts +39 -0
- package/dist/types/ai/retrieval-engine.d.ts +37 -0
- package/dist/types/ai/training/model-training-orchestrator.d.ts +31 -0
- package/dist/types/cache/component-keys.d.ts +7 -0
- package/dist/types/cache/memory-cache.d.ts +54 -0
- package/dist/types/cache/pipeline-cache.d.ts +66 -0
- package/dist/types/cache/redis-cache.d.ts +46 -0
- package/dist/types/chunking/chunking-engine.d.ts +70 -0
- package/dist/types/chunking/strategies/fixed-size.d.ts +17 -0
- package/dist/types/chunking/strategies/recursive.d.ts +20 -0
- package/dist/types/chunking/strategies/semantic.d.ts +19 -0
- package/dist/types/chunking/strategies/sentence.d.ts +13 -0
- package/dist/types/chunking/strategies/structure-aware.d.ts +19 -0
- package/dist/types/citation/citation-tracker.d.ts +64 -0
- package/dist/types/citation/hallucination-detector.d.ts +40 -0
- package/dist/types/citation/source-mapper.d.ts +37 -0
- package/dist/types/connectors/anthropic-connector.d.ts +97 -0
- package/dist/types/connectors/base-connector.d.ts +87 -0
- package/dist/types/connectors/cohere-connector.d.ts +94 -0
- package/dist/types/connectors/connector-utils.d.ts +110 -0
- package/dist/types/connectors/local-embedder.d.ts +95 -0
- package/dist/types/connectors/memory-retriever.d.ts +99 -0
- package/dist/types/connectors/ollama-connector.d.ts +115 -0
- package/dist/types/connectors/openai-connector.d.ts +129 -0
- package/dist/types/connectors/rag-prompt.d.ts +40 -0
- package/dist/types/core/create-pipeline.d.ts +8 -0
- package/dist/types/core/plugin-registry.d.ts +136 -0
- package/dist/types/core/stage-contracts.d.ts +36 -0
- package/dist/types/core/validate-plugin.d.ts +45 -0
- package/dist/types/cost/cost-calculator.d.ts +152 -0
- package/dist/types/cost/cost-tracker.d.ts +126 -0
- package/dist/types/cost/token-budget.d.ts +110 -0
- package/dist/types/dag/core/error-context.d.ts +239 -0
- package/dist/types/dag/core/execution-scheduler.d.ts +191 -0
- package/dist/types/dag/core/topology-validation.d.ts +226 -0
- package/dist/types/dag/dag-engine.d.ts +156 -0
- package/dist/types/dag/dag-node.d.ts +37 -0
- package/dist/types/dashboard/dashboard-generator.d.ts +127 -0
- package/dist/types/dashboard/metrics-aggregator.d.ts +218 -0
- package/dist/types/debug/execution-tracer.d.ts +57 -0
- package/dist/types/debug/trace-inspector.d.ts +31 -0
- package/dist/types/ecosystem/plugin-certification.d.ts +169 -0
- package/dist/types/enterprise/data-governance.d.ts +203 -0
- package/dist/types/enterprise/multi-tenancy.d.ts +137 -0
- package/dist/types/enterprise/sso-integration.d.ts +420 -0
- package/dist/types/evaluate/context-metrics.d.ts +55 -0
- package/dist/types/evaluate/faithfulness.d.ts +41 -0
- package/dist/types/evaluate/groundedness.d.ts +60 -0
- package/dist/types/evaluate/pipeline-evaluator.d.ts +88 -0
- package/dist/types/evaluate/relevance.d.ts +21 -0
- package/dist/types/evaluate/scoring.d.ts +123 -0
- package/dist/types/evaluate/semantic-similarity.d.ts +14 -0
- package/dist/types/experimental.d.ts +15 -0
- package/dist/types/graph/entity-extractor.d.ts +84 -0
- package/dist/types/graph/graph-index.d.ts +49 -0
- package/dist/types/graph/graph-retriever.d.ts +81 -0
- package/dist/types/graph/knowledge-graph.d.ts +129 -0
- package/dist/types/grounding/check-answer.d.ts +101 -0
- package/dist/types/guardrails/guardrails-pipeline.d.ts +63 -0
- package/dist/types/guardrails/injection-detector.d.ts +47 -0
- package/dist/types/guardrails/post-generation-guard.d.ts +61 -0
- package/dist/types/guardrails/pre-retrieval-guard.d.ts +64 -0
- package/dist/types/guardrails/retrieval-guard.d.ts +57 -0
- package/dist/types/mcp/mcp-server.d.ts +85 -0
- package/dist/types/mcp/mcp-tool-builder.d.ts +55 -0
- package/dist/types/reranker/cascade-reranker.d.ts +40 -0
- package/dist/types/reranker/embedding-reranker.d.ts +77 -0
- package/dist/types/reranker/scoring-reranker.d.ts +41 -0
- package/dist/types/retrieval/bm25-search.d.ts +43 -0
- package/dist/types/retrieval/file-vector-store.d.ts +99 -0
- package/dist/types/retrieval/hybrid-retriever.d.ts +35 -0
- package/dist/types/retrieval/rank-fusion.d.ts +10 -0
- package/dist/types/security/jwt-config.d.ts +80 -0
- package/dist/types/security/jwt-validator.d.ts +195 -0
- package/dist/types/security/plugin-sandbox.d.ts +158 -0
- package/dist/types/security/plugin-signature-verifier.d.ts +139 -0
- package/dist/types/security/validator.d.ts +5 -0
- package/dist/types/streaming/embedding-stream.d.ts +65 -0
- package/dist/types/streaming/sse-adapter.d.ts +59 -0
- package/dist/types/streaming/stream-router.d.ts +88 -0
- package/dist/types/streaming/streaming-embedder.d.ts +99 -0
- package/dist/types/streaming/websocket-adapter.d.ts +71 -0
- package/dist/types/templates/scaffold.d.ts +55 -0
- package/dist/types/templates/template-registry.d.ts +63 -0
- package/dist/types/testing.d.ts +168 -0
- package/dist/types/utils/embed-query.d.ts +20 -0
- package/dist/types/utils/logger.d.ts +50 -0
- package/dist/types/utils/rate-limiter.d.ts +66 -0
- package/dist/types/utils/retry.d.ts +27 -0
- package/dist/types/utils/run-abort.d.ts +34 -0
- package/dist/types/utils/secure-logging.d.ts +51 -0
- package/dist/types/utils/structured-logger.d.ts +126 -0
- package/package.json +72 -33
- package/src/adapters/ai-sdk.js +215 -0
- package/src/agentic/agentic-pipeline.js +75 -7
- package/src/agentic/iterative-retriever.js +47 -10
- package/src/agentic/query-planner.js +329 -78
- package/src/cache/component-keys.js +38 -0
- package/src/cache/memory-cache.js +87 -0
- package/src/cache/pipeline-cache.js +260 -0
- package/src/cache/redis-cache.js +83 -0
- package/src/citation/citation-tracker.js +6 -4
- package/src/citation/hallucination-detector.js +2 -2
- package/src/citation/source-mapper.js +15 -28
- package/src/cli/commands/ai.js +106 -0
- package/src/cli/commands/plugin-hub/event-handlers.js +6 -0
- package/src/cli/commands/plugin-hub/hub-commands.js +0 -74
- package/src/cli/commands/plugin-hub/local-commands.js +143 -0
- package/src/cli/commands/plugin-hub.js +114 -49
- package/src/cli/diagnostics/config-checker.js +76 -63
- package/src/cli/diagnostics/environment-checker.js +62 -31
- package/src/cli/diagnostics/plugin-checker.js +36 -89
- package/src/cli/doctor-command.js +39 -5
- package/src/cli/enhanced-cli-commands.js +104 -233
- package/src/cli/handlers/ai-handler.js +364 -0
- package/src/cli/handlers/diagnostics-handler.js +78 -31
- package/src/cli/handlers/ingest-handler.js +28 -22
- package/src/cli/handlers/init-handler.js +148 -25
- package/src/cli/handlers/query-handler.js +203 -25
- package/src/cli/handlers/run-handler.js +16 -167
- package/src/cli/handlers/serve-handler.js +60 -0
- package/src/cli/services/bench-stats.js +54 -0
- package/src/cli/services/benchmark.js +161 -0
- package/src/cli/services/feedback.js +155 -0
- package/src/cli/services/instrument.js +83 -0
- package/src/cli/services/rag-service.js +361 -0
- package/src/cli/shared/cli-utils.js +24 -7
- package/src/cli/shared/config-context.js +21 -0
- package/src/cli/wizard/config-builder.js +81 -0
- package/src/cli/wizard/init-wizard.js +602 -0
- package/src/config/component-factory.js +125 -0
- package/src/config/enhanced-ragrc-schema.js +0 -2
- package/src/config/load-config.js +29 -10
- package/src/config/load-plugin-config.js +46 -2
- package/src/config/validate-schema.js +4 -1
- package/src/connectors/anthropic-connector.js +115 -57
- package/src/connectors/base-connector.js +74 -19
- package/src/connectors/cohere-connector.js +108 -34
- package/src/connectors/connector-utils.js +302 -0
- package/src/connectors/local-embedder.js +63 -5
- package/src/connectors/memory-retriever.js +125 -14
- package/src/connectors/ollama-connector.js +112 -51
- package/src/connectors/openai-connector.js +153 -44
- package/src/connectors/rag-prompt.js +88 -0
- package/src/core/create-pipeline.js +307 -61
- package/src/core/performance/parallel-processor.js +62 -1
- package/src/core/plugin-registry.js +125 -26
- package/src/core/stage-contracts.js +36 -0
- package/src/core/validate-plugin.js +85 -0
- package/src/cost/cost-calculator.js +205 -32
- package/src/debug/execution-tracer.js +14 -4
- package/src/dev/dev-server.js +173 -11
- package/src/dev/hot-reload.js +44 -4
- package/src/ecosystem/plugin-checks.js +181 -0
- package/src/ecosystem/plugin-hub.js +50 -125
- package/src/ecosystem/plugin-installer.js +298 -0
- package/src/ecosystem/plugin-probe.js +127 -0
- package/src/ecosystem/plugin-scanner.js +185 -0
- package/src/ecosystem/tar-extract.js +114 -0
- package/src/enterprise/audit-logging.js +158 -29
- package/src/enterprise/sso-integration.js +716 -228
- package/src/evaluate/context-metrics.js +28 -16
- package/src/evaluate/evaluator.js +3 -3
- package/src/evaluate/faithfulness.js +18 -14
- package/src/evaluate/groundedness.js +43 -12
- package/src/evaluate/pipeline-evaluator.js +35 -8
- package/src/evaluate/scoring.js +251 -3
- package/src/evaluate/semantic-similarity.js +79 -0
- package/src/experimental.js +71 -0
- package/src/grounding/check-answer.js +173 -0
- package/src/guardrails/guardrails-pipeline.js +69 -7
- package/src/guardrails/injection-detector.js +283 -0
- package/src/guardrails/post-generation-guard.js +19 -0
- package/src/guardrails/pre-retrieval-guard.js +36 -36
- package/src/guardrails/retrieval-guard.js +58 -8
- package/src/index.js +58 -1
- package/src/loader/csv-loader.js +3 -2
- package/src/loader/document-reader.js +91 -0
- package/src/loader/html-loader.js +3 -1
- package/src/loader/markdown-loader.js +3 -2
- package/src/reranker/cascade-reranker.js +3 -0
- package/src/reranker/embedding-reranker.js +136 -21
- package/src/reranker/llm-reranker.js +79 -12
- package/src/reranker/scoring-reranker.js +3 -0
- package/src/retrieval/bm25-search.js +12 -6
- package/src/retrieval/file-vector-store.js +241 -0
- package/src/retrieval/hybrid-retriever.js +22 -19
- package/src/security/jwt-config.js +13 -2
- package/src/security/jwt-validator.js +139 -34
- package/src/security/plugin-sandbox.js +18 -1
- package/src/security/plugin-signature-verifier.js +245 -70
- package/src/testing.js +254 -0
- package/src/utils/deprecate.js +69 -0
- package/src/utils/embed-query.js +33 -0
- package/src/utils/input-sanitizer.js +19 -4
- package/src/utils/logger.js +34 -4
- package/src/utils/run-abort.js +102 -0
- package/src/ai/federated-learning.js +0 -211
- package/src/ai/federation.js +0 -71
- package/src/ai/index.js +0 -22
- package/src/ai/model-trainer.js +0 -217
- package/src/ai/model-training.js +0 -202
- package/src/ai/multimodal/multi-modal-processor.js +0 -345
- package/src/ai/orchestrator.js +0 -65
- package/src/ai/retrieval/adaptive-retrieval-engine.js +0 -398
- package/src/cli/commands/ai-ml/adaptive-commands.js +0 -178
- package/src/cli/commands/ai-ml/federated-commands.js +0 -207
- package/src/cli/commands/ai-ml/index.js +0 -17
- package/src/cli/commands/ai-ml/multimodal-commands.js +0 -187
- package/src/cli/commands/ai-ml/shared.js +0 -51
- package/src/cli/commands/ai-ml/training-commands.js +0 -228
- package/src/cli/commands/ai-ml.js +0 -386
- package/src/cli/commands/docs.js +0 -228
- package/src/cli/commands/dx.js +0 -538
- package/src/cli/commands/wizard.js +0 -627
- package/src/cli/input-sanitization-middleware.js +0 -356
- package/src/cli/interactive-wizard.js +0 -373
- package/src/cli/templates/development.template.json +0 -64
- package/src/cli/templates/minimal.template.json +0 -33
- package/src/cli/templates/production.template.json +0 -104
- package/src/cli/templates/testing.template.json +0 -45
- package/src/cli/wizard/configuration.js +0 -148
- package/src/cli/wizard/observability.js +0 -79
- package/src/cli/wizard/performance.js +0 -76
- package/src/cli/wizard/plugin-selection.js +0 -297
- package/src/cli/wizard/preview-save.js +0 -172
- package/src/cli/wizard/project-setup.js +0 -66
- package/src/core/pipeline-factory.js +0 -59
- package/src/dag/enhanced-dag-engine.js +0 -894
- package/src/dx/index.js +0 -40
- package/src/dx/integration-templates.js +0 -662
- package/src/dx/performance-profiler.js +0 -968
- package/src/dx/realtime-debugger.js +0 -755
- package/src/dx/visual-pipeline-builder.js +0 -578
- package/src/ingest.js +0 -59
- package/src/pipeline/pipeline.js +0 -18
- package/src/query.js +0 -117
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
const { randomBytes } = require('crypto');
|
|
2
3
|
|
|
3
4
|
const { BaseConnector } = require('./base-connector');
|
|
5
|
+
const { throwIfAborted } = require('./connector-utils');
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Default configuration for the in-memory retriever.
|
|
@@ -8,32 +10,101 @@ const { BaseConnector } = require('./base-connector');
|
|
|
8
10
|
*/
|
|
9
11
|
const DEFAULT_CONFIG = {
|
|
10
12
|
maxDocuments: 100000,
|
|
13
|
+
// 'error' rejects a store() that would exceed maxDocuments; 'fifo' evicts
|
|
14
|
+
// the oldest documents (insertion order) to make room.
|
|
15
|
+
eviction: 'error',
|
|
11
16
|
};
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* In-memory vector store with cosine similarity retrieval.
|
|
15
20
|
* Stores document vectors and retrieves the most similar ones to a query vector.
|
|
21
|
+
* All vectors in one store must have the same dimension.
|
|
16
22
|
* @extends BaseConnector
|
|
17
23
|
*/
|
|
18
24
|
class MemoryRetriever extends BaseConnector {
|
|
19
25
|
/**
|
|
20
26
|
* @param {Object} options
|
|
21
27
|
* @param {number} [options.maxDocuments=100000] - Maximum number of documents to store
|
|
28
|
+
* @param {'error'|'fifo'} [options.eviction='error'] - What to do when a store()
|
|
29
|
+
* would exceed maxDocuments: throw (default) or evict the oldest documents
|
|
22
30
|
*/
|
|
23
31
|
constructor(options = {}) {
|
|
24
32
|
super({ name: 'memory-retriever', ...options });
|
|
25
33
|
this.config = { ...DEFAULT_CONFIG, ...options };
|
|
34
|
+
if (!['error', 'fifo'].includes(this.config.eviction)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`MemoryRetriever: unknown eviction "${this.config.eviction}" (use "error" or "fifo")`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
26
39
|
/** @type {Map<string, { vector: number[], metadata?: Object, content?: string }>} */
|
|
27
40
|
this.documents = new Map();
|
|
41
|
+
/** @type {number|null} Vector dimension, fixed by the first stored vector */
|
|
42
|
+
this.dimensions = null;
|
|
28
43
|
this.connected = true;
|
|
44
|
+
// Cache identity: unique per instance, bumped on every change, so a
|
|
45
|
+
// shared cache never returns another store's or an older state's results.
|
|
46
|
+
this._cacheId = randomBytes(8).toString('hex');
|
|
47
|
+
this._revision = 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Cache identity for createRagPipeline({ cache }).
|
|
52
|
+
* @param {'retriever'} role
|
|
53
|
+
* @returns {string|null}
|
|
54
|
+
*/
|
|
55
|
+
getCacheKey(role) {
|
|
56
|
+
return role === 'retriever'
|
|
57
|
+
? `memory|${this._cacheId}|${this._revision}`
|
|
58
|
+
: null;
|
|
29
59
|
}
|
|
30
60
|
|
|
31
61
|
/**
|
|
32
|
-
* Store one or more vector documents.
|
|
62
|
+
* Store one or more vector documents. The batch is validated before
|
|
63
|
+
* anything is inserted, so a rejected call leaves the store unchanged.
|
|
64
|
+
*
|
|
33
65
|
* @param {Array<{ id: string, vector: number[], metadata?: Object, content?: string }>} vectors
|
|
34
66
|
* @returns {Promise<void>}
|
|
67
|
+
* @throws {Error} When a vector's dimension differs from the store's, or
|
|
68
|
+
* (eviction 'error') when the store would exceed maxDocuments
|
|
35
69
|
*/
|
|
36
70
|
async store(vectors) {
|
|
71
|
+
let dimensions = this.dimensions;
|
|
72
|
+
const newIds = new Set();
|
|
73
|
+
for (const doc of vectors) {
|
|
74
|
+
const length = doc && doc.vector ? doc.vector.length : undefined;
|
|
75
|
+
if (typeof length !== 'number') {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`MemoryRetriever.store(): document "${doc && doc.id}" has no vector`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (dimensions === null) {
|
|
81
|
+
dimensions = length;
|
|
82
|
+
} else if (length !== dimensions) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`MemoryRetriever.store(): document "${doc.id}" has ${length} dimensions ` +
|
|
85
|
+
`but the store holds ${dimensions}-dimension vectors. Use the same embedder for all documents.`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (!this.documents.has(doc.id)) newIds.add(doc.id);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const { maxDocuments, eviction } = this.config;
|
|
92
|
+
const projected = this.documents.size + newIds.size;
|
|
93
|
+
if (projected > maxDocuments) {
|
|
94
|
+
if (eviction !== 'fifo') {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`MemoryRetriever.store(): storing ${newIds.size} new document(s) would bring the ` +
|
|
97
|
+
`total to ${projected}, exceeding maxDocuments (${maxDocuments}). Raise ` +
|
|
98
|
+
'maxDocuments, call clear(), or construct with { eviction: \'fifo\' }.',
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (newIds.size > maxDocuments) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`MemoryRetriever.store(): batch of ${newIds.size} new documents exceeds maxDocuments (${maxDocuments})`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
37
108
|
for (const doc of vectors) {
|
|
38
109
|
this.documents.set(doc.id, {
|
|
39
110
|
vector: doc.vector,
|
|
@@ -41,32 +112,63 @@ class MemoryRetriever extends BaseConnector {
|
|
|
41
112
|
content: doc.content || '',
|
|
42
113
|
});
|
|
43
114
|
}
|
|
115
|
+
if (vectors.length > 0) this.dimensions = dimensions;
|
|
44
116
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
117
|
+
let evicted = 0;
|
|
118
|
+
if (this.documents.size > maxDocuments) {
|
|
119
|
+
// FIFO eviction: Map iteration follows insertion order. Never evict a
|
|
120
|
+
// document from the current batch.
|
|
121
|
+
for (const id of this.documents.keys()) {
|
|
122
|
+
if (this.documents.size <= maxDocuments) break;
|
|
123
|
+
if (newIds.has(id)) continue;
|
|
124
|
+
this.documents.delete(id);
|
|
125
|
+
evicted++;
|
|
126
|
+
}
|
|
127
|
+
this.emit('evicted', { count: evicted, total: this.documents.size });
|
|
49
128
|
}
|
|
50
129
|
|
|
130
|
+
this._revision += 1;
|
|
51
131
|
this.emit('stored', { count: vectors.length, total: this.documents.size });
|
|
52
132
|
}
|
|
53
133
|
|
|
54
134
|
/**
|
|
55
135
|
* Retrieve the top-k most similar documents to a query vector.
|
|
56
|
-
* @param {number[]|{ query?: string, queryVector: number[], topK?: number }} queryOrObj
|
|
57
|
-
* Either a raw vector array or an object with queryVector
|
|
136
|
+
* @param {number[]|{ query?: string, queryVector: number[], topK?: number, signal?: AbortSignal }} queryOrObj
|
|
137
|
+
* Either a raw vector array or an object with queryVector, optional topK
|
|
138
|
+
* and optional AbortSignal.
|
|
58
139
|
* @param {number} [k=10] - Number of results to return (used when queryOrObj is an array)
|
|
59
140
|
* @returns {Promise<Array<{ id: string, score: number, metadata: Object, content: string }>>}
|
|
141
|
+
* @throws {Error} When the query vector's dimension differs from the stored vectors
|
|
60
142
|
*/
|
|
61
143
|
async retrieve(queryOrObj, k = 10) {
|
|
62
144
|
let queryVector;
|
|
63
145
|
let topK = k;
|
|
146
|
+
let signal;
|
|
64
147
|
|
|
65
148
|
if (Array.isArray(queryOrObj)) {
|
|
66
149
|
queryVector = queryOrObj;
|
|
67
150
|
} else {
|
|
68
|
-
queryVector = queryOrObj
|
|
69
|
-
topK = queryOrObj
|
|
151
|
+
queryVector = queryOrObj?.queryVector;
|
|
152
|
+
topK = queryOrObj?.topK || k;
|
|
153
|
+
signal = queryOrObj?.signal;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
throwIfAborted(signal);
|
|
157
|
+
|
|
158
|
+
if (!Array.isArray(queryVector) && !ArrayBuffer.isView(queryVector)) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
'MemoryRetriever.retrieve(): queryVector is required. Pass an ' +
|
|
161
|
+
'`embedder` to createRagPipeline or supply `queryVector` to run().',
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this.dimensions !== null && this.documents.size > 0) {
|
|
166
|
+
if (queryVector.length !== this.dimensions) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Query vector has ${queryVector.length} dimensions but the store holds ` +
|
|
169
|
+
`${this.dimensions}-dimension vectors. Use the same embedder for queries and documents.`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
70
172
|
}
|
|
71
173
|
|
|
72
174
|
const results = [];
|
|
@@ -83,23 +185,29 @@ class MemoryRetriever extends BaseConnector {
|
|
|
83
185
|
|
|
84
186
|
results.sort((a, b) => b.score - a.score);
|
|
85
187
|
|
|
188
|
+
throwIfAborted(signal);
|
|
86
189
|
return results.slice(0, topK);
|
|
87
190
|
}
|
|
88
191
|
|
|
89
192
|
/**
|
|
90
|
-
* Compute cosine similarity between two vectors.
|
|
193
|
+
* Compute cosine similarity between two vectors of equal length.
|
|
91
194
|
* @param {number[]} a - First vector
|
|
92
195
|
* @param {number[]} b - Second vector
|
|
93
196
|
* @returns {number} Cosine similarity in the range [-1, 1], or 0 for zero vectors
|
|
197
|
+
* @throws {Error} When the vectors have different lengths
|
|
94
198
|
* @private
|
|
95
199
|
*/
|
|
96
200
|
_cosineSimilarity(a, b) {
|
|
97
|
-
|
|
201
|
+
if (a.length !== b.length) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Cannot compare vectors of different dimensions (${a.length} vs ${b.length})`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
98
206
|
let dot = 0;
|
|
99
207
|
let magA = 0;
|
|
100
208
|
let magB = 0;
|
|
101
209
|
|
|
102
|
-
for (let i = 0; i <
|
|
210
|
+
for (let i = 0; i < a.length; i++) {
|
|
103
211
|
dot += a[i] * b[i];
|
|
104
212
|
magA += a[i] * a[i];
|
|
105
213
|
magB += b[i] * b[i];
|
|
@@ -112,21 +220,24 @@ class MemoryRetriever extends BaseConnector {
|
|
|
112
220
|
}
|
|
113
221
|
|
|
114
222
|
/**
|
|
115
|
-
* Remove all stored documents.
|
|
223
|
+
* Remove all stored documents and reset the vector dimension.
|
|
116
224
|
* @returns {Promise<void>}
|
|
117
225
|
*/
|
|
118
226
|
async clear() {
|
|
119
227
|
this.documents.clear();
|
|
228
|
+
this.dimensions = null;
|
|
229
|
+
this._revision += 1;
|
|
120
230
|
}
|
|
121
231
|
|
|
122
232
|
/**
|
|
123
233
|
* Get statistics about the store.
|
|
124
|
-
* @returns {{ documentCount: number, maxDocuments: number }}
|
|
234
|
+
* @returns {{ documentCount: number, maxDocuments: number, dimensions: number|null }}
|
|
125
235
|
*/
|
|
126
236
|
getStats() {
|
|
127
237
|
return {
|
|
128
238
|
documentCount: this.documents.size,
|
|
129
239
|
maxDocuments: this.config.maxDocuments,
|
|
240
|
+
dimensions: this.dimensions,
|
|
130
241
|
};
|
|
131
242
|
}
|
|
132
243
|
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// axios loads on first use, so importing the package does not pay for it
|
|
4
|
+
// (about 125 ms) unless this connector is constructed.
|
|
5
|
+
const loadAxios = () => require('axios'); // eslint-disable-line global-require
|
|
4
6
|
const { BaseConnector } = require('./base-connector');
|
|
7
|
+
const {
|
|
8
|
+
raceAbort,
|
|
9
|
+
signalOptions,
|
|
10
|
+
throwIfAborted,
|
|
11
|
+
} = require('./connector-utils');
|
|
12
|
+
const { buildRagPrompt } = require('./rag-prompt');
|
|
13
|
+
const { componentCacheKey } = require('../cache/component-keys');
|
|
5
14
|
|
|
6
15
|
/**
|
|
7
16
|
* Default configuration for the Ollama connector.
|
|
@@ -13,14 +22,41 @@ const DEFAULT_CONFIG = {
|
|
|
13
22
|
embeddingModel: 'nomic-embed-text',
|
|
14
23
|
timeout: 60000,
|
|
15
24
|
temperature: 0.7,
|
|
25
|
+
maxRetries: 3,
|
|
26
|
+
retryDelay: 1000,
|
|
16
27
|
};
|
|
17
28
|
|
|
18
29
|
/**
|
|
19
30
|
* Connector for Ollama local LLM server.
|
|
20
31
|
* Supports text generation (sync and streaming) and embeddings.
|
|
32
|
+
* Ollama needs no API key.
|
|
21
33
|
* @extends BaseConnector
|
|
22
34
|
*/
|
|
23
35
|
class OllamaConnector extends BaseConnector {
|
|
36
|
+
/**
|
|
37
|
+
* Cache identity for createRagPipeline({ cache }).
|
|
38
|
+
* @param {'embedder'|'llm'} role
|
|
39
|
+
* @returns {string|null}
|
|
40
|
+
*/
|
|
41
|
+
getCacheKey(role) {
|
|
42
|
+
const c = this.config;
|
|
43
|
+
if (role === 'embedder') {
|
|
44
|
+
return componentCacheKey('ollama-embed', {
|
|
45
|
+
baseURL: c.baseURL,
|
|
46
|
+
model: c.embeddingModel,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (role === 'llm') {
|
|
50
|
+
return componentCacheKey('ollama', {
|
|
51
|
+
baseURL: c.baseURL,
|
|
52
|
+
model: c.model,
|
|
53
|
+
temperature: c.temperature,
|
|
54
|
+
systemPrompt: c.systemPrompt,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
24
60
|
/**
|
|
25
61
|
* @param {Object} options
|
|
26
62
|
* @param {string} [options.baseURL='http://localhost:11434'] - Ollama server URL
|
|
@@ -28,11 +64,14 @@ class OllamaConnector extends BaseConnector {
|
|
|
28
64
|
* @param {string} [options.embeddingModel='nomic-embed-text'] - Default embedding model
|
|
29
65
|
* @param {number} [options.timeout=60000] - Request timeout in ms
|
|
30
66
|
* @param {number} [options.temperature=0.7] - Generation temperature
|
|
67
|
+
* @param {number} [options.maxRetries=3] - Retries for transient failures
|
|
68
|
+
* @param {number} [options.retryDelay=1000] - Base backoff delay in ms
|
|
69
|
+
* @param {string} [options.systemPrompt] - System prompt used when RAG context is supplied
|
|
31
70
|
*/
|
|
32
71
|
constructor(options = {}) {
|
|
33
72
|
super({ name: 'ollama', ...options });
|
|
34
73
|
this.config = { ...DEFAULT_CONFIG, ...options };
|
|
35
|
-
this.client =
|
|
74
|
+
this.client = loadAxios().create({
|
|
36
75
|
baseURL: this.config.baseURL,
|
|
37
76
|
timeout: this.config.timeout,
|
|
38
77
|
});
|
|
@@ -67,6 +106,46 @@ class OllamaConnector extends BaseConnector {
|
|
|
67
106
|
}
|
|
68
107
|
}
|
|
69
108
|
|
|
109
|
+
/**
|
|
110
|
+
* POST with the connector's retry policy and cancellation.
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
_post(path, body, signal) {
|
|
114
|
+
throwIfAborted(signal);
|
|
115
|
+
return this.withRetry(
|
|
116
|
+
async () => {
|
|
117
|
+
const response = await this.client.post(
|
|
118
|
+
path,
|
|
119
|
+
body,
|
|
120
|
+
signal ? { signal } : undefined,
|
|
121
|
+
);
|
|
122
|
+
return response.data;
|
|
123
|
+
},
|
|
124
|
+
this.config.maxRetries,
|
|
125
|
+
this.config.retryDelay,
|
|
126
|
+
{ signal },
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Build the /api/generate body. Retrieved context goes into the prompt as
|
|
132
|
+
* delimited, untrusted reference data; instructions go in `system`.
|
|
133
|
+
* @private
|
|
134
|
+
*/
|
|
135
|
+
_buildBody(query, context, options, stream) {
|
|
136
|
+
const prompt = buildRagPrompt(query, context, {
|
|
137
|
+
systemPrompt: this.config.systemPrompt,
|
|
138
|
+
});
|
|
139
|
+
const body = {
|
|
140
|
+
model: options.model || this.config.model,
|
|
141
|
+
prompt: prompt.user,
|
|
142
|
+
stream,
|
|
143
|
+
options: { temperature: options.temperature ?? this.config.temperature },
|
|
144
|
+
};
|
|
145
|
+
if (prompt.system) body.system = prompt.system;
|
|
146
|
+
return body;
|
|
147
|
+
}
|
|
148
|
+
|
|
70
149
|
/**
|
|
71
150
|
* Generate a response for the given query with optional context documents.
|
|
72
151
|
* @param {string} query - The user query
|
|
@@ -74,24 +153,13 @@ class OllamaConnector extends BaseConnector {
|
|
|
74
153
|
* @param {Object} [options={}] - Override options
|
|
75
154
|
* @param {string} [options.model] - Override the generation model
|
|
76
155
|
* @param {number} [options.temperature] - Override temperature
|
|
156
|
+
* @param {AbortSignal} [options.signal] - Cancels the request
|
|
77
157
|
* @returns {Promise<string>} Generated response text
|
|
78
158
|
*/
|
|
79
159
|
async generate(query, context = [], options = {}) {
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const result = await this.withRetry(async () => {
|
|
85
|
-
const response = await this.client.post('/api/generate', {
|
|
86
|
-
model,
|
|
87
|
-
prompt,
|
|
88
|
-
stream: false,
|
|
89
|
-
options: { temperature },
|
|
90
|
-
});
|
|
91
|
-
return response.data.response;
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
return result;
|
|
160
|
+
const body = this._buildBody(query, context, options, false);
|
|
161
|
+
const data = await this._post('/api/generate', body, options.signal);
|
|
162
|
+
return data.response;
|
|
95
163
|
}
|
|
96
164
|
|
|
97
165
|
/**
|
|
@@ -101,31 +169,31 @@ class OllamaConnector extends BaseConnector {
|
|
|
101
169
|
* @param {Object} [options={}] - Override options
|
|
102
170
|
* @param {string} [options.model] - Override the generation model
|
|
103
171
|
* @param {number} [options.temperature] - Override temperature
|
|
172
|
+
* @param {AbortSignal} [options.signal] - Cancels the stream
|
|
104
173
|
* @yields {{ token: string, done: boolean }}
|
|
105
174
|
*/
|
|
106
175
|
async *generateStream(query, context = [], options = {}) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
const
|
|
176
|
+
const { signal } = options;
|
|
177
|
+
throwIfAborted(signal);
|
|
178
|
+
const body = this._buildBody(query, context, options, true);
|
|
110
179
|
|
|
111
180
|
let response;
|
|
112
181
|
try {
|
|
113
|
-
response = await
|
|
114
|
-
'/api/generate',
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
options: { temperature },
|
|
120
|
-
},
|
|
121
|
-
{ responseType: 'stream' },
|
|
182
|
+
response = await raceAbort(
|
|
183
|
+
this.client.post('/api/generate', body, {
|
|
184
|
+
responseType: 'stream',
|
|
185
|
+
...signalOptions(signal),
|
|
186
|
+
}),
|
|
187
|
+
signal,
|
|
122
188
|
);
|
|
123
189
|
} catch (err) {
|
|
190
|
+
if (err && err.name === 'AbortError') throw err;
|
|
124
191
|
throw new Error(`Ollama streaming request failed: ${err.message}`);
|
|
125
192
|
}
|
|
126
193
|
|
|
127
194
|
let buffer = '';
|
|
128
195
|
for await (const chunk of response.data) {
|
|
196
|
+
throwIfAborted(signal);
|
|
129
197
|
buffer += chunk.toString();
|
|
130
198
|
const lines = buffer.split('\n');
|
|
131
199
|
// Keep the last potentially incomplete line in the buffer
|
|
@@ -157,48 +225,41 @@ class OllamaConnector extends BaseConnector {
|
|
|
157
225
|
/**
|
|
158
226
|
* Generate an embedding vector for the given text.
|
|
159
227
|
* @param {string} text - Text to embed
|
|
228
|
+
* @param {Object} [options={}]
|
|
229
|
+
* @param {AbortSignal} [options.signal] - Cancels the request
|
|
160
230
|
* @returns {Promise<number[]>} Embedding vector
|
|
161
231
|
*/
|
|
162
|
-
async embed(text) {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
return result;
|
|
232
|
+
async embed(text, options = {}) {
|
|
233
|
+
const data = await this._post(
|
|
234
|
+
'/api/embeddings',
|
|
235
|
+
{ model: this.config.embeddingModel, prompt: text },
|
|
236
|
+
options.signal,
|
|
237
|
+
);
|
|
238
|
+
return data.embedding;
|
|
172
239
|
}
|
|
173
240
|
|
|
174
241
|
/**
|
|
175
242
|
* Generate an embedding vector for a query string.
|
|
176
243
|
* Alias for {@link embed}.
|
|
177
244
|
* @param {string} query - Query text to embed
|
|
245
|
+
* @param {Object} [options={}] - Same as embed()
|
|
178
246
|
* @returns {Promise<number[]>} Embedding vector
|
|
179
247
|
*/
|
|
180
|
-
async embedQuery(query) {
|
|
181
|
-
return this.embed(query);
|
|
248
|
+
async embedQuery(query, options = {}) {
|
|
249
|
+
return this.embed(query, options);
|
|
182
250
|
}
|
|
183
251
|
|
|
184
252
|
/**
|
|
185
253
|
* Build a prompt string incorporating context documents.
|
|
254
|
+
* Kept for backward compatibility; context is delimited and labelled as
|
|
255
|
+
* untrusted reference data.
|
|
186
256
|
* @param {string} query - The user query
|
|
187
257
|
* @param {Array<Object|string>} context - Context documents
|
|
188
258
|
* @returns {string} Formatted prompt
|
|
189
259
|
* @private
|
|
190
260
|
*/
|
|
191
261
|
_buildPrompt(query, context) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const contextText = context
|
|
195
|
-
.map((doc) => {
|
|
196
|
-
const text = doc.content || doc.text || String(doc);
|
|
197
|
-
return text;
|
|
198
|
-
})
|
|
199
|
-
.join('\n\n');
|
|
200
|
-
|
|
201
|
-
return `Context:\n${contextText}\n\nQuestion: ${query}\n\nAnswer:`;
|
|
262
|
+
return buildRagPrompt(query, context).user;
|
|
202
263
|
}
|
|
203
264
|
}
|
|
204
265
|
|