fix: 工具调用前刷新流式行缓冲 - 修复AI文本丢失
- lineBuffer/mdState提升为实例字段(工具事件回调可访问) - 工具START事件先flushStreamLineBuffer()再渲染工具状态 - 修复: AI在工具调用前发送的文本(无\\n结尾)现在正确显示 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -60,6 +60,11 @@ public class ReplSession {
|
|||||||
/** 流式输出换行跟踪:工具渲染和流式回调共享,保证缩进一致 */
|
/** 流式输出换行跟踪:工具渲染和流式回调共享,保证缩进一致 */
|
||||||
private volatile boolean streamNewLine = false;
|
private volatile boolean streamNewLine = false;
|
||||||
|
|
||||||
|
/** 流式行缓冲:累积 token 到换行再 Markdown 渲染输出 */
|
||||||
|
private final StringBuilder streamLineBuffer = new StringBuilder();
|
||||||
|
/** 流式 Markdown 渲染状态:跨行追踪代码块 */
|
||||||
|
private MarkdownRenderer.StreamState streamMdState = new MarkdownRenderer.StreamState();
|
||||||
|
|
||||||
/** 当前活跃的 LineReader(JLine 模式下用于 AskUser 和权限确认) */
|
/** 当前活跃的 LineReader(JLine 模式下用于 AskUser 和权限确认) */
|
||||||
private volatile LineReader activeReader;
|
private volatile LineReader activeReader;
|
||||||
/** 当前活跃的 Scanner(Scanner 模式下用于 AskUser 和权限确认) */
|
/** 当前活跃的 Scanner(Scanner 模式下用于 AskUser 和权限确认) */
|
||||||
@@ -102,7 +107,8 @@ public class ReplSession {
|
|||||||
switch (event.phase()) {
|
switch (event.phase()) {
|
||||||
case START -> {
|
case START -> {
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
// 如果前面的流式文本没有换行结尾,先换行
|
// 刷新行缓冲(AI 文本可能在工具调用前没有换行结尾)
|
||||||
|
flushStreamLineBuffer();
|
||||||
if (!streamNewLine) {
|
if (!streamNewLine) {
|
||||||
out.println();
|
out.println();
|
||||||
}
|
}
|
||||||
@@ -333,13 +339,11 @@ public class ReplSession {
|
|||||||
try {
|
try {
|
||||||
spinner.start("Thinking...");
|
spinner.start("Thinking...");
|
||||||
streamNewLine = true; // spinner 停止后 onStreamStart 会打印 ● 前缀
|
streamNewLine = true; // spinner 停止后 onStreamStart 会打印 ● 前缀
|
||||||
|
streamLineBuffer.setLength(0); // 重置行缓冲
|
||||||
|
streamMdState = new MarkdownRenderer.StreamState(); // 重置 Markdown 状态
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
// 行缓冲:累积 token 直到换行,再用 MarkdownRenderer 渲染整行输出
|
|
||||||
StringBuilder lineBuffer = new StringBuilder();
|
|
||||||
MarkdownRenderer.StreamState mdState = new MarkdownRenderer.StreamState();
|
|
||||||
|
|
||||||
String response = agentLoop.runStreaming(input, token -> {
|
String response = agentLoop.runStreaming(input, token -> {
|
||||||
for (int i = 0; i < token.length(); i++) {
|
for (int i = 0; i < token.length(); i++) {
|
||||||
char c = token.charAt(i);
|
char c = token.charAt(i);
|
||||||
@@ -349,26 +353,19 @@ public class ReplSession {
|
|||||||
out.print(" "); // 续行缩进(与 ● 后文本对齐)
|
out.print(" "); // 续行缩进(与 ● 后文本对齐)
|
||||||
streamNewLine = false;
|
streamNewLine = false;
|
||||||
}
|
}
|
||||||
String rendered = markdownRenderer.renderStreamingLine(lineBuffer.toString(), mdState);
|
String rendered = markdownRenderer.renderStreamingLine(streamLineBuffer.toString(), streamMdState);
|
||||||
out.println(rendered);
|
out.println(rendered);
|
||||||
lineBuffer.setLength(0);
|
streamLineBuffer.setLength(0);
|
||||||
streamNewLine = true;
|
streamNewLine = true;
|
||||||
} else {
|
} else {
|
||||||
lineBuffer.append(c);
|
streamLineBuffer.append(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.flush();
|
out.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 刷新残留缓冲(最后一行可能无 \n 结尾)
|
// 刷新残留缓冲(最后一行可能无 \n 结尾)
|
||||||
if (!lineBuffer.isEmpty()) {
|
flushStreamLineBuffer();
|
||||||
if (streamNewLine) {
|
|
||||||
out.print(" ");
|
|
||||||
streamNewLine = false;
|
|
||||||
}
|
|
||||||
String rendered = markdownRenderer.renderStreamingLine(lineBuffer.toString(), mdState);
|
|
||||||
out.print(rendered);
|
|
||||||
}
|
|
||||||
|
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
out.println(); // 流式输出结束后换行
|
out.println(); // 流式输出结束后换行
|
||||||
@@ -392,6 +389,22 @@ public class ReplSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新流式行缓冲 —— 将未输出的缓冲内容渲染并打印。
|
||||||
|
* 在工具调用前、流式结束后调用,防止 AI 文本丢失。
|
||||||
|
*/
|
||||||
|
private void flushStreamLineBuffer() {
|
||||||
|
if (!streamLineBuffer.isEmpty()) {
|
||||||
|
if (streamNewLine) {
|
||||||
|
out.print(" ");
|
||||||
|
streamNewLine = false;
|
||||||
|
}
|
||||||
|
String rendered = markdownRenderer.renderStreamingLine(streamLineBuffer.toString(), streamMdState);
|
||||||
|
out.print(rendered);
|
||||||
|
streamLineBuffer.setLength(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 退出时保存对话历史 */
|
/** 退出时保存对话历史 */
|
||||||
private void saveConversation() {
|
private void saveConversation() {
|
||||||
var history = agentLoop.getMessageHistory();
|
var history = agentLoop.getMessageHistory();
|
||||||
|
|||||||
Reference in New Issue
Block a user