Repository: VoltAgent/voltagent
Pull request #29: feat(google-ai): implement initial Google GenAI provider
Author: login: foxy17
Created: 2025-04-23T11:42:44Z | Merged: 2025-04-25T15:14:04Z

Adds the initial implementation for the Google Generative AI provider (`@voltagent/google-ai`).

This includes:
- `GoogleGenAIProvider` class implementing `LLMProvider`.
- Integration with the `@google/genai` SDK.
- Implementation of `generateText`.
- Stubs for `streamText`, `generateObject`, and `streamObject`.
- Configuration handling via `GoogleGenAIProviderOptions`.
- Basic build (`tsup.config.ts`, `tsconfig.json`) and package (`package.json`) setup.
- Test cases for generate and stream functions


Diff:
diff --git a/.changeset/yummy-tires-beg.md b/.changeset/yummy-tires-beg.md
new file mode 100644
index 000000000..696e90bc9
--- /dev/null
+++ b/.changeset/yummy-tires-beg.md
@@ -0,0 +1,14 @@
+---
+"@voltagent/google-ai": major
+---
+
+feat(google-ai): Add initial Google AI provider package
+
+Introduces the `@voltagent/google-ai` package to integrate Google's Generative AI capabilities directly into VoltAgent. This allows developers to leverage powerful models like Gemini within their agents.
+
+This initial version includes:
+
+- The core `GoogleGenAIProvider` class for interfacing with the `@google/genai` SDK.
+- Configuration options for API key authentication.
+- Basic setup and usage examples in the README.
+- Documentation outlining future support and considerations for Vertex AI.
diff --git a/examples/with-google-ai/.gitignore b/examples/with-google-ai/.gitignore
new file mode 100644
index 000000000..0ca39c007
--- /dev/null
+++ b/examples/with-google-ai/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+dist
+.DS_Store
diff --git a/examples/with-google-ai/README.md b/examples/with-google-ai/README.md
new file mode 100644
index 000000000..248f5e037
--- /dev/null
+++ b/examples/with-google-ai/README.md
@@ -0,0 +1,53 @@
+<div align="center">
+<a href="https://voltagent.dev/">
+<img width="1800" alt="435380213-b6253409-8741-462b-a346-834cd18565a9" src="https://github.com/user-attachments/assets/452a03e7-eeda-4394-9ee7-0ffbcf37245c" />
+</a>
+
+<br/>
+<br/>
+
+<div align="center">
+    <a href="https://voltagent.dev">Home Page</a> |
+    <a href="https://voltagent.dev/docs/">Documentation</a> |
+    <a href="https://github.com/voltagent/voltagent/tree/main/examples">Examples</a> |
+    <a href="https://s.voltagent.dev/discord">Discord</a> |
+    <a href="https://voltagent.dev/blog/">Blog</a>
+</div>
+</div>
+
+<br/>
+
+<div align="center">
+    <strong>VoltAgent is an open source TypeScript framework for building and orchestrating AI agents.</strong><br>
+Escape the limitations of no-code builders and the complexity of starting from scratch.
+    <br />
+    <br />
+</div>
+
+<div align="center">
+    
+[![npm version](https://img.shields.io/npm/v/@voltagent/core.svg)](https://www.npmjs.com/package/@voltagent/core)
+[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](CODE_OF_CONDUCT.md)
+[![Discord](https://img.shields.io/discord/1361559153780195478.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://s.voltagent.dev/discord)
+[![Twitter Follow](https://img.shields.io/twitter/follow/voltagent_dev?style=social)](https://twitter.com/voltagent_dev)
+    
+</div>
+
+<br/>
+
+<div align="center">
+<a href="https://voltagent.dev/">
+<img width="896" alt="Screenshot 2025-04-20 at 22 44 38" src="https://github.com/user-attachments/assets/f0627868-6153-4f63-ba7f-bdfcc5dd603d" />
+</a>
+
+</div>
+
+## VoltAgent: Build AI Agents Fast and Flexibly
+
+VoltAgent is an open-source TypeScript framework for creating and managing AI agents. It provides modular components to build, customize, and scale agents with ease. From connecting to APIs and memory management to supporting multiple LLMs, VoltAgent simplifies the process of creating sophisticated AI systems. It enables fast development, maintains clean code, and offers flexibility to switch between models and tools without vendor lock-in.
+
+## Try Example
+
+```bash
+npm create voltagent-app@latest -- --example with-google-ai
+```
diff --git a/examples/with-google-ai/package.json b/examples/with-google-ai/package.json
new file mode 100644
index 000000000..7de92cbbe
--- /dev/null
+++ b/examples/with-google-ai/package.json
@@ -0,0 +1,29 @@
+{
+  "name": "voltagent-example-gemini-ai",
+  "private": true,
+  "keywords": [
+    "voltagent",
+    "ai",
+    "agent"
+  ],
+  "license": "MIT",
+  "author": "",
+  "type": "module",
+  "scripts": {
+    "build": "tsc",
+    "dev": "tsx watch --env-file=.env ./src ",
+    "start": "node dist/index.js",
+    "volt": "volt"
+  },
+  "dependencies": {
+    "@voltagent/cli": "^0.1.2",
+    "@voltagent/core": "^0.1.3",
+    "@voltagent/google-ai": "^0.1.0",
+    "zod": "^3.24.2"
+  },
+  "devDependencies": {
+    "@types/node": "^22.13.5",
+    "tsx": "^4.19.3",
+    "typescript": "^5.8.2"
+  }
+}
diff --git a/examples/with-google-ai/src/index.ts b/examples/with-google-ai/src/index.ts
new file mode 100644
index 000000000..bff20476e
--- /dev/null
+++ b/examples/with-google-ai/src/index.ts
@@ -0,0 +1,15 @@
+import { VoltAgent, Agent } from "@voltagent/core";
+import { GoogleGenAIProvider } from "@voltagent/google-ai";
+
+const agent = new Agent({
+  name: "Google Assistant",
+  description: "A helpful assistant powered by Google Gemini",
+  llm: new GoogleGenAIProvider({}),
+  model: "gemini-2.0-flash",  
+});
+
+new VoltAgent({
+  agents: {
+    agent,
+  },
+}); 
\ No newline at end of file
diff --git a/examples/with-google-ai/tsconfig.json b/examples/with-google-ai/tsconfig.json
new file mode 100644
index 000000000..cee90c6f3
--- /dev/null
+++ b/examples/with-google-ai/tsconfig.json
@@ -0,0 +1,14 @@
+{
+  "compilerOptions": {
+    "target": "ES2022",
+    "module": "NodeNext",
+    "moduleResolution": "NodeNext",
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "outDir": "dist",
+    "skipLibCheck": true
+  },
+  "include": ["src"],
+  "exclude": ["node_modules", "dist"]
+}
diff --git a/packages/google-ai/README.md b/packages/google-ai/README.md
new file mode 100644
index 000000000..1f4ae1235
--- /dev/null
+++ b/packages/google-ai/README.md
@@ -0,0 +1,82 @@
+# @voltagent/google-ai
+
+VoltAgent Google AI provider integration using the Google Generative AI SDK (`@google/genai`).
+
+This package allows you to use Google's Generative AI models (like Gemini) within your VoltAgent agents.
+
+## Installation
+
+```bash
+npm install @voltagent/google-ai
+# or
+yarn add @voltagent/google-ai
+# or
+pnpm add @voltagent/google-ai
+```
+
+## Usage
+
+You need to provide your Google Generative AI API key. You can get one from [Google AI Studio](https://aistudio.google.com/app/apikey).
+
+```typescript
+import { Agent } from "@voltagent/core";
+import { GoogleGenAIProvider } from "@voltagent/google-ai";
+
+// Ensure your API key is stored securely, e.g., in environment variables
+const googleApiKey = process.env.GOOGLE_API_KEY;
+
+// Instantiate the provider
+const googleProvider = new GoogleGenAIProvider({
+  apiKey: googleApiKey,
+});
+
+// Create an agent using a Google model
+const agent = new Agent({
+  name: "Google Assistant",
+  description: "A helpful and friendly assistant that can answer questions clearly and concisely.",
+  llm: googleProvider,
+  model: "gemini-1.5-pro-latest", // Specify the desired Google model
+});
+```
+
+### For using via VertexAi there are some nuances.
+
+1. It will only work on a server side instance, and not on web.
+2. The authentication needs to be handled via the [google-auth-libray](https://www.npmjs.com/package/google-auth-library)
+3. Authentication in Vertex is still and open issue on the official SDK. [link](https://github.com/googleapis/js-genai/issues/426), [link-2](https://github.com/googleapis/js-genai/issues/417)
+
+```typescript
+const googleVertexProvider = new GoogleGenAIProvider({
+  vertexai: true,
+  project: "your-project-id",
+  location: "your-project-location",
+  googleAuthOptions: {
+    credentials: {
+      // never expose your private key in the code, this is just an example
+      private_key: "my-private-key",
+      // this is your service account email created in the google cloud console
+      client_email: "my-client-email",
+    },
+  },
+});
+
+const agent = new Agent({
+  name: "Google Assistant",
+  description: "A helpful and friendly assistant that can answer questions clearly and concisely.",
+  llm: googleProvider,
+  model: "gemini-1.5-pro-latest", // Specify the desired Google model
+});
+```
+
+## Configuration
+
+The `GoogleGenAIProvider` accepts the following options in its constructor:
+
+- `apiKey`: Your Google Generative AI API key (required).
+- **(Advanced - Vertex AI)** `vertexai`: Set to `true` if using Vertex AI endpoints.
+- **(Advanced - Vertex AI)** `project`: Your Google Cloud project ID (required if `vertexai` is `true`).
+- **(Advanced - Vertex AI)** `location`: Your Google Cloud project location (required if `vertexai` is `true`).
+
+## License
+
+Licensed under the MIT License, Copyright © 2025-present VoltAgent.
diff --git a/packages/google-ai/jest.config.js b/packages/google-ai/jest.config.js
new file mode 100644
index 000000000..51110a4ca
--- /dev/null
+++ b/packages/google-ai/jest.config.js
@@ -0,0 +1,11 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+  preset: "ts-jest",
+  testEnvironment: "node",
+  testMatch: ["**/__tests__/**/*.test.ts", "**/*.spec.ts"],
+  transform: {
+    "^.+\\.ts$": "ts-jest",
+  },
+  coverageDirectory: "coverage",
+  collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts", "!src/**/index.ts"],
+};
diff --git a/packages/google-ai/package.json b/packages/google-ai/package.json
new file mode 100644
index 000000000..1f9de2a1a
--- /dev/null
+++ b/packages/google-ai/package.json
@@ -0,0 +1,37 @@
+{
+  "name": "@voltagent/google-ai",
+  "version": "0.1.0",
+  "description": "VoltAgent Google AI - Google Generative AI provider integration for VoltAgent",
+  "license": "MIT",
+  "main": "dist/index.js",
+  "module": "dist/index.mjs",
+  "types": "dist/index.d.ts",
+  "files": [
+    "dist",
+    "src",
+    "dist/*.map"
+  ],
+  "scripts": {
+    "build": "tsup",
+    "dev": "tsup --watch",
+    "lint": "eslint src --ext .ts,.tsx",
+    "test": "jest --passWithNoTests"
+  },
+  "dependencies": {
+    "@google/genai": "^0.9.0",
+    "@voltagent/core": "^0.1.3",
+    "zod": "^3.24.2"
+  },
+  "devDependencies": {
+    "@types/jest": "^29.5.0",
+    "@types/node": "^18.15.11",
+    "eslint": "^8.0.0",
+    "jest": "^29.5.0",
+    "ts-jest": "^29.1.0",
+    "tsup": "^6.7.0",
+    "typescript": "^5.0.4"
+  },
+  "peerDependencies": {
+    "@voltagent/core": "^0.1.0"
+  }
+}
diff --git a/packages/google-ai/src/index.spec.ts b/packages/google-ai/src/index.spec.ts
new file mode 100644
index 000000000..cc22c6fa4
--- /dev/null
+++ b/packages/google-ai/src/index.spec.ts
@@ -0,0 +1,213 @@
+import { FinishReason, type GenerateContentResponse, type GoogleGenAIOptions } from "@google/genai";
+import { GoogleGenAIProvider } from "./index";
+
+const mockGenerateContent = jest.fn();
+
+// Mock the GoogleGenAI class and its methods
+jest.mock("@google/genai", () => {
+  const originalModule = jest.requireActual("@google/genai");
+  return {
+    ...originalModule,
+    GoogleGenAI: jest.fn().mockImplementation(() => {
+      return {
+        models: {
+          generateContent: mockGenerateContent,
+        },
+      };
+    }),
+  };
+});
+
+describe("GoogleGenAIProvider", () => {
+  let provider: GoogleGenAIProvider;
+
+  beforeEach(() => {
+    jest.clearAllMocks();
+
+    const options: GoogleGenAIOptions = {
+      apiKey: "test-api-key",
+    };
+
+    provider = new GoogleGenAIProvider(options);
+  });
+
+  describe("generateText", () => {
+    it("should generate text successfully", async () => {
+      // Mock the response from the Google GenAI API
+      const mockResponse: Partial<GenerateContentResponse> = {
+        text: "Hello, I am a test response!",
+        responseId: "test-response-id",
+        candidates: [{ finishReason: FinishReason.STOP }],
+        usageMetadata: {
+          promptTokenCount: 10,
+          candidatesTokenCount: 20,
+          totalTokenCount: 30,
+        },
+      };
+
+      mockGenerateContent.mockResolvedValueOnce(mockResponse);
+
+      const result = await provider.generateText({
+        messages: [{ role: "user", content: "Hello!" }],
+        model: "gemini-2.0-flash-001",
+      });
+
+      expect(result).toBeDefined();
+      expect(result.text).toBe("Hello, I am a test response!");
+      expect(result.usage).toEqual({
+        promptTokens: 10,
+        completionTokens: 20,
+        totalTokens: 30,
+      });
+      expect(result.finishReason).toBe("STOP");
+
+      // Verify the correct parameters were passed to generateContent
+      expect(mockGenerateContent).toHaveBeenCalledWith({
+        contents: [
+          {
+            role: "user",
+            parts: [{ text: "Hello!" }],
+          },
+        ],
+        model: "gemini-2.0-flash-001",
+      });
+    });
+
+    it("should handle message with onStepFinish callback", async () => {
+      const mockResponse: Partial<GenerateContentResponse> = {
+        text: "Hello, I am a test response!",
+        responseId: "test-response-id",
+        candidates: [{ finishReason: FinishReason.STOP }],
+        usageMetadata: {
+          promptTokenCount: 10,
+          candidatesTokenCount: 20,
+          totalTokenCount: 30,
+        },
+      };
+
+      mockGenerateContent.mockResolvedValueOnce(mockResponse);
+
+      const onStepFinishMock = jest.fn();
+
+      await provider.generateText({
+        messages: [{ role: "user", content: "Hello!" }],
+        model: "gemini-2.0-flash-001",
+        onStepFinish: onStepFinishMock,
+      });
+
+      expect(onStepFinishMock).toHaveBeenCalledTimes(1);
+      expect(onStepFinishMock).toHaveBeenCalledWith({
+        id: "test-response-id",
+        type: "text",
+        content: "Hello, I am a test response!",
+        role: "assistant",
+        usage: {
+          promptTokens: 10,
+          completionTokens: 20,
+          totalTokens: 30,
+        },
+      });
+    });
+
+    it("should handle provider options correctly", async () => {
+      const mockResponse: Partial<GenerateContentResponse> = {
+        text: "Test response with provider options",
+        responseId: "test-response-id",
+        candidates: [{ finishReason: FinishReason.STOP }],
+      };
+
+      mockGenerateContent.mockResolvedValueOnce(mockResponse);
+
+      await provider.generateText({
+        messages: [{ role: "user", content: "Hello!" }],
+        model: "gemini-2.0-flash-001",
+        provider: {
+          temperature: 0.7,
+          topP: 0.9,
+          stopSequences: ["END"],
+          seed: 123456,
+          extraOptions: {
+            customOption: "value",
+          },
+        },
+      });
+
+      expect(mockGenerateContent).toHaveBeenCalledWith({
+        contents: expect.any(Array),
+        model: "gemini-2.0-flash-001",
+        config: {
+          temperature: 0.7,
+          topP: 0.9,
+          stopSequences: ["END"],
+          seed: 123456,
+          customOption: "value",
+        },
+      });
+    });
+  });
+
+  describe("streamText", () => {
+    it("should stream text successfully", async () => {
+      async function* mockGenerator() {
+        yield { text: "Hello", responseId: "chunk1" };
+        yield { text: ", ", responseId: "chunk2" };
+        yield { text: "world!", responseId: "chunk3", candidates: [{ finishReason: "STOP" }] };
+        yield { 
+          text: "", responseId: "final", 
+          usageMetadata: {
+            promptTokenCount: 5,
+            candidatesTokenCount: 15,
+            totalTokenCount: 20
+          }
+        };
+      }
+
+      const mockGenerateContentStream = jest.fn().mockResolvedValue(mockGenerator());
+      
+      const provider = new GoogleGenAIProvider({ apiKey: "test-api-key" });
+      (provider as any).ai.models.generateContentStream = mockGenerateContentStream;
+      
+      const onChunkMock = jest.fn();
+      const onStepFinishMock = jest.fn();
+      
+      const result = await provider.streamText({
+        messages: [{ role: "user", content: "Hello!" }],
+        model: "gemini-2.0-flash-001",
+        onChunk: onChunkMock,
+        onStepFinish: onStepFinishMock
+      });
+      
+      expect(result.textStream).toBeInstanceOf(ReadableStream);
+      
+      // Read all chunks from the stream
+      const reader = result.textStream.getReader();
+      const chunks = [];
+      
+      let done = false;
+      while (!done) {
+        const { value, done: isDone } = await reader.read();
+        if (isDone) {
+          done = true;
+        } else {
+          chunks.push(value);
+        }
+      }
+      
+      expect(chunks).toEqual(["Hello", ", ", "world!"]);
+      
+      expect(onChunkMock).toHaveBeenCalledTimes(3);
+      expect(onStepFinishMock).toHaveBeenCalledTimes(1);
+      expect(onStepFinishMock).toHaveBeenCalledWith({
+        id: expect.any(String),
+        type: "text",
+        content: "Hello, world!",
+        role: "assistant",
+        usage: {
+          promptTokens: 5,
+          completionTokens: 15,
+          totalTokens: 20
+        }
+      });
+    });
+  });
+});
diff --git a/packages/google-ai/src/index.ts b/packages/google-ai/src/index.ts
new file mode 100644
index 000000000..8b22a00ea
--- /dev/null
+++ b/packages/google-ai/src/index.ts
@@ -0,0 +1,333 @@
+import {
+  type Content,
+  type GenerateContentConfig,
+  type GenerateContentParameters,
+  type GenerateContentResponse,
+  type GenerateContentResponseUsageMetadata,
+  GoogleGenAI,
+  type GoogleGenAIOptions,
+} from "@google/genai";
+import type {
+  BaseMessage,
+  LLMProvider,
+  MessageRole,
+  ProviderTextResponse,
+  ProviderTextStreamResponse,
+  StepWithContent,
+  UsageInfo,
+} from "@voltagent/core";
+import type {
+  GoogleGenerateContentStreamResult,
+  GoogleGenerateTextOptions,
+  GoogleProviderRuntimeOptions,
+  GoogleStreamTextOptions,
+} from "./types";
+
+type StreamProcessingState = {
+  accumulatedText: string;
+  finalUsage?: UsageInfo;
+  finalFinishReason?: string;
+};
+export class GoogleGenAIProvider implements LLMProvider<string> {
+  private ai: GoogleGenAI;
+
+  constructor(options: GoogleGenAIOptions) {
+    const hasApiKey = !!options?.apiKey;
+    const hasVertexAIConfig = options.vertexai && options.project && options.location;
+
+    if (!hasApiKey && !hasVertexAIConfig) {
+      throw new Error(
+        "Google GenAI API key is required, or if using Vertex AI, both project and location must be specified.",
+      );
+    }
+
+    if (hasApiKey && hasVertexAIConfig) {
+      throw new Error(
+        "Google GenAI API key and Vertex AI project/location cannot both be provided.",
+      );
+    }
+
+    this.ai = new GoogleGenAI(options);
+
+    this.generateText = this.generateText.bind(this);
+    this.streamText = this.streamText.bind(this);
+    this.toMessage = this.toMessage.bind(this);
+    this._createStepFromChunk = this._createStepFromChunk.bind(this);
+    this.getModelIdentifier = this.getModelIdentifier.bind(this);
+    this._getUsageInfo = this._getUsageInfo.bind(this);
+    this._processStreamChunk = this._processStreamChunk.bind(this);
+    this._finalizeStream = this._finalizeStream.bind(this);
+  }
+
+  getModelIdentifier = (model: string): string => {
+    return model;
+  };
+
+  private toGoogleRole(role: MessageRole): "user" | "model" {
+    switch (role) {
+      case "user":
+        return "user";
+      case "assistant":
+        return "model";
+      case "system":
+        console.warn(`System role conversion might require specific handling. Mapping to 'model'.`);
+        return "model";
+      case "tool":
+        console.warn(
+          `Tool role conversion to Google GenAI format is complex. Mapping to 'model' as placeholder.`,
+        );
+        return "model";
+      default:
+        console.warn(`Unsupported role conversion for: ${role}. Defaulting to 'user'.`);
+        return "user";
+    }
+  }
+
+  toMessage = (message: BaseMessage): Content => {
+    const role = this.toGoogleRole(message.role);
+    if (typeof message.content === "string") {
+      if (role !== "user" && role !== "model") {
+        throw new Error(
+          `Invalid role '${role}' passed to toMessage for string content. Expected 'user' or 'model'.`,
+        );
+      }
+      return { role, parts: [{ text: message.content }] };
+    }
+    const parts = message.content
+      .map((part) => {
+        if (part.type === "text") {
+          return { text: part.text };
+        }
+        console.warn(`Unsupported part type: ${part.type}. Skipping.`);
+        return null;
+      })
+      .filter((part): part is { text: string } => part !== null);
+
+    if (parts.length === 0) {
+      console.warn(
+        `No supported parts found for message with role ${role}. Creating empty text part.`,
+      );
+      return { role, parts: [{ text: "" }] };
+    }
+
+    if (role !== "user" && role !== "model") {
+      throw new Error(
+        `Invalid role '${role}' passed to toMessage for structured content. Expected 'user' or 'model'.`,
+      );
+    }
+    return { role, parts };
+  };
+
+  private _createStepFromChunk(
+    response: GenerateContentResponse,
+    role: MessageRole = "assistant",
+    usage?: UsageInfo,
+  ): StepWithContent | null {
+    const text = response.text;
+    if (text !== undefined && text !== "") {
+      return {
+        id: response.responseId || "",
+        type: "text",
+        content: text,
+        role: role,
+        usage: usage,
+      };
+    }
+    return null;
+  }
+
+  private _getUsageInfo(
+    usageInfo: GenerateContentResponseUsageMetadata | undefined,
+  ): UsageInfo | undefined {
+    if (!usageInfo) return undefined;
+
+    const promptTokens = usageInfo.promptTokenCount ?? 0;
+    const completionTokens = usageInfo.candidatesTokenCount ?? 0;
+    const totalTokens = usageInfo.totalTokenCount ?? 0;
+
+    if (promptTokens > 0 || completionTokens > 0 || totalTokens > 0) {
+      return { promptTokens, completionTokens, totalTokens };
+    }
+
+    return undefined;
+  }
+
+  generateText = async (
+    options: GoogleGenerateTextOptions,
+  ): Promise<ProviderTextResponse<GenerateContentResponse>> => {
+    const model = options.model;
+    const contents = options.messages.map(this.toMessage);
+    const providerOptions: GoogleProviderRuntimeOptions = options.provider || {};
+
+    const config: GenerateContentConfig = {
+      temperature: providerOptions.temperature,
+      topP: providerOptions.topP,
+      stopSequences: providerOptions.stopSequences,
+      seed: providerOptions.seed,
+      presencePenalty: providerOptions.presencePenalty,
+      frequencyPenalty: providerOptions.frequencyPenalty,
+      ...(providerOptions.extraOptions && providerOptions.extraOptions),
+    };
+
+    Object.keys(config).forEach(
+      (key) => (config as any)[key] === undefined && delete (config as any)[key],
+    );
+
+    const generationParams: GenerateContentParameters = {
+      contents: contents,
+      model: model,
+      ...(Object.keys(config).length > 0 ? { config: config } : {}),
+    };
+
+    const result = await this.ai.models.generateContent(generationParams);
+
+    const response = result;
+
+    const responseText = response.text;
+    const usageInfo = response?.usageMetadata;
+    const finishReason = response.candidates?.[0]?.finishReason?.toString();
+    const finalUsage = this._getUsageInfo(usageInfo);
+
+    if (options.onStepFinish) {
+      const step = this._createStepFromChunk(response, "assistant", finalUsage);
+      if (step) await options.onStepFinish(step);
+    }
+
+    const providerResponse: ProviderTextResponse<GenerateContentResponse> = {
+      provider: response,
+      text: responseText ?? "",
+      usage: finalUsage,
+      finishReason: finishReason,
+    };
+
+    return providerResponse;
+  };
+
+  private async _processStreamChunk(
+    chunkResponse: GenerateContentResponse,
+    controller: ReadableStreamDefaultController<string>,
+    state: StreamProcessingState,
+    options: GoogleStreamTextOptions,
+  ): Promise<void> {
+    const textChunk = chunkResponse.text;
+
+    const chunkUsage = this._getUsageInfo(chunkResponse.usageMetadata);
+    const chunkFinishReason = chunkResponse.candidates?.[0]?.finishReason?.toString();
+
+    if (chunkUsage) {
+      state.finalUsage = chunkUsage;
+    }
+    if (chunkFinishReason) {
+      state.finalFinishReason = chunkFinishReason;
+    }
+
+    if (textChunk !== undefined && textChunk !== "") {
+      controller.enqueue(textChunk);
+      state.accumulatedText += textChunk;
+      if (options.onChunk) {
+        const step = this._createStepFromChunk(chunkResponse, "assistant", undefined);
+        if (step) await options.onChunk(step);
+      }
+    }
+
+    if (chunkResponse.promptFeedback && options.onError) {
+      console.warn("Prompt feedback received:", chunkResponse.promptFeedback);
+    }
+  }
+
+  private async _finalizeStream(
+    state: StreamProcessingState,
+    options: GoogleStreamTextOptions,
+    controller: ReadableStreamDefaultController<string>,
+  ): Promise<void> {
+    const finalUsage = state.finalUsage;
+
+    if (options.onStepFinish) {
+      const finalStep: StepWithContent = {
+        id: "",
+        type: "text",
+        content: state.accumulatedText,
+        role: "assistant",
+        usage: finalUsage,
+      };
+      await options.onStepFinish(finalStep);
+    }
+    if (options.onFinish) {
+      const finishResponse: { text: string } = { text: state.accumulatedText };
+      await options.onFinish(finishResponse);
+    }
+    controller.close();
+  }
+
+  async streamText(
+    options: GoogleStreamTextOptions,
+  ): Promise<ProviderTextStreamResponse<GoogleGenerateContentStreamResult>> {
+    const model = options.model;
+    const contents = options.messages.map(this.toMessage);
+    const providerOptions: GoogleProviderRuntimeOptions = options.provider || {};
+
+    const config: GenerateContentConfig = Object.entries({
+      temperature: providerOptions.temperature,
+      topP: providerOptions.topP,
+      stopSequences: providerOptions.stopSequences,
+      seed: providerOptions.seed,
+      presencePenalty: providerOptions.presencePenalty,
+      frequencyPenalty: providerOptions.frequencyPenalty,
+      ...(providerOptions.extraOptions && providerOptions.extraOptions),
+    }).reduce((acc, [key, value]) => {
+      if (value !== undefined) {
+        (acc as any)[key] = value;
+      }
+      return acc;
+    }, {} as GenerateContentConfig);
+
+    const generationParams: GenerateContentParameters = {
+      contents: contents,
+      model: model,
+      ...(Object.keys(config).length > 0 ? { config: config } : {}),
+    };
+
+    const streamResult = await this.ai.models.generateContentStream(generationParams);
+
+    const state = this;
+    const streamState: StreamProcessingState = {
+      accumulatedText: "",
+      finalUsage: undefined as UsageInfo | undefined,
+      finalFinishReason: undefined as string | undefined,
+    };
+
+    const readableStream = new ReadableStream<string>({
+      async start(controller) {
+        try {
+          for await (const chunkResponse of streamResult) {
+            await state._processStreamChunk(chunkResponse, controller, streamState, options);
+          }
+
+          await state._finalizeStream(streamState, options, controller);
+        } catch (error) {
+          console.error("Error during Google GenAI stream processing:", error);
+          if (options.onError) {
+            await options.onError(error);
+          }
+          controller.error(error);
+        }
+      },
+      cancel(reason) {
+        console.log("Google GenAI Stream cancelled:", reason);
+      },
+    });
+
+    return {
+      provider: streamResult,
+      textStream: readableStream,
+    };
+  }
+
+  async generateObject(_options: any): Promise<any> {
+    throw new Error("generateObject is not implemented for GoogleGenAIProvider yet.");
+  }
+
+  async streamObject(_options: any): Promise<any> {
+    throw new Error("streamObject is not implemented for GoogleGenAIProvider yet.");
+  }
+}
diff --git a/packages/google-ai/src/types.ts b/packages/google-ai/src/types.ts
new file mode 100644
index 000000000..fe50a8ebc
--- /dev/null
+++ b/packages/google-ai/src/types.ts
@@ -0,0 +1,36 @@
+import type { GenerateContentConfig, GenerateContentResponse } from "@google/genai";
+
+// Define explicit runtime options to avoid deep generic instantiation
+export interface GoogleProviderRuntimeOptions
+  extends Pick<
+    GenerateContentConfig,
+    "temperature" | "topP" | "stopSequences" | "seed" | "presencePenalty" | "frequencyPenalty"
+  > {
+  extraOptions?: Record<string, any>;
+}
+
+// Define concrete types instead of using Omit with generics since it was causing
+// "Type instantiation is excessively deep and possibly infinite".
+type BaseGoogleTextOptions = {
+  messages: any[];
+  model: string;
+  provider?: GoogleProviderRuntimeOptions;
+  tools?: any[];
+  maxSteps?: number;
+  onStepFinish?: (step: any) => void | Promise<void>;
+  signal?: AbortSignal;
+};
+
+export type GoogleGenerateTextOptions = BaseGoogleTextOptions;
+
+export type GoogleStreamTextOptions = BaseGoogleTextOptions & {
+  onChunk?: (chunk: any) => void | Promise<void>;
+  onFinish?: (result: { text: string }) => void | Promise<void>;
+  onError?: (error: any) => void | Promise<void>;
+};
+
+export type GoogleGenerateContentStreamResult = AsyncGenerator<
+  GenerateContentResponse,
+  GenerateContentResponse,
+  unknown
+>;
diff --git a/packages/google-ai/tsconfig.json b/packages/google-ai/tsconfig.json
new file mode 100644
index 000000000..f956d4bf3
--- /dev/null
+++ b/packages/google-ai/tsconfig.json
@@ -0,0 +1,28 @@
+{
+  "compilerOptions": {
+    "target": "es2015",
+    "module": "esnext",
+    "lib": ["dom", "esnext"],
+    "declaration": true,
+    "sourceMap": true,
+    "moduleResolution": "node",
+    "skipLibCheck": true,
+    "strict": true,
+    "isolatedModules": true,
+    "noFallthroughCasesInSwitch": true,
+    "esModuleInterop": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noImplicitReturns": true,
+    "noImplicitThis": true,
+    "noImplicitAny": true,
+    "strictNullChecks": true,
+    "resolveJsonModule": true,
+    "baseUrl": ".",
+    "paths": {
+      "@/*": ["src/*"]
+    }
+  },
+  "include": ["src"],
+  "exclude": ["node_modules", "dist", "test"]
+}
diff --git a/packages/google-ai/tsup.config.ts b/packages/google-ai/tsup.config.ts
new file mode 100644
index 000000000..f39017936
--- /dev/null
+++ b/packages/google-ai/tsup.config.ts
@@ -0,0 +1,17 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+  entry: ["src/index.ts"],
+  format: ["cjs", "esm"],
+  splitting: false,
+  sourcemap: true,
+  clean: true,
+  target: "es2015",
+  outDir: "dist",
+  minify: false,
+  dts: true,
+  esbuildOptions(options) {
+    options.keepNames = true;
+    return options;
+  },
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 227454c9c..4f5dc7f0c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -571,6 +571,40 @@ importers:
         specifier: ^5.0.4
         version: 5.8.2
 
+  packages/google-ai:
+    dependencies:
+      '@google/genai':
+        specifier: ^0.9.0
+        version: 0.9.0
+      '@voltagent/core':
+        specifier: ^0.1.3
+        version: link:../core
+      zod:
+        specifier: ^3.24.2
+        version: 3.24.2
+    devDependencies:
+      '@types/jest':
+        specifier: ^29.5.0
+        version: 29.5.14
+      '@types/node':
+        specifier: ^18.15.11
+        version: 18.19.79
+      eslint:
+        specifier: ^8.0.0
+        version: 8.57.1
+      jest:
+        specifier: ^29.5.0
+        version: 29.7.0(@types/node@18.19.79)
+      ts-jest:
+        specifier: ^29.1.0
+        version: 29.2.6(@babel/core@7.26.9)(esbuild@0.17.19)(jest@29.7.0)(typescript@5.8.2)
+      tsup:
+        specifier: ^6.7.0
+        version: 6.7.0(typescript@5.8.2)
+      typescript:
+        specifier: ^5.0.4
+        version: 5.8.2
+
   packages/supabase:
     dependencies:
       '@supabase/supabase-js':
@@ -2104,6 +2138,21 @@ packages:
     resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
     dev: true
 
+  /@google/genai@0.9.0:
+    resolution: {integrity: sha512-FD2RizYGInsvfjeaN6O+wQGpRnGVglS1XWrGQr8K7D04AfMmvPodDSw94U9KyFtsVLzWH9kmlPyFM+G4jbmkqg==}
+    engines: {node: '>=18.0.0'}
+    dependencies:
+      google-auth-library: 9.15.1
+      ws: 8.18.1
+      zod: 3.24.2
+      zod-to-json-schema: 3.24.3(zod@3.24.2)
+    transitivePeerDependencies:
+      - bufferutil
+      - encoding
+      - supports-color
+      - utf-8-validate
+    dev: false
+
   /@hono/node-server@1.14.0(hono@3.12.12):
     resolution: {integrity: sha512-YUCxJwgHRKSqjrdTk9e4VMGKN27MK5r4+MGPyZTgKH+IYbK+KtYbHeOcPGJ91KGGD6RIQiz2dAHxvjauNhOS8g==}
     engines: {node: '>=18.14.1'}
@@ -3811,6 +3860,11 @@ packages:
       - supports-color
     dev: true
 
+  /agent-base@7.1.3:
+    resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
+    engines: {node: '>= 14'}
+    dev: false
+
   /agentkeepalive@4.6.0:
     resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==}
     engines: {node: '>= 8.0.0'}
@@ -4116,6 +4170,10 @@ packages:
       is-windows: 1.0.2
     dev: true
 
+  /bignumber.js@9.3.0:
+    resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==}
+    dev: false
+
   /binary-extensions@2.3.0:
     resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
     engines: {node: '>=8'}
@@ -4216,6 +4274,10 @@ packages:
       node-int64: 0.4.0
     dev: true
 
+  /buffer-equal-constant-time@1.0.1:
+    resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
+    dev: false
+
   /buffer-from@1.1.2:
     resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
     dev: true
@@ -5160,6 +5222,12 @@ packages:
     resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
     dev: true
 
+  /ecdsa-sig-formatter@1.0.11:
+    resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
+    dependencies:
+      safe-buffer: 5.2.1
+    dev: false
+
   /ee-first@1.1.1:
     resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
     dev: false
@@ -5637,6 +5705,10 @@ packages:
       - supports-color
     dev: false
 
+  /extend@3.0.2:
+    resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+    dev: false
+
   /extendable-error@0.1.7:
     resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==}
     dev: true
@@ -5957,6 +6029,32 @@ packages:
       wide-align: 1.1.5
     dev: true
 
+  /gaxios@6.7.1:
+    resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==}
+    engines: {node: '>=14'}
+    dependencies:
+      extend: 3.0.2
+      https-proxy-agent: 7.0.6
+      is-stream: 2.0.1
+      node-fetch: 2.7.0
+      uuid: 9.0.1
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: false
+
+  /gcp-metadata@6.1.1:
+    resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==}
+    engines: {node: '>=14'}
+    dependencies:
+      gaxios: 6.7.1
+      google-logging-utils: 0.0.2
+      json-bigint: 1.0.0
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: false
+
   /gensync@1.0.0-beta.2:
     resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
     engines: {node: '>=6.9.0'}
@@ -6238,6 +6336,26 @@ packages:
       unicorn-magic: 0.1.0
     dev: true
 
+  /google-auth-library@9.15.1:
+    resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==}
+    engines: {node: '>=14'}
+    dependencies:
+      base64-js: 1.5.1
+      ecdsa-sig-formatter: 1.0.11
+      gaxios: 6.7.1
+      gcp-metadata: 6.1.1
+      gtoken: 7.1.0
+      jws: 4.0.0
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: false
+
+  /google-logging-utils@0.0.2:
+    resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==}
+    engines: {node: '>=14'}
+    dev: false
+
   /gopd@1.2.0:
     resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
     engines: {node: '>= 0.4'}
@@ -6276,6 +6394,17 @@ packages:
     resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
     dev: true
 
+  /gtoken@7.1.0:
+    resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==}
+    engines: {node: '>=14.0.0'}
+    dependencies:
+      gaxios: 6.7.1
+      jws: 4.0.0
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: false
+
   /handlebars@4.7.8:
     resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
     engines: {node: '>=0.4.7'}
