Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type { GoogleLanguageModelOptions } from '@ai-sdk/google';
import { generateText, stepCountIs, tool } from 'ai';
import { z } from 'zod';
import { run } from '../lib/run';

const model = 'google/gemini-3-pro-preview';
const firstUserMessage =
'Use the lookup_definition tool first for "eventual consistency", then answer in one sentence. USE THE GOOGLE PROVIDER!!';

run(async () => {
const providerOptions = {
gateway: {
only: ['google'],
},
vertex: {
safetySettings: [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the combination of passing vertex providerOptions to the google provider fails passing those options. when you run this example, you will notice that no safety ratings are set.

however passing google providerOptions to vertex provider will work, and the safety ratings will be observed.

Solution:
the conversion of the key from vertex to google needs to happen when gateway falls back to google provider from vertex

{
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
threshold: 'BLOCK_ONLY_HIGH',
},
{
category: 'HARM_CATEGORY_HATE_SPEECH',
threshold: 'BLOCK_ONLY_HIGH',
},
],
} satisfies GoogleLanguageModelOptions,
};
const tools = {
lookup_definition: tool({
description: 'Returns a short definition for a systems concept.',
inputSchema: z.object({
term: z.string(),
}),
execute: async ({ term }) => ({
term,
definition:
'Eventual consistency means replicas may temporarily differ, but converge to the same value after propagation.',
}),
}),
};

const firstTurn = await generateText({
model,
prompt: firstUserMessage,
tools,
stopWhen: stepCountIs(10),
providerOptions,
});

console.log('Turn 1:', firstTurn.text);
console.log(
'Turn 1 provider metadata:',
JSON.stringify(firstTurn.providerMetadata, null, 2),
);
console.log();

const secondTurn = await generateText({
model,
messages: [
{ role: 'user', content: firstUserMessage },
...firstTurn.response.messages,
{
role: 'user',
content:
'Use the lookup_definition tool again, then give a concrete 2-line example involving an inventory update.',
},
],
tools,
stopWhen: stepCountIs(10),
providerOptions,
});

console.log('Turn 2:', secondTurn.text);
console.log(
'Turn 2 provider metadata:',
JSON.stringify(secondTurn.providerMetadata, null, 2),
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { gateway, generateText, tool } from 'ai';
import { z } from 'zod';
import { run } from '../lib/run';

run(async () => {
const result = await generateText({
model: gateway('google/gemini-3-flash-preview'),
tools: {
weather: tool({
description: 'Get the current weather in a given location',
inputSchema: z.object({
location: z
.string()
.describe('The city and state, e.g. San Francisco, CA'),
}),
execute: async ({ location }) => ({
location,
temperature: 72,
unit: 'fahrenheit',
}),
}),
},
stopWhen: result => result.steps.length >= 3,
prompt: 'What is the weather in San Francisco?',
});

console.log('Text:', result.text);
console.log('Steps:', result.steps.length);

for (const step of result.steps) {
console.log('---');
console.log('Step finish reason:', step.finishReason);
for (const content of step.content) {
if (content.type === 'tool-call') {
console.log('Tool call:', content.toolName, content.input);
console.log(
'Provider metadata:',
JSON.stringify(content.providerMetadata),
);
}
if (content.type === 'reasoning') {
console.log(
'Reasoning metadata:',
JSON.stringify(content.providerMetadata),
);
}
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { generateText, tool } from 'ai';
import { z } from 'zod';
import { run } from '../lib/run';

run(async () => {
const google = createGoogleGenerativeAI();
const model = google('gemini-3-flash-preview');

const result = await generateText({
model,
tools: {
weather: tool({
description: 'Get the current weather in a given location',
inputSchema: z.object({
location: z
.string()
.describe('The city and state, e.g. San Francisco, CA'),
}),
execute: async ({ location }) => ({
location,
temperature: 72,
unit: 'fahrenheit',
}),
}),
},
stopWhen: result => result.steps.length >= 3,
prompt: 'What is the weather in San Francisco?',
});

console.log('Text:', result.text);
console.log('Steps:', result.steps.length);

for (const step of result.steps) {
console.log('---');
console.log('Step finish reason:', step.finishReason);
for (const content of step.content) {
if (content.type === 'tool-call') {
console.log('Tool call:', content.toolName, content.input);
console.log(
'Provider metadata:',
JSON.stringify(content.providerMetadata),
);
}
}
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { GoogleLanguageModelOptions } from '@ai-sdk/google';
import { stepCountIs, streamText, tool } from 'ai';
import { z } from 'zod';
import { run } from '../lib/run';

const model = 'google/gemini-3-flash';
const firstUserMessage =
'Use the lookup_definition tool first for "eventual consistency", then answer in one sentence.';

run(async () => {
const providerOptions = {
gateway: {
only: ['google'],
},
vertex: {
safetySettings: [
{
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
threshold: 'BLOCK_ONLY_HIGH',
},
{
category: 'HARM_CATEGORY_HATE_SPEECH',
threshold: 'BLOCK_ONLY_HIGH',
},
],
} satisfies GoogleLanguageModelOptions,
};
const tools = {
lookup_definition: tool({
description: 'Returns a short definition for a systems concept.',
inputSchema: z.object({
term: z.string(),
}),
execute: async ({ term }) => ({
term,
definition:
'Eventual consistency means replicas may temporarily differ, but converge to the same value after propagation.',
}),
}),
};

const firstTurn = streamText({
model,
prompt: firstUserMessage,
tools,
stopWhen: stepCountIs(10),
providerOptions,
});

let firstTurnText = '';
process.stdout.write('Turn 1: ');
for await (const textPart of firstTurn.textStream) {
firstTurnText += textPart;
process.stdout.write(textPart);
}
process.stdout.write('\n');
console.log(
'Turn 1 provider metadata:',
JSON.stringify(await firstTurn.providerMetadata, null, 2),
);
console.log();
const firstTurnResponse = await firstTurn.response;

const secondTurn = streamText({
model,
messages: [
{ role: 'user', content: firstUserMessage },
...firstTurnResponse.messages,
{
role: 'user',
content:
'Use the lookup_definition tool again, then give a concrete 2-line example involving an inventory update.',
},
],
tools,
stopWhen: stepCountIs(10),
providerOptions,
});

process.stdout.write('Turn 2: ');
for await (const textPart of secondTurn.textStream) {
process.stdout.write(textPart);
}
process.stdout.write('\n');
console.log(
'Turn 2 provider metadata:',
JSON.stringify(await secondTurn.providerMetadata, null, 2),
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { gateway, streamText, tool } from 'ai';
import { z } from 'zod';
import { run } from '../lib/run';

run(async () => {
const result = streamText({
model: gateway('google/gemini-3-flash-preview'),
tools: {
weather: tool({
description: 'Get the current weather in a given location',
inputSchema: z.object({
location: z
.string()
.describe('The city and state, e.g. San Francisco, CA'),
}),
execute: async ({ location }) => ({
location,
temperature: 72,
unit: 'fahrenheit',
}),
}),
},
stopWhen: result => result.steps.length >= 3,
prompt: 'What is the weather in San Francisco?',
});

for await (const chunk of result.fullStream) {
switch (chunk.type) {
case 'text-delta':
process.stdout.write(chunk.text);
break;
case 'reasoning-delta':
break;
case 'tool-call':
console.log('Tool call:', chunk.toolName, chunk.input);
break;
case 'tool-result':
console.log('Tool result:', JSON.stringify(chunk.output));
break;
}
}

console.log();
console.log('Steps:', (await result.steps).length);

for (const step of await result.steps) {
console.log('---');
console.log('Step finish reason:', step.finishReason);
for (const content of step.content) {
if (content.type === 'tool-call') {
console.log(
'Provider metadata:',
JSON.stringify(content.providerMetadata),
);
}
}
}
});
62 changes: 62 additions & 0 deletions examples/ai-functions/src/stream-text/google-thinking-roundtrip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { createGoogleGenerativeAI } from '@ai-sdk/google';
import { streamText, tool } from 'ai';
import { z } from 'zod';
import { run } from '../lib/run';

run(async () => {
const google = createGoogleGenerativeAI();
const model = google('gemini-3-flash-preview');

const result = streamText({
model,
tools: {
weather: tool({
description: 'Get the current weather in a given location',
inputSchema: z.object({
location: z
.string()
.describe('The city and state, e.g. San Francisco, CA'),
}),
execute: async ({ location }) => ({
location,
temperature: 72,
unit: 'fahrenheit',
}),
}),
},
stopWhen: result => result.steps.length >= 3,
prompt: 'What is the weather in San Francisco?',
});

for await (const chunk of result.fullStream) {
switch (chunk.type) {
case 'text-delta':
process.stdout.write(chunk.text);
break;
case 'reasoning-delta':
break;
case 'tool-call':
console.log('Tool call:', chunk.toolName, chunk.input);
break;
case 'tool-result':
console.log('Tool result:', JSON.stringify(chunk.output));
break;
}
}

console.log();
console.log('Steps:', (await result.steps).length);

for (const step of await result.steps) {
console.log('---');
console.log('Step finish reason:', step.finishReason);
for (const content of step.content) {
if (content.type === 'tool-call') {
console.log(
'Provider metadata:',
JSON.stringify(content.providerMetadata),
);
}
}
}
});
Loading
Loading