> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nullclaw/nullclaw/llms.txt
> Use this file to discover all available pages before exploring further.

# Gemini Provider

> Google Gemini 2.0, 1.5 Pro, Flash, and vision models

The Gemini provider supports all Google Gemini models including Gemini 2.0, 1.5 Pro, Flash, and vision capabilities.

## Configuration

<ParamField path="provider" type="string" required>
  Set to `"gemini"` or `"google"` or `"google-gemini"`
</ParamField>

<ParamField path="api_key" type="string">
  Google API key. Get yours at [aistudio.google.com/apikey](https://aistudio.google.com/apikey)
</ParamField>

<ParamField path="model" type="string" required>
  Model name: `gemini-2.0-flash`, `gemini-1.5-pro`, etc.
</ParamField>

<ParamField path="temperature" type="number">
  Sampling temperature (0.0-2.0). Defaults to `0.7`.
</ParamField>

<ParamField path="max_tokens" type="number">
  Maximum output tokens (called `maxOutputTokens` in Gemini API). Defaults to `8192`.
</ParamField>

## Example Configuration

```json theme={null}
{
  "provider": "gemini",
  "model": "gemini-2.0-flash",
  "api_key": "AIza...",
  "temperature": 0.7
}
```

## Authentication Methods

The Gemini provider supports **three authentication methods** (in priority order):

### 1. Explicit API Key (Config)

```json theme={null}
{
  "provider": "gemini",
  "model": "gemini-2.0-flash",
  "api_key": "AIza..."
}
```

### 2. Environment Variables

```bash theme={null}
export GEMINI_API_KEY="AIza..."
# or
export GOOGLE_API_KEY="AIza..."
# or
export GEMINI_OAUTH_TOKEN="ya29..."
```

### 3. Gemini CLI OAuth Token

Reuse existing Gemini CLI credentials from `~/.gemini/oauth_creds.json`:

```json theme={null}
{
  "access_token": "ya29.a0ARrdaM...",
  "refresh_token": "1//0eHIDK...",
  "expires_at": 1999999999
}
```

The provider automatically:

* Detects and loads the token
* Checks expiration (5-minute safety buffer)
* Refreshes expired tokens using the refresh token
* Persists refreshed tokens back to `~/.gemini/oauth_creds.json`

**No configuration needed** — just run `gemini auth login` and NullClaw will use those credentials.

## Supported Models

* **Gemini 2.0**: `gemini-2.0-flash`, `gemini-2.0-flash-exp`
* **Gemini 1.5**: `gemini-1.5-pro`, `gemini-1.5-flash`, `gemini-1.5-flash-8b`
* **Legacy**: `gemini-pro`, `gemini-pro-vision`

Model names can optionally include `models/` prefix:

* `gemini-2.0-flash` → auto-expanded to `models/gemini-2.0-flash`
* `models/gemini-1.5-pro` → used as-is

## Capabilities

| Feature          | Support                        |
| ---------------- | ------------------------------ |
| Streaming        | Yes                            |
| Function Calling | No (planned)                   |
| Vision (images)  | Yes                            |
| System Messages  | Yes (via `system_instruction`) |
| Tool Calls       | No                             |

## Message Format

Gemini uses a different message format:

* Roles are `user` and `model` (not `assistant`)
* System messages go in top-level `system_instruction` field
* Content is structured as `parts` array

```json theme={null}
{
  "contents": [
    {
      "role": "user",
      "parts": [{"text": "Hello"}]
    },
    {
      "role": "model",
      "parts": [{"text": "Hi there!"}]
    }
  ],
  "system_instruction": {
    "parts": [{"text": "You are a helpful assistant"}]
  },
  "generationConfig": {
    "temperature": 0.7,
    "maxOutputTokens": 8192
  }
}
```

## Vision Support

Gemini supports multimodal input (text + images):

```json theme={null}
{
  "contents": [
    {
      "role": "user",
      "parts": [
        {"text": "What's in this image?"},
        {"inlineData": {"mimeType": "image/png", "data": "iVBOR..."}}
      ]
    }
  ]
}
```

**Note:** Gemini only supports base64-encoded images via `inlineData`. Image URLs are converted to text references: `[Image: https://example.com/photo.jpg]`.

## Streaming

Gemini uses Server-Sent Events (SSE) for streaming:

**Endpoint:**

```
GET /v1beta/models/{model}:streamGenerateContent?alt=sse&key={api_key}
```

**Response Format:**

```
data: {"candidates":[{"content":{"parts":[{"text":"Hello"}]}}]}

data: {"candidates":[{"content":{"parts":[{"text":" world"}]}}]}
```

The provider automatically:

* Parses SSE lines
* Extracts `candidates[0].content.parts[0].text`
* Calls streaming callback for each delta
* Accumulates full response

## OAuth Token Refresh

When using Gemini CLI credentials, the provider handles token refresh automatically:

```zig theme={null}
pub fn refreshOAuthToken(allocator: std.mem.Allocator, refresh_token: []const u8) !RefreshResponse {
    const client_id = "936475272427.apps.googleusercontent.com";
    const client_secret = "KWaLJfKpIyrGyVOIF2t66XCO";
    
    const body = try buildRefreshFormBody(allocator, refresh_token, client_id, client_secret);
    defer allocator.free(body);
    
    const url = "https://oauth2.googleapis.com/token";
    const resp_body = root.curlPostTimed(allocator, url, body, headers, 20) catch return error.RefreshFailed;
    defer allocator.free(resp_body);
    
    return parseRefreshResponse(allocator, resp_body) orelse return error.RefreshFailed;
}
```

Refresh happens automatically when:

* Token is within 5 minutes of expiration
* `refresh_token` is available

Refreshed tokens are persisted to `~/.gemini/oauth_creds.json`.

## Code Example

From `src/providers/gemini.zig`:

```zig theme={null}
pub const GeminiProvider = struct {
    auth: ?GeminiAuth,
    allocator: std.mem.Allocator,

    const BASE_URL = "https://generativelanguage.googleapis.com/v1beta";

    pub fn init(allocator: std.mem.Allocator, api_key: ?[]const u8) GeminiProvider {
        var auth: ?GeminiAuth = null;

        // 1. Explicit key
        if (api_key) |key| {
            auth = .{ .explicit_key = key };
        }

        // 2. Environment variables
        if (auth == null) {
            if (loadNonEmptyEnv(allocator, "GEMINI_API_KEY")) |value| {
                auth = .{ .env_gemini_key = value };
            }
        }

        // 3. Gemini CLI OAuth
        if (auth == null) {
            if (tryLoadGeminiCliToken(allocator)) |creds| {
                auth = .{ .oauth_token = creds.access_token };
            }
        }

        return .{ .auth = auth, .allocator = allocator };
    }
};
```

## Error Handling

The provider classifies common Gemini API errors:

* `error.RateLimited` — 429 rate limit exceeded
* `error.InvalidApiKey` — Authentication failed
* `error.ApiError` — Generic API error
* `error.NoResponseContent` — Empty response
* `error.RefreshFailed` — OAuth token refresh failed

## Related

* [Providers Overview](/providers/overview)
* [OpenAI Provider](/providers/openai)
* [Anthropic Provider](/providers/anthropic)
