🤖 codex: strip empty assistant blocks from history (#210)
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
isMessagingToolDuplicate,
|
isMessagingToolDuplicate,
|
||||||
normalizeTextForComparison,
|
normalizeTextForComparison,
|
||||||
sanitizeGoogleTurnOrdering,
|
sanitizeGoogleTurnOrdering,
|
||||||
|
sanitizeSessionMessagesImages,
|
||||||
validateGeminiTurns,
|
validateGeminiTurns,
|
||||||
} from "./pi-embedded-helpers.js";
|
} from "./pi-embedded-helpers.js";
|
||||||
import {
|
import {
|
||||||
@@ -250,6 +251,77 @@ describe("sanitizeGoogleTurnOrdering", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("sanitizeSessionMessagesImages", () => {
|
||||||
|
it("removes empty assistant text blocks but preserves tool calls", async () => {
|
||||||
|
const input = [
|
||||||
|
{
|
||||||
|
role: "assistant",
|
||||||
|
content: [
|
||||||
|
{ type: "text", text: "" },
|
||||||
|
{ type: "toolCall", id: "call_1", name: "read", arguments: {} },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] satisfies AgentMessage[];
|
||||||
|
|
||||||
|
const out = await sanitizeSessionMessagesImages(input, "test");
|
||||||
|
|
||||||
|
expect(out).toHaveLength(1);
|
||||||
|
const content = (out[0] as { content?: unknown }).content;
|
||||||
|
expect(Array.isArray(content)).toBe(true);
|
||||||
|
expect(content).toHaveLength(1);
|
||||||
|
expect((content as Array<{ type?: string }>)[0]?.type).toBe("toolCall");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("filters whitespace-only assistant text blocks", async () => {
|
||||||
|
const input = [
|
||||||
|
{
|
||||||
|
role: "assistant",
|
||||||
|
content: [
|
||||||
|
{ type: "text", text: " " },
|
||||||
|
{ type: "text", text: "ok" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] satisfies AgentMessage[];
|
||||||
|
|
||||||
|
const out = await sanitizeSessionMessagesImages(input, "test");
|
||||||
|
|
||||||
|
expect(out).toHaveLength(1);
|
||||||
|
const content = (out[0] as { content?: unknown }).content;
|
||||||
|
expect(Array.isArray(content)).toBe(true);
|
||||||
|
expect(content).toHaveLength(1);
|
||||||
|
expect((content as Array<{ text?: string }>)[0]?.text).toBe("ok");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("drops assistant messages that only contain empty text", async () => {
|
||||||
|
const input = [
|
||||||
|
{ role: "user", content: "hello" },
|
||||||
|
{ role: "assistant", content: [{ type: "text", text: "" }] },
|
||||||
|
] satisfies AgentMessage[];
|
||||||
|
|
||||||
|
const out = await sanitizeSessionMessagesImages(input, "test");
|
||||||
|
|
||||||
|
expect(out).toHaveLength(1);
|
||||||
|
expect(out[0]?.role).toBe("user");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("leaves non-assistant messages unchanged", async () => {
|
||||||
|
const input = [
|
||||||
|
{ role: "user", content: "hello" },
|
||||||
|
{
|
||||||
|
role: "toolResult",
|
||||||
|
toolUseId: "tool-1",
|
||||||
|
content: [{ type: "text", text: "result" }],
|
||||||
|
},
|
||||||
|
] satisfies AgentMessage[];
|
||||||
|
|
||||||
|
const out = await sanitizeSessionMessagesImages(input, "test");
|
||||||
|
|
||||||
|
expect(out).toHaveLength(2);
|
||||||
|
expect(out[0]?.role).toBe("user");
|
||||||
|
expect(out[1]?.role).toBe("toolResult");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("normalizeTextForComparison", () => {
|
describe("normalizeTextForComparison", () => {
|
||||||
it("lowercases text", () => {
|
it("lowercases text", () => {
|
||||||
expect(normalizeTextForComparison("Hello World")).toBe("hello world");
|
expect(normalizeTextForComparison("Hello World")).toBe("hello world");
|
||||||
|
|||||||
@@ -99,6 +99,28 @@ export async function sanitizeSessionMessagesImages(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (role === "assistant") {
|
||||||
|
const assistantMsg = msg as Extract<AgentMessage, { role: "assistant" }>;
|
||||||
|
const content = assistantMsg.content;
|
||||||
|
if (Array.isArray(content)) {
|
||||||
|
const filteredContent = content.filter((block) => {
|
||||||
|
if (!block || typeof block !== "object") return true;
|
||||||
|
const rec = block as { type?: unknown; text?: unknown };
|
||||||
|
if (rec.type !== "text" || typeof rec.text !== "string") return true;
|
||||||
|
return rec.text.trim().length > 0;
|
||||||
|
});
|
||||||
|
const sanitizedContent = (await sanitizeContentBlocksImages(
|
||||||
|
filteredContent as unknown as ContentBlock[],
|
||||||
|
label,
|
||||||
|
)) as unknown as typeof assistantMsg.content;
|
||||||
|
if (sanitizedContent.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.push({ ...assistantMsg, content: sanitizedContent });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out.push(msg);
|
out.push(msg);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
|
|||||||
Reference in New Issue
Block a user