Skip to content

Commit 051361b

Browse files
fix(vertex): add fallback for providerOptions key (#12403)
## Background multi step tool calls fail because the `thoughtSignature` gets lost due to a key mismatch in providerOptions. if the `thoughtSignature` is passed under the google key, the vertex provider silently drops it. the api then rejects the request and we see the errors as observed in #12351 ## Summary fallback check to see if `vertex` key doesn't have a `thoughtSignature`, we check if it's passed within the google key ## Manual Verification verified via the repro code given in the original issue, which is resolved after the fix ## Checklist - [x] Tests have been added / updated (for bug fixes / features) - [ ] Documentation has been added / updated (for bug fixes / features) - [x] A _patch_ changeset for relevant packages has been added (for bug fixes / features - run `pnpm changeset` in the project root) - [x] I have reviewed this pull request (self-review) ## Related Issues fixes #12351
1 parent 6bc4bce commit 051361b

File tree

3 files changed

+134
-1
lines changed

3 files changed

+134
-1
lines changed

.changeset/clean-books-peel.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ai-sdk/google': patch
3+
---
4+
5+
fix(vertex): add fallback for providerOptions keyname

packages/google/src/convert-to-google-generative-ai-messages.test.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,130 @@ describe('thought signatures', () => {
8585
});
8686
});
8787

88+
describe('thought signatures with vertex providerOptionsName', () => {
89+
it('should resolve thoughtSignature from google namespace when using vertex providerOptionsName', async () => {
90+
const result = convertToGoogleGenerativeAIMessages(
91+
[
92+
{
93+
role: 'assistant',
94+
content: [
95+
{
96+
type: 'text',
97+
text: 'Regular text',
98+
providerOptions: { google: { thoughtSignature: 'sig1' } },
99+
},
100+
{
101+
type: 'reasoning',
102+
text: 'Reasoning text',
103+
providerOptions: { google: { thoughtSignature: 'sig2' } },
104+
},
105+
{
106+
type: 'tool-call',
107+
toolCallId: 'call1',
108+
toolName: 'getWeather',
109+
input: { location: 'London' },
110+
providerOptions: { google: { thoughtSignature: 'sig3' } },
111+
},
112+
],
113+
},
114+
],
115+
{ providerOptionsName: 'vertex' },
116+
);
117+
118+
expect(result).toMatchInlineSnapshot(`
119+
{
120+
"contents": [
121+
{
122+
"parts": [
123+
{
124+
"text": "Regular text",
125+
"thoughtSignature": "sig1",
126+
},
127+
{
128+
"text": "Reasoning text",
129+
"thought": true,
130+
"thoughtSignature": "sig2",
131+
},
132+
{
133+
"functionCall": {
134+
"args": {
135+
"location": "London",
136+
},
137+
"name": "getWeather",
138+
},
139+
"thoughtSignature": "sig3",
140+
},
141+
],
142+
"role": "model",
143+
},
144+
],
145+
"systemInstruction": undefined,
146+
}
147+
`);
148+
});
149+
150+
it('should prefer vertex namespace over google namespace when both are present', async () => {
151+
const result = convertToGoogleGenerativeAIMessages(
152+
[
153+
{
154+
role: 'assistant',
155+
content: [
156+
{
157+
type: 'tool-call',
158+
toolCallId: 'call1',
159+
toolName: 'getWeather',
160+
input: { location: 'London' },
161+
providerOptions: {
162+
vertex: { thoughtSignature: 'vertex_sig' },
163+
google: { thoughtSignature: 'google_sig' },
164+
},
165+
},
166+
],
167+
},
168+
],
169+
{ providerOptionsName: 'vertex' },
170+
);
171+
172+
expect(result.contents[0].parts[0]).toEqual({
173+
functionCall: {
174+
name: 'getWeather',
175+
args: { location: 'London' },
176+
},
177+
thoughtSignature: 'vertex_sig',
178+
});
179+
});
180+
181+
it('should resolve thoughtSignature from vertex namespace directly', async () => {
182+
const result = convertToGoogleGenerativeAIMessages(
183+
[
184+
{
185+
role: 'assistant',
186+
content: [
187+
{
188+
type: 'tool-call',
189+
toolCallId: 'call1',
190+
toolName: 'getWeather',
191+
input: { location: 'London' },
192+
providerOptions: {
193+
vertex: { thoughtSignature: 'vertex_sig' },
194+
},
195+
},
196+
],
197+
},
198+
],
199+
{ providerOptionsName: 'vertex' },
200+
);
201+
202+
expect(result.contents[0].parts[0]).toEqual({
203+
functionCall: {
204+
name: 'getWeather',
205+
args: { location: 'London' },
206+
},
207+
thoughtSignature: 'vertex_sig',
208+
});
209+
});
210+
});
211+
88212
describe('Gemma model system instructions', () => {
89213
it('should prepend system instruction to first user message for Gemma models', async () => {
90214
const result = convertToGoogleGenerativeAIMessages(

packages/google/src/convert-to-google-generative-ai-messages.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ export function convertToGoogleGenerativeAIMessages(
8282
role: 'model',
8383
parts: content
8484
.map(part => {
85-
const providerOpts = part.providerOptions?.[providerOptionsName];
85+
const providerOpts =
86+
part.providerOptions?.[providerOptionsName] ??
87+
(providerOptionsName !== 'google'
88+
? part.providerOptions?.google
89+
: undefined);
8690
const thoughtSignature =
8791
providerOpts?.thoughtSignature != null
8892
? String(providerOpts.thoughtSignature)

0 commit comments

Comments
 (0)