@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
79 lines (78 loc) • 3.37 kB
JavaScript
/**
* Tool output preview generation.
* Generates head/tail previews of large tool outputs for context-efficient LLM calls.
* @module
*/
/** Default maximum preview size in bytes (50KB) */
export const DEFAULT_MAX_PREVIEW_BYTES = 50 * 1024;
/** Default maximum preview lines */
export const DEFAULT_MAX_PREVIEW_LINES = 2_000;
/** Default head ratio (25% of preview budget) */
export const DEFAULT_HEAD_RATIO = 0.25;
/** Tool name referenced in truncation notices for on-demand full-output access */
export const RETRIEVE_CONTEXT_TOOL_NAME = "retrieve_context";
/** Default tail ratio (75% of preview budget) */
export const DEFAULT_TAIL_RATIO = 0.75;
/**
* Generate a head/tail preview of a tool output string.
* If the output is within limits, returns it unchanged with truncated: false.
* If over limits, keeps the first 25% and last 75% with an omission notice.
*
* Industry pattern: 25/75 head/tail split. Head captures schema/headers/structure,
* tail captures the most recent and typically most relevant data.
*/
export function generateToolOutputPreview(output, options) {
const maxBytes = options?.maxBytes ?? DEFAULT_MAX_PREVIEW_BYTES;
const maxLines = options?.maxLines ?? DEFAULT_MAX_PREVIEW_LINES;
const rawHeadRatio = options?.headRatio ?? DEFAULT_HEAD_RATIO;
const rawTailRatio = options?.tailRatio ?? DEFAULT_TAIL_RATIO;
// Clamp ratios to valid range to avoid negative omittedBytes
const headRatio = Math.max(0, Math.min(1, rawHeadRatio));
const tailRatio = Math.max(0, Math.min(1, rawTailRatio));
const originalSize = Buffer.byteLength(output, "utf-8");
const lines = output.split("\n");
const exceedsBytes = originalSize > maxBytes;
const exceedsLines = lines.length > maxLines;
if (!exceedsBytes && !exceedsLines) {
return { preview: output, truncated: false, originalSize };
}
// Line-based split
const headLineCount = Math.max(1, Math.floor(maxLines * headRatio));
const tailLineCount = Math.max(1, maxLines - headLineCount);
let head;
let tail;
if (exceedsLines) {
head = lines.slice(0, headLineCount).join("\n");
tail = lines.slice(-tailLineCount).join("\n");
}
else {
head = lines
.slice(0, Math.max(1, Math.floor(lines.length * headRatio)))
.join("\n");
tail = lines
.slice(-Math.max(1, Math.ceil(lines.length * tailRatio)))
.join("\n");
}
// Byte-based cap on each portion
const headMaxBytes = Math.floor(maxBytes * headRatio);
const tailMaxBytes = maxBytes - headMaxBytes;
if (Buffer.byteLength(head, "utf-8") > headMaxBytes) {
head = Buffer.from(head, "utf-8")
.subarray(0, headMaxBytes)
.toString("utf-8");
}
if (Buffer.byteLength(tail, "utf-8") > tailMaxBytes) {
const tailBuf = Buffer.from(tail, "utf-8");
tail = tailBuf.subarray(tailBuf.length - tailMaxBytes).toString("utf-8");
}
const omittedBytes = Math.max(0, originalSize -
Buffer.byteLength(head, "utf-8") -
Buffer.byteLength(tail, "utf-8"));
const notice = `\n\n[... ${omittedBytes} bytes omitted. ` +
`Use ${RETRIEVE_CONTEXT_TOOL_NAME} tool to access full output ...]\n\n`;
return {
preview: head + notice + tail,
truncated: true,
originalSize,
};
}