feat(chat): add configurable streaming output

This commit is contained in:
2026-07-17 13:34:27 +08:00
parent f7076569a7
commit 13fed0467a
11 changed files with 172 additions and 24 deletions

View File

@@ -15,16 +15,47 @@ const markdown = new MarkdownIt({ breaks: true, html: false, linkify: true });
const answer = computed(() => {
if (props.role === "user") return props.content;
// 部分模型会产生嵌套 think 标签,使用贪婪匹配移除完整思考区,再清理流式残留标签。
let content = props.content.replace(/<think>[\s\S]*<\/think>/gi, "");
content = content.replace(/<think>[\s\S]*$/i, "");
content = content.replace(/<\/?think>/gi, "");
return content.trim();
return stripStreamingReasoning(props.content).replace(/^\s+/, "");
});
const renderedContent = computed(() =>
props.role === "assistant" ? markdown.render(answer.value) : answer.value,
);
const hasAnswer = computed(() => answer.value.trim().length > 0);
const renderedContent = computed(() => {
if (props.role !== "assistant") return answer.value;
return hasAnswer.value ? markdown.render(answer.value) : "";
});
function stripStreamingReasoning(content: string) {
const lower = content.toLowerCase();
let visible = "";
let cursor = 0;
let reasoningDepth = 0;
while (cursor < content.length) {
const tagStart = content.indexOf("<", cursor);
if (tagStart === -1) {
if (reasoningDepth === 0) visible += content.slice(cursor);
break;
}
if (reasoningDepth === 0) visible += content.slice(cursor, tagStart);
const tail = lower.slice(tagStart);
const openTag = tail.match(/^<think(?:\s[^>]*)?>/);
if (openTag) {
reasoningDepth += 1;
cursor = tagStart + openTag[0].length;
continue;
}
const closeTag = tail.match(/^<\/think\s*>/);
if (closeTag) {
reasoningDepth = Math.max(0, reasoningDepth - 1);
cursor = tagStart + closeTag[0].length;
continue;
}
if ("<think".startsWith(tail) || "</think>".startsWith(tail)) break;
if (reasoningDepth === 0) visible += "<";
cursor = tagStart + 1;
}
return visible;
}
const displayTime = computed(() => {
// 消息服务返回的是 UTC 无时区字符串,补齐时区后再按用户本地时间展示。