@@ -6408,6 +6537,16 @@ packages:
       - supports-color
     dev: true
 
+  /https-proxy-agent@7.0.6:
+    resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+    engines: {node: '>= 14'}
+    dependencies:
+      agent-base: 7.1.3
+      debug: 4.4.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: false
+
   /human-id@4.1.1:
     resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==}
     hasBin: true
@@ -7362,6 +7501,12 @@ packages:
     hasBin: true
     dev: true
 
+  /json-bigint@1.0.0:
+    resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
+    dependencies:
+      bignumber.js: 9.3.0
+    dev: false
+
   /json-buffer@3.0.0:
     resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==}
     dev: false
@@ -7448,6 +7593,21 @@ packages:
     engines: {'0': node >= 0.2.0}
     dev: true
 
+  /jwa@2.0.0:
+    resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==}
+    dependencies:
+      buffer-equal-constant-time: 1.0.1
+      ecdsa-sig-formatter: 1.0.11
+      safe-buffer: 5.2.1
+    dev: false
+
+  /jws@4.0.0:
+    resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==}
+    dependencies:
+      jwa: 2.0.0
+      safe-buffer: 5.2.1
+    dev: false
+
   /keyv@3.1.0:
     resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==}
     dependencies:
diff --git a/website/docs/agents/providers.md b/website/docs/agents/providers.md
index 26b2b1e95..17d195119 100644
--- a/website/docs/agents/providers.md
+++ b/website/docs/agents/providers.md
@@ -77,6 +77,74 @@ const agent = new Agent({
 
 **Choose `@voltagent/xsai` when:** You need to connect specifically to OpenAI or compatible APIs and prioritize minimal bundle size.
 
+### `@voltagent/google-ai` (Google AI Provider)
+
+Connects to Google's Generative AI models (like Gemini) using the official [`@google/genai`](https://github.com/google/generative-ai-js) SDK.
+
+```ts
+import { Agent } from "@voltagent/core";
+import { GoogleGenAIProvider } from "@voltagent/google-ai";
+
+// Ensure your API key is stored securely, e.g., in environment variables
+const googleApiKey = process.env.GOOGLE_API_KEY;
+
+// Instantiate the provider
+const googleProvider = new GoogleGenAIProvider({
+  apiKey: googleApiKey,
+});
+
+// Create an agent using a Google model
+const agent = new Agent({
+  name: "Google Assistant",
+  description: "A helpful assistant powered by Google Gemini",
+  llm: googleProvider,
+  model: "gemini-1.5-pro-latest", // Specify the desired Google model name
+});
+```
+
+<details>
+<summary>Using with Vertex AI (Server-Side Only)</summary>
+
+Using the Google AI provider with Vertex AI has specific requirements:
+
+1.  **Server-Side Only:** It will only work in a server-side environment, not directly in the browser.
+2.  **Authentication:** Authentication must be handled using the [`google-auth-library`](https://www.npmjs.com/package/google-auth-library). You'll need to configure it with your service account credentials.
+3.  **SDK Limitations:** Be aware of potential ongoing issues with Vertex AI authentication in the official Google AI SDK ([link](https://github.com/googleapis/js-genai/issues/426), [link-2](https://github.com/googleapis/js-genai/issues/417)).
+
+```typescript
+import { Agent } from "@voltagent/core";
+import { GoogleGenAIProvider } from "@voltagent/google-ai";
+// Required for Vertex AI auth
+import { GoogleAuth } from "google-auth-library";
+
+// Ensure your service account credentials are secure and accessible
+const credentials = {
+  client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
+  private_key: process.env.GOOGLE_PRIVATE_KEY,
+};
+
+const googleVertexProvider = new GoogleGenAIProvider({
+  vertexai: true,
+  project: process.env.GOOGLE_CLOUD_PROJECT_ID, // Your project ID
+  location: process.env.GOOGLE_CLOUD_LOCATION, // Your project location (e.g., "us-central1")
+  googleAuthOptions: {
+    credentials,
+  },
+});
+
+const agentVertex = new Agent({
+  name: "Google Vertex Assistant",
+  description: "A helpful assistant powered by Google Gemini via Vertex AI",
+  llm: googleVertexProvider,
+  model: "gemini-1.5-pro-latest", // Specify the desired Google model
+});
+```
+
+</details>
+<br/>
+
+**Choose `@voltagent/google-ai` when:** You specifically want to use Google's Gemini models via their dedicated SDK. It also supports Vertex AI configurations but only for server-side usage.
+
 ## The `model` Parameter
 
 When creating an `Agent`, the `model` parameter's value is interpreted _by the selected `llm` provider_. Each provider uses its `getModelIdentifier` method to process this value.


Reviews:
user: login: omeraplak
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-23T15:50:47Z
user: login: foxy17
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T17:41:19Z

Review comments:
diff_hunk: @@ -0,0 +1,37 @@
+{
+  "name": "@voltagent/google-ai",
+  "version": "0.0.1",
path: packages/google-ai/package.json
user: login: omeraplak
body: ```suggestion
  "version": "0.1.0",
```
created_at: 2025-04-23T15:50:17Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,37 @@
+{
+  "name": "@voltagent/google-ai",
+  "version": "0.0.1",
path: packages/google-ai/package.json
user: login: foxy17
body: Updated
created_at: 2025-04-24T17:41:19Z
author_association: CONTRIBUTOR

PR comments:
user: login: omeraplak
created_at: 2025-04-24T23:04:45Z
body: Hey @foxy17 ,
Thanks so much for the PR! 🙌 I’ll review it shortly and get back to you soon. ⚡
author_association: MEMBER
user: login: omeraplak
created_at: 2025-04-25T04:16:19Z
body: Hey! If you’d like to add a changeset, that would be awesome but no pressure at all.
I can also add it for you if you prefer, just let me know!

Here’s a quick guide on how to create one:
https://voltagent.dev/docs/community/contributing/#creating-a-changeset

Thanks again for the contribution ⚡
author_association: MEMBER
user: login: foxy17
created_at: 2025-04-25T07:58:46Z
body: > Hey! If you’d like to add a changeset, that would be awesome but no pressure at all. I can also add it for you if you prefer, just let me know!
> 
> Here’s a quick guide on how to create one: https://voltagent.dev/docs/community/contributing/#creating-a-changeset
> 
> Thanks again for the contribution ⚡

@omeraplak  Added the changeset, let me know if anything else is needed. Waiting for this to be merge so that I can start working on the Generate Object and Stream object part!
author_association: CONTRIBUTOR
user: login: omeraplak
created_at: 2025-04-25T15:16:57Z
body: Thanks so much for the contribution! 🙌 Just merged really appreciate the effort.
Excited to keep building together! ⚡
author_association: MEMBER