@juspay/neurolink 9.24.0 → 9.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/adapters/tts/googleTTSHandler.js +26 -1
- package/dist/adapters/video/vertexVideoHandler.js +23 -17
- package/dist/cli/commands/config.d.ts +3 -3
- package/dist/cli/commands/observability.d.ts +53 -0
- package/dist/cli/commands/observability.js +453 -0
- package/dist/cli/commands/telemetry.d.ts +63 -0
- package/dist/cli/commands/telemetry.js +689 -0
- package/dist/cli/factories/commandFactory.js +29 -15
- package/dist/cli/parser.js +6 -9
- package/dist/cli/utils/formatters.d.ts +13 -0
- package/dist/cli/utils/formatters.js +23 -0
- package/dist/constants/contextWindows.js +6 -0
- package/dist/constants/enums.d.ts +6 -0
- package/dist/constants/enums.js +8 -2
- package/dist/context/budgetChecker.js +75 -48
- package/dist/context/contextCompactor.js +135 -127
- package/dist/core/baseProvider.d.ts +5 -0
- package/dist/core/baseProvider.js +117 -110
- package/dist/core/conversationMemoryInitializer.js +7 -4
- package/dist/core/conversationMemoryManager.d.ts +2 -0
- package/dist/core/conversationMemoryManager.js +6 -2
- package/dist/core/modules/GenerationHandler.d.ts +2 -2
- package/dist/core/modules/GenerationHandler.js +12 -12
- package/dist/evaluation/ragasEvaluator.js +39 -19
- package/dist/evaluation/scoring.js +46 -20
- package/dist/features/ppt/presentationOrchestrator.js +23 -0
- package/dist/features/ppt/slideGenerator.js +13 -0
- package/dist/features/ppt/slideRenderers.d.ts +1 -1
- package/dist/features/ppt/slideRenderers.js +6 -4
- package/dist/features/ppt/slideTypeInference.d.ts +1 -1
- package/dist/features/ppt/slideTypeInference.js +75 -73
- package/dist/files/fileTools.d.ts +6 -6
- package/dist/index.d.ts +46 -12
- package/dist/index.js +79 -17
- package/dist/lib/adapters/tts/googleTTSHandler.js +26 -1
- package/dist/lib/adapters/video/vertexVideoHandler.js +23 -17
- package/dist/lib/constants/contextWindows.js +6 -0
- package/dist/lib/constants/enums.d.ts +6 -0
- package/dist/lib/constants/enums.js +8 -2
- package/dist/lib/context/budgetChecker.js +75 -48
- package/dist/lib/context/contextCompactor.js +135 -127
- package/dist/lib/core/baseProvider.d.ts +5 -0
- package/dist/lib/core/baseProvider.js +117 -110
- package/dist/lib/core/conversationMemoryInitializer.js +7 -4
- package/dist/lib/core/conversationMemoryManager.d.ts +2 -0
- package/dist/lib/core/conversationMemoryManager.js +6 -2
- package/dist/lib/core/modules/GenerationHandler.d.ts +2 -2
- package/dist/lib/core/modules/GenerationHandler.js +12 -12
- package/dist/lib/evaluation/ragasEvaluator.js +39 -19
- package/dist/lib/evaluation/scoring.js +46 -20
- package/dist/lib/features/ppt/presentationOrchestrator.js +23 -0
- package/dist/lib/features/ppt/slideGenerator.js +13 -0
- package/dist/lib/features/ppt/slideRenderers.d.ts +1 -1
- package/dist/lib/features/ppt/slideRenderers.js +6 -4
- package/dist/lib/features/ppt/slideTypeInference.d.ts +1 -1
- package/dist/lib/features/ppt/slideTypeInference.js +75 -73
- package/dist/lib/files/fileTools.d.ts +6 -6
- package/dist/lib/index.d.ts +46 -12
- package/dist/lib/index.js +79 -17
- package/dist/lib/mcp/httpRateLimiter.js +39 -12
- package/dist/lib/mcp/httpRetryHandler.js +22 -1
- package/dist/lib/mcp/mcpClientFactory.js +13 -15
- package/dist/lib/memory/memoryRetrievalTools.js +22 -0
- package/dist/lib/neurolink.d.ts +64 -72
- package/dist/lib/neurolink.js +1007 -564
- package/dist/lib/observability/exporterRegistry.d.ts +152 -0
- package/dist/lib/observability/exporterRegistry.js +414 -0
- package/dist/lib/observability/exporters/arizeExporter.d.ts +32 -0
- package/dist/lib/observability/exporters/arizeExporter.js +139 -0
- package/dist/lib/observability/exporters/baseExporter.d.ts +117 -0
- package/dist/lib/observability/exporters/baseExporter.js +191 -0
- package/dist/lib/observability/exporters/braintrustExporter.d.ts +30 -0
- package/dist/lib/observability/exporters/braintrustExporter.js +155 -0
- package/dist/lib/observability/exporters/datadogExporter.d.ts +37 -0
- package/dist/lib/observability/exporters/datadogExporter.js +197 -0
- package/dist/lib/observability/exporters/index.d.ts +13 -0
- package/dist/lib/observability/exporters/index.js +14 -0
- package/dist/lib/observability/exporters/laminarExporter.d.ts +48 -0
- package/dist/lib/observability/exporters/laminarExporter.js +303 -0
- package/dist/lib/observability/exporters/langfuseExporter.d.ts +47 -0
- package/dist/lib/observability/exporters/langfuseExporter.js +204 -0
- package/dist/lib/observability/exporters/langsmithExporter.d.ts +26 -0
- package/dist/lib/observability/exporters/langsmithExporter.js +124 -0
- package/dist/lib/observability/exporters/otelExporter.d.ts +39 -0
- package/dist/lib/observability/exporters/otelExporter.js +165 -0
- package/dist/lib/observability/exporters/posthogExporter.d.ts +48 -0
- package/dist/lib/observability/exporters/posthogExporter.js +288 -0
- package/dist/lib/observability/exporters/sentryExporter.d.ts +32 -0
- package/dist/lib/observability/exporters/sentryExporter.js +166 -0
- package/dist/lib/observability/index.d.ts +25 -0
- package/dist/lib/observability/index.js +32 -0
- package/dist/lib/observability/metricsAggregator.d.ts +260 -0
- package/dist/lib/observability/metricsAggregator.js +557 -0
- package/dist/lib/observability/otelBridge.d.ts +49 -0
- package/dist/lib/observability/otelBridge.js +132 -0
- package/dist/lib/observability/retryPolicy.d.ts +192 -0
- package/dist/lib/observability/retryPolicy.js +384 -0
- package/dist/lib/observability/sampling/index.d.ts +4 -0
- package/dist/lib/observability/sampling/index.js +5 -0
- package/dist/lib/observability/sampling/samplers.d.ts +116 -0
- package/dist/lib/observability/sampling/samplers.js +217 -0
- package/dist/lib/observability/spanProcessor.d.ts +129 -0
- package/dist/lib/observability/spanProcessor.js +304 -0
- package/dist/lib/observability/tokenTracker.d.ts +156 -0
- package/dist/lib/observability/tokenTracker.js +414 -0
- package/dist/lib/observability/types/exporterTypes.d.ts +250 -0
- package/dist/lib/observability/types/exporterTypes.js +6 -0
- package/dist/lib/observability/types/index.d.ts +6 -0
- package/dist/lib/observability/types/index.js +5 -0
- package/dist/lib/observability/types/spanTypes.d.ts +244 -0
- package/dist/lib/observability/types/spanTypes.js +93 -0
- package/dist/lib/observability/utils/index.d.ts +4 -0
- package/dist/lib/observability/utils/index.js +5 -0
- package/dist/lib/observability/utils/safeMetadata.d.ts +10 -0
- package/dist/lib/observability/utils/safeMetadata.js +26 -0
- package/dist/lib/observability/utils/spanSerializer.d.ts +115 -0
- package/dist/lib/observability/utils/spanSerializer.js +291 -0
- package/dist/lib/providers/amazonSagemaker.d.ts +5 -4
- package/dist/lib/providers/amazonSagemaker.js +3 -4
- package/dist/lib/providers/googleVertex.d.ts +7 -0
- package/dist/lib/providers/googleVertex.js +76 -2
- package/dist/lib/rag/pipeline/RAGPipeline.d.ts +0 -5
- package/dist/lib/rag/pipeline/RAGPipeline.js +122 -87
- package/dist/lib/rag/ragIntegration.js +30 -0
- package/dist/lib/rag/retrieval/hybridSearch.js +22 -0
- package/dist/lib/server/abstract/baseServerAdapter.js +51 -19
- package/dist/lib/server/middleware/common.js +44 -12
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +2 -2
- package/dist/lib/services/server/ai/observability/instrumentation.js +10 -5
- package/dist/lib/types/conversationMemoryInterface.d.ts +2 -0
- package/dist/lib/types/modelTypes.d.ts +18 -18
- package/dist/lib/types/providers.d.ts +5 -0
- package/dist/lib/utils/pricing.js +25 -1
- package/dist/lib/utils/ttsProcessor.js +74 -59
- package/dist/lib/workflow/config.d.ts +36 -36
- package/dist/lib/workflow/core/ensembleExecutor.js +10 -0
- package/dist/lib/workflow/core/judgeScorer.js +20 -2
- package/dist/lib/workflow/core/workflowRunner.js +34 -1
- package/dist/mcp/httpRateLimiter.js +39 -12
- package/dist/mcp/httpRetryHandler.js +22 -1
- package/dist/mcp/mcpClientFactory.js +13 -15
- package/dist/memory/memoryRetrievalTools.js +22 -0
- package/dist/neurolink.d.ts +64 -72
- package/dist/neurolink.js +1007 -564
- package/dist/observability/FEATURE-STATUS.md +269 -0
- package/dist/observability/exporterRegistry.d.ts +152 -0
- package/dist/observability/exporterRegistry.js +413 -0
- package/dist/observability/exporters/arizeExporter.d.ts +32 -0
- package/dist/observability/exporters/arizeExporter.js +138 -0
- package/dist/observability/exporters/baseExporter.d.ts +117 -0
- package/dist/observability/exporters/baseExporter.js +190 -0
- package/dist/observability/exporters/braintrustExporter.d.ts +30 -0
- package/dist/observability/exporters/braintrustExporter.js +154 -0
- package/dist/observability/exporters/datadogExporter.d.ts +37 -0
- package/dist/observability/exporters/datadogExporter.js +196 -0
- package/dist/observability/exporters/index.d.ts +13 -0
- package/dist/observability/exporters/index.js +13 -0
- package/dist/observability/exporters/laminarExporter.d.ts +48 -0
- package/dist/observability/exporters/laminarExporter.js +302 -0
- package/dist/observability/exporters/langfuseExporter.d.ts +47 -0
- package/dist/observability/exporters/langfuseExporter.js +203 -0
- package/dist/observability/exporters/langsmithExporter.d.ts +26 -0
- package/dist/observability/exporters/langsmithExporter.js +123 -0
- package/dist/observability/exporters/otelExporter.d.ts +39 -0
- package/dist/observability/exporters/otelExporter.js +164 -0
- package/dist/observability/exporters/posthogExporter.d.ts +48 -0
- package/dist/observability/exporters/posthogExporter.js +287 -0
- package/dist/observability/exporters/sentryExporter.d.ts +32 -0
- package/dist/observability/exporters/sentryExporter.js +165 -0
- package/dist/observability/index.d.ts +25 -0
- package/dist/observability/index.js +31 -0
- package/dist/observability/metricsAggregator.d.ts +260 -0
- package/dist/observability/metricsAggregator.js +556 -0
- package/dist/observability/otelBridge.d.ts +49 -0
- package/dist/observability/otelBridge.js +131 -0
- package/dist/observability/retryPolicy.d.ts +192 -0
- package/dist/observability/retryPolicy.js +383 -0
- package/dist/observability/sampling/index.d.ts +4 -0
- package/dist/observability/sampling/index.js +4 -0
- package/dist/observability/sampling/samplers.d.ts +116 -0
- package/dist/observability/sampling/samplers.js +216 -0
- package/dist/observability/spanProcessor.d.ts +129 -0
- package/dist/observability/spanProcessor.js +303 -0
- package/dist/observability/tokenTracker.d.ts +156 -0
- package/dist/observability/tokenTracker.js +413 -0
- package/dist/observability/types/exporterTypes.d.ts +250 -0
- package/dist/observability/types/exporterTypes.js +5 -0
- package/dist/observability/types/index.d.ts +6 -0
- package/dist/observability/types/index.js +4 -0
- package/dist/observability/types/spanTypes.d.ts +244 -0
- package/dist/observability/types/spanTypes.js +92 -0
- package/dist/observability/utils/index.d.ts +4 -0
- package/dist/observability/utils/index.js +4 -0
- package/dist/observability/utils/safeMetadata.d.ts +10 -0
- package/dist/observability/utils/safeMetadata.js +25 -0
- package/dist/observability/utils/spanSerializer.d.ts +115 -0
- package/dist/observability/utils/spanSerializer.js +290 -0
- package/dist/providers/amazonSagemaker.d.ts +5 -4
- package/dist/providers/amazonSagemaker.js +3 -4
- package/dist/providers/googleVertex.d.ts +7 -0
- package/dist/providers/googleVertex.js +76 -2
- package/dist/rag/pipeline/RAGPipeline.d.ts +0 -5
- package/dist/rag/pipeline/RAGPipeline.js +122 -87
- package/dist/rag/ragIntegration.js +30 -0
- package/dist/rag/retrieval/hybridSearch.js +22 -0
- package/dist/server/abstract/baseServerAdapter.js +51 -19
- package/dist/server/middleware/common.js +44 -12
- package/dist/services/server/ai/observability/instrumentation.d.ts +2 -2
- package/dist/services/server/ai/observability/instrumentation.js +10 -5
- package/dist/types/conversationMemoryInterface.d.ts +2 -0
- package/dist/types/providers.d.ts +5 -0
- package/dist/utils/pricing.js +25 -1
- package/dist/utils/ttsProcessor.js +74 -59
- package/dist/workflow/config.d.ts +52 -52
- package/dist/workflow/core/ensembleExecutor.js +10 -0
- package/dist/workflow/core/judgeScorer.js +20 -2
- package/dist/workflow/core/workflowRunner.js +34 -1
- package/package.json +1 -1
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
* Stage 3: LLM Summarization (expensive -- requires LLM call)
|
|
9
9
|
* Stage 4: Sliding Window Truncation (fallback -- no LLM call)
|
|
10
10
|
*/
|
|
11
|
-
import { trace, SpanStatusCode } from "@opentelemetry/api";
|
|
12
11
|
import { estimateMessagesTokens } from "../utils/tokenEstimation.js";
|
|
13
12
|
import { logger } from "../utils/logger.js";
|
|
13
|
+
import { withTimeout } from "../utils/async/withTimeout.js";
|
|
14
|
+
import { SpanSerializer, SpanType, SpanStatus, } from "../observability/index.js";
|
|
15
|
+
import { getMetricsAggregator } from "../observability/index.js";
|
|
14
16
|
import { pruneToolOutputs } from "./stages/toolOutputPruner.js";
|
|
15
17
|
import { deduplicateFileReads } from "./stages/fileReadDeduplicator.js";
|
|
16
18
|
import { summarizeMessages } from "./stages/structuredSummarizer.js";
|
|
@@ -38,155 +40,161 @@ export class ContextCompactor {
|
|
|
38
40
|
* Run the multi-stage compaction pipeline until messages fit within budget.
|
|
39
41
|
*/
|
|
40
42
|
async compact(messages, targetTokens, memoryConfig, requestId) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const stagesUsed = [];
|
|
45
|
-
let currentMessages = [...messages];
|
|
46
|
-
logger.info("[Compaction] Starting", {
|
|
47
|
-
requestId,
|
|
48
|
-
estimatedTokens: tokensBefore,
|
|
49
|
-
budgetTokens: targetTokens,
|
|
43
|
+
const span = SpanSerializer.createSpan(SpanType.CONTEXT_COMPACTION, "context.compact", {
|
|
44
|
+
"context.operation": "compact",
|
|
45
|
+
"context.targetTokens": targetTokens,
|
|
50
46
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
protectedTools: this.config.pruneProtectedTools,
|
|
59
|
-
provider,
|
|
60
|
-
});
|
|
61
|
-
if (pruneResult.pruned) {
|
|
62
|
-
currentMessages = pruneResult.messages;
|
|
63
|
-
stagesUsed.push("prune");
|
|
64
|
-
}
|
|
65
|
-
const stageTokensAfter = estimateMessagesTokens(currentMessages, provider);
|
|
66
|
-
logger.info("[Compaction] Stage 1 (prune)", {
|
|
47
|
+
const spanStartTime = Date.now();
|
|
48
|
+
try {
|
|
49
|
+
const provider = this.config.provider || undefined;
|
|
50
|
+
const tokensBefore = estimateMessagesTokens(messages, provider);
|
|
51
|
+
const stagesUsed = [];
|
|
52
|
+
let currentMessages = [...messages];
|
|
53
|
+
logger.info("[Compaction] Starting", {
|
|
67
54
|
requestId,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
tokensAfter: stageTokensAfter,
|
|
71
|
-
saved: stageTokensBefore - stageTokensAfter,
|
|
55
|
+
estimatedTokens: tokensBefore,
|
|
56
|
+
budgetTokens: targetTokens,
|
|
72
57
|
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
const stageTokensAfter = estimateMessagesTokens(currentMessages, provider);
|
|
84
|
-
logger.info("[Compaction] Stage 2 (deduplicate)", {
|
|
85
|
-
requestId,
|
|
86
|
-
ran: dedupResult.deduplicated,
|
|
87
|
-
tokensBefore: stageTokensBefore,
|
|
88
|
-
tokensAfter: stageTokensAfter,
|
|
89
|
-
saved: stageTokensBefore - stageTokensAfter,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
// Stage 3: LLM Summarization
|
|
93
|
-
if (this.config.enableSummarize &&
|
|
94
|
-
estimateMessagesTokens(currentMessages, provider) > targetTokens) {
|
|
95
|
-
const stageTokensBefore = estimateMessagesTokens(currentMessages, provider);
|
|
96
|
-
try {
|
|
97
|
-
const summarizeResult = await summarizeMessages(currentMessages, {
|
|
98
|
-
provider: this.config.summarizationProvider,
|
|
99
|
-
model: this.config.summarizationModel,
|
|
100
|
-
keepRecentRatio: this.config.keepRecentRatio,
|
|
101
|
-
memoryConfig,
|
|
58
|
+
// Stage 1: Tool Output Pruning
|
|
59
|
+
if (this.config.enablePrune &&
|
|
60
|
+
estimateMessagesTokens(currentMessages, provider) > targetTokens) {
|
|
61
|
+
const stageTokensBefore = estimateMessagesTokens(currentMessages, provider);
|
|
62
|
+
const pruneResult = pruneToolOutputs(currentMessages, {
|
|
63
|
+
protectTokens: this.config.pruneProtectTokens,
|
|
64
|
+
minimumSavings: this.config.pruneMinimumSavings,
|
|
65
|
+
protectedTools: this.config.pruneProtectedTools,
|
|
66
|
+
provider,
|
|
102
67
|
});
|
|
103
|
-
if (
|
|
104
|
-
currentMessages =
|
|
105
|
-
stagesUsed.push("
|
|
68
|
+
if (pruneResult.pruned) {
|
|
69
|
+
currentMessages = pruneResult.messages;
|
|
70
|
+
stagesUsed.push("prune");
|
|
106
71
|
}
|
|
107
72
|
const stageTokensAfter = estimateMessagesTokens(currentMessages, provider);
|
|
108
|
-
logger.info("[Compaction] Stage
|
|
73
|
+
logger.info("[Compaction] Stage 1 (prune)", {
|
|
109
74
|
requestId,
|
|
110
|
-
ran:
|
|
75
|
+
ran: pruneResult.pruned,
|
|
111
76
|
tokensBefore: stageTokensBefore,
|
|
112
77
|
tokensAfter: stageTokensAfter,
|
|
113
78
|
saved: stageTokensBefore - stageTokensAfter,
|
|
114
79
|
});
|
|
115
80
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
81
|
+
// Stage 2: File Read Deduplication
|
|
82
|
+
if (this.config.enableDeduplicate &&
|
|
83
|
+
estimateMessagesTokens(currentMessages, provider) > targetTokens) {
|
|
84
|
+
const stageTokensBefore = estimateMessagesTokens(currentMessages, provider);
|
|
85
|
+
const dedupResult = deduplicateFileReads(currentMessages);
|
|
86
|
+
if (dedupResult.deduplicated) {
|
|
87
|
+
currentMessages = dedupResult.messages;
|
|
88
|
+
stagesUsed.push("deduplicate");
|
|
89
|
+
}
|
|
90
|
+
const stageTokensAfter = estimateMessagesTokens(currentMessages, provider);
|
|
91
|
+
logger.info("[Compaction] Stage 2 (deduplicate)", {
|
|
121
92
|
requestId,
|
|
122
|
-
|
|
123
|
-
errorName,
|
|
93
|
+
ran: dedupResult.deduplicated,
|
|
124
94
|
tokensBefore: stageTokensBefore,
|
|
125
|
-
tokensAfter:
|
|
126
|
-
saved:
|
|
95
|
+
tokensAfter: stageTokensAfter,
|
|
96
|
+
saved: stageTokensBefore - stageTokensAfter,
|
|
127
97
|
});
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
98
|
+
}
|
|
99
|
+
// Stage 3: LLM Summarization
|
|
100
|
+
if (this.config.enableSummarize &&
|
|
101
|
+
estimateMessagesTokens(currentMessages, provider) > targetTokens) {
|
|
102
|
+
const stageTokensBefore = estimateMessagesTokens(currentMessages, provider);
|
|
103
|
+
try {
|
|
104
|
+
const summarizeResult = await withTimeout(summarizeMessages(currentMessages, {
|
|
105
|
+
provider: this.config.summarizationProvider,
|
|
106
|
+
model: this.config.summarizationModel,
|
|
107
|
+
keepRecentRatio: this.config.keepRecentRatio,
|
|
108
|
+
memoryConfig,
|
|
109
|
+
}), 120_000, "LLM summarization timed out after 120s");
|
|
110
|
+
if (summarizeResult.summarized) {
|
|
111
|
+
currentMessages = summarizeResult.messages;
|
|
112
|
+
stagesUsed.push("summarize");
|
|
138
113
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
114
|
+
const stageTokensAfter = estimateMessagesTokens(currentMessages, provider);
|
|
115
|
+
logger.info("[Compaction] Stage 3 (summarize)", {
|
|
116
|
+
requestId,
|
|
117
|
+
ran: summarizeResult.summarized,
|
|
118
|
+
tokensBefore: stageTokensBefore,
|
|
119
|
+
tokensAfter: stageTokensAfter,
|
|
120
|
+
saved: stageTokensBefore - stageTokensAfter,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
125
|
+
logger.warn("[Compaction] Stage 3 (summarize) FAILED", {
|
|
126
|
+
requestId,
|
|
127
|
+
error: err.message,
|
|
128
|
+
errorName: err.name,
|
|
129
|
+
tokensBefore: stageTokensBefore,
|
|
130
|
+
tokensAfter: stageTokensBefore,
|
|
131
|
+
saved: 0,
|
|
132
|
+
});
|
|
133
|
+
// Record failure on the compaction span for trace visibility
|
|
134
|
+
SpanSerializer.updateAttributes(span, {
|
|
135
|
+
"compaction.stage3.error": err.message,
|
|
136
|
+
"compaction.stage3.errorName": err.name,
|
|
137
|
+
"compaction.stage3.tokensBefore": stageTokensBefore,
|
|
138
|
+
"compaction.stage3_failed": true,
|
|
143
139
|
});
|
|
140
|
+
// Fall through to Stage 4 truncation as before
|
|
144
141
|
}
|
|
145
|
-
// Fall through to Stage 4 truncation as before
|
|
146
142
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
143
|
+
// Stage 4: Sliding Window Truncation (fallback)
|
|
144
|
+
if (this.config.enableTruncate &&
|
|
145
|
+
estimateMessagesTokens(currentMessages, provider) > targetTokens) {
|
|
146
|
+
const stageTokensBefore = estimateMessagesTokens(currentMessages, provider);
|
|
147
|
+
const truncResult = truncateWithSlidingWindow(currentMessages, {
|
|
148
|
+
fraction: this.config.truncationFraction,
|
|
149
|
+
});
|
|
150
|
+
if (truncResult.truncated) {
|
|
151
|
+
currentMessages = truncResult.messages;
|
|
152
|
+
stagesUsed.push("truncate");
|
|
153
|
+
}
|
|
154
|
+
const stageTokensAfter = estimateMessagesTokens(currentMessages, provider);
|
|
155
|
+
logger.info("[Compaction] Stage 4 (truncate)", {
|
|
156
|
+
requestId,
|
|
157
|
+
ran: truncResult.truncated,
|
|
158
|
+
tokensBefore: stageTokensBefore,
|
|
159
|
+
tokensAfter: stageTokensAfter,
|
|
160
|
+
saved: stageTokensBefore - stageTokensAfter,
|
|
161
|
+
});
|
|
163
162
|
}
|
|
164
|
-
const
|
|
165
|
-
logger.info("[Compaction]
|
|
163
|
+
const tokensAfter = estimateMessagesTokens(currentMessages, provider);
|
|
164
|
+
logger.info("[Compaction] Complete", {
|
|
166
165
|
requestId,
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
tokensBefore,
|
|
167
|
+
tokensAfter,
|
|
168
|
+
totalSaved: tokensBefore - tokensAfter,
|
|
169
|
+
stagesUsed,
|
|
170
|
+
durationMs: Date.now() - spanStartTime,
|
|
171
171
|
});
|
|
172
|
+
const result = {
|
|
173
|
+
compacted: stagesUsed.length > 0,
|
|
174
|
+
stagesUsed,
|
|
175
|
+
tokensBefore,
|
|
176
|
+
tokensAfter,
|
|
177
|
+
tokensSaved: tokensBefore - tokensAfter,
|
|
178
|
+
messages: currentMessages,
|
|
179
|
+
};
|
|
180
|
+
span.durationMs = Date.now() - spanStartTime;
|
|
181
|
+
const endedSpan = SpanSerializer.endSpan(SpanSerializer.updateAttributes(span, {
|
|
182
|
+
"context.stage": stagesUsed.join(",") || "none",
|
|
183
|
+
"context.tokensBefore": tokensBefore,
|
|
184
|
+
"context.tokensAfter": tokensAfter,
|
|
185
|
+
"context.tokensSaved": tokensBefore - tokensAfter,
|
|
186
|
+
}), SpanStatus.OK);
|
|
187
|
+
getMetricsAggregator().recordSpan(endedSpan);
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
span.durationMs = Date.now() - spanStartTime;
|
|
192
|
+
const endedSpan = SpanSerializer.endSpan(span, SpanStatus.ERROR);
|
|
193
|
+
endedSpan.statusMessage =
|
|
194
|
+
error instanceof Error ? error.message : String(error);
|
|
195
|
+
getMetricsAggregator().recordSpan(endedSpan);
|
|
196
|
+
throw error;
|
|
172
197
|
}
|
|
173
|
-
const tokensAfter = estimateMessagesTokens(currentMessages, provider);
|
|
174
|
-
logger.info("[Compaction] Complete", {
|
|
175
|
-
requestId,
|
|
176
|
-
tokensBefore,
|
|
177
|
-
tokensAfter,
|
|
178
|
-
totalSaved: tokensBefore - tokensAfter,
|
|
179
|
-
stagesUsed,
|
|
180
|
-
durationMs: Date.now() - compactionStartTime,
|
|
181
|
-
});
|
|
182
|
-
return {
|
|
183
|
-
compacted: stagesUsed.length > 0,
|
|
184
|
-
stagesUsed,
|
|
185
|
-
tokensBefore,
|
|
186
|
-
tokensAfter,
|
|
187
|
-
tokensSaved: tokensBefore - tokensAfter,
|
|
188
|
-
messages: currentMessages,
|
|
189
|
-
};
|
|
190
198
|
}
|
|
191
199
|
}
|
|
192
200
|
//# sourceMappingURL=contextCompactor.js.map
|
|
@@ -24,6 +24,11 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
24
24
|
protected sessionId?: string;
|
|
25
25
|
protected userId?: string;
|
|
26
26
|
protected neurolink?: NeuroLink;
|
|
27
|
+
/** Trace context propagated from NeuroLink SDK for span hierarchy */
|
|
28
|
+
_traceContext: {
|
|
29
|
+
traceId: string;
|
|
30
|
+
parentSpanId: string;
|
|
31
|
+
} | null;
|
|
27
32
|
private readonly messageBuilder;
|
|
28
33
|
private readonly streamHandler;
|
|
29
34
|
private readonly generationHandler;
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
+
import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
1
2
|
import { generateText } from "ai";
|
|
2
|
-
import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
|
|
3
|
-
import { tracers } from "../telemetry/tracers.js";
|
|
4
3
|
import { directAgentTools } from "../agent/directTools.js";
|
|
5
4
|
import { IMAGE_GENERATION_MODELS } from "../core/constants.js";
|
|
6
5
|
import { MiddlewareFactory } from "../middleware/factory.js";
|
|
6
|
+
import { ATTR, tracers } from "../telemetry/index.js";
|
|
7
7
|
import { isAbortError } from "../utils/errorHandling.js";
|
|
8
8
|
import { logger } from "../utils/logger.js";
|
|
9
|
-
import { calculateCost } from "../utils/pricing.js";
|
|
10
9
|
import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
|
|
11
10
|
import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
|
|
12
11
|
import { getKeyCount, getKeysAsString } from "../utils/transformationUtils.js";
|
|
13
12
|
import { TTSProcessor } from "../utils/ttsProcessor.js";
|
|
14
|
-
import {
|
|
13
|
+
import { executeVideoAnalysis, hasVideoFrames, } from "../utils/videoAnalysisProcessor.js";
|
|
15
14
|
import { GenerationHandler } from "./modules/GenerationHandler.js";
|
|
16
15
|
// Import modules for composition
|
|
17
16
|
import { MessageBuilder } from "./modules/MessageBuilder.js";
|
|
@@ -19,7 +18,6 @@ import { StreamHandler } from "./modules/StreamHandler.js";
|
|
|
19
18
|
import { TelemetryHandler } from "./modules/TelemetryHandler.js";
|
|
20
19
|
import { ToolsManager } from "./modules/ToolsManager.js";
|
|
21
20
|
import { Utilities } from "./modules/Utilities.js";
|
|
22
|
-
const providerTracer = tracers.provider;
|
|
23
21
|
/**
|
|
24
22
|
* Abstract base class for all AI providers
|
|
25
23
|
* Tools are integrated as first-class citizens - always available by default
|
|
@@ -39,6 +37,8 @@ export class BaseProvider {
|
|
|
39
37
|
sessionId;
|
|
40
38
|
userId;
|
|
41
39
|
neurolink; // Reference to actual NeuroLink instance for MCP tools
|
|
40
|
+
/** Trace context propagated from NeuroLink SDK for span hierarchy */
|
|
41
|
+
_traceContext = null;
|
|
42
42
|
// Composition modules - Single Responsibility Principle
|
|
43
43
|
messageBuilder;
|
|
44
44
|
streamHandler;
|
|
@@ -80,10 +80,18 @@ export class BaseProvider {
|
|
|
80
80
|
* When tools are involved, falls back to generate() with synthetic streaming
|
|
81
81
|
*/
|
|
82
82
|
async stream(optionsOrPrompt, analysisSchema) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
let options = this.normalizeStreamOptions(optionsOrPrompt);
|
|
84
|
+
// OTEL span for provider-level stream tracing
|
|
85
|
+
const otelStreamSpan = tracers.provider.startSpan("neurolink.provider.stream", {
|
|
86
|
+
kind: SpanKind.CLIENT,
|
|
87
|
+
attributes: {
|
|
88
|
+
[ATTR.GEN_AI_SYSTEM]: this.providerName || "unknown",
|
|
89
|
+
[ATTR.GEN_AI_MODEL]: this.modelName || options.model || "unknown",
|
|
90
|
+
[ATTR.GEN_AI_OPERATION]: "stream",
|
|
91
|
+
[ATTR.NL_PROVIDER]: this.providerName || "unknown",
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
try {
|
|
87
95
|
logger.info(`Starting stream`, {
|
|
88
96
|
provider: this.providerName,
|
|
89
97
|
hasTools: !options.disableTools && this.supportsTools(),
|
|
@@ -94,93 +102,91 @@ export class BaseProvider {
|
|
|
94
102
|
temperature: options.temperature,
|
|
95
103
|
timestamp: Date.now(),
|
|
96
104
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (hasVideoFrames(messages)) {
|
|
106
|
-
logger.info(`Video frames detected in stream, using fake streaming for video analysis`, {
|
|
107
|
-
provider: this.providerName,
|
|
108
|
-
model: this.modelName,
|
|
109
|
-
});
|
|
110
|
-
span.setAttribute("neurolink.stream_mode", "fake");
|
|
111
|
-
return await this.executeFakeStreaming(options, analysisSchema);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// Image generation models don't support real streaming
|
|
115
|
-
// Force fake streaming for image models to ensure image output is yielded
|
|
116
|
-
const isImageModel = IMAGE_GENERATION_MODELS.some((m) => this.modelName.includes(m));
|
|
117
|
-
if (isImageModel) {
|
|
118
|
-
logger.info(`Image model detected, forcing fake streaming`, {
|
|
105
|
+
// ===== EARLY MULTIMODAL DETECTION =====
|
|
106
|
+
const hasFileInput = !!options.input?.files?.length || !!options.input?.videoFiles?.length;
|
|
107
|
+
if (hasFileInput) {
|
|
108
|
+
// ===== VIDEO ANALYSIS DETECTION =====
|
|
109
|
+
// Check if video frames are present and handle with fake streaming
|
|
110
|
+
const messages = await this.buildMessagesForStream(options);
|
|
111
|
+
if (hasVideoFrames(messages)) {
|
|
112
|
+
logger.info(`Video frames detected in stream, using fake streaming for video analysis`, {
|
|
119
113
|
provider: this.providerName,
|
|
120
114
|
model: this.modelName,
|
|
121
|
-
reason: "Image generation requires fake streaming to yield image output",
|
|
122
115
|
});
|
|
123
|
-
// Skip real streaming, go directly to fake streaming
|
|
124
|
-
span.setAttribute("neurolink.stream_mode", "fake");
|
|
125
116
|
return await this.executeFakeStreaming(options, analysisSchema);
|
|
126
117
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
118
|
+
}
|
|
119
|
+
// CRITICAL: Image generation models don't support real streaming
|
|
120
|
+
// Force fake streaming for image models to ensure image output is yielded
|
|
121
|
+
const isImageModel = IMAGE_GENERATION_MODELS.some((m) => this.modelName.includes(m));
|
|
122
|
+
if (isImageModel) {
|
|
123
|
+
logger.info(`Image model detected, forcing fake streaming`, {
|
|
124
|
+
provider: this.providerName,
|
|
125
|
+
model: this.modelName,
|
|
126
|
+
reason: "Image generation requires fake streaming to yield image output",
|
|
127
|
+
});
|
|
128
|
+
// Skip real streaming, go directly to fake streaming
|
|
129
|
+
return await this.executeFakeStreaming(options, analysisSchema);
|
|
130
|
+
}
|
|
131
|
+
// Central tool merge: Pre-merge base tools (MCP/built-in) with user-provided
|
|
132
|
+
// tools (e.g. RAG tools) into options.tools. This way, every provider's
|
|
133
|
+
// executeStream() can simply use options.tools (or getAllTools() + options.tools)
|
|
134
|
+
// and get the complete tool set without needing per-provider merge logic.
|
|
135
|
+
if (!options.disableTools && this.supportsTools()) {
|
|
136
|
+
const mergedTools = await this.getToolsForStream(options);
|
|
137
|
+
options = { ...options, tools: mergedTools };
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
options = { ...options, tools: {} };
|
|
141
|
+
}
|
|
142
|
+
// CRITICAL FIX: Always prefer real streaming over fake streaming
|
|
143
|
+
// Try real streaming first, use fake streaming only as fallback
|
|
144
|
+
try {
|
|
145
|
+
logger.debug(`Attempting real streaming`, {
|
|
146
|
+
provider: this.providerName,
|
|
147
|
+
timestamp: Date.now(),
|
|
148
|
+
});
|
|
149
|
+
const realStreamResult = await this.executeStream(options, analysisSchema);
|
|
150
|
+
logger.info(`Real streaming succeeded`, {
|
|
151
|
+
provider: this.providerName,
|
|
152
|
+
timestamp: Date.now(),
|
|
153
|
+
});
|
|
154
|
+
// If real streaming succeeds, return it (with tools support via Vercel AI SDK)
|
|
155
|
+
return realStreamResult;
|
|
156
|
+
}
|
|
157
|
+
catch (realStreamError) {
|
|
158
|
+
logger.warn(`Real streaming failed for ${this.providerName}, falling back to fake streaming:`, {
|
|
159
|
+
error: realStreamError instanceof Error
|
|
160
|
+
? realStreamError.message
|
|
161
|
+
: String(realStreamError),
|
|
162
|
+
timestamp: Date.now(),
|
|
163
|
+
});
|
|
164
|
+
// Fallback to fake streaming only if real streaming fails AND tools are enabled
|
|
131
165
|
if (!options.disableTools && this.supportsTools()) {
|
|
132
|
-
|
|
133
|
-
options = { ...options, tools: mergedTools };
|
|
166
|
+
return await this.executeFakeStreaming(options, analysisSchema);
|
|
134
167
|
}
|
|
135
168
|
else {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// Try real streaming first, use fake streaming only as fallback
|
|
140
|
-
try {
|
|
141
|
-
logger.debug(`Attempting real streaming`, {
|
|
142
|
-
provider: this.providerName,
|
|
143
|
-
timestamp: Date.now(),
|
|
144
|
-
});
|
|
145
|
-
const realStreamResult = await this.executeStream(options, analysisSchema);
|
|
146
|
-
logger.info(`Real streaming succeeded`, {
|
|
147
|
-
provider: this.providerName,
|
|
148
|
-
timestamp: Date.now(),
|
|
149
|
-
});
|
|
150
|
-
span.setAttribute("neurolink.stream_mode", "real");
|
|
151
|
-
// If real streaming succeeds, return it (with tools support via Vercel AI SDK)
|
|
152
|
-
return realStreamResult;
|
|
153
|
-
}
|
|
154
|
-
catch (realStreamError) {
|
|
155
|
-
logger.warn(`Real streaming failed for ${this.providerName}, falling back to fake streaming:`, {
|
|
156
|
-
error: realStreamError instanceof Error
|
|
157
|
-
? realStreamError.message
|
|
158
|
-
: String(realStreamError),
|
|
159
|
-
timestamp: Date.now(),
|
|
160
|
-
});
|
|
161
|
-
// Fallback to fake streaming only if real streaming fails AND tools are enabled
|
|
162
|
-
if (!options.disableTools && this.supportsTools()) {
|
|
163
|
-
span.setAttribute("neurolink.stream_mode", "fake");
|
|
164
|
-
return await this.executeFakeStreaming(options, analysisSchema);
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
// If real streaming failed and no tools are enabled, re-throw the original error
|
|
168
|
-
logger.error(`Real streaming failed for ${this.providerName}:`, realStreamError);
|
|
169
|
-
throw this.handleProviderError(realStreamError);
|
|
170
|
-
}
|
|
169
|
+
// If real streaming failed and no tools are enabled, re-throw the original error
|
|
170
|
+
logger.error(`Real streaming failed for ${this.providerName}:`, realStreamError);
|
|
171
|
+
throw this.handleProviderError(realStreamError);
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
otelStreamSpan.setStatus({
|
|
177
|
+
code: SpanStatusCode.ERROR,
|
|
178
|
+
message: error instanceof Error ? error.message : String(error),
|
|
179
|
+
});
|
|
180
|
+
otelStreamSpan.end();
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
// End OTEL span on success (only if not already ended via error path)
|
|
185
|
+
if (otelStreamSpan.isRecording()) {
|
|
186
|
+
otelStreamSpan.setStatus({ code: SpanStatusCode.OK });
|
|
187
|
+
otelStreamSpan.end();
|
|
182
188
|
}
|
|
183
|
-
}
|
|
189
|
+
}
|
|
184
190
|
}
|
|
185
191
|
/**
|
|
186
192
|
* Execute fake streaming - extracted method for reusability
|
|
@@ -468,12 +474,24 @@ export class BaseProvider {
|
|
|
468
474
|
* for consistency and better performance
|
|
469
475
|
*/
|
|
470
476
|
async generate(optionsOrPrompt, _analysisSchema) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
+
const options = this.normalizeTextOptions(optionsOrPrompt);
|
|
478
|
+
this.validateOptions(options);
|
|
479
|
+
const startTime = Date.now();
|
|
480
|
+
// OTEL span for provider-level generate tracing
|
|
481
|
+
// Use startActiveSpan pattern via context.with() so child spans become descendants
|
|
482
|
+
const otelSpan = tracers.provider.startSpan("neurolink.provider.generate", {
|
|
483
|
+
kind: SpanKind.CLIENT,
|
|
484
|
+
attributes: {
|
|
485
|
+
[ATTR.GEN_AI_SYSTEM]: this.providerName || "unknown",
|
|
486
|
+
[ATTR.GEN_AI_MODEL]: this.modelName || options.model || "unknown",
|
|
487
|
+
[ATTR.GEN_AI_OPERATION]: "generate",
|
|
488
|
+
[ATTR.NL_PROVIDER]: this.providerName || "unknown",
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
// Set this span as the active context so child spans (GenerationHandler, etc.) become descendants
|
|
492
|
+
const activeCtx = trace.setSpan(context.active(), otelSpan);
|
|
493
|
+
let otelSpanEnded = false;
|
|
494
|
+
return await context.with(activeCtx, async () => {
|
|
477
495
|
try {
|
|
478
496
|
// ===== VIDEO GENERATION MODE =====
|
|
479
497
|
// Generate video from image + prompt using Veo 3.1
|
|
@@ -647,29 +665,15 @@ export class BaseProvider {
|
|
|
647
665
|
});
|
|
648
666
|
}
|
|
649
667
|
}
|
|
650
|
-
// Set token usage on span from the result
|
|
651
|
-
if (enhancedResult?.usage) {
|
|
652
|
-
span.setAttribute("gen_ai.usage.input_tokens", enhancedResult.usage.input || 0);
|
|
653
|
-
span.setAttribute("gen_ai.usage.output_tokens", enhancedResult.usage.output || 0);
|
|
654
|
-
// Cost on span so users can query "what did this trace cost?"
|
|
655
|
-
const cost = calculateCost(this.providerName, this.modelName, {
|
|
656
|
-
input: enhancedResult.usage.input || 0,
|
|
657
|
-
output: enhancedResult.usage.output || 0,
|
|
658
|
-
total: enhancedResult.usage.total || 0,
|
|
659
|
-
});
|
|
660
|
-
span.setAttribute("neurolink.cost", cost ?? 0);
|
|
661
|
-
}
|
|
662
|
-
if (enhancedResult?.finishReason) {
|
|
663
|
-
span.setAttribute("gen_ai.response.finish_reason", enhancedResult.finishReason);
|
|
664
|
-
}
|
|
665
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
666
668
|
return await this.enhanceResult(enhancedResult, options, startTime);
|
|
667
669
|
}
|
|
668
670
|
catch (error) {
|
|
669
|
-
|
|
671
|
+
otelSpan.setStatus({
|
|
670
672
|
code: SpanStatusCode.ERROR,
|
|
671
673
|
message: error instanceof Error ? error.message : String(error),
|
|
672
674
|
});
|
|
675
|
+
otelSpan.end();
|
|
676
|
+
otelSpanEnded = true;
|
|
673
677
|
// Abort errors are expected when a generation is cancelled — log at info, not error
|
|
674
678
|
if (isAbortError(error)) {
|
|
675
679
|
logger.info(`Generate aborted for ${this.providerName}`, {
|
|
@@ -682,9 +686,12 @@ export class BaseProvider {
|
|
|
682
686
|
throw this.handleProviderError(error);
|
|
683
687
|
}
|
|
684
688
|
finally {
|
|
685
|
-
|
|
689
|
+
if (!otelSpanEnded) {
|
|
690
|
+
otelSpan.setStatus({ code: SpanStatusCode.OK });
|
|
691
|
+
otelSpan.end();
|
|
692
|
+
}
|
|
686
693
|
}
|
|
687
|
-
});
|
|
694
|
+
}); // end context.with
|
|
688
695
|
}
|
|
689
696
|
/**
|
|
690
697
|
* Alias for generate method - implements AIProvider interface
|