UNPKG

n8n

Version:

n8n Workflow Automation Tool

161 lines 6.43 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PREVIEW_CONTEXT_CLOSE_TAG = exports.PREVIEW_CONTEXT_OPEN_TAG = void 0; exports.formatPreviewSessionContext = formatPreviewSessionContext; exports.PREVIEW_CONTEXT_OPEN_TAG = '<preview_session_context>'; exports.PREVIEW_CONTEXT_CLOSE_TAG = '</preview_session_context>'; const MAX_TEXT_CHARS = 4_000; const MAX_TOOL_VALUE_CHARS = 2_000; const MAX_BLOCK_CHARS = 100_000; const TRUNCATION_FOOTER = '… [truncated]'; function truncate(value, max) { if (value.length <= max) return value; return value.slice(0, max) + TRUNCATION_FOOTER; } function truncateBlockToFit(value, max) { if (max <= TRUNCATION_FOOTER.length) return null; if (value.length <= max) return value; return value.slice(0, max - TRUNCATION_FOOTER.length) + TRUNCATION_FOOTER; } function stringifyToolValue(value) { if (value === undefined) return '(none)'; let serialized; try { serialized = JSON.stringify(value) ?? 'undefined'; } catch { serialized = String(value); } return truncate(serialized, MAX_TOOL_VALUE_CHARS); } function formatTimelineEvent(event) { if (event.type === 'text') { return `Assistant: ${truncate(event.content, MAX_TEXT_CHARS)}`; } if (event.type === 'suspension') { return `[suspended waiting on ${event.toolName}]`; } const durationMs = event.endTime > 0 ? event.endTime - event.startTime : null; const headerParts = [ `Tool call: ${event.name}`, `kind=${event.kind}`, event.success ? 'succeeded' : 'failed', ]; if (durationMs !== null) headerParts.push(`${durationMs}ms`); if (event.workflowName) headerParts.push(`workflow=${event.workflowName}`); if (event.nodeDisplayName) headerParts.push(`node=${event.nodeDisplayName}`); return [ headerParts.join(' | '), ` Input: ${stringifyToolValue(event.input)}`, ` Output: ${stringifyToolValue(event.output)}`, ].join('\n'); } function formatExecution(execution) { const headerParts = [`status=${execution.status}`]; if (execution.model) headerParts.push(`model=${execution.model}`); headerParts.push(`duration=${execution.duration}ms`); if (execution.totalTokens !== null) headerParts.push(`tokens=${execution.totalTokens}`); if (execution.error) headerParts.push(`error=${execution.error}`); const lines = [`## Turn (${headerParts.join(', ')})`]; if (execution.userMessage !== null) { lines.push(`User: ${truncate(execution.userMessage, MAX_TEXT_CHARS)}`); } for (const event of execution.timeline ?? []) { lines.push(formatTimelineEvent(event)); } return lines.join('\n'); } function selectTurnExecutions(executions, executionId) { let anchorIndex = executions.findIndex((execution) => execution.id === executionId); if (anchorIndex === -1) return null; while (anchorIndex > 0 && executions[anchorIndex].userMessage === null) { anchorIndex--; } const selected = [executions[anchorIndex]]; for (let i = anchorIndex + 1; i < executions.length; i++) { if (executions[i].userMessage !== null) break; selected.push(executions[i]); } return selected; } function formatPreviewSessionContext(thread, executions, executionId) { const scoped = executionId === undefined ? executions : selectTurnExecutions(executions, executionId); if (scoped === null) return null; const scopeLabel = executionId === undefined ? 'whole session' : 'single turn'; const header = `Preview session transcript — title: ${thread.title ?? '(untitled)'}, ` + `session #${thread.sessionNumber}, scope: ${scopeLabel}, turns: ${scoped.length}`; const prefix = [ exports.PREVIEW_CONTEXT_OPEN_TAG, header, '', 'The user shared this transcript of a real conversation with the target agent', 'so you can assess its behavior and improve its configuration.', ].join('\n'); const closing = `\n${exports.PREVIEW_CONTEXT_CLOSE_TAG}`; const includedNewestFirst = []; let includedLength = 0; let boundaryTurn = null; let omittedEarlierTurns = 0; for (let i = scoped.length - 1; i >= 0; i--) { const formatted = `\n${formatExecution(scoped[i])}`; const projectedLength = prefix.length + includedLength + formatted.length + closing.length; if (projectedLength > MAX_BLOCK_CHARS) { boundaryTurn = formatted; omittedEarlierTurns = i; break; } includedNewestFirst.push(formatted); includedLength += formatted.length; } const includedTurns = includedNewestFirst.reverse(); if (boundaryTurn !== null) { for (;;) { const footerCandidate = omittedEarlierTurns > 0 ? `\n[transcript truncated: ${omittedEarlierTurns} earlier turns omitted]` : ''; const remainingBudget = MAX_BLOCK_CHARS - prefix.length - includedLength - footerCandidate.length - closing.length; const truncatedBoundary = truncateBlockToFit(boundaryTurn, remainingBudget); if (truncatedBoundary !== null) { includedTurns.unshift(truncatedBoundary); includedLength += truncatedBoundary.length; break; } omittedEarlierTurns++; if (includedTurns.length === 0) break; const removedTurn = includedTurns.shift(); if (!removedTurn) break; includedLength -= removedTurn.length; } } let footer = ''; while (omittedEarlierTurns > 0) { const footerCandidate = `\n[transcript truncated: ${omittedEarlierTurns} earlier turns omitted]`; const projectedLength = prefix.length + includedLength + footerCandidate.length + closing.length; if (projectedLength <= MAX_BLOCK_CHARS) { footer = footerCandidate; break; } const removedTurn = includedTurns.shift(); if (!removedTurn) break; includedLength -= removedTurn.length; omittedEarlierTurns++; } return prefix + includedTurns.join('') + footer + closing; } //# sourceMappingURL=format-preview-context.js.map