mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
fix(heartbeat): suppress reasoning-only acknowledgements (#114454)
This commit is contained in:
committed by
GitHub
parent
2d33a15e3e
commit
bec06affef
+8
-1
@@ -216,7 +216,14 @@ describe("embedded attempt context injection", () => {
|
||||
{ role: "user", content: "real question", timestamp: 1 } as AgentMessage,
|
||||
{ role: "assistant", content: "real answer", timestamp: 2 } as unknown as AgentMessage,
|
||||
{ role: "user", content: HEARTBEAT_PROMPT, timestamp: 3 } as AgentMessage,
|
||||
{ role: "assistant", content: "HEARTBEAT_OK", timestamp: 4 } as unknown as AgentMessage,
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "reasoning", text: "Checking the heartbeat." },
|
||||
{ type: "text", text: "HEARTBEAT_OK" },
|
||||
],
|
||||
timestamp: 4,
|
||||
} as unknown as AgentMessage,
|
||||
];
|
||||
|
||||
const heartbeatFiltered = filterHeartbeatTranscriptArtifacts(
|
||||
|
||||
@@ -13,6 +13,12 @@ import {
|
||||
} from "./heartbeat.js";
|
||||
import { MESSAGE_TOOL_DELIVERY_HINTS } from "./reply/delivery-hints.js";
|
||||
|
||||
const HIDDEN_REASONING_BLOCKS = [
|
||||
["thinking", { type: "thinking", thinking: "Checking the heartbeat." }],
|
||||
["reasoning", { type: "reasoning", text: "Checking the heartbeat." }],
|
||||
["redacted thinking", { type: "redacted_thinking", data: "opaque-reasoning" }],
|
||||
] as const;
|
||||
|
||||
describe("isHeartbeatUserMessage", () => {
|
||||
it("matches heartbeat prompts", () => {
|
||||
expect(
|
||||
@@ -93,17 +99,17 @@ describe("isHeartbeatOkResponse", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["thinking", { type: "thinking", thinking: "Checking the heartbeat." }],
|
||||
["redacted thinking", { type: "redacted_thinking", data: "opaque-reasoning" }],
|
||||
])("matches acknowledgements with hidden %s blocks", (_label, thinkingBlock) => {
|
||||
expect(
|
||||
isHeartbeatOkResponse({
|
||||
role: "assistant",
|
||||
content: [thinkingBlock, { type: "text", text: "HEARTBEAT_OK" }],
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
it.each(HIDDEN_REASONING_BLOCKS)(
|
||||
"matches acknowledgements with hidden %s blocks",
|
||||
(_label, reasoningBlock) => {
|
||||
expect(
|
||||
isHeartbeatOkResponse({
|
||||
role: "assistant",
|
||||
content: [reasoningBlock, { type: "text", text: "HEARTBEAT_OK" }],
|
||||
}),
|
||||
).toBe(true);
|
||||
},
|
||||
);
|
||||
|
||||
it("preserves meaningful or non-text responses", () => {
|
||||
expect(
|
||||
@@ -170,24 +176,24 @@ describe("filterHeartbeatTranscriptArtifacts", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["thinking", { type: "thinking", thinking: "Checking the heartbeat." }],
|
||||
["redacted thinking", { type: "redacted_thinking", data: "opaque-reasoning" }],
|
||||
])("removes no-op heartbeat pairs with hidden %s blocks", (_label, thinkingBlock) => {
|
||||
const nextUserMessage = { role: "user", content: "What time is it?" };
|
||||
const messages = [
|
||||
{ role: "user", content: HEARTBEAT_TRANSCRIPT_PROMPT },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [thinkingBlock, { type: "text", text: "HEARTBEAT_OK" }],
|
||||
},
|
||||
nextUserMessage,
|
||||
];
|
||||
it.each(HIDDEN_REASONING_BLOCKS)(
|
||||
"removes no-op heartbeat pairs with hidden %s blocks",
|
||||
(_label, reasoningBlock) => {
|
||||
const nextUserMessage = { role: "user", content: "What time is it?" };
|
||||
const messages = [
|
||||
{ role: "user", content: HEARTBEAT_TRANSCRIPT_PROMPT },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [reasoningBlock, { type: "text", text: "HEARTBEAT_OK" }],
|
||||
},
|
||||
nextUserMessage,
|
||||
];
|
||||
|
||||
expect(filterHeartbeatTranscriptArtifacts(messages, undefined, HEARTBEAT_PROMPT)).toEqual([
|
||||
nextUserMessage,
|
||||
]);
|
||||
});
|
||||
expect(filterHeartbeatTranscriptArtifacts(messages, undefined, HEARTBEAT_PROMPT)).toEqual([
|
||||
nextUserMessage,
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
it("removes OpenAI Responses input/output text heartbeat pairs", () => {
|
||||
for (const deliveryHint of MESSAGE_TOOL_DELIVERY_HINTS) {
|
||||
|
||||
@@ -269,9 +269,13 @@ function resolveMessageText(content: unknown): { text: string; hasNonTextContent
|
||||
hasNonTextContent = true;
|
||||
continue;
|
||||
}
|
||||
// Provider thinking is not user-visible output; it must not keep a
|
||||
// Provider thinking/reasoning is not user-visible output; it must not keep a
|
||||
// no-op heartbeat acknowledgement in future model request history.
|
||||
if (block.type === "thinking" || block.type === "redacted_thinking") {
|
||||
if (
|
||||
block.type === "thinking" ||
|
||||
block.type === "reasoning" ||
|
||||
block.type === "redacted_thinking"
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (block.type !== "text" && block.type !== "input_text" && block.type !== "output_text") {
|
||||
|
||||
@@ -688,7 +688,14 @@ describe("SessionHistorySseState", () => {
|
||||
content: `${HEARTBEAT_PROMPT}\nWhen reading HEARTBEAT.md, use workspace file /tmp/HEARTBEAT.md (exact case). Do not read docs/heartbeat.md.`,
|
||||
__openclaw: { seq: 1 },
|
||||
},
|
||||
assistantTextMessage("HEARTBEAT_OK", 2),
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "reasoning", text: "Checking the heartbeat." },
|
||||
{ type: "text", text: "HEARTBEAT_OK" },
|
||||
],
|
||||
__openclaw: { seq: 2 },
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: HEARTBEAT_PROMPT,
|
||||
|
||||
Reference in New Issue
Block a user