@juspay/neurolink 9.15.0 → 9.16.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 +6 -0
- package/dist/adapters/video/videoAnalyzer.d.ts +1 -1
- package/dist/adapters/video/videoAnalyzer.js +10 -8
- package/dist/cli/commands/setup-anthropic.js +1 -14
- package/dist/cli/commands/setup-azure.js +1 -12
- package/dist/cli/commands/setup-bedrock.js +1 -9
- package/dist/cli/commands/setup-google-ai.js +1 -12
- package/dist/cli/commands/setup-openai.js +1 -14
- package/dist/cli/commands/workflow.d.ts +27 -0
- package/dist/cli/commands/workflow.js +216 -0
- package/dist/cli/factories/commandFactory.js +79 -20
- package/dist/cli/index.js +0 -1
- package/dist/cli/parser.js +4 -1
- package/dist/cli/utils/maskCredential.d.ts +11 -0
- package/dist/cli/utils/maskCredential.js +23 -0
- package/dist/constants/contextWindows.js +107 -16
- package/dist/constants/enums.d.ts +99 -15
- package/dist/constants/enums.js +152 -22
- package/dist/context/budgetChecker.js +1 -1
- package/dist/context/contextCompactor.js +31 -4
- package/dist/context/emergencyTruncation.d.ts +21 -0
- package/dist/context/emergencyTruncation.js +88 -0
- package/dist/context/errorDetection.d.ts +16 -0
- package/dist/context/errorDetection.js +48 -1
- package/dist/context/errors.d.ts +19 -0
- package/dist/context/errors.js +21 -0
- package/dist/context/stages/slidingWindowTruncator.d.ts +6 -0
- package/dist/context/stages/slidingWindowTruncator.js +159 -24
- package/dist/core/baseProvider.js +306 -200
- package/dist/core/conversationMemoryManager.js +104 -61
- package/dist/core/evaluationProviders.js +16 -33
- package/dist/core/factory.js +237 -164
- package/dist/core/modules/GenerationHandler.js +175 -116
- package/dist/core/modules/MessageBuilder.js +222 -170
- package/dist/core/modules/StreamHandler.d.ts +1 -0
- package/dist/core/modules/StreamHandler.js +95 -27
- package/dist/core/modules/TelemetryHandler.d.ts +10 -1
- package/dist/core/modules/TelemetryHandler.js +25 -7
- package/dist/core/modules/ToolsManager.js +115 -191
- package/dist/core/redisConversationMemoryManager.js +418 -282
- package/dist/factories/providerRegistry.d.ts +5 -0
- package/dist/factories/providerRegistry.js +20 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/dist/lib/adapters/video/videoAnalyzer.d.ts +1 -1
- package/dist/lib/adapters/video/videoAnalyzer.js +10 -8
- package/dist/lib/constants/contextWindows.js +107 -16
- package/dist/lib/constants/enums.d.ts +99 -15
- package/dist/lib/constants/enums.js +152 -22
- package/dist/lib/context/budgetChecker.js +1 -1
- package/dist/lib/context/contextCompactor.js +31 -4
- package/dist/lib/context/emergencyTruncation.d.ts +21 -0
- package/dist/lib/context/emergencyTruncation.js +89 -0
- package/dist/lib/context/errorDetection.d.ts +16 -0
- package/dist/lib/context/errorDetection.js +48 -1
- package/dist/lib/context/errors.d.ts +19 -0
- package/dist/lib/context/errors.js +22 -0
- package/dist/lib/context/stages/slidingWindowTruncator.d.ts +6 -0
- package/dist/lib/context/stages/slidingWindowTruncator.js +159 -24
- package/dist/lib/core/baseProvider.js +306 -200
- package/dist/lib/core/conversationMemoryManager.js +104 -61
- package/dist/lib/core/evaluationProviders.js +16 -33
- package/dist/lib/core/factory.js +237 -164
- package/dist/lib/core/modules/GenerationHandler.js +175 -116
- package/dist/lib/core/modules/MessageBuilder.js +222 -170
- package/dist/lib/core/modules/StreamHandler.d.ts +1 -0
- package/dist/lib/core/modules/StreamHandler.js +95 -27
- package/dist/lib/core/modules/TelemetryHandler.d.ts +10 -1
- package/dist/lib/core/modules/TelemetryHandler.js +25 -7
- package/dist/lib/core/modules/ToolsManager.js +115 -191
- package/dist/lib/core/redisConversationMemoryManager.js +418 -282
- package/dist/lib/factories/providerRegistry.d.ts +5 -0
- package/dist/lib/factories/providerRegistry.js +20 -2
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +4 -2
- package/dist/lib/mcp/externalServerManager.js +66 -0
- package/dist/lib/mcp/mcpCircuitBreaker.js +24 -0
- package/dist/lib/mcp/mcpClientFactory.js +16 -0
- package/dist/lib/mcp/toolDiscoveryService.js +32 -6
- package/dist/lib/mcp/toolRegistry.js +193 -123
- package/dist/lib/neurolink.d.ts +6 -0
- package/dist/lib/neurolink.js +1162 -646
- package/dist/lib/providers/amazonBedrock.d.ts +1 -1
- package/dist/lib/providers/amazonBedrock.js +521 -319
- package/dist/lib/providers/anthropic.js +73 -17
- package/dist/lib/providers/anthropicBaseProvider.js +77 -17
- package/dist/lib/providers/googleAiStudio.d.ts +1 -1
- package/dist/lib/providers/googleAiStudio.js +292 -227
- package/dist/lib/providers/googleVertex.d.ts +36 -1
- package/dist/lib/providers/googleVertex.js +553 -260
- package/dist/lib/providers/ollama.js +329 -278
- package/dist/lib/providers/openAI.js +77 -19
- package/dist/lib/providers/sagemaker/parsers.js +3 -3
- package/dist/lib/providers/sagemaker/streaming.js +3 -3
- package/dist/lib/proxy/proxyFetch.js +81 -48
- package/dist/lib/rag/ChunkerFactory.js +1 -1
- package/dist/lib/rag/chunkers/MarkdownChunker.d.ts +22 -0
- package/dist/lib/rag/chunkers/MarkdownChunker.js +213 -9
- package/dist/lib/rag/chunking/markdownChunker.d.ts +16 -0
- package/dist/lib/rag/chunking/markdownChunker.js +174 -2
- package/dist/lib/rag/pipeline/contextAssembly.js +2 -1
- package/dist/lib/rag/ragIntegration.d.ts +18 -1
- package/dist/lib/rag/ragIntegration.js +94 -14
- package/dist/lib/rag/retrieval/vectorQueryTool.js +21 -4
- package/dist/lib/server/abstract/baseServerAdapter.js +4 -1
- package/dist/lib/server/adapters/fastifyAdapter.js +35 -30
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +32 -0
- package/dist/lib/services/server/ai/observability/instrumentation.js +39 -0
- package/dist/lib/telemetry/attributes.d.ts +52 -0
- package/dist/lib/telemetry/attributes.js +61 -0
- package/dist/lib/telemetry/index.d.ts +3 -0
- package/dist/lib/telemetry/index.js +3 -0
- package/dist/lib/telemetry/telemetryService.d.ts +6 -0
- package/dist/lib/telemetry/telemetryService.js +6 -0
- package/dist/lib/telemetry/tracers.d.ts +15 -0
- package/dist/lib/telemetry/tracers.js +17 -0
- package/dist/lib/telemetry/withSpan.d.ts +9 -0
- package/dist/lib/telemetry/withSpan.js +35 -0
- package/dist/lib/types/contextTypes.d.ts +10 -0
- package/dist/lib/types/streamTypes.d.ts +14 -0
- package/dist/lib/utils/conversationMemory.js +121 -82
- package/dist/lib/utils/logger.d.ts +5 -0
- package/dist/lib/utils/logger.js +50 -2
- package/dist/lib/utils/messageBuilder.js +22 -42
- package/dist/lib/utils/modelDetection.js +3 -3
- package/dist/lib/utils/providerRetry.d.ts +41 -0
- package/dist/lib/utils/providerRetry.js +114 -0
- package/dist/lib/utils/retryability.d.ts +14 -0
- package/dist/lib/utils/retryability.js +23 -0
- package/dist/lib/utils/sanitizers/svg.js +4 -5
- package/dist/lib/utils/tokenEstimation.d.ts +11 -1
- package/dist/lib/utils/tokenEstimation.js +19 -4
- package/dist/lib/utils/videoAnalysisProcessor.js +7 -3
- package/dist/mcp/externalServerManager.js +66 -0
- package/dist/mcp/mcpCircuitBreaker.js +24 -0
- package/dist/mcp/mcpClientFactory.js +16 -0
- package/dist/mcp/toolDiscoveryService.js +32 -6
- package/dist/mcp/toolRegistry.js +193 -123
- package/dist/neurolink.d.ts +6 -0
- package/dist/neurolink.js +1162 -646
- package/dist/providers/amazonBedrock.d.ts +1 -1
- package/dist/providers/amazonBedrock.js +521 -319
- package/dist/providers/anthropic.js +73 -17
- package/dist/providers/anthropicBaseProvider.js +77 -17
- package/dist/providers/googleAiStudio.d.ts +1 -1
- package/dist/providers/googleAiStudio.js +292 -227
- package/dist/providers/googleVertex.d.ts +36 -1
- package/dist/providers/googleVertex.js +553 -260
- package/dist/providers/ollama.js +329 -278
- package/dist/providers/openAI.js +77 -19
- package/dist/providers/sagemaker/parsers.js +3 -3
- package/dist/providers/sagemaker/streaming.js +3 -3
- package/dist/proxy/proxyFetch.js +81 -48
- package/dist/rag/ChunkerFactory.js +1 -1
- package/dist/rag/chunkers/MarkdownChunker.d.ts +22 -0
- package/dist/rag/chunkers/MarkdownChunker.js +213 -9
- package/dist/rag/chunking/markdownChunker.d.ts +16 -0
- package/dist/rag/chunking/markdownChunker.js +174 -2
- package/dist/rag/pipeline/contextAssembly.js +2 -1
- package/dist/rag/ragIntegration.d.ts +18 -1
- package/dist/rag/ragIntegration.js +94 -14
- package/dist/rag/retrieval/vectorQueryTool.js +21 -4
- package/dist/server/abstract/baseServerAdapter.js +4 -1
- package/dist/server/adapters/fastifyAdapter.js +35 -30
- package/dist/services/server/ai/observability/instrumentation.d.ts +32 -0
- package/dist/services/server/ai/observability/instrumentation.js +39 -0
- package/dist/telemetry/attributes.d.ts +52 -0
- package/dist/telemetry/attributes.js +60 -0
- package/dist/telemetry/index.d.ts +3 -0
- package/dist/telemetry/index.js +3 -0
- package/dist/telemetry/telemetryService.d.ts +6 -0
- package/dist/telemetry/telemetryService.js +6 -0
- package/dist/telemetry/tracers.d.ts +15 -0
- package/dist/telemetry/tracers.js +16 -0
- package/dist/telemetry/withSpan.d.ts +9 -0
- package/dist/telemetry/withSpan.js +34 -0
- package/dist/types/contextTypes.d.ts +10 -0
- package/dist/types/streamTypes.d.ts +14 -0
- package/dist/utils/conversationMemory.js +121 -82
- package/dist/utils/logger.d.ts +5 -0
- package/dist/utils/logger.js +50 -2
- package/dist/utils/messageBuilder.js +22 -42
- package/dist/utils/modelDetection.js +3 -3
- package/dist/utils/providerRetry.d.ts +41 -0
- package/dist/utils/providerRetry.js +113 -0
- package/dist/utils/retryability.d.ts +14 -0
- package/dist/utils/retryability.js +22 -0
- package/dist/utils/sanitizers/svg.js +4 -5
- package/dist/utils/tokenEstimation.d.ts +11 -1
- package/dist/utils/tokenEstimation.js +19 -4
- package/dist/utils/videoAnalysisProcessor.js +7 -3
- package/dist/workflow/config.d.ts +26 -26
- package/package.json +1 -1
|
@@ -13,9 +13,14 @@
|
|
|
13
13
|
* @module core/modules/GenerationHandler
|
|
14
14
|
*/
|
|
15
15
|
import { generateText, Output, NoObjectGeneratedError } from "ai";
|
|
16
|
+
import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
|
|
17
|
+
import { tracers } from "../../telemetry/tracers.js";
|
|
16
18
|
import { logger } from "../../utils/logger.js";
|
|
17
19
|
import { extractTokenUsage, extractCacheCreationTokens, extractCacheReadTokens, calculateCacheSavingsPercent, } from "../../utils/tokenUtils.js";
|
|
20
|
+
import { withProviderRetry } from "../../utils/providerRetry.js";
|
|
21
|
+
import { calculateCost } from "../../utils/pricing.js";
|
|
18
22
|
import { DEFAULT_MAX_STEPS } from "../constants.js";
|
|
23
|
+
const genTracer = tracers.generation;
|
|
19
24
|
/**
|
|
20
25
|
* Safely preview-serialize a value for debug logging.
|
|
21
26
|
* Handles undefined, circular references, and non-serializable values.
|
|
@@ -59,10 +64,14 @@ export class GenerationHandler {
|
|
|
59
64
|
const isAnthropicProvider = this.providerName === "anthropic" ||
|
|
60
65
|
this.providerName === "bedrock" ||
|
|
61
66
|
(this.providerName === "vertex" && this.modelName?.startsWith("claude-"));
|
|
62
|
-
|
|
67
|
+
// Gemini 2.5 and earlier cannot use tools + structured JSON output simultaneously.
|
|
68
|
+
// When both are requested on a Google provider, disable structured output (tools take priority).
|
|
69
|
+
const wantsStructuredOutput = includeStructuredOutput &&
|
|
63
70
|
!!options.schema &&
|
|
64
71
|
(options.output?.format === "json" ||
|
|
65
72
|
options.output?.format === "structured");
|
|
73
|
+
const useStructuredOutput = wantsStructuredOutput &&
|
|
74
|
+
!(isGoogleProvider && shouldUseTools && Object.keys(tools).length > 0);
|
|
66
75
|
// Annotate the last tool with cache_control so the full tool-definition
|
|
67
76
|
// block becomes a cache breakpoint for Anthropic-family providers.
|
|
68
77
|
// Non-Anthropic providers harmlessly ignore unknown providerOptions.
|
|
@@ -98,6 +107,7 @@ export class GenerationHandler {
|
|
|
98
107
|
}),
|
|
99
108
|
temperature: options.temperature,
|
|
100
109
|
maxTokens: options.maxTokens,
|
|
110
|
+
maxRetries: 0, // NL11: Disable AI SDK's invisible internal retries; we handle retries with OTel instrumentation
|
|
101
111
|
abortSignal: options.abortSignal,
|
|
102
112
|
...(useStructuredOutput &&
|
|
103
113
|
options.schema && {
|
|
@@ -153,108 +163,59 @@ export class GenerationHandler {
|
|
|
153
163
|
* Execute the generation with AI SDK
|
|
154
164
|
*/
|
|
155
165
|
async executeGeneration(model, messages, tools, options) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
"unknown";
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (logger.shouldLog("debug")) {
|
|
172
|
-
try {
|
|
173
|
-
logger.debug("[Observability] Full generateText parameters", {
|
|
174
|
-
requestId,
|
|
175
|
-
model: model.modelId || "unknown",
|
|
176
|
-
messageCount: messages.length,
|
|
177
|
-
messages: messages.map((msg, i) => ({
|
|
178
|
-
index: i,
|
|
179
|
-
role: msg.role,
|
|
180
|
-
contentLength: typeof msg.content === "string"
|
|
181
|
-
? msg.content.length
|
|
182
|
-
: safePreview(msg.content).length,
|
|
183
|
-
contentPreview: typeof msg.content === "string"
|
|
184
|
-
? msg.content.substring(0, 200)
|
|
185
|
-
: "[multimodal]",
|
|
186
|
-
})),
|
|
187
|
-
toolNames: Object.keys(tools || {}),
|
|
188
|
-
toolCount: Object.keys(tools || {}).length,
|
|
189
|
-
maxSteps: options.maxSteps,
|
|
190
|
-
temperature: options.temperature,
|
|
191
|
-
maxTokens: options.maxTokens,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
// Ignore serialization errors in debug logging
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
const genStartTime = Date.now();
|
|
199
|
-
try {
|
|
200
|
-
const result = await this.callGenerateText(model, messages, tools, options, shouldUseTools, true);
|
|
201
|
-
logger.info("[GenerationHandler] generateText returned", {
|
|
166
|
+
return genTracer.startActiveSpan("neurolink.executeGeneration", { kind: SpanKind.INTERNAL }, async (span) => {
|
|
167
|
+
const shouldUseTools = !options.disableTools && this.supportsToolsFn();
|
|
168
|
+
const toolCount = Object.keys(tools || {}).length;
|
|
169
|
+
const useStructuredOutput = !!options.schema &&
|
|
170
|
+
(options.output?.format === "json" ||
|
|
171
|
+
options.output?.format === "structured");
|
|
172
|
+
span.setAttribute("gen_ai.system", this.providerName || "unknown");
|
|
173
|
+
span.setAttribute("neurolink.structured_output", useStructuredOutput);
|
|
174
|
+
span.setAttribute("neurolink.tool_count", toolCount);
|
|
175
|
+
span.setAttribute("neurolink.message_count", messages.length);
|
|
176
|
+
span.setAttribute("gen_ai.request.model", model.modelId || this.modelName || "unknown");
|
|
177
|
+
const requestId = options.requestId ||
|
|
178
|
+
options.context?.requestId ||
|
|
179
|
+
"unknown";
|
|
180
|
+
logger.info("[GenerationHandler] Calling generateText", {
|
|
202
181
|
requestId,
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
182
|
+
model: model.modelId || "unknown",
|
|
183
|
+
messageCount: messages.length,
|
|
184
|
+
toolCount,
|
|
185
|
+
maxSteps: options.maxSteps,
|
|
186
|
+
temperature: options.temperature,
|
|
208
187
|
});
|
|
209
188
|
if (logger.shouldLog("debug")) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
stepIndex: i,
|
|
225
|
-
stepType: step.stepType,
|
|
226
|
-
textPreview: step.text?.substring(0, 200),
|
|
227
|
-
textLength: step.text?.length || 0,
|
|
228
|
-
toolCalls: step.toolCalls?.map((tc) => ({
|
|
229
|
-
toolName: tc.toolName,
|
|
230
|
-
argsPreview: safePreview(tc.args),
|
|
189
|
+
try {
|
|
190
|
+
logger.debug("[Observability] Full generateText parameters", {
|
|
191
|
+
requestId,
|
|
192
|
+
model: model.modelId || "unknown",
|
|
193
|
+
messageCount: messages.length,
|
|
194
|
+
messages: messages.map((msg, i) => ({
|
|
195
|
+
index: i,
|
|
196
|
+
role: msg.role,
|
|
197
|
+
contentLength: typeof msg.content === "string"
|
|
198
|
+
? msg.content.length
|
|
199
|
+
: safePreview(msg.content).length,
|
|
200
|
+
contentPreview: typeof msg.content === "string"
|
|
201
|
+
? msg.content.substring(0, 200)
|
|
202
|
+
: "[multimodal]",
|
|
231
203
|
})),
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
204
|
+
toolNames: Object.keys(tools || {}),
|
|
205
|
+
toolCount,
|
|
206
|
+
maxSteps: options.maxSteps,
|
|
207
|
+
temperature: options.temperature,
|
|
208
|
+
maxTokens: options.maxTokens,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Ignore serialization errors in debug logging
|
|
213
|
+
}
|
|
242
214
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
// fall back to generating without experimental_output and extract JSON manually
|
|
248
|
-
if (error instanceof NoObjectGeneratedError && useStructuredOutput) {
|
|
249
|
-
logger.debug("[GenerationHandler] NoObjectGeneratedError caught - falling back to manual JSON extraction", {
|
|
250
|
-
provider: this.providerName,
|
|
251
|
-
model: this.modelName,
|
|
252
|
-
error: error.message,
|
|
253
|
-
});
|
|
254
|
-
// Retry without experimental_output - the formatEnhancedResult method
|
|
255
|
-
// will extract JSON from the text response
|
|
256
|
-
const result = await this.callGenerateText(model, messages, tools, options, shouldUseTools, false);
|
|
257
|
-
logger.info("[GenerationHandler] generateText returned (fallback)", {
|
|
215
|
+
const genStartTime = Date.now();
|
|
216
|
+
try {
|
|
217
|
+
const result = await withProviderRetry(() => this.callGenerateText(model, messages, tools, options, shouldUseTools, true), span, "generateText");
|
|
218
|
+
logger.info("[GenerationHandler] generateText returned", {
|
|
258
219
|
requestId,
|
|
259
220
|
durationMs: Date.now() - genStartTime,
|
|
260
221
|
finishReason: result.finishReason,
|
|
@@ -262,11 +223,107 @@ export class GenerationHandler {
|
|
|
262
223
|
toolCallsTotal: result.toolCalls?.length || 0,
|
|
263
224
|
responseChars: result.text?.length || 0,
|
|
264
225
|
});
|
|
226
|
+
if (logger.shouldLog("debug")) {
|
|
227
|
+
logger.debug("[Observability] LLM response metadata", {
|
|
228
|
+
requestId,
|
|
229
|
+
responseLength: result.text?.length || 0,
|
|
230
|
+
hasToolCalls: !!(result.toolCalls && result.toolCalls.length > 0),
|
|
231
|
+
toolCallCount: result.toolCalls?.length || 0,
|
|
232
|
+
toolNames: result.toolCalls?.map((tc) => tc.toolName),
|
|
233
|
+
finishReason: result.finishReason,
|
|
234
|
+
stepCount: result.steps?.length || 0,
|
|
235
|
+
steps: result.steps?.map((step, i) => ({
|
|
236
|
+
stepIndex: i,
|
|
237
|
+
stepType: step.stepType,
|
|
238
|
+
textLength: step.text?.length || 0,
|
|
239
|
+
toolCallCount: step.toolCalls?.length || 0,
|
|
240
|
+
toolNames: step.toolCalls?.map((tc) => tc.toolName),
|
|
241
|
+
toolResultCount: step.toolResults?.length || 0,
|
|
242
|
+
finishReason: step.finishReason,
|
|
243
|
+
})),
|
|
244
|
+
usage: result.usage,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
// Set token usage and completion attributes on span
|
|
248
|
+
if (result.usage) {
|
|
249
|
+
span.setAttribute("gen_ai.usage.input_tokens", result.usage.promptTokens || 0);
|
|
250
|
+
span.setAttribute("gen_ai.usage.output_tokens", result.usage.completionTokens || 0);
|
|
251
|
+
// Cost on span so users can query "what did this trace cost?"
|
|
252
|
+
const cost = calculateCost(this.providerName, this.modelName, {
|
|
253
|
+
input: result.usage.promptTokens || 0,
|
|
254
|
+
output: result.usage.completionTokens || 0,
|
|
255
|
+
total: (result.usage.promptTokens || 0) +
|
|
256
|
+
(result.usage.completionTokens || 0),
|
|
257
|
+
});
|
|
258
|
+
span.setAttribute("neurolink.cost", cost ?? 0);
|
|
259
|
+
}
|
|
260
|
+
if (result.finishReason) {
|
|
261
|
+
span.setAttribute("gen_ai.response.finish_reason", result.finishReason);
|
|
262
|
+
}
|
|
263
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
265
264
|
return result;
|
|
266
265
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
catch (error) {
|
|
267
|
+
// If NoObjectGeneratedError is thrown when using schema + tools together,
|
|
268
|
+
// fall back to generating without experimental_output and extract JSON manually
|
|
269
|
+
if (error instanceof NoObjectGeneratedError && useStructuredOutput) {
|
|
270
|
+
span.setAttribute("neurolink.has_fallback", true);
|
|
271
|
+
// NLK-GAP-007: Record initial failure event before fallback retry
|
|
272
|
+
span.addEvent("retry.initial_failure", {
|
|
273
|
+
"error.message": error.message,
|
|
274
|
+
"retry.attempt": 1,
|
|
275
|
+
"retry.reason": "NoObjectGeneratedError_structured_output_fallback",
|
|
276
|
+
});
|
|
277
|
+
logger.debug("[GenerationHandler] NoObjectGeneratedError caught - falling back to manual JSON extraction", {
|
|
278
|
+
provider: this.providerName,
|
|
279
|
+
model: this.modelName,
|
|
280
|
+
error: error.message,
|
|
281
|
+
});
|
|
282
|
+
// Retry without experimental_output - the formatEnhancedResult method
|
|
283
|
+
// will extract JSON from the text response
|
|
284
|
+
const result = await withProviderRetry(() => this.callGenerateText(model, messages, tools, options, shouldUseTools, false), span, "generateText(fallback)");
|
|
285
|
+
// NLK-GAP-007: Record recovery event after successful fallback
|
|
286
|
+
span.addEvent("retry.recovered", {
|
|
287
|
+
"retry.attempts": 2,
|
|
288
|
+
"retry.strategy": "structured_output_disabled",
|
|
289
|
+
});
|
|
290
|
+
span.setAttribute("retry.count", 1);
|
|
291
|
+
logger.info("[GenerationHandler] generateText returned (fallback)", {
|
|
292
|
+
requestId,
|
|
293
|
+
durationMs: Date.now() - genStartTime,
|
|
294
|
+
finishReason: result.finishReason,
|
|
295
|
+
steps: result.steps?.length || 1,
|
|
296
|
+
toolCallsTotal: result.toolCalls?.length || 0,
|
|
297
|
+
responseChars: result.text?.length || 0,
|
|
298
|
+
});
|
|
299
|
+
if (result.usage) {
|
|
300
|
+
span.setAttribute("gen_ai.usage.input_tokens", result.usage.promptTokens || 0);
|
|
301
|
+
span.setAttribute("gen_ai.usage.output_tokens", result.usage.completionTokens || 0);
|
|
302
|
+
const fallbackCost = calculateCost(this.providerName, this.modelName, {
|
|
303
|
+
input: result.usage.promptTokens || 0,
|
|
304
|
+
output: result.usage.completionTokens || 0,
|
|
305
|
+
total: (result.usage.promptTokens || 0) +
|
|
306
|
+
(result.usage.completionTokens || 0),
|
|
307
|
+
});
|
|
308
|
+
span.setAttribute("neurolink.cost", fallbackCost ?? 0);
|
|
309
|
+
}
|
|
310
|
+
if (result.finishReason) {
|
|
311
|
+
span.setAttribute("gen_ai.response.finish_reason", result.finishReason);
|
|
312
|
+
}
|
|
313
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
span.setStatus({
|
|
317
|
+
code: SpanStatusCode.ERROR,
|
|
318
|
+
message: error instanceof Error ? error.message : String(error),
|
|
319
|
+
});
|
|
320
|
+
// Re-throw other errors
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
span.end();
|
|
325
|
+
}
|
|
326
|
+
});
|
|
270
327
|
}
|
|
271
328
|
/**
|
|
272
329
|
* Extract cache metrics from provider metadata (e.g. Anthropic's providerMetadata.anthropic)
|
|
@@ -297,21 +354,23 @@ export class GenerationHandler {
|
|
|
297
354
|
*/
|
|
298
355
|
logGenerationComplete(generateResult) {
|
|
299
356
|
const cacheMetrics = this.extractCacheMetricsFromProviderMetadata(generateResult);
|
|
300
|
-
logger.debug
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
357
|
+
if (logger.shouldLog("debug")) {
|
|
358
|
+
logger.debug(`generateText completed`, {
|
|
359
|
+
provider: this.providerName,
|
|
360
|
+
model: this.modelName,
|
|
361
|
+
responseLength: generateResult.text?.length || 0,
|
|
362
|
+
toolResultsCount: generateResult.toolResults?.length || 0,
|
|
363
|
+
finishReason: generateResult.finishReason,
|
|
364
|
+
usage: generateResult.usage,
|
|
365
|
+
...(cacheMetrics.cacheCreationTokens !== undefined && {
|
|
366
|
+
cacheCreationTokens: cacheMetrics.cacheCreationTokens,
|
|
367
|
+
}),
|
|
368
|
+
...(cacheMetrics.cacheReadTokens !== undefined && {
|
|
369
|
+
cacheReadTokens: cacheMetrics.cacheReadTokens,
|
|
370
|
+
}),
|
|
371
|
+
timestamp: Date.now(),
|
|
372
|
+
});
|
|
373
|
+
}
|
|
315
374
|
}
|
|
316
375
|
/**
|
|
317
376
|
* Extract tool information from generation result
|