@juspay/neurolink 9.23.0 → 9.25.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/CHANGELOG.md +12 -0
- package/README.md +10 -13
- 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.d.ts +34 -0
- package/dist/cli/factories/commandFactory.js +321 -116
- 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 +158 -102
- 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/index.d.ts +1 -1
- package/dist/features/ppt/index.js +1 -1
- 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 +158 -102
- 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/index.d.ts +1 -1
- package/dist/lib/features/ppt/index.js +1 -1
- 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 +984 -566
- 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 +200 -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 +553 -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 +288 -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/spanSerializer.d.ts +115 -0
- package/dist/lib/observability/utils/spanSerializer.js +287 -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 +80 -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/cli.d.ts +18 -2
- package/dist/lib/types/conversationMemoryInterface.d.ts +2 -0
- package/dist/lib/types/generateTypes.d.ts +2 -2
- 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 +984 -566
- 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 +199 -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 +552 -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 +287 -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/spanSerializer.d.ts +115 -0
- package/dist/observability/utils/spanSerializer.js +286 -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 +80 -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/cli.d.ts +18 -2
- package/dist/types/conversationMemoryInterface.d.ts +2 -0
- package/dist/types/generateTypes.d.ts +2 -2
- 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
|
@@ -1,9 +1,12 @@
|
|
|
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 { getMetricsAggregator } from "../observability/metricsAggregator.js";
|
|
7
|
+
import { SpanStatus, SpanType } from "../observability/types/spanTypes.js";
|
|
8
|
+
import { SpanSerializer } from "../observability/utils/spanSerializer.js";
|
|
9
|
+
import { ATTR, tracers } from "../telemetry/index.js";
|
|
7
10
|
import { isAbortError } from "../utils/errorHandling.js";
|
|
8
11
|
import { logger } from "../utils/logger.js";
|
|
9
12
|
import { calculateCost } from "../utils/pricing.js";
|
|
@@ -11,7 +14,7 @@ import { composeAbortSignals, createTimeoutController, TimeoutError, } from "../
|
|
|
11
14
|
import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
|
|
12
15
|
import { getKeyCount, getKeysAsString } from "../utils/transformationUtils.js";
|
|
13
16
|
import { TTSProcessor } from "../utils/ttsProcessor.js";
|
|
14
|
-
import {
|
|
17
|
+
import { executeVideoAnalysis, hasVideoFrames, } from "../utils/videoAnalysisProcessor.js";
|
|
15
18
|
import { GenerationHandler } from "./modules/GenerationHandler.js";
|
|
16
19
|
// Import modules for composition
|
|
17
20
|
import { MessageBuilder } from "./modules/MessageBuilder.js";
|
|
@@ -19,7 +22,6 @@ import { StreamHandler } from "./modules/StreamHandler.js";
|
|
|
19
22
|
import { TelemetryHandler } from "./modules/TelemetryHandler.js";
|
|
20
23
|
import { ToolsManager } from "./modules/ToolsManager.js";
|
|
21
24
|
import { Utilities } from "./modules/Utilities.js";
|
|
22
|
-
const providerTracer = tracers.provider;
|
|
23
25
|
/**
|
|
24
26
|
* Abstract base class for all AI providers
|
|
25
27
|
* Tools are integrated as first-class citizens - always available by default
|
|
@@ -39,6 +41,8 @@ export class BaseProvider {
|
|
|
39
41
|
sessionId;
|
|
40
42
|
userId;
|
|
41
43
|
neurolink; // Reference to actual NeuroLink instance for MCP tools
|
|
44
|
+
/** Trace context propagated from NeuroLink SDK for span hierarchy */
|
|
45
|
+
_traceContext = null;
|
|
42
46
|
// Composition modules - Single Responsibility Principle
|
|
43
47
|
messageBuilder;
|
|
44
48
|
streamHandler;
|
|
@@ -80,10 +84,26 @@ export class BaseProvider {
|
|
|
80
84
|
* When tools are involved, falls back to generate() with synthetic streaming
|
|
81
85
|
*/
|
|
82
86
|
async stream(optionsOrPrompt, analysisSchema) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
let options = this.normalizeStreamOptions(optionsOrPrompt);
|
|
88
|
+
// Observability: create metrics span for provider.stream
|
|
89
|
+
const metricsSpan = SpanSerializer.createSpan(SpanType.MODEL_GENERATION, "provider.stream", {
|
|
90
|
+
"ai.provider": this.providerName || "unknown",
|
|
91
|
+
"ai.model": this.modelName || options.model || "unknown",
|
|
92
|
+
"ai.temperature": options.temperature,
|
|
93
|
+
"ai.max_tokens": options.maxTokens,
|
|
94
|
+
}, this._traceContext?.parentSpanId, this._traceContext?.traceId);
|
|
95
|
+
let metricsSpanRecorded = false;
|
|
96
|
+
// OTEL span for provider-level stream tracing
|
|
97
|
+
const otelStreamSpan = tracers.provider.startSpan("neurolink.provider.stream", {
|
|
98
|
+
kind: SpanKind.CLIENT,
|
|
99
|
+
attributes: {
|
|
100
|
+
[ATTR.GEN_AI_SYSTEM]: this.providerName || "unknown",
|
|
101
|
+
[ATTR.GEN_AI_MODEL]: this.modelName || options.model || "unknown",
|
|
102
|
+
[ATTR.GEN_AI_OPERATION]: "stream",
|
|
103
|
+
[ATTR.NL_PROVIDER]: this.providerName || "unknown",
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
try {
|
|
87
107
|
logger.info(`Starting stream`, {
|
|
88
108
|
provider: this.providerName,
|
|
89
109
|
hasTools: !options.disableTools && this.supportsTools(),
|
|
@@ -94,93 +114,97 @@ export class BaseProvider {
|
|
|
94
114
|
temperature: options.temperature,
|
|
95
115
|
timestamp: Date.now(),
|
|
96
116
|
});
|
|
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`, {
|
|
117
|
+
// ===== EARLY MULTIMODAL DETECTION =====
|
|
118
|
+
const hasFileInput = !!options.input?.files?.length || !!options.input?.videoFiles?.length;
|
|
119
|
+
if (hasFileInput) {
|
|
120
|
+
// ===== VIDEO ANALYSIS DETECTION =====
|
|
121
|
+
// Check if video frames are present and handle with fake streaming
|
|
122
|
+
const messages = await this.buildMessagesForStream(options);
|
|
123
|
+
if (hasVideoFrames(messages)) {
|
|
124
|
+
logger.info(`Video frames detected in stream, using fake streaming for video analysis`, {
|
|
119
125
|
provider: this.providerName,
|
|
120
126
|
model: this.modelName,
|
|
121
|
-
reason: "Image generation requires fake streaming to yield image output",
|
|
122
127
|
});
|
|
123
|
-
// Skip real streaming, go directly to fake streaming
|
|
124
|
-
span.setAttribute("neurolink.stream_mode", "fake");
|
|
125
128
|
return await this.executeFakeStreaming(options, analysisSchema);
|
|
126
129
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
}
|
|
131
|
+
// CRITICAL: Image generation models don't support real streaming
|
|
132
|
+
// Force fake streaming for image models to ensure image output is yielded
|
|
133
|
+
const isImageModel = IMAGE_GENERATION_MODELS.some((m) => this.modelName.includes(m));
|
|
134
|
+
if (isImageModel) {
|
|
135
|
+
logger.info(`Image model detected, forcing fake streaming`, {
|
|
136
|
+
provider: this.providerName,
|
|
137
|
+
model: this.modelName,
|
|
138
|
+
reason: "Image generation requires fake streaming to yield image output",
|
|
139
|
+
});
|
|
140
|
+
// Skip real streaming, go directly to fake streaming
|
|
141
|
+
return await this.executeFakeStreaming(options, analysisSchema);
|
|
142
|
+
}
|
|
143
|
+
// Central tool merge: Pre-merge base tools (MCP/built-in) with user-provided
|
|
144
|
+
// tools (e.g. RAG tools) into options.tools. This way, every provider's
|
|
145
|
+
// executeStream() can simply use options.tools (or getAllTools() + options.tools)
|
|
146
|
+
// and get the complete tool set without needing per-provider merge logic.
|
|
147
|
+
if (!options.disableTools && this.supportsTools()) {
|
|
148
|
+
const mergedTools = await this.getToolsForStream(options);
|
|
149
|
+
options = { ...options, tools: mergedTools };
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
options = { ...options, tools: {} };
|
|
153
|
+
}
|
|
154
|
+
// CRITICAL FIX: Always prefer real streaming over fake streaming
|
|
155
|
+
// Try real streaming first, use fake streaming only as fallback
|
|
156
|
+
try {
|
|
157
|
+
logger.debug(`Attempting real streaming`, {
|
|
158
|
+
provider: this.providerName,
|
|
159
|
+
timestamp: Date.now(),
|
|
160
|
+
});
|
|
161
|
+
const realStreamResult = await this.executeStream(options, analysisSchema);
|
|
162
|
+
logger.info(`Real streaming succeeded`, {
|
|
163
|
+
provider: this.providerName,
|
|
164
|
+
timestamp: Date.now(),
|
|
165
|
+
});
|
|
166
|
+
// If real streaming succeeds, return it (with tools support via Vercel AI SDK)
|
|
167
|
+
return realStreamResult;
|
|
168
|
+
}
|
|
169
|
+
catch (realStreamError) {
|
|
170
|
+
logger.warn(`Real streaming failed for ${this.providerName}, falling back to fake streaming:`, {
|
|
171
|
+
error: realStreamError instanceof Error
|
|
172
|
+
? realStreamError.message
|
|
173
|
+
: String(realStreamError),
|
|
174
|
+
timestamp: Date.now(),
|
|
175
|
+
});
|
|
176
|
+
// Fallback to fake streaming only if real streaming fails AND tools are enabled
|
|
131
177
|
if (!options.disableTools && this.supportsTools()) {
|
|
132
|
-
|
|
133
|
-
options = { ...options, tools: mergedTools };
|
|
178
|
+
return await this.executeFakeStreaming(options, analysisSchema);
|
|
134
179
|
}
|
|
135
180
|
else {
|
|
136
|
-
|
|
181
|
+
// If real streaming failed and no tools are enabled, re-throw the original error
|
|
182
|
+
logger.error(`Real streaming failed for ${this.providerName}:`, realStreamError);
|
|
183
|
+
throw this.handleProviderError(realStreamError);
|
|
137
184
|
}
|
|
138
|
-
// CRITICAL FIX: Always prefer real streaming over fake streaming
|
|
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
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
span.setStatus({
|
|
175
|
-
code: SpanStatusCode.ERROR,
|
|
176
|
-
message: error instanceof Error ? error.message : String(error),
|
|
177
|
-
});
|
|
178
|
-
throw error;
|
|
179
185
|
}
|
|
180
|
-
|
|
181
|
-
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
// Observability: record failed stream span
|
|
189
|
+
metricsSpanRecorded = true;
|
|
190
|
+
const endedStreamSpan = SpanSerializer.endSpan(metricsSpan, SpanStatus.ERROR, error instanceof Error ? error.message : String(error));
|
|
191
|
+
getMetricsAggregator().recordSpan(endedStreamSpan);
|
|
192
|
+
otelStreamSpan.setStatus({
|
|
193
|
+
code: SpanStatusCode.ERROR,
|
|
194
|
+
message: error instanceof Error ? error.message : String(error),
|
|
195
|
+
});
|
|
196
|
+
otelStreamSpan.end();
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
// Observability: record successful stream span (only if not already ended via error path)
|
|
201
|
+
if (!metricsSpanRecorded) {
|
|
202
|
+
const endedStreamSpan = SpanSerializer.endSpan(metricsSpan, SpanStatus.OK);
|
|
203
|
+
getMetricsAggregator().recordSpan(endedStreamSpan);
|
|
204
|
+
otelStreamSpan.setStatus({ code: SpanStatusCode.OK });
|
|
205
|
+
otelStreamSpan.end();
|
|
182
206
|
}
|
|
183
|
-
}
|
|
207
|
+
}
|
|
184
208
|
}
|
|
185
209
|
/**
|
|
186
210
|
* Execute fake streaming - extracted method for reusability
|
|
@@ -468,12 +492,31 @@ export class BaseProvider {
|
|
|
468
492
|
* for consistency and better performance
|
|
469
493
|
*/
|
|
470
494
|
async generate(optionsOrPrompt, _analysisSchema) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
495
|
+
const options = this.normalizeTextOptions(optionsOrPrompt);
|
|
496
|
+
this.validateOptions(options);
|
|
497
|
+
const startTime = Date.now();
|
|
498
|
+
// Observability: create metrics span for provider.generate
|
|
499
|
+
const metricsSpan = SpanSerializer.createSpan(SpanType.MODEL_GENERATION, "provider.generate", {
|
|
500
|
+
"ai.provider": this.providerName || "unknown",
|
|
501
|
+
"ai.model": this.modelName || options.model || "unknown",
|
|
502
|
+
"ai.temperature": options.temperature,
|
|
503
|
+
"ai.max_tokens": options.maxTokens,
|
|
504
|
+
}, this._traceContext?.parentSpanId, this._traceContext?.traceId);
|
|
505
|
+
// OTEL span for provider-level generate tracing
|
|
506
|
+
// Use startActiveSpan pattern via context.with() so child spans become descendants
|
|
507
|
+
const otelSpan = tracers.provider.startSpan("neurolink.provider.generate", {
|
|
508
|
+
kind: SpanKind.CLIENT,
|
|
509
|
+
attributes: {
|
|
510
|
+
[ATTR.GEN_AI_SYSTEM]: this.providerName || "unknown",
|
|
511
|
+
[ATTR.GEN_AI_MODEL]: this.modelName || options.model || "unknown",
|
|
512
|
+
[ATTR.GEN_AI_OPERATION]: "generate",
|
|
513
|
+
[ATTR.NL_PROVIDER]: this.providerName || "unknown",
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
// Set this span as the active context so child spans (GenerationHandler, etc.) become descendants
|
|
517
|
+
const activeCtx = trace.setSpan(context.active(), otelSpan);
|
|
518
|
+
let otelSpanEnded = false;
|
|
519
|
+
return await context.with(activeCtx, async () => {
|
|
477
520
|
try {
|
|
478
521
|
// ===== VIDEO GENERATION MODE =====
|
|
479
522
|
// Generate video from image + prompt using Veo 3.1
|
|
@@ -647,29 +690,39 @@ export class BaseProvider {
|
|
|
647
690
|
});
|
|
648
691
|
}
|
|
649
692
|
}
|
|
650
|
-
//
|
|
693
|
+
// Observability: record successful generate span with token/cost data
|
|
694
|
+
let enrichedGenerateSpan = { ...metricsSpan };
|
|
651
695
|
if (enhancedResult?.usage) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
696
|
+
enrichedGenerateSpan = SpanSerializer.enrichWithTokenUsage(enrichedGenerateSpan, {
|
|
697
|
+
promptTokens: enhancedResult.usage.input || 0,
|
|
698
|
+
completionTokens: enhancedResult.usage.output || 0,
|
|
699
|
+
totalTokens: enhancedResult.usage.total || 0,
|
|
700
|
+
});
|
|
655
701
|
const cost = calculateCost(this.providerName, this.modelName, {
|
|
656
702
|
input: enhancedResult.usage.input || 0,
|
|
657
703
|
output: enhancedResult.usage.output || 0,
|
|
658
704
|
total: enhancedResult.usage.total || 0,
|
|
659
705
|
});
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
706
|
+
if (cost && cost > 0) {
|
|
707
|
+
enrichedGenerateSpan = SpanSerializer.enrichWithCost(enrichedGenerateSpan, {
|
|
708
|
+
totalCost: cost,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
664
711
|
}
|
|
665
|
-
|
|
712
|
+
const endedGenerateSpan = SpanSerializer.endSpan(enrichedGenerateSpan, SpanStatus.OK);
|
|
713
|
+
getMetricsAggregator().recordSpan(endedGenerateSpan);
|
|
666
714
|
return await this.enhanceResult(enhancedResult, options, startTime);
|
|
667
715
|
}
|
|
668
716
|
catch (error) {
|
|
669
|
-
span
|
|
717
|
+
// Observability: record failed generate span
|
|
718
|
+
const endedGenerateSpan = SpanSerializer.endSpan(metricsSpan, SpanStatus.ERROR, error instanceof Error ? error.message : String(error));
|
|
719
|
+
getMetricsAggregator().recordSpan(endedGenerateSpan);
|
|
720
|
+
otelSpan.setStatus({
|
|
670
721
|
code: SpanStatusCode.ERROR,
|
|
671
722
|
message: error instanceof Error ? error.message : String(error),
|
|
672
723
|
});
|
|
724
|
+
otelSpan.end();
|
|
725
|
+
otelSpanEnded = true;
|
|
673
726
|
// Abort errors are expected when a generation is cancelled — log at info, not error
|
|
674
727
|
if (isAbortError(error)) {
|
|
675
728
|
logger.info(`Generate aborted for ${this.providerName}`, {
|
|
@@ -682,9 +735,12 @@ export class BaseProvider {
|
|
|
682
735
|
throw this.handleProviderError(error);
|
|
683
736
|
}
|
|
684
737
|
finally {
|
|
685
|
-
|
|
738
|
+
if (!otelSpanEnded) {
|
|
739
|
+
otelSpan.setStatus({ code: SpanStatusCode.OK });
|
|
740
|
+
otelSpan.end();
|
|
741
|
+
}
|
|
686
742
|
}
|
|
687
|
-
});
|
|
743
|
+
}); // end context.with
|
|
688
744
|
}
|
|
689
745
|
/**
|
|
690
746
|
* Alias for generate method - implements AIProvider interface
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Conversation Memory Initializer
|
|
3
3
|
* Provides integration with Redis storage for conversation memory
|
|
4
4
|
*/
|
|
5
|
-
import { createConversationMemoryManager, getStorageType, getRedisConfigFromEnv, } from "./conversationMemoryFactory.js";
|
|
6
5
|
import { applyConversationMemoryDefaults } from "../utils/conversationMemory.js";
|
|
7
6
|
import { logger } from "../utils/logger.js";
|
|
7
|
+
import { createConversationMemoryManager, getRedisConfigFromEnv, getStorageType, } from "./conversationMemoryFactory.js";
|
|
8
8
|
/**
|
|
9
9
|
* Initialize conversation memory for NeuroLink
|
|
10
10
|
* This function decides whether to use in-memory or Redis storage
|
|
@@ -30,11 +30,14 @@ export async function initializeConversationMemory(config) {
|
|
|
30
30
|
maxTurnsPerSession: memoryConfig.maxTurnsPerSession,
|
|
31
31
|
enableSummarization: memoryConfig.enableSummarization,
|
|
32
32
|
});
|
|
33
|
-
// Determine storage type
|
|
34
|
-
|
|
33
|
+
// Determine storage type: if redisConfig is passed in the SDK config, use Redis
|
|
34
|
+
// regardless of STORAGE_TYPE env var. This lets consumers configure Redis via the API.
|
|
35
|
+
const hasRedisConfig = !!config.conversationMemory?.redisConfig;
|
|
36
|
+
const storageType = hasRedisConfig ? "redis" : getStorageType();
|
|
35
37
|
logger.debug("[conversationMemoryInitializer] Storage type determined", {
|
|
36
38
|
storageType,
|
|
37
|
-
|
|
39
|
+
fromConfig: hasRedisConfig,
|
|
40
|
+
fromEnv: !hasRedisConfig && !!process.env.STORAGE_TYPE,
|
|
38
41
|
});
|
|
39
42
|
if (storageType === "redis") {
|
|
40
43
|
logger.info("[conversationMemoryInitializer] Initializing Redis-based conversation memory manager");
|
|
@@ -71,4 +71,6 @@ export declare class ConversationMemoryManager implements IConversationMemoryMan
|
|
|
71
71
|
* Resets summary pointers since old pointers may reference messages that no longer exist.
|
|
72
72
|
*/
|
|
73
73
|
setSessionMessages(sessionId: string, messages: ChatMessage[], userId?: string): Promise<void>;
|
|
74
|
+
/** Close/shutdown — no-op for in-memory manager (no external connections to release) */
|
|
75
|
+
close(): Promise<void>;
|
|
74
76
|
}
|
|
@@ -6,11 +6,11 @@ import { randomUUID } from "crypto";
|
|
|
6
6
|
import { DEFAULT_MAX_SESSIONS, MEMORY_THRESHOLD_PERCENTAGE, MESSAGES_PER_TURN, } from "../config/conversationMemory.js";
|
|
7
7
|
import { TokenUtils } from "../constants/tokens.js";
|
|
8
8
|
import { SummarizationEngine } from "../context/summarizationEngine.js";
|
|
9
|
+
import { runWithCurrentLangfuseContext } from "../services/server/ai/observability/instrumentation.js";
|
|
10
|
+
import { tracers, withSpan } from "../telemetry/index.js";
|
|
9
11
|
import { ConversationMemoryError } from "../types/conversation.js";
|
|
10
12
|
import { buildContextFromPointer, getEffectiveTokenThreshold, } from "../utils/conversationMemory.js";
|
|
11
|
-
import { runWithCurrentLangfuseContext } from "../services/server/ai/observability/instrumentation.js";
|
|
12
13
|
import { logger } from "../utils/logger.js";
|
|
13
|
-
import { tracers, withSpan } from "../telemetry/index.js";
|
|
14
14
|
export class ConversationMemoryManager {
|
|
15
15
|
sessions = new Map();
|
|
16
16
|
config;
|
|
@@ -337,5 +337,9 @@ export class ConversationMemoryManager {
|
|
|
337
337
|
session.lastCountedAt = undefined;
|
|
338
338
|
session.lastActivity = Date.now();
|
|
339
339
|
}
|
|
340
|
+
/** Close/shutdown — no-op for in-memory manager (no external connections to release) */
|
|
341
|
+
async close() {
|
|
342
|
+
// In-memory manager has nothing to close
|
|
343
|
+
}
|
|
340
344
|
}
|
|
341
345
|
//# sourceMappingURL=conversationMemoryManager.js.map
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @module core/modules/GenerationHandler
|
|
14
14
|
*/
|
|
15
|
-
import type {
|
|
15
|
+
import type { CoreMessage, LanguageModelV1, Tool } from "ai";
|
|
16
16
|
import { generateText } from "ai";
|
|
17
|
-
import type {
|
|
17
|
+
import type { AIProviderName, EnhancedGenerateResult, StandardRecord, TextGenerationOptions } from "../../types/index.js";
|
|
18
18
|
/**
|
|
19
19
|
* GenerationHandler class - Handles text generation operations for AI providers
|
|
20
20
|
*/
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @module core/modules/GenerationHandler
|
|
14
14
|
*/
|
|
15
|
-
import { generateText, Output, NoObjectGeneratedError } from "ai";
|
|
16
15
|
import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
|
|
16
|
+
import { generateText, NoObjectGeneratedError, Output } from "ai";
|
|
17
17
|
import { tracers } from "../../telemetry/tracers.js";
|
|
18
18
|
import { logger } from "../../utils/logger.js";
|
|
19
|
-
import { extractTokenUsage, extractCacheCreationTokens, extractCacheReadTokens, calculateCacheSavingsPercent, } from "../../utils/tokenUtils.js";
|
|
20
|
-
import { withProviderRetry } from "../../utils/providerRetry.js";
|
|
21
19
|
import { calculateCost } from "../../utils/pricing.js";
|
|
20
|
+
import { withProviderRetry } from "../../utils/providerRetry.js";
|
|
21
|
+
import { calculateCacheSavingsPercent, extractCacheCreationTokens, extractCacheReadTokens, extractTokenUsage, } from "../../utils/tokenUtils.js";
|
|
22
22
|
import { DEFAULT_MAX_STEPS } from "../constants.js";
|
|
23
23
|
const genTracer = tracers.generation;
|
|
24
24
|
/**
|
|
@@ -67,8 +67,8 @@ export class GenerationHandler {
|
|
|
67
67
|
// Gemini 2.5 and earlier cannot use tools + structured JSON output simultaneously.
|
|
68
68
|
// When both are requested on a Google provider, disable structured output (tools take priority).
|
|
69
69
|
const wantsStructuredOutput = includeStructuredOutput &&
|
|
70
|
-
!!options.schema
|
|
71
|
-
|
|
70
|
+
(!!options.schema ||
|
|
71
|
+
options.output?.format === "json" ||
|
|
72
72
|
options.output?.format === "structured");
|
|
73
73
|
const useStructuredOutput = wantsStructuredOutput &&
|
|
74
74
|
!(isGoogleProvider && shouldUseTools && Object.keys(tools).length > 0);
|
|
@@ -166,9 +166,9 @@ export class GenerationHandler {
|
|
|
166
166
|
return genTracer.startActiveSpan("neurolink.executeGeneration", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
167
167
|
const shouldUseTools = !options.disableTools && this.supportsToolsFn();
|
|
168
168
|
const toolCount = Object.keys(tools || {}).length;
|
|
169
|
-
const useStructuredOutput = !!options.schema
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
const useStructuredOutput = !!options.schema ||
|
|
170
|
+
options.output?.format === "json" ||
|
|
171
|
+
options.output?.format === "structured";
|
|
172
172
|
span.setAttribute("gen_ai.system", this.providerName || "unknown");
|
|
173
173
|
span.setAttribute("neurolink.structured_output", useStructuredOutput);
|
|
174
174
|
span.setAttribute("neurolink.tool_count", toolCount);
|
|
@@ -452,10 +452,10 @@ export class GenerationHandler {
|
|
|
452
452
|
* Format the enhanced result
|
|
453
453
|
*/
|
|
454
454
|
formatEnhancedResult(generateResult, tools, toolsUsed, toolExecutions, options) {
|
|
455
|
-
// Structured output check
|
|
456
|
-
const useStructuredOutput = !!options.schema
|
|
457
|
-
|
|
458
|
-
|
|
455
|
+
// Structured output check — schema alone is sufficient to activate
|
|
456
|
+
const useStructuredOutput = !!options.schema ||
|
|
457
|
+
options.output?.format === "json" ||
|
|
458
|
+
options.output?.format === "structured";
|
|
459
459
|
let content;
|
|
460
460
|
if (useStructuredOutput) {
|
|
461
461
|
try {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AIProviderFactory } from "../core/factory.js";
|
|
2
2
|
import { PromptBuilder } from "./prompts.js";
|
|
3
3
|
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { SpanSerializer, SpanType, SpanStatus, } from "../observability/index.js";
|
|
5
|
+
import { getMetricsAggregator } from "../observability/index.js";
|
|
4
6
|
/**
|
|
5
7
|
* Implements a RAGAS-style evaluator that uses a "judge" LLM to score the
|
|
6
8
|
* quality of an AI response based on rich, contextual information.
|
|
@@ -32,27 +34,45 @@ export class RAGASEvaluator {
|
|
|
32
34
|
* @returns A promise that resolves to a detailed `EvaluationResult`.
|
|
33
35
|
*/
|
|
34
36
|
async evaluate(context) {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
input: { text: prompt },
|
|
37
|
+
const span = SpanSerializer.createSpan(SpanType.EVALUATION, "evaluation.ragas", {
|
|
38
|
+
"evaluation.dimension": "relevance|accuracy|completeness",
|
|
39
|
+
"ai.provider": this.providerName,
|
|
40
|
+
"ai.model": this.evaluationModel,
|
|
40
41
|
});
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
const startTime = Date.now();
|
|
43
|
+
try {
|
|
44
|
+
const prompt = this.promptBuilder.buildEvaluationPrompt(context, this.promptGenerator);
|
|
45
|
+
const provider = await AIProviderFactory.createProvider(this.providerName, this.evaluationModel);
|
|
46
|
+
const result = await provider.generate({
|
|
47
|
+
input: { text: prompt },
|
|
48
|
+
});
|
|
49
|
+
if (!result) {
|
|
50
|
+
throw new Error("Evaluation generation failed to return a result.");
|
|
51
|
+
}
|
|
52
|
+
const rawEvaluationResponse = result.content;
|
|
53
|
+
const parsedResult = this.parseEvaluationResponse(rawEvaluationResponse);
|
|
54
|
+
const evaluationTime = Date.now() - startTime;
|
|
55
|
+
const finalResult = {
|
|
56
|
+
...parsedResult,
|
|
57
|
+
isPassing: parsedResult.finalScore >= this.threshold, // This will be recalculated, but is needed for the type
|
|
58
|
+
evaluationModel: this.evaluationModel,
|
|
59
|
+
evaluationTime,
|
|
60
|
+
attemptNumber: context.attemptNumber,
|
|
61
|
+
rawEvaluationResponse,
|
|
62
|
+
};
|
|
63
|
+
span.durationMs = Date.now() - startTime;
|
|
64
|
+
const endedSpan = SpanSerializer.endSpan(span, SpanStatus.OK);
|
|
65
|
+
getMetricsAggregator().recordSpan(endedSpan);
|
|
66
|
+
return finalResult;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
span.durationMs = Date.now() - startTime;
|
|
70
|
+
const endedSpan = SpanSerializer.endSpan(span, SpanStatus.ERROR);
|
|
71
|
+
endedSpan.statusMessage =
|
|
72
|
+
error instanceof Error ? error.message : String(error);
|
|
73
|
+
getMetricsAggregator().recordSpan(endedSpan);
|
|
74
|
+
throw error;
|
|
43
75
|
}
|
|
44
|
-
const rawEvaluationResponse = result.content;
|
|
45
|
-
const parsedResult = this.parseEvaluationResponse(rawEvaluationResponse);
|
|
46
|
-
const evaluationTime = Date.now() - startTime;
|
|
47
|
-
const finalResult = {
|
|
48
|
-
...parsedResult,
|
|
49
|
-
isPassing: parsedResult.finalScore >= this.threshold, // This will be recalculated, but is needed for the type
|
|
50
|
-
evaluationModel: this.evaluationModel,
|
|
51
|
-
evaluationTime,
|
|
52
|
-
attemptNumber: context.attemptNumber,
|
|
53
|
-
rawEvaluationResponse,
|
|
54
|
-
};
|
|
55
|
-
return finalResult;
|
|
56
76
|
}
|
|
57
77
|
/**
|
|
58
78
|
* Parses the raw JSON string from the judge LLM into a structured `EvaluationResult` object.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @file Contains the logic for mapping raw evaluation results to the structured EvaluationData type.
|
|
3
3
|
*/
|
|
4
|
+
import { SpanSerializer, SpanType, SpanStatus, } from "../observability/index.js";
|
|
5
|
+
import { getMetricsAggregator } from "../observability/index.js";
|
|
4
6
|
/**
|
|
5
7
|
* Maps a raw `EvaluationResult` to the structured `EvaluationData` format.
|
|
6
8
|
* This includes calculating derived fields like `isOffTopic` and `alertSeverity`.
|
|
@@ -12,25 +14,49 @@
|
|
|
12
14
|
* @returns A structured `EvaluationData` object.
|
|
13
15
|
*/
|
|
14
16
|
export function mapToEvaluationData(evalContext, result, threshold, offTopicThreshold = 5, highSeverityThreshold = 4) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
const span = SpanSerializer.createSpan(SpanType.EVALUATION, "evaluation.score", {
|
|
18
|
+
"evaluation.dimension": "relevance|accuracy|completeness|overall",
|
|
19
|
+
scores: {
|
|
20
|
+
relevance: result.relevanceScore,
|
|
21
|
+
accuracy: result.accuracyScore,
|
|
22
|
+
completeness: result.completenessScore,
|
|
23
|
+
overall: result.finalScore,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
try {
|
|
28
|
+
const isPassing = result.finalScore >= threshold;
|
|
29
|
+
const evaluationData = {
|
|
30
|
+
relevance: result.relevanceScore,
|
|
31
|
+
accuracy: result.accuracyScore,
|
|
32
|
+
completeness: result.completenessScore,
|
|
33
|
+
overall: result.finalScore,
|
|
34
|
+
isOffTopic: result.finalScore < offTopicThreshold,
|
|
35
|
+
alertSeverity: isPassing
|
|
36
|
+
? "none"
|
|
37
|
+
: result.finalScore < highSeverityThreshold
|
|
38
|
+
? "high"
|
|
39
|
+
: "medium",
|
|
40
|
+
reasoning: result.reasoning,
|
|
41
|
+
suggestedImprovements: result.suggestedImprovements,
|
|
42
|
+
evaluationModel: result.evaluationModel,
|
|
43
|
+
evaluationTime: result.evaluationTime,
|
|
44
|
+
evaluationAttempt: result.attemptNumber,
|
|
45
|
+
responseContent: evalContext.aiResponse,
|
|
46
|
+
queryContent: evalContext.userQuery,
|
|
47
|
+
};
|
|
48
|
+
span.durationMs = Date.now() - startTime;
|
|
49
|
+
const endedSpan = SpanSerializer.endSpan(span, SpanStatus.OK);
|
|
50
|
+
getMetricsAggregator().recordSpan(endedSpan);
|
|
51
|
+
return evaluationData;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
span.durationMs = Date.now() - startTime;
|
|
55
|
+
const endedSpan = SpanSerializer.endSpan(span, SpanStatus.ERROR);
|
|
56
|
+
endedSpan.statusMessage =
|
|
57
|
+
error instanceof Error ? error.message : String(error);
|
|
58
|
+
getMetricsAggregator().recordSpan(endedSpan);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
35
61
|
}
|
|
36
62
|
//# sourceMappingURL=scoring.js.map
|
|
@@ -22,5 +22,5 @@ export { SlideGenerator, createSlideGenerator, generateSlidesFromPlan, PptxGenJS
|
|
|
22
22
|
export { generatePresentation } from "./presentationOrchestrator.js";
|
|
23
23
|
export { validatePPTGenerationInput, validatePPTOutputOptions, validatePPTProvider, } from "../../utils/parameterValidation.js";
|
|
24
24
|
export type { EnhancedValidationResult as PPTValidationResult } from "../../types/tools.js";
|
|
25
|
-
export { PPT_VALID_PROVIDERS, getEffectivePPTProvider, generateOutputPath, ensureOutputDirectory, normalizeLogoConfig, getLayoutName, getFailureStage, toError, isObject, isLogoConfig, } from "./utils.js";
|
|
25
|
+
export { PPT_VALID_PROVIDERS, getEffectivePPTProvider, generateOutputPath, ensureOutputDirectory, normalizeLogoConfig, getLayoutName, getFailureStage, toError, isObject, isLogoConfig, validateImageBuffer, } from "./utils.js";
|
|
26
26
|
export type { EffectivePPTProviderResult } from "./types.js";
|