@devilsdev/rag-pipeline-utils 2.3.0 → 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 -361
- 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,281 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trace Inspector
|
|
5
|
+
*
|
|
6
|
+
* Analyzes execution traces for bottlenecks and optimization opportunities.
|
|
7
|
+
* Provides summary reports, comparisons, and anomaly detection across traces.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { EventEmitter } = require('events');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Trace inspector that analyzes execution traces produced by ExecutionTracer
|
|
14
|
+
* @extends EventEmitter
|
|
15
|
+
*/
|
|
16
|
+
class TraceInspector extends EventEmitter {
|
|
17
|
+
/**
|
|
18
|
+
* Create a new TraceInspector
|
|
19
|
+
* @param {object} [options] - Inspector configuration
|
|
20
|
+
*/
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
super();
|
|
23
|
+
this.config = options;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Analyze a single trace for performance and quality insights
|
|
28
|
+
* @param {object} trace - Trace record from ExecutionTracer
|
|
29
|
+
* @returns {object} Analysis result with summary, breakdown, bottleneck, quality, and recommendations
|
|
30
|
+
*/
|
|
31
|
+
analyze(trace) {
|
|
32
|
+
const totalDuration = trace.totalDurationMs || 0;
|
|
33
|
+
const steps = trace.steps || [];
|
|
34
|
+
|
|
35
|
+
// Time breakdown per step
|
|
36
|
+
const timeBreakdown = {};
|
|
37
|
+
for (const step of steps) {
|
|
38
|
+
timeBreakdown[step.step] = step.durationMs || 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const stepDurationSum = steps.reduce(
|
|
42
|
+
(sum, s) => sum + (s.durationMs || 0),
|
|
43
|
+
0,
|
|
44
|
+
);
|
|
45
|
+
if (steps.length > 0) {
|
|
46
|
+
timeBreakdown['overhead'] = Math.max(0, totalDuration - stepDurationSum);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Identify bottleneck
|
|
50
|
+
let bottleneck = null;
|
|
51
|
+
if (steps.length > 0) {
|
|
52
|
+
const slowest = steps.reduce((a, b) =>
|
|
53
|
+
(a.durationMs || 0) >= (b.durationMs || 0) ? a : b,
|
|
54
|
+
);
|
|
55
|
+
bottleneck = {
|
|
56
|
+
step: slowest.step,
|
|
57
|
+
durationMs: slowest.durationMs || 0,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Result count
|
|
62
|
+
const resultCount = (trace.metadata && trace.metadata.resultCount) || 0;
|
|
63
|
+
|
|
64
|
+
// Quality metrics
|
|
65
|
+
const qualityMetrics = {};
|
|
66
|
+
const metadata = trace.metadata || {};
|
|
67
|
+
|
|
68
|
+
if (metadata.citations) {
|
|
69
|
+
const citations = metadata.citations;
|
|
70
|
+
if (citations.groundedness !== undefined) {
|
|
71
|
+
qualityMetrics.groundedness = citations.groundedness;
|
|
72
|
+
}
|
|
73
|
+
if (
|
|
74
|
+
Array.isArray(citations) &&
|
|
75
|
+
citations.length > 0 &&
|
|
76
|
+
citations[0].groundedness !== undefined
|
|
77
|
+
) {
|
|
78
|
+
qualityMetrics.groundedness = citations[0].groundedness;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (metadata.evaluation) {
|
|
83
|
+
const evaluation = metadata.evaluation;
|
|
84
|
+
if (evaluation.groundedness !== undefined) {
|
|
85
|
+
qualityMetrics.groundedness = evaluation.groundedness;
|
|
86
|
+
}
|
|
87
|
+
if (evaluation.faithfulness !== undefined) {
|
|
88
|
+
qualityMetrics.faithfulness = evaluation.faithfulness;
|
|
89
|
+
}
|
|
90
|
+
if (evaluation.relevance !== undefined) {
|
|
91
|
+
qualityMetrics.relevance = evaluation.relevance;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Recommendations
|
|
96
|
+
const recommendations = [];
|
|
97
|
+
|
|
98
|
+
if (totalDuration > 5000) {
|
|
99
|
+
recommendations.push('Consider caching frequent queries');
|
|
100
|
+
}
|
|
101
|
+
if (resultCount === 0) {
|
|
102
|
+
recommendations.push(
|
|
103
|
+
'No results retrieved - check retriever configuration',
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (
|
|
107
|
+
qualityMetrics.groundedness !== undefined &&
|
|
108
|
+
qualityMetrics.groundedness < 0.5
|
|
109
|
+
) {
|
|
110
|
+
recommendations.push(
|
|
111
|
+
'Low groundedness - consider increasing retrieval topK',
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (!metadata.hasCitations) {
|
|
115
|
+
recommendations.push('Enable citations for source tracking');
|
|
116
|
+
}
|
|
117
|
+
if (!metadata.hasEvaluation) {
|
|
118
|
+
recommendations.push('Enable evaluation for quality metrics');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const analysis = {
|
|
122
|
+
summary: {
|
|
123
|
+
totalDuration,
|
|
124
|
+
stepCount: steps.length,
|
|
125
|
+
resultCount,
|
|
126
|
+
},
|
|
127
|
+
timeBreakdown,
|
|
128
|
+
bottleneck,
|
|
129
|
+
qualityMetrics,
|
|
130
|
+
recommendations,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
this.emit('analyzed', analysis);
|
|
134
|
+
return analysis;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Compare multiple traces for consistency and trends
|
|
139
|
+
* @param {Array<object>} traces - Array of trace records
|
|
140
|
+
* @returns {object} Comparison result with averages, ranges, and quality trend
|
|
141
|
+
*/
|
|
142
|
+
compare(traces) {
|
|
143
|
+
if (!traces || traces.length === 0) {
|
|
144
|
+
return {
|
|
145
|
+
avgDuration: 0,
|
|
146
|
+
minDuration: 0,
|
|
147
|
+
maxDuration: 0,
|
|
148
|
+
avgResultCount: 0,
|
|
149
|
+
qualityTrend: {},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const durations = traces.map((t) => t.totalDurationMs || 0);
|
|
154
|
+
const resultCounts = traces.map(
|
|
155
|
+
(t) => (t.metadata && t.metadata.resultCount) || 0,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const sum = (arr) => arr.reduce((a, b) => a + b, 0);
|
|
159
|
+
|
|
160
|
+
const avgDuration = sum(durations) / durations.length;
|
|
161
|
+
const minDuration = Math.min(...durations);
|
|
162
|
+
const maxDuration = Math.max(...durations);
|
|
163
|
+
const avgResultCount = sum(resultCounts) / resultCounts.length;
|
|
164
|
+
|
|
165
|
+
// Quality trend: collect quality metrics across traces
|
|
166
|
+
const qualityTrend = {};
|
|
167
|
+
const metricAccumulators = {};
|
|
168
|
+
|
|
169
|
+
for (const trace of traces) {
|
|
170
|
+
const metadata = trace.metadata || {};
|
|
171
|
+
const evaluation = metadata.evaluation || {};
|
|
172
|
+
for (const key of ['groundedness', 'faithfulness', 'relevance']) {
|
|
173
|
+
if (evaluation[key] !== undefined) {
|
|
174
|
+
if (!metricAccumulators[key]) {
|
|
175
|
+
metricAccumulators[key] = [];
|
|
176
|
+
}
|
|
177
|
+
metricAccumulators[key].push(evaluation[key]);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
for (const [key, values] of Object.entries(metricAccumulators)) {
|
|
183
|
+
qualityTrend[key] = sum(values) / values.length;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const comparison = {
|
|
187
|
+
avgDuration,
|
|
188
|
+
minDuration,
|
|
189
|
+
maxDuration,
|
|
190
|
+
avgResultCount,
|
|
191
|
+
qualityTrend,
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
this.emit('compared', comparison);
|
|
195
|
+
return comparison;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Find anomalous traces that deviate significantly from the norm
|
|
200
|
+
* @param {Array<object>} traces - Array of trace records
|
|
201
|
+
* @returns {Array<object>} Array of anomaly descriptors with traceId, anomalyType, details
|
|
202
|
+
*/
|
|
203
|
+
findAnomalies(traces) {
|
|
204
|
+
if (!traces || traces.length === 0) {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const anomalies = [];
|
|
209
|
+
|
|
210
|
+
const durations = traces.map((t) => t.totalDurationMs || 0);
|
|
211
|
+
const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
|
|
212
|
+
|
|
213
|
+
const resultCounts = traces.map(
|
|
214
|
+
(t) => (t.metadata && t.metadata.resultCount) || 0,
|
|
215
|
+
);
|
|
216
|
+
const hasResults = resultCounts.some((c) => c > 0);
|
|
217
|
+
|
|
218
|
+
// Collect quality scores for average calculation
|
|
219
|
+
const qualityScores = {};
|
|
220
|
+
for (const trace of traces) {
|
|
221
|
+
const evaluation = (trace.metadata && trace.metadata.evaluation) || {};
|
|
222
|
+
for (const key of ['groundedness', 'faithfulness', 'relevance']) {
|
|
223
|
+
if (evaluation[key] !== undefined) {
|
|
224
|
+
if (!qualityScores[key]) {
|
|
225
|
+
qualityScores[key] = [];
|
|
226
|
+
}
|
|
227
|
+
qualityScores[key].push({
|
|
228
|
+
traceId: trace.id,
|
|
229
|
+
score: evaluation[key],
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const qualityAverages = {};
|
|
236
|
+
for (const [key, entries] of Object.entries(qualityScores)) {
|
|
237
|
+
qualityAverages[key] =
|
|
238
|
+
entries.reduce((sum, e) => sum + e.score, 0) / entries.length;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (const trace of traces) {
|
|
242
|
+
const duration = trace.totalDurationMs || 0;
|
|
243
|
+
const resultCount = (trace.metadata && trace.metadata.resultCount) || 0;
|
|
244
|
+
|
|
245
|
+
// Duration > 2x average
|
|
246
|
+
if (avgDuration > 0 && duration > 2 * avgDuration) {
|
|
247
|
+
anomalies.push({
|
|
248
|
+
traceId: trace.id,
|
|
249
|
+
anomalyType: 'slow_execution',
|
|
250
|
+
details: `Duration ${duration}ms is more than 2x the average ${Math.round(avgDuration)}ms`,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Zero results when others have results
|
|
255
|
+
if (hasResults && resultCount === 0) {
|
|
256
|
+
anomalies.push({
|
|
257
|
+
traceId: trace.id,
|
|
258
|
+
anomalyType: 'no_results',
|
|
259
|
+
details: 'No results retrieved while other traces returned results',
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Quality score significantly lower than average (> 0.3 below)
|
|
264
|
+
const evaluation = (trace.metadata && trace.metadata.evaluation) || {};
|
|
265
|
+
for (const [key, avg] of Object.entries(qualityAverages)) {
|
|
266
|
+
if (evaluation[key] !== undefined && evaluation[key] < avg - 0.3) {
|
|
267
|
+
anomalies.push({
|
|
268
|
+
traceId: trace.id,
|
|
269
|
+
anomalyType: 'low_quality',
|
|
270
|
+
details: `${key} score ${evaluation[key].toFixed(2)} is significantly below average ${avg.toFixed(2)}`,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
this.emit('anomaliesFound', anomalies);
|
|
277
|
+
return anomalies;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
module.exports = { TraceInspector };
|
package/src/dev/dev-server.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
const { EventEmitter } = require('events');
|
|
21
21
|
const { createHotReloadManager } = require('./hot-reload');
|
|
22
|
-
const logger = require('../utils/logger');
|
|
22
|
+
const { logger } = require('../utils/logger');
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Development Server
|
|
@@ -290,17 +290,17 @@ ${this.options.watchPaths.map((p) => `║ - ${p.padEnd(56)}║`).join('\n')}
|
|
|
290
290
|
|
|
291
291
|
switch (level) {
|
|
292
292
|
case 'success':
|
|
293
|
-
|
|
293
|
+
logger.info(`${prefix} ${message}`);
|
|
294
294
|
break;
|
|
295
295
|
case 'error':
|
|
296
|
-
|
|
296
|
+
logger.error(`${prefix} ${message}`);
|
|
297
297
|
break;
|
|
298
298
|
case 'warn':
|
|
299
|
-
|
|
299
|
+
logger.warn(`${prefix} ${message}`);
|
|
300
300
|
break;
|
|
301
301
|
case 'info':
|
|
302
302
|
default:
|
|
303
|
-
|
|
303
|
+
logger.info(`${prefix} ${message}`);
|
|
304
304
|
break;
|
|
305
305
|
}
|
|
306
306
|
}
|
package/src/dev/hot-reload.js
CHANGED
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
wrapError,
|
|
27
27
|
ERROR_CODES,
|
|
28
28
|
} = require('../utils/error-formatter');
|
|
29
|
+
const { logger } = require('../utils/logger');
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Hot Reload Manager
|
|
@@ -701,9 +702,9 @@ class HotReloadManager extends EventEmitter {
|
|
|
701
702
|
if (this.options.verbose || isError) {
|
|
702
703
|
const prefix = '[HotReload]';
|
|
703
704
|
if (isError) {
|
|
704
|
-
|
|
705
|
+
logger.error(`${prefix} ${message}`);
|
|
705
706
|
} else {
|
|
706
|
-
|
|
707
|
+
logger.info(`${prefix} ${message}`);
|
|
707
708
|
}
|
|
708
709
|
}
|
|
709
710
|
}
|
|
@@ -660,6 +660,3 @@ module.exports._config = _config;
|
|
|
660
660
|
module.exports.generateIntegration = generateIntegration;
|
|
661
661
|
module.exports.getTemplateStats = getTemplateStats;
|
|
662
662
|
module.exports.addTemplate = addTemplate;
|
|
663
|
-
|
|
664
|
-
// CJS+ESM interop pattern
|
|
665
|
-
module.exports.default = module.exports;
|
|
@@ -9,6 +9,7 @@ const path = require('path');
|
|
|
9
9
|
// eslint-disable-line global-require
|
|
10
10
|
const { EventEmitter } = require('events');
|
|
11
11
|
// eslint-disable-line global-require
|
|
12
|
+
const { logger } = require('../utils/logger');
|
|
12
13
|
|
|
13
14
|
class PluginAnalyticsDashboard extends EventEmitter {
|
|
14
15
|
constructor(_options = {}) {
|
|
@@ -749,8 +750,7 @@ class PluginAnalyticsDashboard extends EventEmitter {
|
|
|
749
750
|
try {
|
|
750
751
|
callback(data);
|
|
751
752
|
} catch (error) {
|
|
752
|
-
|
|
753
|
-
// eslint-disable-line no-console
|
|
753
|
+
logger.error('Error notifying subscriber:', error);
|
|
754
754
|
}
|
|
755
755
|
});
|
|
756
756
|
}
|
|
@@ -7,6 +7,7 @@ const fs = require('fs').promises; // eslint-disable-line global-require
|
|
|
7
7
|
const path = require('path'); // eslint-disable-line global-require
|
|
8
8
|
const crypto = require('crypto'); // eslint-disable-line global-require
|
|
9
9
|
const { EventEmitter } = require('events'); // eslint-disable-line global-require
|
|
10
|
+
const { logger } = require('../utils/logger');
|
|
10
11
|
|
|
11
12
|
function pickFetch(pref) {
|
|
12
13
|
// Prefer a provided fetch if it is a function; otherwise fall back to globalThis.fetch if available.
|
|
@@ -709,8 +710,7 @@ class PluginAnalytics {
|
|
|
709
710
|
} catch (error) {
|
|
710
711
|
// Silence warnings during tests to avoid "Cannot log after tests are done"
|
|
711
712
|
if (process.env.NODE_ENV !== 'test') {
|
|
712
|
-
|
|
713
|
-
console.warn('Analytics flush failed:', error.message);
|
|
713
|
+
logger.warn('Analytics flush failed:', error.message);
|
|
714
714
|
}
|
|
715
715
|
}
|
|
716
716
|
}
|
|
@@ -11,6 +11,7 @@ const crypto = require('crypto');
|
|
|
11
11
|
// eslint-disable-line global-require
|
|
12
12
|
const { EventEmitter } = require('events');
|
|
13
13
|
// eslint-disable-line global-require
|
|
14
|
+
const { logger } = require('../utils/logger');
|
|
14
15
|
|
|
15
16
|
class AuditLogger extends EventEmitter {
|
|
16
17
|
constructor(_options = {}) {
|
|
@@ -916,8 +917,7 @@ class AuditLogger extends EventEmitter {
|
|
|
916
917
|
|
|
917
918
|
try {
|
|
918
919
|
// Mock webhook sending - would use actual HTTP client
|
|
919
|
-
|
|
920
|
-
// eslint-disable-line no-console
|
|
920
|
+
logger.info(`Sending ${logs.length} audit logs to webhook`);
|
|
921
921
|
} catch (error) {
|
|
922
922
|
this.emit('webhook_failed', { error: error.message, count: logs.length });
|
|
923
923
|
}
|
|
@@ -15,7 +15,26 @@ class SSOManager extends EventEmitter {
|
|
|
15
15
|
constructor(_options = {}) {
|
|
16
16
|
super();
|
|
17
17
|
|
|
18
|
-
this._config =
|
|
18
|
+
this._config = this._buildConfig(_options);
|
|
19
|
+
|
|
20
|
+
this.sessions = new Map();
|
|
21
|
+
this.providers = new Map();
|
|
22
|
+
this.userCache = new Map();
|
|
23
|
+
|
|
24
|
+
this._initializeRateLimiter();
|
|
25
|
+
this._initializeJWTValidator(_options);
|
|
26
|
+
this._wireJWTEvents();
|
|
27
|
+
this._initializeProviders();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build the full SSO configuration from user-supplied options.
|
|
32
|
+
* @param {Object} _options - User-supplied configuration options
|
|
33
|
+
* @returns {Object} Complete SSO configuration
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
_buildConfig(_options) {
|
|
37
|
+
return {
|
|
19
38
|
providers: {
|
|
20
39
|
saml: {
|
|
21
40
|
enabled: false,
|
|
@@ -69,12 +88,13 @@ class SSOManager extends EventEmitter {
|
|
|
69
88
|
},
|
|
70
89
|
..._options,
|
|
71
90
|
};
|
|
91
|
+
}
|
|
72
92
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Create and configure the rate limiter for login attempt throttling.
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
_initializeRateLimiter() {
|
|
78
98
|
if (this._config.rateLimit.enabled) {
|
|
79
99
|
this.rateLimiter = new RateLimiter({
|
|
80
100
|
maxAttempts: this._config.rateLimit.maxLoginAttempts,
|
|
@@ -82,8 +102,14 @@ class SSOManager extends EventEmitter {
|
|
|
82
102
|
blockDurationMs: this._config.rateLimit.blockDurationMs,
|
|
83
103
|
});
|
|
84
104
|
}
|
|
105
|
+
}
|
|
85
106
|
|
|
86
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Create and configure the hardened JWT validator for token operations.
|
|
109
|
+
* @param {Object} _options - Original user-supplied options for JWT settings
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
112
|
+
_initializeJWTValidator(_options) {
|
|
87
113
|
this.jwtValidator = new JWTValidator(
|
|
88
114
|
createJWTConfig({
|
|
89
115
|
secret: this._config.security.jwtSecret,
|
|
@@ -99,29 +125,26 @@ class SSOManager extends EventEmitter {
|
|
|
99
125
|
enableAuditLog: _options.jwtEnableAuditLog !== false,
|
|
100
126
|
}),
|
|
101
127
|
);
|
|
128
|
+
}
|
|
102
129
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.emit('jwt_replay_detected', event);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
this._initializeProviders();
|
|
130
|
+
/**
|
|
131
|
+
* Forward JWT validator events to the SSOManager for audit logging.
|
|
132
|
+
* @private
|
|
133
|
+
*/
|
|
134
|
+
_wireJWTEvents() {
|
|
135
|
+
const eventMappings = [
|
|
136
|
+
['token_generated', 'jwt_token_generated'],
|
|
137
|
+
['token_validated', 'jwt_token_validated'],
|
|
138
|
+
['validation_failed', 'jwt_validation_failed'],
|
|
139
|
+
['algorithm_mismatch', 'jwt_algorithm_mismatch'],
|
|
140
|
+
['replay_detected', 'jwt_replay_detected'],
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
for (const [sourceEvent, targetEvent] of eventMappings) {
|
|
144
|
+
this.jwtValidator.on(sourceEvent, (event) => {
|
|
145
|
+
this.emit(targetEvent, event);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
125
148
|
}
|
|
126
149
|
|
|
127
150
|
/**
|
|
@@ -1067,6 +1090,3 @@ module.exports = {
|
|
|
1067
1090
|
ActiveDirectoryProvider,
|
|
1068
1091
|
OIDCProvider,
|
|
1069
1092
|
};
|
|
1070
|
-
|
|
1071
|
-
// Ensure module.exports is properly defined
|
|
1072
|
-
module.exports.default = module.exports;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
tokenize,
|
|
5
|
+
splitSentences,
|
|
6
|
+
computeJaccardSimilarity,
|
|
7
|
+
} = require('./scoring');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Compute context precision: the fraction of retrieved documents that are relevant to the query.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} query - The user query
|
|
13
|
+
* @param {Array<{content: string}>} retrievedDocs - Array of retrieved documents
|
|
14
|
+
* @param {object} [options={}] - Configuration options
|
|
15
|
+
* @param {number} [options.threshold=0.2] - Minimum Jaccard similarity for a doc to be considered relevant
|
|
16
|
+
* @returns {{ score: number, relevantDocs: number, totalDocs: number, perDocScores: Array<{ index: number, score: number, isRelevant: boolean }> }}
|
|
17
|
+
*/
|
|
18
|
+
function computeContextPrecision(query, retrievedDocs, options = {}) {
|
|
19
|
+
const { threshold = 0.2 } = options;
|
|
20
|
+
|
|
21
|
+
if (!query || typeof query !== 'string' || query.trim().length === 0) {
|
|
22
|
+
return { score: 0, relevantDocs: 0, totalDocs: 0, perDocScores: [] };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!Array.isArray(retrievedDocs) || retrievedDocs.length === 0) {
|
|
26
|
+
return { score: 0, relevantDocs: 0, totalDocs: 0, perDocScores: [] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const queryTokens = tokenize(query);
|
|
30
|
+
const perDocScores = [];
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < retrievedDocs.length; i++) {
|
|
33
|
+
const doc = retrievedDocs[i];
|
|
34
|
+
const docContent = doc && (doc.content || doc.text || '');
|
|
35
|
+
|
|
36
|
+
if (!docContent) {
|
|
37
|
+
perDocScores.push({ index: i, score: 0, isRelevant: false });
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const docTokens = tokenize(docContent);
|
|
42
|
+
const score = computeJaccardSimilarity(queryTokens, docTokens);
|
|
43
|
+
perDocScores.push({ index: i, score, isRelevant: score >= threshold });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const relevantDocs = perDocScores.filter((d) => d.isRelevant).length;
|
|
47
|
+
const totalDocs = perDocScores.length;
|
|
48
|
+
const score = totalDocs > 0 ? relevantDocs / totalDocs : 0;
|
|
49
|
+
|
|
50
|
+
return { score, relevantDocs, totalDocs, perDocScores };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Compute context recall: the fraction of answer claims that are supported by retrieved documents.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} answer - The generated answer text
|
|
57
|
+
* @param {Array<{content: string}>} retrievedDocs - Array of retrieved documents
|
|
58
|
+
* @param {object} [options={}] - Configuration options
|
|
59
|
+
* @param {number} [options.threshold=0.3] - Minimum Jaccard similarity for a claim to be considered supported
|
|
60
|
+
* @returns {{ score: number, supportedClaims: number, totalClaims: number, details: Array<{ claim: string, score: number, isSupported: boolean, bestDocIndex: number }> }}
|
|
61
|
+
*/
|
|
62
|
+
function computeContextRecall(answer, retrievedDocs, options = {}) {
|
|
63
|
+
const { threshold = 0.3 } = options;
|
|
64
|
+
|
|
65
|
+
if (!answer || typeof answer !== 'string' || answer.trim().length === 0) {
|
|
66
|
+
return { score: 0, supportedClaims: 0, totalClaims: 0, details: [] };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const claims = splitSentences(answer);
|
|
70
|
+
|
|
71
|
+
if (claims.length === 0) {
|
|
72
|
+
return { score: 0, supportedClaims: 0, totalClaims: 0, details: [] };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!Array.isArray(retrievedDocs) || retrievedDocs.length === 0) {
|
|
76
|
+
const details = claims.map((claim) => ({
|
|
77
|
+
claim,
|
|
78
|
+
score: 0,
|
|
79
|
+
isSupported: false,
|
|
80
|
+
bestDocIndex: -1,
|
|
81
|
+
}));
|
|
82
|
+
return {
|
|
83
|
+
score: 0,
|
|
84
|
+
supportedClaims: 0,
|
|
85
|
+
totalClaims: claims.length,
|
|
86
|
+
details,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const details = claims.map((claim) => {
|
|
91
|
+
const claimTokens = tokenize(claim);
|
|
92
|
+
let bestScore = 0;
|
|
93
|
+
let bestDocIndex = -1;
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < retrievedDocs.length; i++) {
|
|
96
|
+
const doc = retrievedDocs[i];
|
|
97
|
+
const docContent = doc && (doc.content || doc.text || '');
|
|
98
|
+
|
|
99
|
+
if (!docContent) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const docTokens = tokenize(docContent);
|
|
104
|
+
const similarity = computeJaccardSimilarity(claimTokens, docTokens);
|
|
105
|
+
|
|
106
|
+
if (similarity > bestScore) {
|
|
107
|
+
bestScore = similarity;
|
|
108
|
+
bestDocIndex = i;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
claim,
|
|
114
|
+
score: bestScore,
|
|
115
|
+
isSupported: bestScore >= threshold,
|
|
116
|
+
bestDocIndex,
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const supportedClaims = details.filter((d) => d.isSupported).length;
|
|
121
|
+
const totalClaims = details.length;
|
|
122
|
+
const score = totalClaims > 0 ? supportedClaims / totalClaims : 0;
|
|
123
|
+
|
|
124
|
+
return { score, supportedClaims, totalClaims, details };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = { computeContextPrecision, computeContextRecall };
|
|
@@ -23,8 +23,10 @@ async function evaluateRagDataset(_datasetPath, _config) {
|
|
|
23
23
|
for (const { prompt, expected } of cases) {
|
|
24
24
|
try {
|
|
25
25
|
logger.info({ prompt }, 'Evaluating case');
|
|
26
|
-
const
|
|
27
|
-
const
|
|
26
|
+
const result = await pipeline.run({ query: prompt });
|
|
27
|
+
const actual = result.results || '';
|
|
28
|
+
const success =
|
|
29
|
+
result.success && normalizeText(actual) === normalizeText(expected);
|
|
28
30
|
const scores = scoreAnswer(actual, expected);
|
|
29
31
|
results.push({ prompt, expected, actual, success, scores });
|
|
30
32
|
} catch (err) {
|