fix(google): prevent empty contents error for gemini (#74465)

* fix(google): prevent empty contents error for gemini

* test(google): cover empty Gemini contents fallback

* docs(changelog): note Gemini empty content fallback

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Carl
2026-04-30 00:30:51 +08:00
committed by GitHub
parent df0074768c
commit 5e384fed6d
3 changed files with 59 additions and 4 deletions

View File

@@ -514,4 +514,54 @@ describe("google transport stream", () => {
expect(params.cachedContent).toBe("cachedContents/prebuilt-context");
});
it("uses a non-empty text placeholder for empty user text", () => {
const params = buildGoogleGenerativeAiParams(buildGeminiModel(), {
messages: [
{ role: "user", content: "", timestamp: 0 },
{
role: "user",
content: [{ type: "text", text: "" }],
timestamp: 1,
},
],
} as never);
expect(params.contents).toEqual([
{ role: "user", parts: [{ text: " " }] },
{ role: "user", parts: [{ text: " " }] },
]);
});
it("uses a text placeholder when user parts are filtered out for text-only models", () => {
const params = buildGoogleGenerativeAiParams(buildGeminiModel({ input: ["text"] }), {
messages: [
{
role: "user",
content: [{ type: "image", mimeType: "image/png", data: "png-bytes" }],
timestamp: 0,
},
],
} as never);
expect(params.contents).toEqual([{ role: "user", parts: [{ text: " " }] }]);
});
it("uses a user placeholder when converted Gemini contents would otherwise be empty", () => {
const params = buildGoogleGenerativeAiParams(buildGeminiModel(), {
messages: [
{
role: "assistant",
provider: "google",
api: "google-generative-ai",
model: "gemini-2.5-pro",
stopReason: "stop",
timestamp: 0,
content: [{ type: "text", text: " " }],
},
],
} as never);
expect(params.contents).toEqual([{ role: "user", parts: [{ text: " " }] }]);
});
});

View File

@@ -315,14 +315,14 @@ function convertGoogleMessages(model: GoogleTransportModel, context: Context) {
if (typeof msg.content === "string") {
contents.push({
role: "user",
parts: [{ text: sanitizeTransportPayloadText(msg.content) }],
parts: [{ text: sanitizeTransportPayloadText(msg.content) || " " }],
});
continue;
}
const parts = msg.content
.map((item) =>
item.type === "text"
? { text: sanitizeTransportPayloadText(item.text) }
? { text: sanitizeTransportPayloadText(item.text) || " " }
: {
inlineData: {
mimeType: item.mimeType,
@@ -331,9 +331,10 @@ function convertGoogleMessages(model: GoogleTransportModel, context: Context) {
},
)
.filter((item) => model.input.includes("image") || !("inlineData" in item));
if (parts.length > 0) {
contents.push({ role: "user", parts });
if (parts.length === 0) {
parts.push({ text: " " });
}
contents.push({ role: "user", parts });
continue;
}
@@ -437,6 +438,9 @@ function convertGoogleMessages(model: GoogleTransportModel, context: Context) {
}
}
}
if (contents.length === 0) {
contents.push({ role: "user", parts: [{ text: " " }] });
}
return contents;
}