@devilsdev/rag-pipeline-utils 2.3.1 → 2.4.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/README.md +293 -369
- package/contracts/loader-contract.json +11 -6
- package/contracts/reranker-contract.json +18 -26
- package/dist/index.cjs +21190 -47
- package/dist/index.d.ts +298 -68
- package/dist/index.mjs +9 -76
- package/package.json +20 -8
- package/src/agentic/agentic-pipeline.js +235 -0
- package/src/agentic/iterative-retriever.js +213 -0
- package/src/agentic/query-planner.js +208 -0
- package/src/agentic/self-critique.js +102 -0
- package/src/ai/adaptive-retrieval.js +0 -3
- package/src/ai/federated-learning.js +0 -3
- package/src/ai/federation.js +1 -6
- package/src/ai/index.js +9 -20
- package/src/ai/model-training.js +0 -3
- package/src/ai/multimodal-processing.js +0 -3
- package/src/ai/multimodal.js +1 -6
- package/src/ai/orchestrator.js +1 -6
- package/src/ai/retrieval-engine.js +1 -6
- package/src/chunking/chunking-engine.js +142 -0
- package/src/chunking/strategies/fixed-size.js +54 -0
- package/src/chunking/strategies/recursive.js +127 -0
- package/src/chunking/strategies/semantic.js +128 -0
- package/src/chunking/strategies/sentence.js +32 -0
- package/src/chunking/strategies/structure-aware.js +138 -0
- package/src/citation/citation-tracker.js +161 -0
- package/src/citation/hallucination-detector.js +74 -0
- package/src/citation/source-mapper.js +95 -0
- package/src/cli/commands/ai-ml/adaptive-commands.js +178 -0
- package/src/cli/commands/ai-ml/federated-commands.js +207 -0
- package/src/cli/commands/ai-ml/index.js +17 -0
- package/src/cli/commands/ai-ml/multimodal-commands.js +187 -0
- package/src/cli/commands/ai-ml/shared.js +51 -0
- package/src/cli/commands/ai-ml/training-commands.js +228 -0
- package/src/cli/commands/ai-ml.js +176 -757
- package/src/cli/commands/plugin-hub/certification-commands.js +221 -0
- package/src/cli/commands/plugin-hub/event-handlers.js +64 -0
- package/src/cli/commands/plugin-hub/hub-commands.js +290 -0
- package/src/cli/commands/plugin-hub/review-commands.js +78 -0
- package/src/cli/commands/plugin-hub.js +106 -782
- package/src/cli/diagnostics/config-checker.js +81 -0
- package/src/cli/diagnostics/dependency-checker.js +69 -0
- package/src/cli/diagnostics/environment-checker.js +45 -0
- package/src/cli/diagnostics/performance-checker.js +51 -0
- package/src/cli/diagnostics/plugin-checker.js +106 -0
- package/src/cli/diagnostics/security-checker.js +61 -0
- package/src/cli/doctor-command.js +70 -369
- package/src/cli/enhanced-cli-commands.js +162 -706
- package/src/cli/handlers/config-handler.js +126 -0
- package/src/cli/handlers/diagnostics-handler.js +113 -0
- package/src/cli/handlers/ingest-handler.js +42 -0
- package/src/cli/handlers/init-handler.js +51 -0
- package/src/cli/handlers/query-handler.js +45 -0
- package/src/cli/handlers/run-handler.js +181 -0
- package/src/cli/interactive-wizard.js +99 -723
- package/src/cli/shared/cli-utils.js +89 -0
- package/src/cli/wizard/configuration.js +148 -0
- package/src/cli/wizard/observability.js +79 -0
- package/src/cli/wizard/performance.js +76 -0
- package/src/cli/wizard/plugin-selection.js +297 -0
- package/src/cli/wizard/preview-save.js +172 -0
- package/src/cli/wizard/project-setup.js +66 -0
- package/src/config/memory-optimization.js +3 -3
- package/src/connectors/anthropic-connector.js +179 -0
- package/src/connectors/base-connector.js +94 -0
- package/src/connectors/cohere-connector.js +143 -0
- package/src/connectors/local-embedder.js +171 -0
- package/src/connectors/memory-retriever.js +150 -0
- package/src/connectors/ollama-connector.js +205 -0
- package/src/connectors/openai-connector.js +173 -0
- package/src/core/create-pipeline.js +21 -1
- package/src/core/observability/tracing.js +2 -1
- package/src/core/pipeline-factory.js +0 -1
- package/src/core/plugin-registry.js +14 -14
- package/src/cost/cost-calculator.js +146 -0
- package/src/cost/cost-tracker.js +260 -0
- package/src/cost/token-budget.js +245 -0
- package/src/dag/core/execution-scheduler.js +2 -1
- package/src/dag/core/topology-validation.js +0 -3
- package/src/dag/dag-engine.js +3 -5
- package/src/dag/dag-node.js +0 -1
- package/src/dag/enhanced-dag-engine.js +171 -99
- package/src/dashboard/dashboard-generator.js +642 -0
- package/src/dashboard/metrics-aggregator.js +409 -0
- package/src/debug/execution-tracer.js +210 -0
- package/src/debug/trace-inspector.js +281 -0
- package/src/dev/dev-server.js +5 -5
- package/src/dev/hot-reload.js +3 -2
- package/src/dx/integration-templates.js +0 -3
- package/src/ecosystem/plugin-analytics-dashboard.js +2 -2
- package/src/ecosystem/plugin-hub.js +2 -2
- package/src/enterprise/audit-logging.js +2 -2
- package/src/enterprise/sso-integration.js +52 -32
- package/src/evaluate/context-metrics.js +127 -0
- package/src/evaluate/evaluator.js +4 -2
- package/src/evaluate/faithfulness.js +117 -0
- package/src/evaluate/groundedness.js +60 -0
- package/src/evaluate/pipeline-evaluator.js +156 -0
- package/src/evaluate/relevance.js +129 -0
- package/src/evaluate/scoring.js +27 -0
- package/src/graph/entity-extractor.js +183 -0
- package/src/graph/graph-index.js +113 -0
- package/src/graph/graph-retriever.js +244 -0
- package/src/graph/knowledge-graph.js +256 -0
- package/src/guardrails/guardrails-pipeline.js +136 -0
- package/src/guardrails/post-generation-guard.js +170 -0
- package/src/guardrails/pre-retrieval-guard.js +161 -0
- package/src/guardrails/retrieval-guard.js +126 -0
- package/src/index.js +255 -9
- package/src/loader/html-loader.js +4 -15
- package/src/loader/markdown-loader.js +4 -15
- package/src/mcp/mcp-server.js +199 -0
- package/src/mcp/mcp-tool-builder.js +121 -0
- package/src/observability/audit-logger.js +0 -2
- package/src/observability/event-logger.js +2 -1
- package/src/observability/security-monitor.js +0 -2
- package/src/observability/telemetry.js +0 -2
- package/src/reranker/cascade-reranker.js +79 -0
- package/src/reranker/embedding-reranker.js +124 -0
- package/src/reranker/llm-reranker.js +6 -3
- package/src/reranker/scoring-reranker.js +123 -0
- package/src/retrieval/bm25-search.js +157 -0
- package/src/retrieval/hybrid-retriever.js +111 -0
- package/src/retrieval/rank-fusion.js +62 -0
- package/src/security/jwt-config.js +4 -3
- package/src/security/jwt-validator.js +2 -1
- package/src/security/plugin-sandbox.js +0 -3
- package/src/security/plugin-signature-verifier.js +4 -6
- package/src/security/validator.js +0 -2
- package/src/streaming/embedding-stream.js +197 -0
- package/src/streaming/sse-adapter.js +168 -0
- package/src/streaming/stream-router.js +210 -0
- package/src/streaming/streaming-embedder.js +231 -0
- package/src/streaming/websocket-adapter.js +171 -0
- package/src/templates/scaffold.js +147 -0
- package/src/templates/template-registry.js +272 -0
- package/src/utils/contract-validator.js +2 -1
- package/src/utils/input-sanitizer.js +80 -99
- package/src/utils/performance-monitor.js +0 -3
- package/src/utils/plugin-scaffolder.js +9 -16
- package/src/utils/rate-limiter.js +0 -1
- package/src/utils/retry-policy.js +0 -3
- package/src/utils/safe-expression-evaluator.js +0 -3
- package/src/utils/secure-logger-config.js +2 -4
- package/src/utils/secure-logging.js +0 -3
- package/src/utils/structured-logger.js +0 -3
- package/src/ai/index-fixed.js +0 -509
- package/src/ai/module-template.js +0 -37
- package/src/ci/workflow-analyzer.js +0 -71
- package/src/cli/plugin-marketplace-commands.js +0 -869
- package/src/dev/README.md +0 -126
- package/src/dev/examples/hot-reload-usage.js +0 -355
- package/src/mocks/openai-embedder.js +0 -119
- package/src/mocks/openai-llm.js +0 -77
- package/src/mocks/pdf-loader.js +0 -21
- package/src/mocks/pinecone-retriever.js +0 -106
- package/src/scripts/script-utilities.js +0 -36
- package/src/utils/ci/diagnostic-reporter.js +0 -57
- package/src/utils/examples/error-formatter-usage.js +0 -351
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preview and Save Step
|
|
3
|
+
* Handles configuration preview, validation, saving, and testing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require("fs/promises");
|
|
7
|
+
const {
|
|
8
|
+
validateRagrc,
|
|
9
|
+
extractPluginDependencies,
|
|
10
|
+
} = require("../../config/enhanced-ragrc-schema.js");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Preview configuration and save
|
|
14
|
+
* @param {object} config - Current wizard configuration state
|
|
15
|
+
* @param {object} inquirer - Inquirer instance for prompting
|
|
16
|
+
* @param {object} options - Wizard options (outputPath, etc.)
|
|
17
|
+
* @returns {Promise<object>} Updated configuration state (possibly normalized)
|
|
18
|
+
*/
|
|
19
|
+
async function previewAndSave(config, inquirer, options) {
|
|
20
|
+
console.log("👀 Configuration Preview\n");
|
|
21
|
+
// eslint-disable-line no-console
|
|
22
|
+
|
|
23
|
+
// Show configuration summary
|
|
24
|
+
showConfigSummary(config);
|
|
25
|
+
|
|
26
|
+
const answers = await inquirer.prompt([
|
|
27
|
+
{
|
|
28
|
+
_type: "confirm",
|
|
29
|
+
name: "save",
|
|
30
|
+
message: "Save this configuration?",
|
|
31
|
+
default: true,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
_type: "input",
|
|
35
|
+
name: "filename",
|
|
36
|
+
message: "Configuration filename:",
|
|
37
|
+
default: options.outputPath,
|
|
38
|
+
when: (answers) => answers.save,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
_type: "confirm",
|
|
42
|
+
name: "testRun",
|
|
43
|
+
message: "Run a test to validate the configuration?",
|
|
44
|
+
default: false,
|
|
45
|
+
when: (answers) => answers.save,
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
if (!answers.save) {
|
|
50
|
+
console.log("Configuration not saved."); // eslint-disable-line no-console
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Validate and normalize configuration
|
|
55
|
+
const validation = validateRagrc(config);
|
|
56
|
+
if (!validation.valid) {
|
|
57
|
+
console.error("❌ Configuration validation failed:"); // eslint-disable-line no-console
|
|
58
|
+
validation.errors?.forEach((error) => {
|
|
59
|
+
console.error(` ${error.instancePath}: ${error.message}`); // eslint-disable-line no-console
|
|
60
|
+
});
|
|
61
|
+
throw new Error("Invalid configuration generated");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Use normalized configuration
|
|
65
|
+
const normalizedConfig = validation.normalized;
|
|
66
|
+
|
|
67
|
+
// Save configuration
|
|
68
|
+
await fs.writeFile(
|
|
69
|
+
answers.filename,
|
|
70
|
+
JSON.stringify(normalizedConfig, null, 2),
|
|
71
|
+
"utf-8",
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
options.outputPath = answers.filename;
|
|
75
|
+
|
|
76
|
+
if (answers.testRun) {
|
|
77
|
+
await runConfigurationTest(normalizedConfig);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return normalizedConfig;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Show configuration summary
|
|
85
|
+
* @param {object} config - Configuration to summarize
|
|
86
|
+
*/
|
|
87
|
+
function showConfigSummary(config) {
|
|
88
|
+
console.log("📋 Configuration Summary:");
|
|
89
|
+
// eslint-disable-line no-console
|
|
90
|
+
console.log(` Project: ${config.metadata?.name || "Unnamed"}`);
|
|
91
|
+
// eslint-disable-line no-console
|
|
92
|
+
console.log(
|
|
93
|
+
` Environment: ${config.metadata?.environment || "development"}`,
|
|
94
|
+
);
|
|
95
|
+
// eslint-disable-line no-console
|
|
96
|
+
|
|
97
|
+
console.log("\n🔌 Plugins:");
|
|
98
|
+
// eslint-disable-line no-console
|
|
99
|
+
for (const [_type, plugins] of Object.entries(config.plugins)) {
|
|
100
|
+
if (Object.keys(plugins).length > 0) {
|
|
101
|
+
console.log(` ${_type}: ${Object.keys(plugins).join(", ")}`);
|
|
102
|
+
// eslint-disable-line no-console
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log("\n🔄 Pipeline:");
|
|
107
|
+
// eslint-disable-line no-console
|
|
108
|
+
console.log(
|
|
109
|
+
` Stages: ${config.pipeline?.stages?.join(" → ") || "default"}`,
|
|
110
|
+
);
|
|
111
|
+
// eslint-disable-line no-console
|
|
112
|
+
|
|
113
|
+
if (config.performance?.parallel?.enabled) {
|
|
114
|
+
console.log("\n⚡ Performance:");
|
|
115
|
+
// eslint-disable-line no-console
|
|
116
|
+
console.log(
|
|
117
|
+
` Parallel processing: ${config.performance.parallel.maxConcurrency} concurrent`,
|
|
118
|
+
);
|
|
119
|
+
// eslint-disable-line no-console
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
config.observability?.tracing?.enabled ||
|
|
124
|
+
config.observability?.metrics?.enabled
|
|
125
|
+
) {
|
|
126
|
+
console.log("\n📊 Observability:");
|
|
127
|
+
// eslint-disable-line no-console
|
|
128
|
+
if (config.observability.tracing?.enabled)
|
|
129
|
+
console.log(" Tracing: enabled");
|
|
130
|
+
// eslint-disable-line no-console
|
|
131
|
+
if (config.observability.metrics?.enabled)
|
|
132
|
+
console.log(" Metrics: enabled");
|
|
133
|
+
// eslint-disable-line no-console
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log("");
|
|
137
|
+
// eslint-disable-line no-console
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Run configuration test
|
|
142
|
+
* @param {object} config - Configuration to test
|
|
143
|
+
*/
|
|
144
|
+
async function runConfigurationTest(config) {
|
|
145
|
+
console.log("🧪 Running configuration test...\n");
|
|
146
|
+
// eslint-disable-line no-console
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
// Simulate pipeline creation and basic validation
|
|
150
|
+
console.log("✅ Configuration syntax: valid");
|
|
151
|
+
// eslint-disable-line no-console
|
|
152
|
+
console.log("✅ Plugin dependencies: resolved");
|
|
153
|
+
// eslint-disable-line no-console
|
|
154
|
+
console.log("✅ Pipeline stages: configured");
|
|
155
|
+
// eslint-disable-line no-console
|
|
156
|
+
|
|
157
|
+
// Check for potential issues
|
|
158
|
+
const dependencies = extractPluginDependencies(config);
|
|
159
|
+
if (dependencies.length === 0) {
|
|
160
|
+
console.warn("⚠️ No plugins configured - pipeline will not function");
|
|
161
|
+
// eslint-disable-line no-console
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log("\n🎉 Configuration test passed!");
|
|
165
|
+
// eslint-disable-line no-console
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error("❌ Configuration test failed:", error.message);
|
|
168
|
+
// eslint-disable-line no-console
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
module.exports = { previewAndSave, showConfigSummary, runConfigurationTest };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project Setup Step
|
|
3
|
+
* Handles project metadata configuration in the interactive wizard
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require("path");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Run the project setup step
|
|
10
|
+
* @param {object} config - Current wizard configuration state
|
|
11
|
+
* @param {object} inquirer - Inquirer instance for prompting
|
|
12
|
+
* @returns {Promise<object>} Updated configuration state
|
|
13
|
+
*/
|
|
14
|
+
async function setupProject(config, inquirer) {
|
|
15
|
+
console.log("📋 Project Setup\n");
|
|
16
|
+
// eslint-disable-line no-console
|
|
17
|
+
|
|
18
|
+
const answers = await inquirer.prompt([
|
|
19
|
+
{
|
|
20
|
+
_type: "input",
|
|
21
|
+
name: "name",
|
|
22
|
+
message: "Project name:",
|
|
23
|
+
default: path.basename(process.cwd()),
|
|
24
|
+
validate: (input) => input.length > 0 || "Project name is required",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
_type: "input",
|
|
28
|
+
name: "description",
|
|
29
|
+
message: "Project description:",
|
|
30
|
+
default: "A RAG pipeline project",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
_type: "input",
|
|
34
|
+
name: "author",
|
|
35
|
+
message: "Author:",
|
|
36
|
+
default: "Unknown",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
_type: "list",
|
|
40
|
+
name: "environment",
|
|
41
|
+
message: "Target environment:",
|
|
42
|
+
choices: [
|
|
43
|
+
{ name: "Development", value: "development" },
|
|
44
|
+
{ name: "Production", value: "production" },
|
|
45
|
+
{ name: "Testing", value: "testing" },
|
|
46
|
+
],
|
|
47
|
+
default: "development",
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
config.metadata = {
|
|
52
|
+
name: answers.name,
|
|
53
|
+
version: "1.0.0",
|
|
54
|
+
description: answers.description,
|
|
55
|
+
author: answers.author,
|
|
56
|
+
environment: answers.environment,
|
|
57
|
+
createdAt: new Date().toISOString(),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
console.log(`\n✅ Project "${answers.name}" configured\n`);
|
|
61
|
+
// eslint-disable-line no-console
|
|
62
|
+
|
|
63
|
+
return config;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { setupProject };
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
* @since 2.3.0
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
const { logger } = require("../utils/logger");
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* Light Profile
|
|
15
17
|
*
|
|
@@ -248,9 +250,7 @@ function selectProfileByEnvironment() {
|
|
|
248
250
|
case "standard":
|
|
249
251
|
return STANDARD_PROFILE;
|
|
250
252
|
default:
|
|
251
|
-
|
|
252
|
-
`Unknown MEMORY_PROFILE: ${memoryProfile}, using STANDARD`,
|
|
253
|
-
);
|
|
253
|
+
logger.warn(`Unknown MEMORY_PROFILE: ${memoryProfile}, using STANDARD`);
|
|
254
254
|
return STANDARD_PROFILE;
|
|
255
255
|
}
|
|
256
256
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const axios = require('axios');
|
|
4
|
+
const { BaseConnector } = require('./base-connector');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default configuration for the Anthropic connector.
|
|
8
|
+
* @type {Object}
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_CONFIG = {
|
|
11
|
+
model: 'claude-3-5-sonnet-20241022',
|
|
12
|
+
apiKey: process.env.ANTHROPIC_API_KEY || '',
|
|
13
|
+
baseURL: 'https://api.anthropic.com',
|
|
14
|
+
apiVersion: '2023-06-01',
|
|
15
|
+
maxTokens: 4096,
|
|
16
|
+
temperature: 0.7,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* LLM connector for the Anthropic Claude API.
|
|
21
|
+
* Uses axios for HTTP calls and supports streaming via SSE.
|
|
22
|
+
*
|
|
23
|
+
* @extends BaseConnector
|
|
24
|
+
*/
|
|
25
|
+
class AnthropicConnector extends BaseConnector {
|
|
26
|
+
/**
|
|
27
|
+
* @param {Object} [options]
|
|
28
|
+
* @param {string} [options.model='claude-3-5-sonnet-20241022'] - Claude model to use
|
|
29
|
+
* @param {string} [options.apiKey] - Anthropic API key
|
|
30
|
+
* @param {string} [options.baseURL='https://api.anthropic.com'] - API base URL
|
|
31
|
+
* @param {string} [options.apiVersion='2023-06-01'] - API version header
|
|
32
|
+
* @param {number} [options.maxTokens=4096] - Max tokens in response
|
|
33
|
+
* @param {number} [options.temperature=0.7] - Sampling temperature
|
|
34
|
+
*/
|
|
35
|
+
constructor(options = {}) {
|
|
36
|
+
super({ name: 'anthropic', ...options });
|
|
37
|
+
this.config = { ...DEFAULT_CONFIG, ...options };
|
|
38
|
+
this.client = axios.create({
|
|
39
|
+
baseURL: this.config.baseURL,
|
|
40
|
+
headers: {
|
|
41
|
+
'x-api-key': this.config.apiKey,
|
|
42
|
+
'anthropic-version': this.config.apiVersion,
|
|
43
|
+
'content-type': 'application/json',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Connect to Anthropic. Verifies the API key is present.
|
|
50
|
+
* @returns {Promise<void>}
|
|
51
|
+
* @throws {Error} If no API key is configured
|
|
52
|
+
*/
|
|
53
|
+
async connect() {
|
|
54
|
+
if (!this.config.apiKey) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'Anthropic API key required. Set ANTHROPIC_API_KEY or pass apiKey option.',
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
await super.connect();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate a response from Claude using the provided query and optional RAG context.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} query - The user query / prompt
|
|
66
|
+
* @param {Array<Object|string>} [context=[]] - Retrieved documents for RAG
|
|
67
|
+
* @param {Object} [options={}] - Override model, temperature, maxTokens per-call
|
|
68
|
+
* @returns {Promise<string>} The generated text
|
|
69
|
+
*/
|
|
70
|
+
async generate(query, context = [], options = {}) {
|
|
71
|
+
return this.withRetry(async () => {
|
|
72
|
+
const body = {
|
|
73
|
+
model: options.model || this.config.model,
|
|
74
|
+
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
75
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
76
|
+
messages: [
|
|
77
|
+
{ role: 'user', content: this._buildPrompt(query, context) },
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Add system message when context is provided
|
|
82
|
+
if (context && context.length > 0) {
|
|
83
|
+
const contextText = context
|
|
84
|
+
.map((doc) => doc.content || doc.text || String(doc))
|
|
85
|
+
.join('\n\n');
|
|
86
|
+
body.system = `Use the following context to answer the question:\n\n${contextText}`;
|
|
87
|
+
body.messages = [{ role: 'user', content: query }];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const response = await this.client.post('/v1/messages', body);
|
|
91
|
+
return response.data.content[0].text;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Stream a response from Claude token-by-token via SSE.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} query - The user query / prompt
|
|
99
|
+
* @param {Array<Object|string>} [context=[]] - Retrieved documents for RAG
|
|
100
|
+
* @param {Object} [options={}] - Override model, temperature, maxTokens per-call
|
|
101
|
+
* @yields {{ token: string, done: boolean }}
|
|
102
|
+
*/
|
|
103
|
+
async *generateStream(query, context = [], options = {}) {
|
|
104
|
+
const body = {
|
|
105
|
+
model: options.model || this.config.model,
|
|
106
|
+
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
107
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
108
|
+
messages: [{ role: 'user', content: this._buildPrompt(query, context) }],
|
|
109
|
+
stream: true,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
if (context && context.length > 0) {
|
|
113
|
+
const contextText = context
|
|
114
|
+
.map((doc) => doc.content || doc.text || String(doc))
|
|
115
|
+
.join('\n\n');
|
|
116
|
+
body.system = `Use the following context to answer the question:\n\n${contextText}`;
|
|
117
|
+
body.messages = [{ role: 'user', content: query }];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const response = await this.client.post('/v1/messages', body, {
|
|
121
|
+
responseType: 'stream',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
let buffer = '';
|
|
125
|
+
|
|
126
|
+
for await (const chunk of response.data) {
|
|
127
|
+
buffer += chunk.toString();
|
|
128
|
+
const lines = buffer.split('\n');
|
|
129
|
+
// Keep the last incomplete line in the buffer
|
|
130
|
+
buffer = lines.pop() || '';
|
|
131
|
+
|
|
132
|
+
for (const line of lines) {
|
|
133
|
+
if (!line.startsWith('data: ')) continue;
|
|
134
|
+
|
|
135
|
+
const jsonStr = line.slice(6).trim();
|
|
136
|
+
if (!jsonStr || jsonStr === '[DONE]') continue;
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const event = JSON.parse(jsonStr);
|
|
140
|
+
|
|
141
|
+
if (
|
|
142
|
+
event.type === 'content_block_delta' &&
|
|
143
|
+
event.delta &&
|
|
144
|
+
event.delta.text
|
|
145
|
+
) {
|
|
146
|
+
yield { token: event.delta.text, done: false };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (event.type === 'message_stop') {
|
|
150
|
+
yield { token: '', done: true };
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// Skip malformed JSON lines
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
yield { token: '', done: true };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Build a prompt string that includes RAG context when available.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} query - User query
|
|
166
|
+
* @param {Array<Object|string>} context - RAG context documents
|
|
167
|
+
* @returns {string} Combined prompt
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
_buildPrompt(query, context) {
|
|
171
|
+
if (!context || context.length === 0) return query;
|
|
172
|
+
const contextText = context
|
|
173
|
+
.map((doc) => doc.content || doc.text || String(doc))
|
|
174
|
+
.join('\n\n');
|
|
175
|
+
return `Context:\n${contextText}\n\nQuestion: ${query}`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
module.exports = { AnthropicConnector, DEFAULT_CONFIG };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { EventEmitter } = require('events');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Abstract base class for all connectors.
|
|
7
|
+
* Provides connection lifecycle, health checking, and retry logic.
|
|
8
|
+
* @extends EventEmitter
|
|
9
|
+
*/
|
|
10
|
+
class BaseConnector extends EventEmitter {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} options
|
|
13
|
+
* @param {string} [options.name] - Connector display name
|
|
14
|
+
*/
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
super();
|
|
17
|
+
this.options = options;
|
|
18
|
+
this.connected = false;
|
|
19
|
+
this.name = options.name || this.constructor.name;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Establish the connection.
|
|
24
|
+
* Override in subclass for custom connection logic.
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
27
|
+
async connect() {
|
|
28
|
+
this.connected = true;
|
|
29
|
+
this.emit('connected', { name: this.name });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Tear down the connection.
|
|
34
|
+
* Override in subclass for custom disconnection logic.
|
|
35
|
+
* @returns {Promise<void>}
|
|
36
|
+
*/
|
|
37
|
+
async disconnect() {
|
|
38
|
+
this.connected = false;
|
|
39
|
+
this.emit('disconnected', { name: this.name });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check whether the connector is healthy and operational.
|
|
44
|
+
* Override in subclass for custom health checks.
|
|
45
|
+
* @returns {Promise<boolean>}
|
|
46
|
+
*/
|
|
47
|
+
async isHealthy() {
|
|
48
|
+
return this.connected;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Retry wrapper that attempts a function up to N times with a delay between retries.
|
|
53
|
+
* Emits a 'retry' event on each retry attempt.
|
|
54
|
+
* @param {Function} fn - Async function to execute
|
|
55
|
+
* @param {number} [retries=3] - Maximum number of retry attempts
|
|
56
|
+
* @param {number} [delay=1000] - Delay in ms between retries
|
|
57
|
+
* @returns {Promise<*>} Result of the function
|
|
58
|
+
* @throws {Error} Last error if all retries are exhausted
|
|
59
|
+
*/
|
|
60
|
+
async withRetry(fn, retries = 3, delay = 1000) {
|
|
61
|
+
let lastError;
|
|
62
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
63
|
+
try {
|
|
64
|
+
return await fn();
|
|
65
|
+
} catch (err) {
|
|
66
|
+
lastError = err;
|
|
67
|
+
if (attempt < retries) {
|
|
68
|
+
this.emit('retry', {
|
|
69
|
+
name: this.name,
|
|
70
|
+
attempt: attempt + 1,
|
|
71
|
+
maxRetries: retries,
|
|
72
|
+
error: err.message,
|
|
73
|
+
});
|
|
74
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw lastError;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Return basic information about the connector.
|
|
83
|
+
* @returns {{ name: string, connected: boolean, type: string }}
|
|
84
|
+
*/
|
|
85
|
+
getInfo() {
|
|
86
|
+
return {
|
|
87
|
+
name: this.name,
|
|
88
|
+
connected: this.connected,
|
|
89
|
+
type: this.constructor.name,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = { BaseConnector };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const axios = require('axios');
|
|
4
|
+
const { BaseConnector } = require('./base-connector');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Default configuration for the Cohere connector.
|
|
8
|
+
* @type {Object}
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_CONFIG = {
|
|
11
|
+
apiKey: process.env.COHERE_API_KEY || '',
|
|
12
|
+
baseURL: 'https://api.cohere.ai/v1',
|
|
13
|
+
embeddingModel: 'embed-english-v3.0',
|
|
14
|
+
rerankModel: 'rerank-english-v3.0',
|
|
15
|
+
inputType: 'search_document',
|
|
16
|
+
queryInputType: 'search_query',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Embedder + reranker connector for the Cohere API.
|
|
21
|
+
* Provides text embeddings (with separate document/query input types) and
|
|
22
|
+
* document reranking capabilities.
|
|
23
|
+
*
|
|
24
|
+
* @extends BaseConnector
|
|
25
|
+
*/
|
|
26
|
+
class CohereConnector extends BaseConnector {
|
|
27
|
+
/**
|
|
28
|
+
* @param {Object} [options]
|
|
29
|
+
* @param {string} [options.apiKey] - Cohere API key
|
|
30
|
+
* @param {string} [options.baseURL='https://api.cohere.ai/v1'] - API base URL
|
|
31
|
+
* @param {string} [options.embeddingModel='embed-english-v3.0'] - Embedding model
|
|
32
|
+
* @param {string} [options.rerankModel='rerank-english-v3.0'] - Reranking model
|
|
33
|
+
* @param {string} [options.inputType='search_document'] - Input type for document embeddings
|
|
34
|
+
* @param {string} [options.queryInputType='search_query'] - Input type for query embeddings
|
|
35
|
+
*/
|
|
36
|
+
constructor(options = {}) {
|
|
37
|
+
super({ name: 'cohere', ...options });
|
|
38
|
+
this.config = { ...DEFAULT_CONFIG, ...options };
|
|
39
|
+
this.client = axios.create({
|
|
40
|
+
baseURL: this.config.baseURL,
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Connect to Cohere. Verifies the API key is present.
|
|
50
|
+
* @returns {Promise<void>}
|
|
51
|
+
* @throws {Error} If no API key is configured
|
|
52
|
+
*/
|
|
53
|
+
async connect() {
|
|
54
|
+
if (!this.config.apiKey) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
'Cohere API key required. Set COHERE_API_KEY or pass apiKey option.',
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
await super.connect();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate an embedding vector for document text.
|
|
64
|
+
* Uses the document input type by default.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} text - Input text to embed
|
|
67
|
+
* @returns {Promise<number[]>} Embedding vector
|
|
68
|
+
*/
|
|
69
|
+
async embed(text) {
|
|
70
|
+
return this.withRetry(async () => {
|
|
71
|
+
const response = await this.client.post('/embed', {
|
|
72
|
+
texts: [text],
|
|
73
|
+
model: this.config.embeddingModel,
|
|
74
|
+
input_type: this.config.inputType,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return response.data.embeddings[0];
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Generate an embedding vector for a search query.
|
|
83
|
+
* Uses the query input type for better retrieval accuracy.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} query - Query text to embed
|
|
86
|
+
* @returns {Promise<number[]>} Embedding vector
|
|
87
|
+
*/
|
|
88
|
+
async embedQuery(query) {
|
|
89
|
+
return this.withRetry(async () => {
|
|
90
|
+
const response = await this.client.post('/embed', {
|
|
91
|
+
texts: [query],
|
|
92
|
+
model: this.config.embeddingModel,
|
|
93
|
+
input_type: this.config.queryInputType,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return response.data.embeddings[0];
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Rerank documents by relevance to a query using Cohere's reranker.
|
|
102
|
+
*
|
|
103
|
+
* @param {string} query - The search query
|
|
104
|
+
* @param {Array<Object|string>} documents - Documents to rerank
|
|
105
|
+
* @param {Object} [options={}]
|
|
106
|
+
* @param {number} [options.topK] - Number of top results to return (defaults to all)
|
|
107
|
+
* @returns {Promise<Array<Object>>} Reranked documents with relevance scores
|
|
108
|
+
*/
|
|
109
|
+
async rerank(query, documents, options = {}) {
|
|
110
|
+
return this.withRetry(async () => {
|
|
111
|
+
const docs = documents.map(
|
|
112
|
+
(doc) => doc.content || doc.text || String(doc),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const response = await this.client.post('/rerank', {
|
|
116
|
+
model: this.config.rerankModel,
|
|
117
|
+
query,
|
|
118
|
+
documents: docs,
|
|
119
|
+
top_n: options.topK || documents.length,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return response.data.results.map((result) => ({
|
|
123
|
+
...documents[result.index],
|
|
124
|
+
score: result.relevance_score,
|
|
125
|
+
}));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check connector health by performing a lightweight embed call.
|
|
131
|
+
* @returns {Promise<boolean>}
|
|
132
|
+
*/
|
|
133
|
+
async isHealthy() {
|
|
134
|
+
try {
|
|
135
|
+
await this.embed('health check');
|
|
136
|
+
return true;
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = { CohereConnector, DEFAULT_CONFIG };
|