FIX: QVAC Updates
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Split Qwen-style thinking blocks from assistant text for UI.
|
||||
* Supports complete and in-progress streams.
|
||||
*
|
||||
* Common tags: <think>…</think>, <think>…</think>, <thinking>…
|
||||
*/
|
||||
|
||||
const TAG = String.raw`think|thinking|redacted[_-]?thinking|redacted[_-]?reasoning`
|
||||
const THINK_OPEN = new RegExp(`<\\s*(?:${TAG})\\s*>`, 'i')
|
||||
const THINK_PAIR = new RegExp(
|
||||
`<\\s*(?:${TAG})\\s*>([\\s\\S]*?)<\\s*/\\s*(?:${TAG})\\s*>`,
|
||||
'gi'
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @returns {{ thinking: string, answer: string, thinkingOpen: boolean }}
|
||||
*/
|
||||
export function partitionThink(text) {
|
||||
const full = String(text || '')
|
||||
/** @type {string[]} */
|
||||
const blocks = []
|
||||
let answer = full.replace(THINK_PAIR, (_, body) => {
|
||||
const t = String(body || '').trim()
|
||||
if (t) blocks.push(t)
|
||||
return ''
|
||||
})
|
||||
|
||||
// Incomplete open block at end of stream
|
||||
let thinkingOpen = false
|
||||
const openMatch = answer.match(THINK_OPEN)
|
||||
if (openMatch && openMatch.index != null) {
|
||||
thinkingOpen = true
|
||||
const openIdx = openMatch.index
|
||||
const after = answer.slice(openIdx + openMatch[0].length)
|
||||
if (after.trim()) blocks.push(after.trim())
|
||||
answer = answer.slice(0, openIdx)
|
||||
}
|
||||
|
||||
return {
|
||||
thinking: blocks.join('\n\n').trim(),
|
||||
answer: answer.replace(/^\s+/, '').replace(/\s+$/, ''),
|
||||
thinkingOpen,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render assistant body HTML: optional collapsible think + answer.
|
||||
* @param {string} raw
|
||||
* @param {(s: string) => string} formatBody
|
||||
* @param {{ openThink?: boolean }} [opts]
|
||||
*/
|
||||
export function renderAssistantHtml(raw, formatBody, opts = {}) {
|
||||
const { thinking, answer, thinkingOpen } = partitionThink(raw)
|
||||
const open = opts.openThink !== false && (thinkingOpen || Boolean(thinking))
|
||||
let html = ''
|
||||
if (thinking) {
|
||||
html += `<details class="qvac-think"${open ? ' open' : ''}>
|
||||
<summary class="qvac-think-summary">
|
||||
<span class="qvac-think-ico" aria-hidden="true">◐</span>
|
||||
<span>Thinking</span>
|
||||
${thinkingOpen ? '<span class="qvac-think-live">live</span>' : ''}
|
||||
</summary>
|
||||
<div class="qvac-think-body">${formatBody(thinking)}</div>
|
||||
</details>`
|
||||
}
|
||||
const ans = answer || (!thinking ? '…' : '')
|
||||
if (ans) {
|
||||
html += `<div class="qvac-msg-answer">${formatBody(ans)}</div>`
|
||||
} else if (thinkingOpen) {
|
||||
html += `<div class="qvac-msg-answer qvac-msg-pending muted">Working…</div>`
|
||||
}
|
||||
return html || formatBody(raw || '…')
|
||||
}
|
||||
Reference in New Issue
Block a user