Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,8 @@ export function matchingNativeChatUserTexts(
}

/**
* How many leading pending texts concatenate exactly to `userText`.
* Covers rapid-send glue ("joke"+"continue" → "jokecontinue") without matching
* unrelated prefixes ("hi" ↛ "history").
* How many leading pending texts concatenate exactly to `userText`, with or
* without one normalized whitespace separator at each send boundary.
*/
export function countLeadingPendingTextsGluedToUserText(
pendingTexts: readonly string[],
Expand All @@ -125,17 +124,23 @@ export function countLeadingPendingTextsGluedToUserText(
if (pendingTexts.length === 0 || userText.length === 0) {
return 0
}
let combined = ''
let candidates = new Set<string>()
for (let index = 0; index < pendingTexts.length; index += 1) {
const piece = pendingTexts[index]
if (!piece) {
return 0
}
combined += piece
if (combined === userText) {
const next =
index === 0
? new Set([piece])
: new Set(
[...candidates].flatMap((candidate) => [candidate + piece, `${candidate} ${piece}`])
)
candidates = new Set([...next].filter((candidate) => userText.startsWith(candidate)))
if (candidates.has(userText)) {
return index + 1
}
if (!userText.startsWith(combined)) {
if (candidates.size === 0) {
return 0
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,36 @@ describe('prunePendingSends', () => {
).toEqual([])
})

it('prunes optimistic sends glued with a retained whitespace separator', () => {
const pending = [pendingOf('p1', 'tell me a joke '), pendingOf('p2', 'continue')]
expect(
prunePendingSends(pending, [
userMessage('u1', 'tell me a joke continue'),
assistantMessage('a1', 'a joke')
])
).toEqual([])
})

it('matches mixed separated and separator-less rapid sends', () => {
const pending = [pendingOf('p1', 'first'), pendingOf('p2', 'second '), pendingOf('p3', 'third')]
expect(
prunePendingSends(pending, [
userMessage('u1', 'firstsecond third'),
assistantMessage('a1', 'done')
])
).toEqual([])
})

it('prunes repeated prompts represented by one separated transcript turn', () => {
const pending = [pendingOf('p1', 'repeat '), pendingOf('p2', 'repeat')]
expect(
prunePendingSends(pending, [
userMessage('u1', 'repeat repeat'),
assistantMessage('a1', 'done')
])
).toEqual([])
})

it('does not treat an unrelated longer user turn as a glued match', () => {
const pending = [pendingOf('p1', 'hi')]
expect(
Expand All @@ -198,6 +228,16 @@ describe('prunePendingSends', () => {
])
).toEqual(pending)
})

it('does not accept a separated glue candidate that is only a user-text prefix', () => {
const pending = [pendingOf('p1', 'hi'), pendingOf('p2', 'story')]
expect(
prunePendingSends(pending, [
userMessage('u1', 'hi story continued'),
assistantMessage('a1', 'ok')
])
).toEqual(pending)
})
})

describe('pendingSendsAsMessages', () => {
Expand Down
Loading