Merge pull request #28829 from NousResearch/bb/tui-no-history-truncation
fix(tui): render full assistant text in scrollback (no history truncation)
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
import {
|
||||
HISTORY_RENDER_MAX_CHARS,
|
||||
HISTORY_RENDER_MAX_LINES,
|
||||
LIVE_RENDER_MAX_CHARS,
|
||||
LIVE_RENDER_MAX_LINES,
|
||||
THINKING_COT_MAX
|
||||
@@ -129,11 +127,6 @@ export const boundedLiveRenderText = (
|
||||
{ maxChars = LIVE_RENDER_MAX_CHARS, maxLines = LIVE_RENDER_MAX_LINES } = {}
|
||||
) => boundedRenderText(text, 'showing live tail', { maxChars, maxLines })
|
||||
|
||||
export const boundedHistoryRenderText = (
|
||||
text: string,
|
||||
{ maxChars = HISTORY_RENDER_MAX_CHARS, maxLines = HISTORY_RENDER_MAX_LINES } = {}
|
||||
) => boundedRenderText(text, 'showing tail', { maxChars, maxLines })
|
||||
|
||||
const boundedRenderText = (
|
||||
text: string,
|
||||
labelPrefix: string,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { Msg } from '../types.js'
|
||||
|
||||
import { transcriptBodyWidth } from './inputMetrics.js'
|
||||
import { boundedHistoryRenderText } from './text.js'
|
||||
|
||||
const hashText = (text: string) => {
|
||||
let h = 5381
|
||||
@@ -30,10 +29,40 @@ export const messageHeightKey = (msg: Msg) => {
|
||||
].join(':')
|
||||
}
|
||||
|
||||
export const wrappedLines = (text: string, width: number) => {
|
||||
const w = Math.max(1, width)
|
||||
// Hard cap on rows the estimator will count. Each row above this is
|
||||
// invisible to the estimator (gets clipped to MAX_ESTIMATE_LINES), but
|
||||
// post-mount Yoga measurement converges to the real height on first
|
||||
// render. Without this, a long assistant turn (10k+ chars) costs O(text)
|
||||
// per offset rebuild × every uncached item — cold-mounting a 1000-row
|
||||
// transcript becomes a multi-million-char wrap walk that blocks the UI.
|
||||
//
|
||||
// 800 covers any realistic assistant message (the prior history-clip
|
||||
// ceiling was 16 lines, then full text — this is the sane middle).
|
||||
const MAX_ESTIMATE_LINES = 800
|
||||
|
||||
return text.split('\n').reduce((n, line) => n + Math.max(1, Math.ceil(line.length / w)), 0)
|
||||
export const wrappedLines = (text: string, width: number, maxLines: number = MAX_ESTIMATE_LINES) => {
|
||||
const w = Math.max(1, width)
|
||||
// Worst case: every cell is its own row at width=1, plus a small
|
||||
// slack for the trailing partial line. Walking past this byte budget
|
||||
// cannot increase n any further once n is already past maxLines, so
|
||||
// bail. Saves O(text) walks on multi-megabyte single-line messages.
|
||||
const budget = Math.min(text.length, maxLines * w + maxLines)
|
||||
let n = 0
|
||||
let start = 0
|
||||
|
||||
for (let i = 0; i <= budget; i++) {
|
||||
if (i === text.length || i === budget || text.charCodeAt(i) === 10) {
|
||||
const rows = Math.max(1, Math.ceil((i - start) / w))
|
||||
n += rows >= maxLines - n ? maxLines - n : rows
|
||||
start = i + 1
|
||||
|
||||
if (n >= maxLines) {
|
||||
return maxLines
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
export const estimatedMsgHeight = (
|
||||
@@ -42,13 +71,11 @@ export const estimatedMsgHeight = (
|
||||
{
|
||||
compact,
|
||||
details,
|
||||
limitHistory = false,
|
||||
userPrompt = '',
|
||||
withSeparator = false
|
||||
}: {
|
||||
compact: boolean
|
||||
details: boolean
|
||||
limitHistory?: boolean
|
||||
userPrompt?: string
|
||||
withSeparator?: boolean
|
||||
}
|
||||
@@ -70,11 +97,16 @@ export const estimatedMsgHeight = (
|
||||
}
|
||||
|
||||
const bodyWidth = transcriptBodyWidth(cols, msg.role, userPrompt)
|
||||
const text = msg.role === 'assistant' && limitHistory ? boundedHistoryRenderText(msg.text) : msg.text
|
||||
const text = msg.text
|
||||
let h = wrappedLines(text || ' ', bodyWidth)
|
||||
|
||||
if (!compact && msg.role === 'assistant') {
|
||||
h += Math.min(6, (text.match(/\n\s*\n/g) ?? []).length)
|
||||
// Paragraph gaps add up to 6 extra rows of breathing room. Slice
|
||||
// first so the regex never walks more than the first ~16k chars of
|
||||
// a giant assistant message — post-mount Yoga measurement converges
|
||||
// to the real height regardless of how the estimate undercounts.
|
||||
const scan = text.length > 16_000 ? text.slice(0, 16_000) : text
|
||||
h += Math.min(6, (scan.match(/\n\s*\n/g) ?? []).length)
|
||||
}
|
||||
|
||||
if (details) {
|
||||
|
||||
Reference in New Issue
Block a user