Swift Markdown Kit · DOCS

流式与 Chat

流式与 Chat

流式渲染

面向 LLM 的 token 流,直接驱动视图控制器:

renderer.startStreaming()
renderer.appendChunk("Streaming **Markdown")
renderer.appendChunk("** with math $x^2$", isFinal: true)

渲染器用 requestAnimationFrame 合并 chunk,并以增量扫描缓存到最后一个安全块 边界的 HTML,所以整条流的总渲染成本随最终答案的长度线性增长,而不是每次更新 重新解析全文。

也可以把一个不断增长的字符串绑定给 MarkdownRenderView:当新值是旧值的前缀扩展 时,视图只追加增量。

Chat 模式

整段会话复用同一个渲染内核,一个会话一个 WKWebView —— 不是每条消息一个 WebView。流式过程中只更新当前那条消息。

let messages = [
    MarkdownChatMessage(id: "u1", role: .user, markdown: "解释一下流式渲染。"),
    MarkdownChatMessage(id: "a1", role: .assistant, markdown: "", status: .streaming)
]
let options = MarkdownChatRenderOptions(
    streamingPresentation: .smooth,
    messageActions: MarkdownChatMessageActionConfiguration(isEnabled: true)
)

MarkdownChatRenderView(messages: messages, options: options) { event in
    if case .messageAction(let action) = event {
        print(action.messageID, action.action)
    }
}

.smooth 会把不均匀的网络 chunk 放进逐帧展示队列;需要原样即时显示时用 .immediate。原生「回到底部」控件可以持有 MarkdownChatRenderProxy 后调用 scrollToBottom(animated:)

UIKit 也可以直接驱动 Chat 渲染器:

let chat = MarkdownChatRenderViewController()
chat.setMessages(messages)
chat.appendChunk(messageID: "a1", chunk: "Streaming **Markdown")
chat.appendChunk(messageID: "a1", chunk: "** with math $x^2$", isFinal: true)