fix(agents): preserve mixed image attachment order (#99902)

This commit is contained in:
Vincent Koc
2026-07-04 02:32:03 -07:00
committed by GitHub
parent f27d494d00
commit a0e6048624
2 changed files with 79 additions and 6 deletions
@@ -61,6 +61,59 @@ describe("resolveCurrentTurnImages", () => {
});
});
it("preserves the full order when only inline image payloads are present", async () => {
const inlineImage = {
type: "image" as const,
data: Buffer.from("inline").toString("base64"),
mimeType: "image/png",
};
const result = await resolveCurrentTurnImages({
ctx: { Body: "compare these" } satisfies MsgContext,
cfg: {} as OpenClawConfig,
images: [inlineImage],
imageOrder: ["offloaded", "inline", "offloaded"],
});
expect(result).toEqual({
images: [inlineImage],
imageOrder: ["offloaded", "inline", "offloaded"],
});
});
it("preserves all-offloaded image order without inline payloads", async () => {
const result = await resolveCurrentTurnImages({
ctx: { Body: "compare these" } satisfies MsgContext,
cfg: {} as OpenClawConfig,
images: [],
imageOrder: ["offloaded", "offloaded"],
});
expect(result).toEqual({
imageOrder: ["offloaded", "offloaded"],
});
});
it("preserves interleaved offloaded slots around inline image payloads", async () => {
const inlineImages = ["first", "second"].map((data) => ({
type: "image" as const,
data: Buffer.from(data).toString("base64"),
mimeType: "image/png",
}));
const result = await resolveCurrentTurnImages({
ctx: { Body: "compare these" } satisfies MsgContext,
cfg: {} as OpenClawConfig,
images: inlineImages,
imageOrder: ["inline", "offloaded", "inline"],
});
expect(result).toEqual({
images: inlineImages,
imageOrder: ["inline", "offloaded", "inline"],
});
});
it("appends extracted PDF page images without dropping current image attachments", async () => {
await withTempDir({ prefix: "openclaw-current-turn-pdf-images-" }, async (base) => {
const imagePath = path.join(base, "photo.png");
+26 -6
View File
@@ -20,7 +20,7 @@ type CurrentImageAttachment = {
};
type OrderedTurnImage = {
image: ImageContent;
image?: ImageContent;
imageOrder: PromptImageOrderEntry;
sourceIndex?: number;
sequence: number;
@@ -105,13 +105,32 @@ function appendOrderedImages(params: {
imageOrder?: PromptImageOrderEntry[];
sourceIndex?: number;
}) {
if (!params.images || params.images.length === 0) {
const images = params.images ?? [];
if (!params.imageOrder || params.imageOrder.length === 0) {
for (const image of images) {
params.entries.push({
image,
imageOrder: "inline",
sourceIndex: params.sourceIndex,
sequence: params.entries.length,
});
}
return;
}
for (const [index, image] of params.images.entries()) {
let inlineIndex = 0;
for (const imageOrder of params.imageOrder) {
params.entries.push({
image,
imageOrder: params.imageOrder?.[index] ?? "inline",
image: imageOrder === "inline" ? images[inlineIndex++] : undefined,
imageOrder,
sourceIndex: params.sourceIndex,
sequence: params.entries.length,
});
}
while (inlineIndex < images.length) {
params.entries.push({
image: images[inlineIndex++],
imageOrder: "inline",
sourceIndex: params.sourceIndex,
sequence: params.entries.length,
});
@@ -134,8 +153,9 @@ function resolveMergedTurnImages(entries: OrderedTurnImage[]): {
}
return left.sequence - right.sequence;
});
const images = merged.flatMap((entry) => (entry.image ? [entry.image] : []));
return {
images: merged.map((entry) => entry.image),
...(images.length > 0 ? { images } : {}),
imageOrder: merged.map((entry) => entry.imageOrder),
};
}