fix(google): forward Gemini stop sequences

Forward configured stop sequences to Gemini generationConfig.stopSequences in the bundled Google transport, matching the shared Google provider behavior and the @google/genai request contract.\n\nThanks @coder999999999.
This commit is contained in:
Coder
2026-06-02 09:02:27 -04:00
committed by GitHub
parent 3509f7613e
commit 4bb86877e2
2 changed files with 73 additions and 0 deletions

View File

@@ -1628,6 +1628,76 @@ describe("google transport stream", () => {
expect(generationConfig).not.toHaveProperty("thinkingConfig");
});
it("forwards configured stop sequences to the Gemini generationConfig", () => {
const params = buildGoogleGenerativeAiParams(
buildGeminiModel(),
{
messages: [{ role: "user", content: "hello", timestamp: 0 }],
} as never,
{
stop: ["</tool>", "\n\nObservation:"],
} as never,
);
const generationConfig = requireGenerationConfig(params);
expect(generationConfig.stopSequences).toEqual(["</tool>", "\n\nObservation:"]);
});
it("omits stopSequences when the stop list is empty", () => {
const params = buildGoogleGenerativeAiParams(
buildGeminiModel(),
{
messages: [{ role: "user", content: "hello", timestamp: 0 }],
} as never,
{
stop: [],
} as never,
);
expect(params.generationConfig ?? {}).not.toHaveProperty("stopSequences");
});
it("sends stopSequences in the serialized Gemini request body via the guarded fetch transport", async () => {
guardedFetchMock.mockResolvedValueOnce(buildSseResponse([]));
const model = attachModelProviderRequestTransport(
{
id: "gemini-3.1-pro-preview",
name: "Gemini 3.1 Pro Preview",
api: "google-generative-ai",
provider: "google",
baseUrl: "https://generativelanguage.googleapis.com",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128000,
maxTokens: 8192,
} satisfies Model<"google-generative-ai">,
{},
);
const streamFn = createGoogleGenerativeAiTransportStreamFn();
const stream = await Promise.resolve(
streamFn(
model,
{
messages: [{ role: "user", content: "hello", timestamp: 0 }],
} as Parameters<typeof streamFn>[1],
{
apiKey: "gemini-api-key",
stop: ["</tool>", "\n\nObservation:"],
} as Parameters<typeof streamFn>[2],
),
);
await stream.result();
const guardedCall = requireMockCall(guardedFetchMock, 0, "guarded fetch");
const init = requireRequestInit(guardedCall, "guarded fetch");
const payload = parseRequestJsonBody(init);
const generationConfig = requireGenerationConfig(payload);
expect(generationConfig.stopSequences).toEqual(["</tool>", "\n\nObservation:"]);
});
it("strips explicit thinkingBudget=0 but preserves includeThoughts for Gemini 2.5 Pro", () => {
const params = buildGoogleGenerativeAiParams(
buildGeminiModel(),

View File

@@ -703,6 +703,9 @@ export function buildGoogleGenerativeAiParams(
if (typeof options?.maxTokens === "number") {
generationConfig.maxOutputTokens = options.maxTokens;
}
if (options?.stop !== undefined && options.stop.length > 0) {
generationConfig.stopSequences = options.stop;
}
const thinkingConfig = resolveGoogleThinkingConfig(model, options);
if (thinkingConfig) {
generationConfig.thinkingConfig = thinkingConfig;