-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Feature: Support Gemma 4 thinking format in extractReasoningMiddleware #14217
Copy link
Copy link
Open
Labels
ai/corecore functions like generateText, streamText, etc. Provider utils, and provider spec.core functions like generateText, streamText, etc. Provider utils, and provider spec.ai/providerrelated to a provider package. Must be assigned together with at least one `provider/*` labelrelated to a provider package. Must be assigned together with at least one `provider/*` labelfeatureNew feature or requestNew feature or requestprovider/google-vertexIssues related to the @ai-sdk/google-vertex providerIssues related to the @ai-sdk/google-vertex providersupport
Metadata
Metadata
Assignees
Labels
ai/corecore functions like generateText, streamText, etc. Provider utils, and provider spec.core functions like generateText, streamText, etc. Provider utils, and provider spec.ai/providerrelated to a provider package. Must be assigned together with at least one `provider/*` labelrelated to a provider package. Must be assigned together with at least one `provider/*` labelfeatureNew feature or requestNew feature or requestprovider/google-vertexIssues related to the @ai-sdk/google-vertex providerIssues related to the @ai-sdk/google-vertex providersupport
Type
Fields
Give feedbackNo fields configured for issues without a type.
Summary
extractReasoningMiddlewarecannot handle the reasoning token format used by Gemma 4 (available on Google Vertex AI). Gemma 4 uses asymmetric, non-XML tokens for its thinking output, which the currenttagName-based API cannot express.Background
Gemma 4 thinking format (from Vertex AI Model Garden):
<|think|>at the start of the system prompt.<|channel>thought\n(literal string, includes a newline)<channel|>Problem
The current API only accepts a single
tagNamestring and constructs symmetric<tagName>/</tagName>markers:Gemma 4's opening token (
<|channel>thought\n) and closing token (<channel|>) are:|and>placement doesn't match<tag>/</tag>.There is no way to configure this today without forking the middleware.
Proposed Fix
Allow
tagNameto accept either a plain string (existing behaviour) or an object withopeningandclosingproperties:The internal resolution becomes:
Special regex characters in the delimiter strings (e.g.
|) must be escaped before use inwrapGenerate'snew RegExp(...)call. ThewrapStreampath usesindexOf-based matching and requires no changes.Multi-turn note
Gemma's docs state that thoughts must not appear in conversation history — only the final answer should be kept between turns. This matches the existing middleware behavior (reasoning is stripped from the text part), so no additional work is needed for multi-turn handling.
Acceptance Criteria
tagNameacceptsstring | { opening: string; closing: string }.tagName: { opening: '<|channel>thought\n', closing: '<channel|>' }correctly extracts Gemma 4 reasoning in bothgenerateTextandstreamText.tagName: stringusage is fully backward-compatible (no breaking change).examples/ai-functions/src/stream-text/google-vertex/gemma4-reasoning.tsexample is added.Affected file
packages/ai/src/middleware/extract-reasoning-middleware.tsCode of Conduct