@juspay/neurolink 9.10.0 → 9.11.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/dist/adapters/video/videoAnalyzer.d.ts +3 -3
- package/dist/adapters/video/videoAnalyzer.js +39 -25
- package/dist/agent/directTools.d.ts +3 -3
- package/dist/cli/commands/config.d.ts +9 -9
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/constants/contextWindows.d.ts +6 -3
- package/dist/constants/contextWindows.js +30 -3
- package/dist/constants/index.d.ts +3 -3
- package/dist/constants/retry.d.ts +4 -4
- package/dist/constants/retry.js +1 -1
- package/dist/context/contextCompactor.d.ts +1 -1
- package/dist/context/contextCompactor.js +59 -1
- package/dist/context/summarizationEngine.d.ts +2 -2
- package/dist/context/summarizationEngine.js +44 -18
- package/dist/context/toolOutputLimits.d.ts +22 -13
- package/dist/context/toolOutputLimits.js +58 -64
- package/dist/core/baseProvider.d.ts +11 -2
- package/dist/core/baseProvider.js +30 -1
- package/dist/core/conversationMemoryManager.d.ts +13 -1
- package/dist/core/conversationMemoryManager.js +36 -5
- package/dist/core/modules/GenerationHandler.d.ts +6 -0
- package/dist/core/modules/GenerationHandler.js +192 -7
- package/dist/core/modules/MessageBuilder.js +42 -4
- package/dist/core/modules/TelemetryHandler.js +4 -1
- package/dist/core/redisConversationMemoryManager.d.ts +19 -3
- package/dist/core/redisConversationMemoryManager.js +253 -58
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/lib/adapters/video/videoAnalyzer.d.ts +3 -3
- package/dist/lib/adapters/video/videoAnalyzer.js +39 -25
- package/dist/lib/agent/directTools.d.ts +7 -7
- package/dist/lib/constants/contextWindows.d.ts +6 -3
- package/dist/lib/constants/contextWindows.js +30 -3
- package/dist/lib/constants/index.d.ts +3 -3
- package/dist/lib/constants/retry.d.ts +4 -4
- package/dist/lib/constants/retry.js +1 -1
- package/dist/lib/context/contextCompactor.d.ts +1 -1
- package/dist/lib/context/contextCompactor.js +59 -1
- package/dist/lib/context/summarizationEngine.d.ts +2 -2
- package/dist/lib/context/summarizationEngine.js +44 -18
- package/dist/lib/context/toolOutputLimits.d.ts +22 -13
- package/dist/lib/context/toolOutputLimits.js +58 -64
- package/dist/lib/core/baseProvider.d.ts +11 -2
- package/dist/lib/core/baseProvider.js +30 -1
- package/dist/lib/core/conversationMemoryManager.d.ts +13 -1
- package/dist/lib/core/conversationMemoryManager.js +36 -5
- package/dist/lib/core/modules/GenerationHandler.d.ts +6 -0
- package/dist/lib/core/modules/GenerationHandler.js +192 -7
- package/dist/lib/core/modules/MessageBuilder.js +42 -4
- package/dist/lib/core/modules/TelemetryHandler.js +4 -1
- package/dist/lib/core/redisConversationMemoryManager.d.ts +19 -3
- package/dist/lib/core/redisConversationMemoryManager.js +253 -58
- package/dist/lib/files/fileTools.d.ts +3 -3
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +3 -0
- package/dist/lib/mcp/externalServerManager.js +36 -1
- package/dist/lib/memory/memoryRetrievalTools.d.ts +166 -0
- package/dist/lib/memory/memoryRetrievalTools.js +145 -0
- package/dist/lib/neurolink.d.ts +35 -1
- package/dist/lib/neurolink.js +471 -16
- package/dist/lib/providers/amazonBedrock.d.ts +1 -1
- package/dist/lib/providers/amazonBedrock.js +78 -45
- package/dist/lib/providers/amazonSagemaker.d.ts +1 -1
- package/dist/lib/providers/amazonSagemaker.js +1 -1
- package/dist/lib/providers/anthropic.d.ts +1 -1
- package/dist/lib/providers/anthropic.js +7 -7
- package/dist/lib/providers/anthropicBaseProvider.d.ts +1 -1
- package/dist/lib/providers/anthropicBaseProvider.js +7 -6
- package/dist/lib/providers/azureOpenai.d.ts +1 -1
- package/dist/lib/providers/azureOpenai.js +1 -1
- package/dist/lib/providers/googleAiStudio.d.ts +1 -1
- package/dist/lib/providers/googleAiStudio.js +5 -5
- package/dist/lib/providers/googleVertex.d.ts +1 -1
- package/dist/lib/providers/googleVertex.js +74 -17
- package/dist/lib/providers/huggingFace.d.ts +1 -1
- package/dist/lib/providers/huggingFace.js +1 -1
- package/dist/lib/providers/litellm.d.ts +1 -1
- package/dist/lib/providers/litellm.js +18 -16
- package/dist/lib/providers/mistral.d.ts +1 -1
- package/dist/lib/providers/mistral.js +1 -1
- package/dist/lib/providers/ollama.d.ts +1 -1
- package/dist/lib/providers/ollama.js +8 -7
- package/dist/lib/providers/openAI.d.ts +1 -1
- package/dist/lib/providers/openAI.js +6 -6
- package/dist/lib/providers/openRouter.d.ts +1 -1
- package/dist/lib/providers/openRouter.js +6 -2
- package/dist/lib/providers/openaiCompatible.d.ts +1 -1
- package/dist/lib/providers/openaiCompatible.js +1 -1
- package/dist/lib/proxy/proxyFetch.js +291 -65
- package/dist/lib/server/utils/validation.d.ts +4 -4
- package/dist/lib/services/server/ai/observability/instrumentation.js +12 -3
- package/dist/lib/telemetry/telemetryService.d.ts +2 -1
- package/dist/lib/telemetry/telemetryService.js +8 -1
- package/dist/lib/types/contextTypes.d.ts +26 -2
- package/dist/lib/types/conversation.d.ts +72 -40
- package/dist/lib/types/conversationMemoryInterface.d.ts +5 -1
- package/dist/lib/types/generateTypes.d.ts +26 -0
- package/dist/lib/types/modelTypes.d.ts +2 -2
- package/dist/lib/types/multimodal.d.ts +2 -0
- package/dist/lib/types/observability.d.ts +10 -0
- package/dist/lib/types/sdkTypes.d.ts +1 -1
- package/dist/lib/utils/conversationMemory.d.ts +4 -3
- package/dist/lib/utils/conversationMemory.js +44 -6
- package/dist/lib/utils/errorHandling.d.ts +5 -0
- package/dist/lib/utils/errorHandling.js +7 -2
- package/dist/lib/utils/logger.d.ts +8 -0
- package/dist/lib/utils/logger.js +56 -1
- package/dist/lib/utils/messageBuilder.js +74 -4
- package/dist/lib/utils/redis.js +6 -1
- package/dist/lib/utils/tokenEstimation.d.ts +2 -2
- package/dist/lib/utils/tokenEstimation.js +16 -1
- package/dist/lib/utils/videoAnalysisProcessor.d.ts +2 -1
- package/dist/lib/utils/videoAnalysisProcessor.js +7 -2
- package/dist/lib/workflow/config.d.ts +110 -110
- package/dist/mcp/externalServerManager.js +36 -1
- package/dist/memory/memoryRetrievalTools.d.ts +166 -0
- package/dist/memory/memoryRetrievalTools.js +144 -0
- package/dist/neurolink.d.ts +35 -1
- package/dist/neurolink.js +471 -16
- package/dist/providers/amazonBedrock.d.ts +1 -1
- package/dist/providers/amazonBedrock.js +78 -45
- package/dist/providers/amazonSagemaker.d.ts +1 -1
- package/dist/providers/amazonSagemaker.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +7 -7
- package/dist/providers/anthropicBaseProvider.d.ts +1 -1
- package/dist/providers/anthropicBaseProvider.js +7 -6
- package/dist/providers/azureOpenai.d.ts +1 -1
- package/dist/providers/azureOpenai.js +1 -1
- package/dist/providers/googleAiStudio.d.ts +1 -1
- package/dist/providers/googleAiStudio.js +5 -5
- package/dist/providers/googleVertex.d.ts +1 -1
- package/dist/providers/googleVertex.js +74 -17
- package/dist/providers/huggingFace.d.ts +1 -1
- package/dist/providers/huggingFace.js +1 -1
- package/dist/providers/litellm.d.ts +1 -1
- package/dist/providers/litellm.js +18 -16
- package/dist/providers/mistral.d.ts +1 -1
- package/dist/providers/mistral.js +1 -1
- package/dist/providers/ollama.d.ts +1 -1
- package/dist/providers/ollama.js +8 -7
- package/dist/providers/openAI.d.ts +1 -1
- package/dist/providers/openAI.js +6 -6
- package/dist/providers/openRouter.d.ts +1 -1
- package/dist/providers/openRouter.js +6 -2
- package/dist/providers/openaiCompatible.d.ts +1 -1
- package/dist/providers/openaiCompatible.js +1 -1
- package/dist/proxy/proxyFetch.js +291 -65
- package/dist/services/server/ai/observability/instrumentation.js +12 -3
- package/dist/telemetry/telemetryService.d.ts +2 -1
- package/dist/telemetry/telemetryService.js +8 -1
- package/dist/types/contextTypes.d.ts +26 -2
- package/dist/types/conversation.d.ts +72 -40
- package/dist/types/conversationMemoryInterface.d.ts +5 -1
- package/dist/types/generateTypes.d.ts +26 -0
- package/dist/types/modelTypes.d.ts +10 -10
- package/dist/types/multimodal.d.ts +2 -0
- package/dist/types/observability.d.ts +10 -0
- package/dist/types/sdkTypes.d.ts +1 -1
- package/dist/utils/conversationMemory.d.ts +4 -3
- package/dist/utils/conversationMemory.js +44 -6
- package/dist/utils/errorHandling.d.ts +5 -0
- package/dist/utils/errorHandling.js +7 -2
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +56 -1
- package/dist/utils/messageBuilder.js +74 -4
- package/dist/utils/redis.js +6 -1
- package/dist/utils/tokenEstimation.d.ts +2 -2
- package/dist/utils/tokenEstimation.js +16 -1
- package/dist/utils/videoAnalysisProcessor.d.ts +2 -1
- package/dist/utils/videoAnalysisProcessor.js +7 -2
- package/dist/workflow/config.d.ts +12 -12
- package/package.json +1 -1
|
@@ -21,19 +21,20 @@ export class SummarizationEngine {
|
|
|
21
21
|
* @param logPrefix - Prefix for log messages
|
|
22
22
|
* @returns True if summarization was performed
|
|
23
23
|
*/
|
|
24
|
-
async checkAndSummarize(session, threshold, config, logPrefix = "[SummarizationEngine]") {
|
|
25
|
-
const contextMessages = buildContextFromPointer(session);
|
|
24
|
+
async checkAndSummarize(session, threshold, config, logPrefix = "[SummarizationEngine]", requestId) {
|
|
25
|
+
const contextMessages = buildContextFromPointer(session, requestId);
|
|
26
26
|
const tokenCount = this.estimateTokens(contextMessages);
|
|
27
27
|
session.lastTokenCount = tokenCount;
|
|
28
28
|
session.lastCountedAt = Date.now();
|
|
29
|
-
logger.
|
|
29
|
+
logger.info("[Summarization] Check", {
|
|
30
|
+
requestId,
|
|
30
31
|
sessionId: session.sessionId,
|
|
31
32
|
tokenCount,
|
|
32
33
|
threshold,
|
|
33
|
-
|
|
34
|
+
willSummarize: tokenCount >= threshold,
|
|
34
35
|
});
|
|
35
36
|
if (tokenCount >= threshold) {
|
|
36
|
-
await this.summarizeSession(session, threshold, config, logPrefix);
|
|
37
|
+
await this.summarizeSession(session, threshold, config, logPrefix, requestId);
|
|
37
38
|
return true;
|
|
38
39
|
}
|
|
39
40
|
return false;
|
|
@@ -46,7 +47,8 @@ export class SummarizationEngine {
|
|
|
46
47
|
* @param config - Conversation memory configuration (partial allowed)
|
|
47
48
|
* @param logPrefix - Prefix for log messages
|
|
48
49
|
*/
|
|
49
|
-
async summarizeSession(session, threshold, config, logPrefix = "[SummarizationEngine]") {
|
|
50
|
+
async summarizeSession(session, threshold, config, logPrefix = "[SummarizationEngine]", requestId) {
|
|
51
|
+
const startTime = Date.now();
|
|
50
52
|
const startIndex = session.summarizedUpToMessageId
|
|
51
53
|
? session.messages.findIndex((m) => m.id === session.summarizedUpToMessageId) + 1
|
|
52
54
|
: 0;
|
|
@@ -60,21 +62,45 @@ export class SummarizationEngine {
|
|
|
60
62
|
if (messagesToSummarize.length === 0) {
|
|
61
63
|
return;
|
|
62
64
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
const recentToKeep = recentMessages.length - messagesToSummarize.length;
|
|
66
|
+
logger.info("[Summarization] Starting", {
|
|
67
|
+
requestId,
|
|
68
|
+
sessionId: session.sessionId,
|
|
69
|
+
messagesToSummarize: messagesToSummarize.length,
|
|
70
|
+
recentToKeep,
|
|
71
|
+
hasPreviousSummary: !!session.summarizedMessage,
|
|
72
|
+
});
|
|
73
|
+
try {
|
|
74
|
+
const summary = await generateSummary(messagesToSummarize, config, logPrefix, session.summarizedMessage, requestId);
|
|
75
|
+
if (!summary) {
|
|
76
|
+
logger.warn(`${logPrefix} Summary generation failed`, {
|
|
77
|
+
requestId,
|
|
78
|
+
sessionId: session.sessionId,
|
|
79
|
+
durationMs: Date.now() - startTime,
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const lastSummarized = messagesToSummarize[messagesToSummarize.length - 1];
|
|
84
|
+
session.summarizedUpToMessageId = lastSummarized.id;
|
|
85
|
+
session.summarizedMessage = summary;
|
|
86
|
+
logger.info("[Summarization] Complete", {
|
|
87
|
+
requestId,
|
|
66
88
|
sessionId: session.sessionId,
|
|
89
|
+
summaryChars: summary.length,
|
|
90
|
+
newPointerId: lastSummarized.id,
|
|
91
|
+
durationMs: Date.now() - startTime,
|
|
67
92
|
});
|
|
68
|
-
return;
|
|
69
93
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
catch (err) {
|
|
95
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
96
|
+
logger.error("[Summarization] Error", {
|
|
97
|
+
requestId,
|
|
98
|
+
sessionId: session.sessionId,
|
|
99
|
+
error: errorMessage,
|
|
100
|
+
durationMs: Date.now() - startTime,
|
|
101
|
+
});
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
78
104
|
}
|
|
79
105
|
/**
|
|
80
106
|
* Estimate total tokens for a message array.
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tool
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Can save full output to disk with a pointer.
|
|
6
|
-
* Modeled on OpenCode's approach.
|
|
2
|
+
* Tool output preview generation.
|
|
3
|
+
* Generates head/tail previews of large tool outputs for context-efficient LLM calls.
|
|
4
|
+
* @module
|
|
7
5
|
*/
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
/**
|
|
11
|
-
export declare const
|
|
12
|
-
|
|
13
|
-
export
|
|
6
|
+
import type { ToolOutputPreviewOptions, ToolOutputPreviewResult } from "../types/contextTypes.js";
|
|
7
|
+
export type { ToolOutputPreviewOptions, ToolOutputPreviewResult, } from "../types/contextTypes.js";
|
|
8
|
+
/** Default maximum preview size in bytes (50KB) */
|
|
9
|
+
export declare const DEFAULT_MAX_PREVIEW_BYTES: number;
|
|
10
|
+
/** Default maximum preview lines */
|
|
11
|
+
export declare const DEFAULT_MAX_PREVIEW_LINES = 2000;
|
|
12
|
+
/** Default head ratio (25% of preview budget) */
|
|
13
|
+
export declare const DEFAULT_HEAD_RATIO = 0.25;
|
|
14
|
+
/** Tool name referenced in truncation notices for on-demand full-output access */
|
|
15
|
+
export declare const RETRIEVE_CONTEXT_TOOL_NAME = "retrieve_context";
|
|
16
|
+
/** Default tail ratio (75% of preview budget) */
|
|
17
|
+
export declare const DEFAULT_TAIL_RATIO = 0.75;
|
|
14
18
|
/**
|
|
15
|
-
*
|
|
19
|
+
* Generate a head/tail preview of a tool output string.
|
|
20
|
+
* If the output is within limits, returns it unchanged with truncated: false.
|
|
21
|
+
* If over limits, keeps the first 25% and last 75% with an omission notice.
|
|
22
|
+
*
|
|
23
|
+
* Industry pattern: 25/75 head/tail split. Head captures schema/headers/structure,
|
|
24
|
+
* tail captures the most recent and typically most relevant data.
|
|
16
25
|
*/
|
|
17
|
-
export declare function
|
|
26
|
+
export declare function generateToolOutputPreview(output: string, options?: ToolOutputPreviewOptions): ToolOutputPreviewResult;
|
|
@@ -1,84 +1,78 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tool
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Can save full output to disk with a pointer.
|
|
6
|
-
* Modeled on OpenCode's approach.
|
|
2
|
+
* Tool output preview generation.
|
|
3
|
+
* Generates head/tail previews of large tool outputs for context-efficient LLM calls.
|
|
4
|
+
* @module
|
|
7
5
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
export const
|
|
14
|
-
/**
|
|
15
|
-
export const
|
|
6
|
+
/** Default maximum preview size in bytes (50KB) */
|
|
7
|
+
export const DEFAULT_MAX_PREVIEW_BYTES = 50 * 1024;
|
|
8
|
+
/** Default maximum preview lines */
|
|
9
|
+
export const DEFAULT_MAX_PREVIEW_LINES = 2_000;
|
|
10
|
+
/** Default head ratio (25% of preview budget) */
|
|
11
|
+
export const DEFAULT_HEAD_RATIO = 0.25;
|
|
12
|
+
/** Tool name referenced in truncation notices for on-demand full-output access */
|
|
13
|
+
export const RETRIEVE_CONTEXT_TOOL_NAME = "retrieve_context";
|
|
14
|
+
/** Default tail ratio (75% of preview budget) */
|
|
15
|
+
export const DEFAULT_TAIL_RATIO = 0.75;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Generate a head/tail preview of a tool output string.
|
|
18
|
+
* If the output is within limits, returns it unchanged with truncated: false.
|
|
19
|
+
* If over limits, keeps the first 25% and last 75% with an omission notice.
|
|
20
|
+
*
|
|
21
|
+
* Industry pattern: 25/75 head/tail split. Head captures schema/headers/structure,
|
|
22
|
+
* tail captures the most recent and typically most relevant data.
|
|
18
23
|
*/
|
|
19
|
-
export function
|
|
20
|
-
const maxBytes = options?.maxBytes ??
|
|
21
|
-
const maxLines = options?.maxLines ??
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
+
export function generateToolOutputPreview(output, options) {
|
|
25
|
+
const maxBytes = options?.maxBytes ?? DEFAULT_MAX_PREVIEW_BYTES;
|
|
26
|
+
const maxLines = options?.maxLines ?? DEFAULT_MAX_PREVIEW_LINES;
|
|
27
|
+
const rawHeadRatio = options?.headRatio ?? DEFAULT_HEAD_RATIO;
|
|
28
|
+
const rawTailRatio = options?.tailRatio ?? DEFAULT_TAIL_RATIO;
|
|
29
|
+
// Clamp ratios to valid range to avoid negative omittedBytes
|
|
30
|
+
const headRatio = Math.max(0, Math.min(1, rawHeadRatio));
|
|
31
|
+
const tailRatio = Math.max(0, Math.min(1, rawTailRatio));
|
|
24
32
|
const originalSize = Buffer.byteLength(output, "utf-8");
|
|
25
|
-
// Check byte limit
|
|
26
|
-
const exceedsBytes = originalSize > maxBytes;
|
|
27
|
-
// Check line limit
|
|
28
33
|
const lines = output.split("\n");
|
|
34
|
+
const exceedsBytes = originalSize > maxBytes;
|
|
29
35
|
const exceedsLines = lines.length > maxLines;
|
|
30
36
|
if (!exceedsBytes && !exceedsLines) {
|
|
31
|
-
return {
|
|
37
|
+
return { preview: output, truncated: false, originalSize };
|
|
32
38
|
}
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
mkdirSync(saveDir, { recursive: true });
|
|
39
|
-
savedPath = join(saveDir, `tool-output-${randomUUID()}.txt`);
|
|
40
|
-
writeFileSync(savedPath, output, "utf-8");
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
// Silently fail disk save
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
// Apply truncation
|
|
47
|
-
let truncated;
|
|
39
|
+
// Line-based split
|
|
40
|
+
const headLineCount = Math.max(1, Math.floor(maxLines * headRatio));
|
|
41
|
+
const tailLineCount = Math.max(1, maxLines - headLineCount);
|
|
42
|
+
let head;
|
|
43
|
+
let tail;
|
|
48
44
|
if (exceedsLines) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
truncated = lines.slice(-maxLines).join("\n");
|
|
54
|
-
}
|
|
45
|
+
head = lines.slice(0, headLineCount).join("\n");
|
|
46
|
+
tail = lines.slice(-tailLineCount).join("\n");
|
|
55
47
|
}
|
|
56
48
|
else {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
truncated = truncated.slice(-maxBytes);
|
|
66
|
-
}
|
|
49
|
+
head = lines
|
|
50
|
+
.slice(0, Math.max(1, Math.floor(lines.length * headRatio)))
|
|
51
|
+
.join("\n");
|
|
52
|
+
tail = lines
|
|
53
|
+
.slice(-Math.max(1, Math.ceil(lines.length * tailRatio)))
|
|
54
|
+
.join("\n");
|
|
67
55
|
}
|
|
68
|
-
//
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
56
|
+
// Byte-based cap on each portion
|
|
57
|
+
const headMaxBytes = Math.floor(maxBytes * headRatio);
|
|
58
|
+
const tailMaxBytes = maxBytes - headMaxBytes;
|
|
59
|
+
if (Buffer.byteLength(head, "utf-8") > headMaxBytes) {
|
|
60
|
+
head = Buffer.from(head, "utf-8")
|
|
61
|
+
.subarray(0, headMaxBytes)
|
|
62
|
+
.toString("utf-8");
|
|
74
63
|
}
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
if (Buffer.byteLength(tail, "utf-8") > tailMaxBytes) {
|
|
65
|
+
const tailBuf = Buffer.from(tail, "utf-8");
|
|
66
|
+
tail = tailBuf.subarray(tailBuf.length - tailMaxBytes).toString("utf-8");
|
|
77
67
|
}
|
|
68
|
+
const omittedBytes = Math.max(0, originalSize -
|
|
69
|
+
Buffer.byteLength(head, "utf-8") -
|
|
70
|
+
Buffer.byteLength(tail, "utf-8"));
|
|
71
|
+
const notice = `\n\n[... ${omittedBytes} bytes omitted. ` +
|
|
72
|
+
`Use ${RETRIEVE_CONTEXT_TOOL_NAME} tool to access full output ...]\n\n`;
|
|
78
73
|
return {
|
|
79
|
-
|
|
74
|
+
preview: head + notice + tail,
|
|
80
75
|
truncated: true,
|
|
81
|
-
savedPath,
|
|
82
76
|
originalSize,
|
|
83
77
|
};
|
|
84
78
|
}
|
|
@@ -218,9 +218,18 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
218
218
|
*/
|
|
219
219
|
setSessionContext(sessionId?: string, userId?: string): void;
|
|
220
220
|
/**
|
|
221
|
-
* Provider-specific error
|
|
221
|
+
* Provider-specific error formatting.
|
|
222
|
+
* Subclasses implement this to produce human-readable error messages
|
|
223
|
+
* (e.g., "❌ Google Vertex AI Provider Error\n\n...").
|
|
222
224
|
*/
|
|
223
|
-
protected abstract
|
|
225
|
+
protected abstract formatProviderError(error: unknown): Error;
|
|
226
|
+
/**
|
|
227
|
+
* Handle provider errors with abort passthrough.
|
|
228
|
+
* AbortErrors are never wrapped — they must propagate with their
|
|
229
|
+
* original identity so that isAbortError() can detect them in
|
|
230
|
+
* retry/fallback loops (directProviderGeneration, performMCPGenerationRetries).
|
|
231
|
+
*/
|
|
232
|
+
protected handleProviderError(error: unknown): Error;
|
|
224
233
|
/**
|
|
225
234
|
* Image generation method. Providers that support it should override this.
|
|
226
235
|
* By default, it throws an error indicating that the functionality is not supported.
|
|
@@ -86,6 +86,20 @@ export class BaseProvider {
|
|
|
86
86
|
temperature: options.temperature,
|
|
87
87
|
timestamp: Date.now(),
|
|
88
88
|
});
|
|
89
|
+
// ===== EARLY MULTIMODAL DETECTION =====
|
|
90
|
+
const hasFileInput = !!options.input?.files?.length || !!options.input?.videoFiles?.length;
|
|
91
|
+
if (hasFileInput) {
|
|
92
|
+
// ===== VIDEO ANALYSIS DETECTION =====
|
|
93
|
+
// Check if video frames are present and handle with fake streaming
|
|
94
|
+
const messages = await this.buildMessagesForStream(options);
|
|
95
|
+
if (hasVideoFrames(messages)) {
|
|
96
|
+
logger.info(`Video frames detected in stream, using fake streaming for video analysis`, {
|
|
97
|
+
provider: this.providerName,
|
|
98
|
+
model: this.modelName,
|
|
99
|
+
});
|
|
100
|
+
return await this.executeFakeStreaming(options, analysisSchema);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
89
103
|
// 🔧 CRITICAL: Image generation models don't support real streaming
|
|
90
104
|
// Force fake streaming for image models to ensure image output is yielded
|
|
91
105
|
const isImageModel = IMAGE_GENERATION_MODELS.some((m) => this.modelName.includes(m));
|
|
@@ -160,7 +174,7 @@ export class BaseProvider {
|
|
|
160
174
|
temperature: options.temperature,
|
|
161
175
|
maxTokens: options.maxTokens,
|
|
162
176
|
tools: options.tools, // 🔧 FIX: Pass user-provided tools (including RAG tools) to generation pipeline
|
|
163
|
-
disableTools:
|
|
177
|
+
disableTools: !!options.disableTools,
|
|
164
178
|
maxSteps: options.maxSteps || 5,
|
|
165
179
|
provider: options.provider,
|
|
166
180
|
model: options.model,
|
|
@@ -753,6 +767,21 @@ export class BaseProvider {
|
|
|
753
767
|
this.userId = userId;
|
|
754
768
|
this.toolsManager.setSessionContext(sessionId, userId);
|
|
755
769
|
}
|
|
770
|
+
/**
|
|
771
|
+
* Handle provider errors with abort passthrough.
|
|
772
|
+
* AbortErrors are never wrapped — they must propagate with their
|
|
773
|
+
* original identity so that isAbortError() can detect them in
|
|
774
|
+
* retry/fallback loops (directProviderGeneration, performMCPGenerationRetries).
|
|
775
|
+
*/
|
|
776
|
+
handleProviderError(error) {
|
|
777
|
+
if (isAbortError(error)) {
|
|
778
|
+
// Preserve AbortError identity — never wrap in provider-specific formatting
|
|
779
|
+
return error instanceof Error
|
|
780
|
+
? error
|
|
781
|
+
: new DOMException("The operation was aborted", "AbortError");
|
|
782
|
+
}
|
|
783
|
+
return this.formatProviderError(error);
|
|
784
|
+
}
|
|
756
785
|
/**
|
|
757
786
|
* Image generation method. Providers that support it should override this.
|
|
758
787
|
* By default, it throws an error indicating that the functionality is not supported.
|
|
@@ -50,7 +50,7 @@ export declare class ConversationMemoryManager implements IConversationMemoryMan
|
|
|
50
50
|
* Returns messages from pointer onwards (or all if no pointer)
|
|
51
51
|
* Now consistently async to match Redis implementation
|
|
52
52
|
*/
|
|
53
|
-
buildContextMessages(sessionId: string): Promise<ChatMessage[]>;
|
|
53
|
+
buildContextMessages(sessionId: string, _userId?: string, _enableSummarization?: boolean, requestId?: string): Promise<ChatMessage[]>;
|
|
54
54
|
getSession(sessionId: string, _userId?: string): SessionMemory | undefined;
|
|
55
55
|
createSummarySystemMessage(content: string, summarizesFrom?: string, summarizesTo?: string): ChatMessage;
|
|
56
56
|
private ensureInitialized;
|
|
@@ -59,4 +59,16 @@ export declare class ConversationMemoryManager implements IConversationMemoryMan
|
|
|
59
59
|
getStats(): Promise<ConversationMemoryStats>;
|
|
60
60
|
clearSession(sessionId: string): Promise<boolean>;
|
|
61
61
|
clearAllSessions(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Get the raw messages array for a session.
|
|
64
|
+
* Returns the full messages list without context filtering or summarization.
|
|
65
|
+
* Returns a deep copy to prevent external mutation of internal state.
|
|
66
|
+
*/
|
|
67
|
+
getSessionMessages(sessionId: string, _userId?: string): Promise<ChatMessage[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Replace the entire messages array for a session.
|
|
70
|
+
* Creates the session if it does not exist.
|
|
71
|
+
* Resets summary pointers since old pointers may reference messages that no longer exist.
|
|
72
|
+
*/
|
|
73
|
+
setSessionMessages(sessionId: string, messages: ChatMessage[], userId?: string): Promise<void>;
|
|
62
74
|
}
|
|
@@ -92,11 +92,12 @@ export class ConversationMemoryManager {
|
|
|
92
92
|
if (!this.summarizationInProgress.has(options.sessionId)) {
|
|
93
93
|
setImmediate(async () => {
|
|
94
94
|
try {
|
|
95
|
-
await this.checkAndSummarize(session, tokenThreshold);
|
|
95
|
+
await this.checkAndSummarize(session, tokenThreshold, options.requestId);
|
|
96
96
|
}
|
|
97
97
|
catch (error) {
|
|
98
98
|
logger.error("Background summarization failed", {
|
|
99
99
|
sessionId: session.sessionId,
|
|
100
|
+
requestId: options.requestId,
|
|
100
101
|
error: error instanceof Error ? error.message : String(error),
|
|
101
102
|
});
|
|
102
103
|
}
|
|
@@ -154,7 +155,7 @@ export class ConversationMemoryManager {
|
|
|
154
155
|
/**
|
|
155
156
|
* Check if summarization is needed based on token count
|
|
156
157
|
*/
|
|
157
|
-
async checkAndSummarize(session, threshold) {
|
|
158
|
+
async checkAndSummarize(session, threshold, requestId) {
|
|
158
159
|
// Acquire lock - if already in progress, skip
|
|
159
160
|
if (this.summarizationInProgress.has(session.sessionId)) {
|
|
160
161
|
logger.debug("[ConversationMemoryManager] Summarization already in progress, skipping", {
|
|
@@ -164,7 +165,7 @@ export class ConversationMemoryManager {
|
|
|
164
165
|
}
|
|
165
166
|
this.summarizationInProgress.add(session.sessionId);
|
|
166
167
|
try {
|
|
167
|
-
await this.summarizationEngine.checkAndSummarize(session, threshold, this.config, "[ConversationMemory]");
|
|
168
|
+
await this.summarizationEngine.checkAndSummarize(session, threshold, this.config, "[ConversationMemory]", requestId);
|
|
168
169
|
}
|
|
169
170
|
catch (error) {
|
|
170
171
|
logger.error("Token counting or summarization failed", {
|
|
@@ -195,9 +196,9 @@ export class ConversationMemoryManager {
|
|
|
195
196
|
* Returns messages from pointer onwards (or all if no pointer)
|
|
196
197
|
* Now consistently async to match Redis implementation
|
|
197
198
|
*/
|
|
198
|
-
async buildContextMessages(sessionId) {
|
|
199
|
+
async buildContextMessages(sessionId, _userId, _enableSummarization, requestId) {
|
|
199
200
|
const session = this.sessions.get(sessionId);
|
|
200
|
-
return session ? buildContextFromPointer(session) : [];
|
|
201
|
+
return session ? buildContextFromPointer(session, requestId) : [];
|
|
201
202
|
}
|
|
202
203
|
getSession(sessionId, _userId) {
|
|
203
204
|
return this.sessions.get(sessionId);
|
|
@@ -263,4 +264,34 @@ export class ConversationMemoryManager {
|
|
|
263
264
|
this.sessions.clear();
|
|
264
265
|
logger.info("All sessions cleared", { clearedCount: sessionIds.length });
|
|
265
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Get the raw messages array for a session.
|
|
269
|
+
* Returns the full messages list without context filtering or summarization.
|
|
270
|
+
* Returns a deep copy to prevent external mutation of internal state.
|
|
271
|
+
*/
|
|
272
|
+
async getSessionMessages(sessionId, _userId) {
|
|
273
|
+
await this.ensureInitialized();
|
|
274
|
+
const session = this.sessions.get(sessionId);
|
|
275
|
+
return session ? session.messages.map((msg) => ({ ...msg })) : [];
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Replace the entire messages array for a session.
|
|
279
|
+
* Creates the session if it does not exist.
|
|
280
|
+
* Resets summary pointers since old pointers may reference messages that no longer exist.
|
|
281
|
+
*/
|
|
282
|
+
async setSessionMessages(sessionId, messages, userId) {
|
|
283
|
+
await this.ensureInitialized();
|
|
284
|
+
let session = this.sessions.get(sessionId);
|
|
285
|
+
if (!session) {
|
|
286
|
+
session = this.createNewSession(sessionId, userId);
|
|
287
|
+
this.sessions.set(sessionId, session);
|
|
288
|
+
this.enforceSessionLimit();
|
|
289
|
+
}
|
|
290
|
+
session.messages = [...messages];
|
|
291
|
+
session.summarizedUpToMessageId = undefined;
|
|
292
|
+
session.summarizedMessage = undefined;
|
|
293
|
+
session.lastTokenCount = undefined;
|
|
294
|
+
session.lastCountedAt = undefined;
|
|
295
|
+
session.lastActivity = Date.now();
|
|
296
|
+
}
|
|
266
297
|
}
|
|
@@ -38,6 +38,12 @@ export declare class GenerationHandler {
|
|
|
38
38
|
* Execute the generation with AI SDK
|
|
39
39
|
*/
|
|
40
40
|
executeGeneration(model: LanguageModelV1, messages: CoreMessage[], tools: Record<string, Tool>, options: TextGenerationOptions): Promise<Awaited<ReturnType<typeof generateText>>>;
|
|
41
|
+
/**
|
|
42
|
+
* Extract cache metrics from provider metadata (e.g. Anthropic's providerMetadata.anthropic)
|
|
43
|
+
* The Vercel AI SDK's LanguageModelUsage only has promptTokens/completionTokens/totalTokens.
|
|
44
|
+
* Cache metrics are surfaced via providerMetadata by provider-specific SDK adapters.
|
|
45
|
+
*/
|
|
46
|
+
private extractCacheMetricsFromProviderMetadata;
|
|
41
47
|
/**
|
|
42
48
|
* Log generation completion information
|
|
43
49
|
*/
|