Skip to content

Commit 2b991c4

Browse files
authored
Add Google Generative AI provider for ai/core functions. (#1261)
1 parent 1088e04 commit 2b991c4

22 files changed

+983
-9
lines changed

.changeset/cool-experts-pull.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
Add Google Generative AI provider for ai/core functions.

examples/ai-core/.env.example

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
OPENAI_API_KEY=""
22
MISTRAL_API_KEY=""
3-
PERPLEXITY_API_KEY=""
4-
FIREWORKS_API_KEY=""
3+
GOOGLE_GENERATIVE_AI_API_KEY=""
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { experimental_generateText } from 'ai';
2+
import { google } from 'ai/google';
3+
import dotenv from 'dotenv';
4+
import fs from 'node:fs';
5+
6+
dotenv.config();
7+
8+
async function main() {
9+
const result = await experimental_generateText({
10+
model: google.generativeAI('models/gemini-pro-vision'),
11+
maxTokens: 512,
12+
messages: [
13+
{
14+
role: 'user',
15+
content: [
16+
{ type: 'text', text: 'Describe the image in detail.' },
17+
{ type: 'image', image: fs.readFileSync('./data/comic-cat.png') },
18+
],
19+
},
20+
],
21+
});
22+
23+
console.log(result.text);
24+
}
25+
26+
main();
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { experimental_generateText, tool } from 'ai';
2+
import { google } from 'ai/google';
3+
import dotenv from 'dotenv';
4+
import { z } from 'zod';
5+
import { weatherTool } from '../tools/weather-tool';
6+
7+
dotenv.config();
8+
9+
async function main() {
10+
const result = await experimental_generateText({
11+
model: google.generativeAI('models/gemini-pro'),
12+
maxTokens: 512,
13+
tools: {
14+
weather: weatherTool,
15+
cityAttractions: tool({
16+
parameters: z.object({ city: z.string() }),
17+
}),
18+
},
19+
prompt:
20+
'What is the weather in San Francisco and what attractions should I visit?',
21+
});
22+
23+
// typed tool calls:
24+
for (const toolCall of result.toolCalls) {
25+
switch (toolCall.toolName) {
26+
case 'cityAttractions': {
27+
toolCall.args.city; // string
28+
break;
29+
}
30+
31+
case 'weather': {
32+
toolCall.args.location; // string
33+
break;
34+
}
35+
}
36+
}
37+
38+
// typed tool results for tools with execute method:
39+
for (const toolResult of result.toolResults) {
40+
switch (toolResult.toolName) {
41+
// NOT AVAILABLE (NO EXECUTE METHOD)
42+
// case 'cityAttractions': {
43+
// toolResult.args.city; // string
44+
// toolResult.result;
45+
// break;
46+
// }
47+
48+
case 'weather': {
49+
toolResult.args.location; // string
50+
toolResult.result.location; // string
51+
toolResult.result.temperature; // number
52+
break;
53+
}
54+
}
55+
}
56+
57+
console.log(JSON.stringify(result, null, 2));
58+
}
59+
60+
main();
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { experimental_generateText } from 'ai';
2+
import { google } from 'ai/google';
3+
import dotenv from 'dotenv';
4+
5+
dotenv.config();
6+
7+
async function main() {
8+
const result = await experimental_generateText({
9+
model: google.generativeAI('models/gemini-pro'),
10+
prompt: 'Invent a new holiday and describe its traditions.',
11+
});
12+
13+
console.log(result.text);
14+
console.log();
15+
console.log('Token usage:', result.usage);
16+
console.log('Finish reason:', result.finishReason);
17+
}
18+
19+
main();

examples/ai-core/src/generate-text/openai-multimodal-url.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ async function main() {
1818
{
1919
type: 'image',
2020
image: new URL(
21-
'https://raw.githubusercontent.com/vercel/ai/v3.1-canary/examples/ai-core/data/comic-cat.png',
21+
'https://github.com/vercel/ai/blob/main/examples/ai-core/data/comic-cat.png?raw=true',
2222
),
2323
},
2424
],
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import {
2+
ExperimentalMessage,
3+
ToolCallPart,
4+
ToolResultPart,
5+
experimental_streamText,
6+
} from 'ai';
7+
import { google } from 'ai/google';
8+
import dotenv from 'dotenv';
9+
import * as readline from 'node:readline/promises';
10+
import { weatherTool } from '../tools/weather-tool';
11+
12+
dotenv.config();
13+
14+
const terminal = readline.createInterface({
15+
input: process.stdin,
16+
output: process.stdout,
17+
});
18+
19+
const messages: ExperimentalMessage[] = [];
20+
21+
async function main() {
22+
let toolResponseAvailable = false;
23+
24+
while (true) {
25+
if (!toolResponseAvailable) {
26+
const userInput = await terminal.question('You: ');
27+
messages.push({ role: 'user', content: userInput });
28+
}
29+
30+
const result = await experimental_streamText({
31+
model: google.generativeAI('models/gemini-pro'),
32+
tools: { weatherTool },
33+
system: `You are a helpful, respectful and honest assistant.`,
34+
messages,
35+
});
36+
37+
toolResponseAvailable = false;
38+
let fullResponse = '';
39+
const toolCalls: ToolCallPart[] = [];
40+
const toolResponses: ToolResultPart[] = [];
41+
42+
for await (const delta of result.fullStream) {
43+
switch (delta.type) {
44+
case 'text-delta': {
45+
if (fullResponse.length === 0) {
46+
process.stdout.write('\nAssistant: ');
47+
}
48+
49+
fullResponse += delta.textDelta;
50+
process.stdout.write(delta.textDelta);
51+
break;
52+
}
53+
54+
case 'tool-call': {
55+
toolCalls.push(delta);
56+
57+
process.stdout.write(
58+
`\nTool call: '${delta.toolName}' ${JSON.stringify(delta.args)}`,
59+
);
60+
break;
61+
}
62+
63+
case 'tool-result': {
64+
toolResponses.push(delta);
65+
66+
process.stdout.write(
67+
`\nTool response: '${delta.toolName}' ${JSON.stringify(
68+
delta.result,
69+
)}`,
70+
);
71+
break;
72+
}
73+
}
74+
}
75+
process.stdout.write('\n\n');
76+
77+
messages.push({
78+
role: 'assistant',
79+
content: [{ type: 'text', text: fullResponse }, ...toolCalls],
80+
});
81+
82+
if (toolResponses.length > 0) {
83+
messages.push({ role: 'tool', content: toolResponses });
84+
}
85+
86+
toolResponseAvailable = toolCalls.length > 0;
87+
}
88+
}
89+
90+
main().catch(console.error);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { ExperimentalMessage, experimental_streamText } from 'ai';
2+
import { google } from 'ai/google';
3+
import dotenv from 'dotenv';
4+
import * as readline from 'node:readline/promises';
5+
6+
dotenv.config();
7+
8+
const terminal = readline.createInterface({
9+
input: process.stdin,
10+
output: process.stdout,
11+
});
12+
13+
const messages: ExperimentalMessage[] = [];
14+
15+
async function main() {
16+
while (true) {
17+
const userInput = await terminal.question('You: ');
18+
19+
messages.push({ role: 'user', content: userInput });
20+
21+
const result = await experimental_streamText({
22+
model: google.generativeAI('models/gemini-pro'),
23+
system: `You are a helpful, respectful and honest assistant.`,
24+
messages,
25+
});
26+
27+
let fullResponse = '';
28+
process.stdout.write('\nAssistant: ');
29+
for await (const delta of result.textStream) {
30+
fullResponse += delta;
31+
process.stdout.write(delta);
32+
}
33+
process.stdout.write('\n\n');
34+
35+
messages.push({ role: 'assistant', content: fullResponse });
36+
}
37+
}
38+
39+
main().catch(console.error);
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { experimental_streamText } from 'ai';
2+
import { google } from 'ai/google';
3+
import dotenv from 'dotenv';
4+
import { z } from 'zod';
5+
import { weatherTool } from '../tools/weather-tool';
6+
7+
dotenv.config();
8+
9+
async function main() {
10+
const result = await experimental_streamText({
11+
model: google.generativeAI('models/gemini-pro'),
12+
tools: {
13+
weather: weatherTool,
14+
cityAttractions: {
15+
parameters: z.object({ city: z.string() }),
16+
},
17+
},
18+
prompt: 'What is the weather in San Francisco?',
19+
});
20+
21+
for await (const part of result.fullStream) {
22+
switch (part.type) {
23+
case 'text-delta': {
24+
console.log('Text delta:', part.textDelta);
25+
break;
26+
}
27+
28+
case 'tool-call': {
29+
switch (part.toolName) {
30+
case 'cityAttractions': {
31+
console.log('TOOL CALL cityAttractions');
32+
console.log(`city: ${part.args.city}`); // string
33+
break;
34+
}
35+
36+
case 'weather': {
37+
console.log('TOOL CALL weather');
38+
console.log(`location: ${part.args.location}`); // string
39+
break;
40+
}
41+
}
42+
43+
break;
44+
}
45+
46+
case 'tool-result': {
47+
switch (part.toolName) {
48+
// NOT AVAILABLE (NO EXECUTE METHOD)
49+
// case 'cityAttractions': {
50+
// console.log('TOOL RESULT cityAttractions');
51+
// console.log(`city: ${part.args.city}`); // string
52+
// console.log(`result: ${part.result}`);
53+
// break;
54+
// }
55+
56+
case 'weather': {
57+
console.log('TOOL RESULT weather');
58+
console.log(`location: ${part.args.location}`); // string
59+
console.log(`temperature: ${part.result.temperature}`); // number
60+
break;
61+
}
62+
}
63+
64+
break;
65+
}
66+
67+
case 'finish': {
68+
console.log('Finish reason:', part.finishReason);
69+
console.log('Usage:', part.usage);
70+
break;
71+
}
72+
73+
case 'error':
74+
console.error('Error:', part.error);
75+
break;
76+
}
77+
}
78+
}
79+
80+
main();
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { experimental_streamText } from 'ai';
2+
import { google } from 'ai/google';
3+
import dotenv from 'dotenv';
4+
5+
dotenv.config();
6+
7+
async function main() {
8+
const result = await experimental_streamText({
9+
model: google.generativeAI('models/gemini-pro'),
10+
prompt: 'Invent a new holiday and describe its traditions.',
11+
});
12+
13+
for await (const textPart of result.textStream) {
14+
process.stdout.write(textPart);
15+
}
16+
}
17+
18+
main();

0 commit comments

Comments
 (0)