feat: 流式Markdown渲染 - 行缓冲+ANSI样式输出
- MarkdownRenderer新增StreamState和renderStreamingLine()方法 - 流式输出改为行缓冲模式:累积token到换行再渲染输出 - 支持:粗体/斜体→ANSI样式, 代码块→语法高亮边框, 列表→●圆点, 标题→彩色, 引用→竖线, 链接→下划线 - 代码块状态跨行追踪(StreamState) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -329,31 +329,47 @@ public class ReplSession {
|
||||
conversationSummary = input.length() > 40 ? input.substring(0, 40) : input;
|
||||
}
|
||||
|
||||
// Agent 循环(流式输出)
|
||||
// Agent 循环(流式输出 + 行缓冲 Markdown 渲染)
|
||||
try {
|
||||
spinner.start("Thinking...");
|
||||
streamNewLine = true; // spinner 停止后 onStreamStart 会打印 ● 前缀
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
// 流式回调:逐 token 输出到终端,自动在每行开头加缩进
|
||||
// 行缓冲:累积 token 直到换行,再用 MarkdownRenderer 渲染整行输出
|
||||
StringBuilder lineBuffer = new StringBuilder();
|
||||
MarkdownRenderer.StreamState mdState = new MarkdownRenderer.StreamState();
|
||||
|
||||
String response = agentLoop.runStreaming(input, token -> {
|
||||
for (int i = 0; i < token.length(); i++) {
|
||||
char c = token.charAt(i);
|
||||
if (c == '\n') {
|
||||
out.println();
|
||||
streamNewLine = true;
|
||||
} else {
|
||||
// 行完成 → 渲染 Markdown 并输出
|
||||
if (streamNewLine) {
|
||||
out.print(" "); // 续行缩进(与 ● 后文本对齐)
|
||||
streamNewLine = false;
|
||||
}
|
||||
out.print(c);
|
||||
String rendered = markdownRenderer.renderStreamingLine(lineBuffer.toString(), mdState);
|
||||
out.println(rendered);
|
||||
lineBuffer.setLength(0);
|
||||
streamNewLine = true;
|
||||
} else {
|
||||
lineBuffer.append(c);
|
||||
}
|
||||
}
|
||||
out.flush();
|
||||
});
|
||||
|
||||
// 刷新残留缓冲(最后一行可能无 \n 结尾)
|
||||
if (!lineBuffer.isEmpty()) {
|
||||
if (streamNewLine) {
|
||||
out.print(" ");
|
||||
streamNewLine = false;
|
||||
}
|
||||
String rendered = markdownRenderer.renderStreamingLine(lineBuffer.toString(), mdState);
|
||||
out.print(rendered);
|
||||
}
|
||||
|
||||
spinner.stop();
|
||||
out.println(); // 流式输出结束后换行
|
||||
|
||||
|
||||
Reference in New Issue
Block a user