






















1+import type { Part } from "@google/genai";
2+import { describe, expect, it } from "vitest";
3+import type { Context, Model } from "../types.js";
4+import { convertMessages } from "./google-shared.js";
5+import { makeGoogleAssistantMessage } from "./google-shared.test-helpers.js";
6+7+const convertMessagesForTest = convertMessages as unknown as (
8+model: Model<"google-generative-ai">,
9+context: Context,
10+) => ReturnType<typeof convertMessages>;
11+12+const makeVisionModel = (id: string): Model<"google-generative-ai"> =>
13+({
14+ id,
15+name: id,
16+api: "google-generative-ai",
17+provider: "google",
18+baseUrl: "https://example.invalid",
19+reasoning: false,
20+input: ["text", "image"],
21+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
22+contextWindow: 1,
23+maxTokens: 1,
24+}) as Model<"google-generative-ai">;
25+26+function countFunctionResponses(parts: readonly Part[] | undefined): number {
27+return (parts ?? []).filter((p) => p.functionResponse != null).length;
28+}
29+30+function functionResponseNames(parts: readonly Part[] | undefined): string[] {
31+return (parts ?? []).flatMap((part) =>
32+part.functionResponse?.name ? [part.functionResponse.name] : [],
33+);
34+}
35+36+describe("google-shared convertMessages — parallel tool results with an image (Gemini < 3)", () => {
37+it.each([
38+["image first", ["screenshot", "weather"]],
39+["image last", ["weather", "screenshot"]],
40+] as const)(
41+"keeps the response run immediate and retains a deferred %s result",
42+(_label, resultOrder) => {
43+const model = makeVisionModel("gemini-2.5-flash");
44+const toolResults = {
45+screenshot: {
46+role: "toolResult",
47+toolCallId: "call_1",
48+toolName: "screenshot",
49+content: [{ type: "image", mimeType: "image/png", data: "AAAA" }],
50+isError: false,
51+timestamp: 0,
52+},
53+weather: {
54+role: "toolResult",
55+toolCallId: "call_2",
56+toolName: "weather",
57+content: [{ type: "text", text: "Sunny, 21C" }],
58+isError: false,
59+timestamp: 0,
60+},
61+} as const;
62+const context = {
63+messages: [
64+{ role: "user", content: "Screenshot the page and check the weather." },
65+makeGoogleAssistantMessage(model.id, [
66+{ type: "toolCall", id: "call_1", name: "screenshot", arguments: {} },
67+{ type: "toolCall", id: "call_2", name: "weather", arguments: {} },
68+]),
69+ ...resultOrder.map((name) => toolResults[name]),
70+],
71+} as unknown as Context;
72+73+const contents = convertMessagesForTest(model, context);
74+75+expect(contents.map((content) => content.role)).toEqual(["user", "model", "user", "user"]);
76+expect(functionResponseNames(contents[2].parts)).toEqual(resultOrder);
77+expect(contents[3]).toEqual({
78+role: "user",
79+parts: [
80+{ text: "Tool result image:" },
81+{ inlineData: { mimeType: "image/png", data: "AAAA" } },
82+],
83+});
84+expect(contents.slice(3).some((c) => countFunctionResponses(c.parts) > 0)).toBe(false);
85+},
86+);
87+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。