mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
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:
@@ -23,6 +23,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Exec: reject invalid per-call `host` values instead of silently falling back to the default target, so hostname-like values fail before commands run. Fixes #74426. Thanks @scr00ge-00 and @vyctorbrzezowski.
|
||||
- Google/Gemini: send non-empty placeholder content when a Gemini run is triggered with empty or filtered user content, avoiding `contents is not specified` API errors. Thanks @CaoYuhaoCarl.
|
||||
- Build/Gateway: route restart, shutdown, respawn, diagnostics, command-queue cleanup, and runtime cleanup through one stable gateway lifecycle runtime entry so rebuilt packages do not strand long-running gateways on stale hashed chunks. Carries forward #73964. Thanks @pashpashpash.
|
||||
- Memory/wiki: keep broad shared-source and generated related-link blocks from turning every page into a search hit, cap noisy backlinks, support all-term searches such as people-routing queries, and prefer readable page body snippets over generated metadata. Thanks @vincentkoc.
|
||||
- Cron/Gateway: abort and bounded-clean up timed-out isolated agent turns before recording the timeout, so stale cron sessions cannot leave Discord or other chat lanes stuck in `processing` after a timeout. Thanks @vincentkoc.
|
||||
|
||||
@@ -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: " " }] }]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user