fix(yuanbao-adapter): correct fragment merge and json candidate parsing

Stop dropping short repeated fragments in mergeText (append them unless
the duplicate is long enough to be a real echo) and drop the unreliable
reverse-overlap branch. Return early from extractJSONCandidates when the
whole body parses, and drop the seen-set so line-level frames are no
longer deduped away.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 14:57:25 +08:00
parent d2cb2faa61
commit 977ffb1335
2 changed files with 40 additions and 13 deletions
@@ -69,6 +69,36 @@ describe('yuanbao adapter helpers', () => {
expect(mergeText(first, repeated)).toBe(repeated)
})
it('preserves repeated Markdown tokens from incremental SSE frames', () => {
const fragments = [
'### 推荐清单\n\n- **',
'拾光里',
'**:本地工厂',
'\n- **',
'欧派',
'**:全国连锁',
'\n\n1. **',
'看板材',
'**',
'\n2. **',
'看封边',
'**',
]
const summary = parseYuanbaoCaptures([
{
url: 'https://yuanbao.tencent.com/api/chat/conversation-markdown',
status: 200,
contentType: 'text/event-stream',
body: fragments.map((msg) => `data: ${JSON.stringify({ type: 'text', msg })}`).join('\n\n'),
},
])
expect(summary.answer).toBe(
'### 推荐清单\n\n- **拾光里**:本地工厂\n- **欧派**:全国连锁\n\n1. **看板材**\n2. **看封边**',
)
})
it('keeps a valid API SSE answer even when the DOM fallback is longer', () => {
const apiAnswer =
'### 核心结论\n\n混动车兼顾补能效率与低速油耗。\n\n- 适合长途出行\n- 适合没有固定充电桩的用户'
@@ -426,8 +426,8 @@ function mergeText(current: string | null, nextFragment: string | null): string
if (!current) {
return nextFragment
}
if (current === nextFragment || current.includes(nextFragment)) {
return current
if (current === nextFragment) {
return nextFragment.length >= 16 ? current : `${current}${nextFragment}`
}
if (nextFragment.startsWith(current)) {
return nextFragment
@@ -438,11 +438,6 @@ function mergeText(current: string | null, nextFragment: string | null): string
return `${current}${nextFragment.slice(forwardOverlap)}`
}
const reverseOverlap = sharedBoundaryLength(nextFragment, current)
if (reverseOverlap > 0) {
return `${nextFragment}${current.slice(reverseOverlap)}`
}
const commonPrefixLength = sharedPrefixLength(current, nextFragment)
const duplicatePrefixThreshold = Math.max(
16,
@@ -978,22 +973,24 @@ function attachYuanbaoResponseCapture(
function extractJSONCandidates(body: string): unknown[] {
const values: unknown[] = []
const seen = new Set<string>()
const pushParsed = (raw: string) => {
const pushParsed = (raw: string): boolean => {
const normalized = raw.trim()
if (!normalized || seen.has(normalized)) {
return
if (!normalized) {
return false
}
try {
values.push(JSON.parse(normalized))
seen.add(normalized)
return true
} catch {
// Ignore malformed frames and continue with the remaining candidates.
return false
}
}
pushParsed(body)
if (pushParsed(body)) {
return values
}
for (const rawLine of body.split(/\r?\n/)) {
const line = rawLine.trim()