fix(native-chat): preserve image caption whitespace

This commit is contained in:
Brennan Benson
2026-08-12 19:43:14 -07:00
parent 3d4658ed12
commit ddbec7b9b0
2 changed files with 29 additions and 9 deletions
@@ -43,14 +43,25 @@ describe('normalizeImageTranscriptMessages', () => {
it.each([
['[Image #1] describe this', 'describe this'],
['[Image #1]\t describe this', 'describe this'],
['describe this [Image #1]', 'describe this'],
['describe [Image #1] this', 'describe this'],
['describe this \t[Image #1]', 'describe this'],
['describe [Image #1] this', 'describe this'],
['describe [Image #1]\t this', 'describe \t this'],
['describe[Image #1]\t this', 'describe\t this'],
['describe\n[Image #1]\nthis', 'describe\n\nthis'],
['com[Image #1]pare this', 'compare this'],
['[Image #1] [Image #2]', '']
['[Image #1] [Image #2]', ''],
['literal [Image #x] text', 'literal [Image #x] text']
])('strips image prompt markers anywhere in text', (text, expected) => {
expect(stripImagePromptMarker(text)).toBe(expected)
})
it('returns long marker-free whitespace without regex backtracking', () => {
const text = ' '.repeat(50_000)
expect(stripImagePromptMarker(text)).toBe(text)
})
it('converts a lone [Image: source] turn (no prompt) into an image-ref instead of raw text', () => {
const out = normalizeImageTranscriptMessages([
userText('a', '[Image: source: /Users/me/Pictures/hero-image-2.png]')
@@ -2,7 +2,11 @@ import { isTextBlock, type NativeChatBlock, type NativeChatMessage } from './nat
const IMAGE_SOURCE_MARKER = /^\[Image:\s*source:\s*(.+?)\]\s*$/
const IMAGE_PROMPT_MARKER = /\[Image #\d+\]/
const IMAGE_PROMPT_MARKER_RUN = /[^\S\r\n]*(?:\[Image #\d+\][^\S\r\n]*)+/g
const IMAGE_PROMPT_MARKERS = /\[Image #\d+\]/g
const IMAGE_PROMPT_MARKER_AT_START = /^\[Image #\d+\]/
const IMAGE_PROMPT_MARKER_AT_END = /\[Image #\d+\]$/
const HORIZONTAL_WHITESPACE_START = /^[^\S\r\n]+/
const HORIZONTAL_WHITESPACE_END = /[^\S\r\n]+$/
function soleText(message: NativeChatMessage): string | null {
return message.blocks.length === 1 && isTextBlock(message.blocks[0])
@@ -15,12 +19,17 @@ export function imageSourcePathFromText(text: string): string | null {
}
export function stripImagePromptMarker(text: string): string {
return text.replace(IMAGE_PROMPT_MARKER_RUN, (run, offset: number, source: string) => {
const hasTextBefore = offset > 0
const hasTextAfter = offset + run.length < source.length
const hadSurroundingWhitespace = run.replace(/\[Image #\d+\]/g, '').length > 0
return hasTextBefore && hasTextAfter && hadSurroundingWhitespace ? ' ' : ''
})
const stripped = text.replace(IMAGE_PROMPT_MARKERS, '')
if (stripped === text) {
return text
}
let result = IMAGE_PROMPT_MARKER_AT_START.test(text)
? stripped.replace(HORIZONTAL_WHITESPACE_START, '')
: stripped
if (IMAGE_PROMPT_MARKER_AT_END.test(text)) {
result = result.replace(HORIZONTAL_WHITESPACE_END, '')
}
return result
}
function stripImagePromptMarkersFromFirstText(