From 0baf83a233f3b26cbeea908a56be7ad8435b9903 Mon Sep 17 00:00:00 2001 From: chilung Date: Sun, 18 Oct 2026 20:15:50 +0800 Subject: [PATCH 1/4] feat(google): honor showThinkingSummary on Vertex and direct Gemini Cloud Code Assist Gemini requests opt into generationConfig.thinkingConfig.includeThoughts and classify returned thought: true parts as thinking_delta summaries when provider.showThinkingSummary is enabled, while Vertex and direct Gemini only forwarded thinkingLevel and left Google streaming silent for the entire reasoning phase (triggering 200s response-byte inactivity timeouts on long reasoning turns). Honor provider.showThinkingSummary on thinking-eligible Vertex and direct Gemini requests as well, subject to the same per-request hideThinkingSummary gate, non-image guard, and Gemini model-family check. Unopted providers, unladdered Vertex models, image models, and explicit summary:"none" requests remain unchanged. --- .../docs/reference/configuration/providers.md | 2 +- src/adapters/google.ts | 23 +++-- structure/providers/google.md | 11 ++- tests/adapters/google/google-adapter.test.ts | 95 +++++++++++++++++++ 4 files changed, 118 insertions(+), 13 deletions(-) diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index 3752356b52e..1c556d44899 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -357,7 +357,7 @@ Providers can expose a built-in shorthand, such as `agy` for `google-antigravity | `inlineThinkTagModels?` | `string[]` | Opt-in recovery for `openai-chat` gateways without a server-side reasoning parser. A leading `` / `` / `` block (optionally after whitespace) activates splitting in streamed and buffered replies. All answer whitespace is preserved. Subsequent tags are delimiters anywhere, including same-line interleaving and code fences; this mode does not interpret Markdown. Ordinary text or a code fence before the first tag keeps the whole reply untouched. Off by default; prefer structured upstream reasoning or `reasoningSplitModels` where supported. | | `reasoningDetailsModels?` | `string[]` | Models whose endpoint returns thinking as a structured `reasoning_details` array (MiniMax M-series with `reasoning_split`); stream deltas are cumulative snapshots that are prefix-diffed, and preserved reasoning replays as a `reasoning_details` array instead of a `reasoning_content` string. | | `requiresReasoningPlaceholderModels?` | `string[]` | Models whose upstream rejects a tool_call continuation missing `reasoning_content` (DeepSeek thinking mode); a minimal placeholder is injected when the replay cache misses. Defaults to `preserveReasoningContentModels`; set `[]` to opt out. A provider save that keeps the destination keeps the stored list, including `[]`; see [What a provider save keeps](#what-a-provider-save-keeps). `PATCH /api/providers?name=` accepts an array or `null` to clear it. | -| `showThinkingSummary?` | `boolean` | Display provider-authored summaries when a Responses client omits `reasoning.summary`. Explicit wire `"none"` wins; a client that serializes its preference as omission cannot be distinguished. Raw reasoning remains content and is never relabeled as a summary. The `google-antigravity` preset defaults to `true`; explicit `false` disables that default. CCA Gemini requests also opt into `generationConfig.thinkingConfig.includeThoughts` when display is enabled; image, Claude and gpt-oss requests do not. This does not change client configuration or global catalog summary defaults. | +| `showThinkingSummary?` | `boolean` | Display provider-authored summaries when a Responses client omits `reasoning.summary`. Explicit wire `"none"` wins; a client that serializes its preference as omission cannot be distinguished. Raw reasoning remains content and is never relabeled as a summary. The `google-antigravity` preset defaults to `true`; explicit `false` disables that default. Eligible CCA, Vertex and direct Gemini requests also opt into `generationConfig.thinkingConfig.includeThoughts` when display is enabled; image, Claude, gpt-oss and unladdered Vertex requests do not. This does not change client configuration or global catalog summary defaults. | | `hideRawReasoning?` | `boolean` | Keep a routed provider's raw chain of thought off the client-facing content channel (openai-chat `reasoning_content`, kiro tags, `response.reasoning_text.*`) while provider-authored summaries still stream on the summary channel. Explicit wire `reasoning.summary: "none"` keeps hiding both. This controls what a client displays; it does not keep the text from the client. A Responses bridge route still sends it inside the `ocxr1` replay envelope in the reasoning item's `encrypted_content`, which is base64 JSON rather than encryption, so the client can echo it back on the next turn. The direct Chat and Messages encoders have no envelope on their wire, so those clients receive no copy and replay comes from the server-side reasoning replay cache. `preserveReasoningContentModels` replay and DeepSeek-style continuations are unaffected either way. Off by default; a native passthrough route relays the upstream's own frames and ignores this option. A save of the same provider name keeps an omitted value even after a destination move; `PATCH` accepts a boolean or `null` to clear it. | | `thinkingToggleModels?` | `string[]` | Chat models using `thinking.enabled` rather than an effort ladder. | | `thinkingBudgetModels?` | `string[]` | Chat models using integer `thinking_budget`; effort maps to a budget fraction. | diff --git a/src/adapters/google.ts b/src/adapters/google.ts index ed55b5870f5..5d5bf1901e6 100644 --- a/src/adapters/google.ts +++ b/src/adapters/google.ts @@ -1026,7 +1026,12 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte : provider.googleMode === "vertex" || openCodeZenGeminiEndpoint(provider, parsed.modelId) !== undefined ? parsed.modelId : resolveDirectGeminiWireModelId(parsed.modelId, provider.directGeminiWireRenames !== false); - returnsThoughtSummaries = provider.googleMode === "cloud-code-assist" + const thinkingEligible = provider.googleMode !== "cloud-code-assist" + && !isImageCapableModel(parsed.modelId) + && (configuredReasoningEfforts(provider, parsed.modelId) !== undefined + || (provider.googleMode !== "vertex" + && (parsed.modelId === "gemini-3.5-flash" || parsed.modelId === "gemini-3.6-flash"))); + returnsThoughtSummaries = (provider.googleMode === "cloud-code-assist" || (thinkingEligible && provider.showThinkingSummary === true)) && /^gemini-/.test(routedModelId) && !isImageCapableModel(parsed.modelId); // AI Studio's `-tiered` spelling is wire-only; CCA aliases may migrate to another generation. const identityModelId = provider.googleMode === "cloud-code-assist" ? routedModelId : parsed.modelId; @@ -1065,15 +1070,19 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte // current behavior; Vertex participates only through an explicitly configured ladder (the // seed google-vertex entry ships none). Image models are excluded — thinkingConfig would // suppress the responseModalities fallback below. CCA maps effort on its envelope path. - const thinkingEligible = provider.googleMode !== "cloud-code-assist" - && !isImageCapableModel(parsed.modelId) - && (configuredReasoningEfforts(provider, parsed.modelId) !== undefined - || (provider.googleMode !== "vertex" - && (parsed.modelId === "gemini-3.5-flash" || parsed.modelId === "gemini-3.6-flash"))); const thinkingLevel = thinkingEligible ? mapReasoningEffort(provider, parsed.modelId, parsed.options.reasoning) : undefined; - if (thinkingLevel) generationConfig.thinkingConfig = { thinkingLevel }; + const includeThoughts = thinkingEligible + && provider.showThinkingSummary === true + && parsed.options.hideThinkingSummary !== true + && /^gemini-/.test(routedModelId); + if (thinkingLevel || includeThoughts) { + generationConfig.thinkingConfig = { + ...(thinkingLevel ? { thinkingLevel } : {}), + ...(includeThoughts ? { includeThoughts: true } : {}), + }; + } if (!generationConfig.thinkingConfig && isImageCapableModel(parsed.modelId)) { generationConfig.responseModalities = ["TEXT", "IMAGE"]; } diff --git a/structure/providers/google.md b/structure/providers/google.md index 050021d8257..fd99d84bfbd 100644 --- a/structure/providers/google.md +++ b/structure/providers/google.md @@ -75,12 +75,13 @@ these fixtures do not attest live upstream availability. ## Google thought-text visibility boundary Google-family parts with `thought: true` stay separate from assistant output. After a CCA -Gemini request is built, the shared streaming/buffered classifier emits `thinking_delta` for -these provider-authored summaries. Other Google wires, non-Gemini CCA models and uninitialized -adapters retain `reasoning_raw_delta`. Model provenance is refreshed on every build. +Gemini request or a `showThinkingSummary`-opted-in Gemini request is built, the shared +streaming/buffered classifier emits `thinking_delta` for these provider-authored summaries. +Other Google wires without the opt-in, non-Gemini models and uninitialized adapters retain +`reasoning_raw_delta`. Model provenance is refreshed on every build. `showThinkingSummary` defaults on only for the Antigravity preset; explicit provider false and -explicit wire summary none win. Eligible CCA Gemini requests use `includeThoughts: true` only -when provider opt-in and per-request display both allow it. Thought signatures remain attached +explicit wire summary none win. Eligible CCA, Vertex and direct Gemini requests use +`includeThoughts: true` only when provider opt-in and per-request display both allow it. Thought signatures remain attached to their tool calls independently; they never become Anthropic thinking signatures. The visibility marker is the boolean `thought: true`, not the presence of a `thoughtSignature` diff --git a/tests/adapters/google/google-adapter.test.ts b/tests/adapters/google/google-adapter.test.ts index ad11c4a6c5a..e3b3afd574f 100644 --- a/tests/adapters/google/google-adapter.test.ts +++ b/tests/adapters/google/google-adapter.test.ts @@ -870,6 +870,101 @@ describe("google adapter — Antigravity thought-text opt-in", () => { }); +describe("google adapter — Vertex and direct Gemini thought-text opt-in", () => { + const vertexBase = { + adapter: "google", + googleMode: "vertex", + baseUrl: "https://aiplatform.googleapis.com", + apiKey: "vertex-key", + project: "fixture-proj", + location: "global", + modelReasoningEfforts: { "gemini-4-argon": ["low", "medium", "high"] }, + } as const; + const directBase = { + adapter: "google", + baseUrl: "https://generativelanguage.googleapis.com", + apiKey: "direct-key", + modelReasoningEfforts: { "gemini-4-argon": ["low", "medium", "high"] }, + } as const; + + function makeParsed(modelId: string, effort?: string, hideThinkingSummary?: boolean): OcxParsedRequest { + return { + modelId, + stream: false, + options: { ...(effort ? { reasoning: effort } : {}), ...(hideThinkingSummary ? { hideThinkingSummary } : {}) }, + context: { messages: [{ role: "user", content: "hi" }], tools: [] }, + } as unknown as OcxParsedRequest; + } + + async function wireThinkingConfig( + providerConfig: Record, + modelId: string, + effort?: string, + hideThinkingSummary?: boolean, + ): Promise | undefined> { + const { body } = await createGoogleAdapter(providerConfig as never) + .buildRequest(makeParsed(modelId, effort, hideThinkingSummary)); + const parsedBody = JSON.parse(body) as { + generationConfig?: { thinkingConfig?: Record }; + }; + return parsedBody.generationConfig?.thinkingConfig; + } + + test("opts Vertex and direct Gemini into includeThoughts when showThinkingSummary is true", async () => { + const vertexOptIn = { ...vertexBase, showThinkingSummary: true }; + const directOptIn = { ...directBase, showThinkingSummary: true }; + expect(await wireThinkingConfig(vertexOptIn, "gemini-4-argon", "high")) + .toEqual({ thinkingLevel: "high", includeThoughts: true }); + expect(await wireThinkingConfig(vertexOptIn, "gemini-4-argon")) + .toEqual({ includeThoughts: true }); + expect(await wireThinkingConfig(directOptIn, "gemini-4-argon", "medium")) + .toEqual({ thinkingLevel: "medium", includeThoughts: true }); + expect(await wireThinkingConfig(directOptIn, "gemini-3.5-flash")) + .toEqual({ includeThoughts: true }); + }); + + test("omits includeThoughts when showThinkingSummary is absent, false, or client-suppressed", async () => { + const vertexOptIn = { ...vertexBase, showThinkingSummary: true }; + expect(await wireThinkingConfig(vertexBase, "gemini-4-argon", "high")) + .toEqual({ thinkingLevel: "high" }); + expect(await wireThinkingConfig({ ...vertexBase, showThinkingSummary: false }, "gemini-4-argon", "high")) + .toEqual({ thinkingLevel: "high" }); + expect(await wireThinkingConfig(vertexOptIn, "gemini-4-argon", "high", true)) + .toEqual({ thinkingLevel: "high" }); + expect(await wireThinkingConfig(vertexOptIn, "gemini-4-argon", undefined, true)) + .toBeUndefined(); + }); + + test("excludes unladdered Vertex models and image-capable models from includeThoughts", async () => { + const vertexOptIn = { ...vertexBase, showThinkingSummary: true }; + expect(await wireThinkingConfig(vertexOptIn, "gemini-2.5-flash", "high")).toBeUndefined(); + expect(await wireThinkingConfig(vertexOptIn, "gemini-3.1-flash-image", "high")).toBeUndefined(); + }); + + test("emits thinking_delta for Vertex and direct Gemini only when showThinkingSummary is true", async () => { + const payload = { + candidates: [{ + content: { parts: [{ thought: true, text: "vertex summary" }, { text: "answer" }] }, + finishReason: "STOP", + }], + }; + const optedInAdapter = withTestTranslatorBudget(createGoogleAdapter({ ...vertexBase, showThinkingSummary: true } as never)); + await optedInAdapter.buildRequest(makeParsed("gemini-4-argon", "high")); + const optedInEvents = await optedInAdapter.parseResponse!(Response.json(payload)); + expect(optedInEvents[0]).toEqual({ type: "thinking_delta", thinking: "vertex summary" }); + + const defaultAdapter = withTestTranslatorBudget(createGoogleAdapter(vertexBase as never)); + await defaultAdapter.buildRequest(makeParsed("gemini-4-argon", "high")); + const defaultEvents = await defaultAdapter.parseResponse!(Response.json(payload)); + expect(defaultEvents[0]).toEqual({ type: "reasoning_raw_delta", text: "vertex summary" }); + + const unladderedAdapter = withTestTranslatorBudget(createGoogleAdapter({ ...vertexBase, showThinkingSummary: true } as never)); + await unladderedAdapter.buildRequest(makeParsed("gemini-2.5-flash", "high")); + const unladderedEvents = await unladderedAdapter.parseResponse!(Response.json(payload)); + expect(unladderedEvents[0]).toEqual({ type: "reasoning_raw_delta", text: "vertex summary" }); + }); +}); + describe("CCA thought summary provenance and replay", () => { const cca = { adapter: "google", googleMode: "cloud-code-assist", baseUrl: "https://daily-cloudcode-pa.googleapis.com", apiKey: "fixture-key", project: "fixture-project", showThinkingSummary: true } as const; From 4fd43d5035688016de6eaf0dbb33b59cb1c01477 Mon Sep 17 00:00:00 2001 From: chilung Date: Sun, 18 Oct 2026 20:24:54 +0800 Subject: [PATCH 2/4] fix(google): require a non-empty reasoning ladder for thinking eligibility --- src/adapters/google.ts | 6 ++++-- tests/adapters/google/google-adapter.test.ts | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/adapters/google.ts b/src/adapters/google.ts index 5d5bf1901e6..90e7e9fd504 100644 --- a/src/adapters/google.ts +++ b/src/adapters/google.ts @@ -1026,10 +1026,12 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte : provider.googleMode === "vertex" || openCodeZenGeminiEndpoint(provider, parsed.modelId) !== undefined ? parsed.modelId : resolveDirectGeminiWireModelId(parsed.modelId, provider.directGeminiWireRenames !== false); + const configuredEfforts = configuredReasoningEfforts(provider, parsed.modelId); const thinkingEligible = provider.googleMode !== "cloud-code-assist" && !isImageCapableModel(parsed.modelId) - && (configuredReasoningEfforts(provider, parsed.modelId) !== undefined - || (provider.googleMode !== "vertex" + && (configuredEfforts !== undefined + ? configuredEfforts.length > 0 + : (provider.googleMode !== "vertex" && (parsed.modelId === "gemini-3.5-flash" || parsed.modelId === "gemini-3.6-flash"))); returnsThoughtSummaries = (provider.googleMode === "cloud-code-assist" || (thinkingEligible && provider.showThinkingSummary === true)) && /^gemini-/.test(routedModelId) && !isImageCapableModel(parsed.modelId); diff --git a/tests/adapters/google/google-adapter.test.ts b/tests/adapters/google/google-adapter.test.ts index e3b3afd574f..e465d1c4c9d 100644 --- a/tests/adapters/google/google-adapter.test.ts +++ b/tests/adapters/google/google-adapter.test.ts @@ -939,6 +939,8 @@ describe("google adapter — Vertex and direct Gemini thought-text opt-in", () = const vertexOptIn = { ...vertexBase, showThinkingSummary: true }; expect(await wireThinkingConfig(vertexOptIn, "gemini-2.5-flash", "high")).toBeUndefined(); expect(await wireThinkingConfig(vertexOptIn, "gemini-3.1-flash-image", "high")).toBeUndefined(); + expect(await wireThinkingConfig({ ...vertexOptIn, noReasoningModels: ["gemini-4-argon"] }, "gemini-4-argon", "high")).toBeUndefined(); + expect(await wireThinkingConfig({ ...directBase, showThinkingSummary: true, noReasoningModels: ["gemini-3.5-flash"] }, "gemini-3.5-flash", "high")).toBeUndefined(); }); test("emits thinking_delta for Vertex and direct Gemini only when showThinkingSummary is true", async () => { From de218cd2498814329e09862379daffbd10be3ff6 Mon Sep 17 00:00:00 2001 From: chilung Date: Sun, 18 Oct 2026 23:16:26 +0800 Subject: [PATCH 3/4] test(google): cover stream parity and adapter reuse for showThinkingSummary --- tests/adapters/google/google-adapter.test.ts | 126 ++++++++++++++++--- 1 file changed, 112 insertions(+), 14 deletions(-) diff --git a/tests/adapters/google/google-adapter.test.ts b/tests/adapters/google/google-adapter.test.ts index e465d1c4c9d..ed72249346c 100644 --- a/tests/adapters/google/google-adapter.test.ts +++ b/tests/adapters/google/google-adapter.test.ts @@ -950,20 +950,118 @@ describe("google adapter — Vertex and direct Gemini thought-text opt-in", () = finishReason: "STOP", }], }; - const optedInAdapter = withTestTranslatorBudget(createGoogleAdapter({ ...vertexBase, showThinkingSummary: true } as never)); - await optedInAdapter.buildRequest(makeParsed("gemini-4-argon", "high")); - const optedInEvents = await optedInAdapter.parseResponse!(Response.json(payload)); - expect(optedInEvents[0]).toEqual({ type: "thinking_delta", thinking: "vertex summary" }); - - const defaultAdapter = withTestTranslatorBudget(createGoogleAdapter(vertexBase as never)); - await defaultAdapter.buildRequest(makeParsed("gemini-4-argon", "high")); - const defaultEvents = await defaultAdapter.parseResponse!(Response.json(payload)); - expect(defaultEvents[0]).toEqual({ type: "reasoning_raw_delta", text: "vertex summary" }); - - const unladderedAdapter = withTestTranslatorBudget(createGoogleAdapter({ ...vertexBase, showThinkingSummary: true } as never)); - await unladderedAdapter.buildRequest(makeParsed("gemini-2.5-flash", "high")); - const unladderedEvents = await unladderedAdapter.parseResponse!(Response.json(payload)); - expect(unladderedEvents[0]).toEqual({ type: "reasoning_raw_delta", text: "vertex summary" }); + + async function extractFirstEvent( + adapter: ReturnType, + stream: boolean, + ): Promise { + if (stream) { + for await (const event of adapter.parseStream( + new Response(`data: ${JSON.stringify(payload)} + +`, { + headers: { "content-type": "text/event-stream" }, + }), + )) { + return event; + } + return undefined; + } + const events = await adapter.parseResponse!(Response.json(payload)); + return events[0]; + } + + for (const stream of [false, true]) { + const optedInVertexAdapter = withTestTranslatorBudget( + createGoogleAdapter({ ...vertexBase, showThinkingSummary: true } as never), + ); + await optedInVertexAdapter.buildRequest(makeParsed("gemini-4-argon", "high")); + expect(await extractFirstEvent(optedInVertexAdapter, stream)).toEqual({ + type: "thinking_delta", + thinking: "vertex summary", + }); + + const optedInDirectAdapter = withTestTranslatorBudget( + createGoogleAdapter({ ...directBase, showThinkingSummary: true } as never), + ); + await optedInDirectAdapter.buildRequest(makeParsed("gemini-3.5-flash", "high")); + expect(await extractFirstEvent(optedInDirectAdapter, stream)).toEqual({ + type: "thinking_delta", + thinking: "vertex summary", + }); + + const defaultAdapter = withTestTranslatorBudget(createGoogleAdapter(vertexBase as never)); + await defaultAdapter.buildRequest(makeParsed("gemini-4-argon", "high")); + expect(await extractFirstEvent(defaultAdapter, stream)).toEqual({ + type: "reasoning_raw_delta", + text: "vertex summary", + }); + + const unladderedAdapter = withTestTranslatorBudget( + createGoogleAdapter({ ...vertexBase, showThinkingSummary: true } as never), + ); + await unladderedAdapter.buildRequest(makeParsed("gemini-2.5-flash", "high")); + expect(await extractFirstEvent(unladderedAdapter, stream)).toEqual({ + type: "reasoning_raw_delta", + text: "vertex summary", + }); + } + }); + + test("reused adapter resets summary provenance across eligible and ineligible Vertex and direct Gemini models", async () => { + const payload = { + candidates: [{ + content: { parts: [{ thought: true, text: "thought text" }, { text: "answer" }] }, + finishReason: "STOP", + }], + }; + + async function extractFirstEvent( + adapter: ReturnType, + stream: boolean, + ): Promise { + if (stream) { + for await (const event of adapter.parseStream( + new Response(`data: ${JSON.stringify(payload)} + +`, { + headers: { "content-type": "text/event-stream" }, + }), + )) { + return event; + } + return undefined; + } + const events = await adapter.parseResponse!(Response.json(payload)); + return events[0]; + } + + for (const stream of [false, true]) { + const vertexAdapter = withTestTranslatorBudget( + createGoogleAdapter({ ...vertexBase, showThinkingSummary: true } as never), + ); + for (const [modelId, expected] of [ + ["gemini-4-argon", { type: "thinking_delta", thinking: "thought text" }], + ["gemini-2.5-flash", { type: "reasoning_raw_delta", text: "thought text" }], + ["gemini-4-argon", { type: "thinking_delta", thinking: "thought text" }], + ["gemini-3.1-flash-image", { type: "reasoning_raw_delta", text: "thought text" }], + ] as const) { + await vertexAdapter.buildRequest(makeParsed(modelId, "high")); + expect(await extractFirstEvent(vertexAdapter, stream)).toEqual(expected); + } + + const directAdapter = withTestTranslatorBudget( + createGoogleAdapter({ ...directBase, showThinkingSummary: true } as never), + ); + for (const [modelId, expected] of [ + ["gemini-3.5-flash", { type: "thinking_delta", thinking: "thought text" }], + ["gemini-3.1-flash-image", { type: "reasoning_raw_delta", text: "thought text" }], + ["gemini-3.5-flash", { type: "thinking_delta", thinking: "thought text" }], + ] as const) { + await directAdapter.buildRequest(makeParsed(modelId, "high")); + expect(await extractFirstEvent(directAdapter, stream)).toEqual(expected); + } + } }); }); From 2856074117650b775fa5bb89ae1ad1b95f4b04df Mon Sep 17 00:00:00 2001 From: chilung Date: Sun, 18 Oct 2026 23:39:00 +0800 Subject: [PATCH 4/4] docs(test): add JSDoc comments to thought-summary test helpers --- tests/adapters/google/google-adapter.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/adapters/google/google-adapter.test.ts b/tests/adapters/google/google-adapter.test.ts index ed72249346c..8d54e0c442b 100644 --- a/tests/adapters/google/google-adapter.test.ts +++ b/tests/adapters/google/google-adapter.test.ts @@ -887,6 +887,7 @@ describe("google adapter — Vertex and direct Gemini thought-text opt-in", () = modelReasoningEfforts: { "gemini-4-argon": ["low", "medium", "high"] }, } as const; + /** Builds a minimal parsed request for Vertex/direct Gemini thought-opt-in tests. */ function makeParsed(modelId: string, effort?: string, hideThinkingSummary?: boolean): OcxParsedRequest { return { modelId, @@ -896,6 +897,7 @@ describe("google adapter — Vertex and direct Gemini thought-text opt-in", () = } as unknown as OcxParsedRequest; } + /** Extracts the compiled generationConfig.thinkingConfig from a built request. */ async function wireThinkingConfig( providerConfig: Record, modelId: string, @@ -951,6 +953,7 @@ describe("google adapter — Vertex and direct Gemini thought-text opt-in", () = }], }; + /** Reads the first adapter event from either a streaming SSE or buffered JSON response. */ async function extractFirstEvent( adapter: ReturnType, stream: boolean, @@ -1016,6 +1019,7 @@ describe("google adapter — Vertex and direct Gemini thought-text opt-in", () = }], }; + /** Reads the first adapter event from either a streaming SSE or buffered JSON response. */ async function extractFirstEvent( adapter: ReturnType, stream: boolean,