Skip to main content

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.

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

Configuration

provider
string
required
Set to "gemini" or "google" or "google-gemini"
api_key
string
Google API key. Get yours at aistudio.google.com/apikey
model
string
required
Model name: gemini-2.0-flash, gemini-1.5-pro, etc.
temperature
number
Sampling temperature (0.0-2.0). Defaults to 0.7.
max_tokens
number
Maximum output tokens (called maxOutputTokens in Gemini API). Defaults to 8192.

Example Configuration

{
  "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)

{
  "provider": "gemini",
  "model": "gemini-2.0-flash",
  "api_key": "AIza..."
}

2. Environment Variables

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:
{
  "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

FeatureSupport
StreamingYes
Function CallingNo (planned)
Vision (images)Yes
System MessagesYes (via system_instruction)
Tool CallsNo

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
{
  "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):
{
  "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:
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:
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

Build docs developers (and LLMs) love