fix(coding-agent): Coalesce adjacent assistant thinking blocks into one section
This commit is contained in:
@@ -34,11 +34,12 @@
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed configured-provider catalog refresh to parse pi.dev's model-ID keyed responses, treat unimplemented routes as unavailable overlays, and show concise refresh status in `/model`.
|
- Fixed configured-provider catalog refresh to parse pi.dev's model-ID keyed responses, throttle checks to once per four hours, send the versioned pi user agent, treat unimplemented routes as unavailable overlays, and show concise refresh status in `/model`.
|
||||||
- Fixed inherited OpenRouter model context windows to use the top provider's actual context length ([#6481](https://github.com/earendil-works/pi-mono/pull/6481) by [@davidbrai](https://github.com/davidbrai)).
|
- Fixed inherited OpenRouter model context windows to use the top provider's actual context length ([#6481](https://github.com/earendil-works/pi-mono/pull/6481) by [@davidbrai](https://github.com/davidbrai)).
|
||||||
- Fixed inherited OpenRouter OpenAI-compatible session IDs to use the `x-session-id` header instead of OpenAI-specific session-affinity fields ([#6366](https://github.com/earendil-works/pi/issues/6366)).
|
- Fixed inherited OpenRouter OpenAI-compatible session IDs to use the `x-session-id` header instead of OpenAI-specific session-affinity fields ([#6366](https://github.com/earendil-works/pi/issues/6366)).
|
||||||
- Fixed `Ctrl+V` to paste clipboard text when the pasteboard does not contain an image.
|
- Fixed `Ctrl+V` to paste clipboard text when the pasteboard does not contain an image.
|
||||||
- Fixed `/login amazon-bedrock` to prompt for and save a Bedrock API key instead of only displaying ambient AWS credential setup instructions.
|
- Fixed `/login amazon-bedrock` to prompt for and save a Bedrock API key instead of only displaying ambient AWS credential setup instructions.
|
||||||
|
- Fixed adjacent assistant thinking blocks to render as one thinking section.
|
||||||
|
|
||||||
## [0.80.6] - 2026-07-09
|
## [0.80.6] - 2026-07-09
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,24 @@ export class AssistantMessageComponent extends Container {
|
|||||||
// Assistant text messages with no background - trim the text
|
// Assistant text messages with no background - trim the text
|
||||||
// Set paddingY=0 to avoid extra spacing before tool executions
|
// Set paddingY=0 to avoid extra spacing before tool executions
|
||||||
this.contentContainer.addChild(new Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme));
|
this.contentContainer.addChild(new Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme));
|
||||||
} else if (content.type === "thinking" && content.thinking.trim()) {
|
} else if (content.type === "thinking") {
|
||||||
|
const thinkingBlocks: string[] = [];
|
||||||
|
for (; i < message.content.length; i++) {
|
||||||
|
const thinkingContent = message.content[i];
|
||||||
|
if (thinkingContent.type !== "thinking") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const thinking = thinkingContent.thinking.trim();
|
||||||
|
if (thinking) {
|
||||||
|
thinkingBlocks.push(thinking);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i--;
|
||||||
|
|
||||||
|
if (thinkingBlocks.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Add spacing only when another visible assistant content block follows.
|
// Add spacing only when another visible assistant content block follows.
|
||||||
// This avoids a superfluous blank line before separately-rendered tool execution blocks.
|
// This avoids a superfluous blank line before separately-rendered tool execution blocks.
|
||||||
const hasVisibleContentAfter = message.content
|
const hasVisibleContentAfter = message.content
|
||||||
@@ -109,24 +126,21 @@ export class AssistantMessageComponent extends Container {
|
|||||||
.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
|
.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
|
||||||
|
|
||||||
if (this.hideThinkingBlock) {
|
if (this.hideThinkingBlock) {
|
||||||
// Show static thinking label when hidden
|
// Show one static label for each run of thinking blocks when hidden.
|
||||||
this.contentContainer.addChild(
|
this.contentContainer.addChild(
|
||||||
new Text(theme.italic(theme.fg("thinkingText", this.hiddenThinkingLabel)), this.outputPad, 0),
|
new Text(theme.italic(theme.fg("thinkingText", this.hiddenThinkingLabel)), this.outputPad, 0),
|
||||||
);
|
);
|
||||||
if (hasVisibleContentAfter) {
|
|
||||||
this.contentContainer.addChild(new Spacer(1));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Thinking traces in thinkingText color, italic
|
// Render each run of thinking blocks as one Markdown section.
|
||||||
this.contentContainer.addChild(
|
this.contentContainer.addChild(
|
||||||
new Markdown(content.thinking.trim(), this.outputPad, 0, this.markdownTheme, {
|
new Markdown(thinkingBlocks.join("\n\n"), this.outputPad, 0, this.markdownTheme, {
|
||||||
color: (text: string) => theme.fg("thinkingText", text),
|
color: (text: string) => theme.fg("thinkingText", text),
|
||||||
italic: true,
|
italic: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if (hasVisibleContentAfter) {
|
}
|
||||||
this.contentContainer.addChild(new Spacer(1));
|
if (hasVisibleContentAfter) {
|
||||||
}
|
this.contentContainer.addChild(new Spacer(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,24 @@ describe("AssistantMessageComponent", () => {
|
|||||||
expect(rendered).toContain("response may be incomplete");
|
expect(rendered).toContain("response may be incomplete");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("coalesces adjacent thinking blocks into one hidden thinking label", () => {
|
||||||
|
initTheme("dark");
|
||||||
|
|
||||||
|
const component = new AssistantMessageComponent(
|
||||||
|
createAssistantMessage([
|
||||||
|
{ type: "thinking", thinking: "first thought" },
|
||||||
|
{ type: "thinking", thinking: "" },
|
||||||
|
{ type: "thinking", thinking: "second thought" },
|
||||||
|
{ type: "text", text: "answer" },
|
||||||
|
]),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
const rendered = stripAnsi(component.render(80).join("\n"));
|
||||||
|
|
||||||
|
expect(rendered.match(/Thinking\.\.\./g)).toHaveLength(1);
|
||||||
|
expect(rendered).toContain("answer");
|
||||||
|
});
|
||||||
|
|
||||||
test("uses configured output padding for text and thinking", () => {
|
test("uses configured output padding for text and thinking", () => {
|
||||||
initTheme("dark");
|
initTheme("dark");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user