gemini

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 12 Imported by: 0

README

deps.dev License License Stay with Ukraine

gemini

gemini is a Go client for the Google Gemini API. It implements the github.com/goloop/ai interface, so it looks and works like every other goloop AI provider, and exposes Gemini's native endpoints with their full options on top.

Features

  • Content generation: Generate for a single response, Stream for token-by-token output through iter.Seq2.
  • Tool use (function calling), multimodal image input and system instructions.
  • Native GenerateContent and StreamGenerateContent with the full option set (generation config, response schema, tool config).
  • Embeddings, token counting and model listing.
  • Retries on 429 and 5xx with backoff; normalized, typed API errors.
  • Depends only on github.com/goloop/ai and the standard library.
  • Structured output: ai.Format maps onto responseMimeType and responseJsonSchema; read the reply with resp.JSON(&v).
  • Hosted web search: ai.Request.Hosted maps onto grounding with Google Search, with byte-accurate citation ranges and a report of whether it actually ran.

Installation

go get github.com/goloop/gemini

Quick start

package main

import (
	"context"
	"fmt"
	"os"

	"github.com/goloop/ai"
	"github.com/goloop/gemini"
)

func main() {
	c := gemini.New(os.Getenv("GEMINI_API_KEY"))

	resp, err := c.Generate(context.Background(), &ai.Request{
		Model:    gemini.ModelGemini25Flash,
		Messages: []ai.Message{ai.UserText("Say hello in one word.")},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(resp.Text())
}

Streaming

for chunk, err := range c.Stream(ctx, req) {
	if err != nil {
		break
	}
	fmt.Print(chunk.Text)
	if chunk.Done && chunk.Usage != nil {
		fmt.Printf("\n[%d in / %d out]\n",
			chunk.Usage.InputTokens, chunk.Usage.OutputTokens)
	}
}

Tools, images and system prompts

Tools, images and system prompts use the same shared ai types as every other provider (see the reference). Gemini keys tool results by function name, so the driver gives each call a unique ID (the function name plus a counter) and resolves it back to the name automatically.

For Gemini-only options such as a JSON response schema, build a native GenerateRequest:

resp, _ := c.GenerateContent(ctx, gemini.ModelGemini25Flash, &gemini.GenerateRequest{
	Contents: []gemini.Content{{Role: "user", Parts: []gemini.Part{{Text: "List two colors."}}}},
	GenerationConfig: &gemini.GenerationConfig{
		ResponseMIMEType: "application/json",
	},
})

Native endpoints

c.Embed(ctx, "text-embedding-004", "hello", "world")
c.CountTokens(ctx, gemini.ModelGemini25Flash, req)
c.Models(ctx)
c.GetModel(ctx, gemini.ModelGemini25Flash)

Documentation

Full reference: DOC.md (Ukrainian: DOC.UK.md).

Contributing

See CONTRIBUTING.md.

License

MIT - see LICENSE.

Documentation

Overview

Package gemini is a client for the Google Gemini API, built on the goloop/ai interface.

The Client implements ai.Client, so Generate and Stream work the same as with any other goloop AI provider. On top of that it exposes Gemini's native endpoints and their full options: generateContent, streamGenerateContent, image generation with Imagen (GenerateImage through the predict endpoint), embeddings, token counting and model listing.

c := gemini.New(os.Getenv("GEMINI_API_KEY"))
resp, err := c.Generate(ctx, &ai.Request{
    Model:    gemini.ModelGemini25Flash,
    Messages: []ai.Message{ai.UserText("Say hello in one word.")},
})

Structured output

ai.Request.Format maps onto responseMimeType, with ai.FormatJSONSchema sending the schema as responseJsonSchema beside it, so a request for JSON is enforced rather than merely asked for. ai.Response.JSON decodes the reply. The API has a second schema field, responseSchema, which takes an OpenAPI 3.0 subset rather than JSON Schema; it stays available on GenerationConfig for callers who mean that dialect.

ai.Request.Hosted maps onto grounding with Google Search, which goes in the tools list as an entry of its own beside the caller's functions:

resp, err := c.Generate(ctx, &ai.Request{
    Model:    gemini.ModelGemini25Flash,
    Messages: []ai.Message{ai.UserText("What shipped this week?")},
    Hosted:   []ai.Hosted{{Kind: ai.HostedWebSearch}},
})
for _, c := range resp.Citations() { ... }

The search runs on the provider's side, so nothing new reaches a tool loop. The sources come back as ai.Citation values on the text they support, and this provider is unusual in reporting where: its offsets are byte offsets, so Citation.StartByte and Citation.EndByte cut ai.Text.Text exactly, and a caller can show which sentence each source backs. It never reports the source's own words, so Citation.CitedText stays empty here.

A stream is the exception: it carries the sources without a range. The provider measures a range against the finished answer, and there is no finished answer to measure against while it is still arriving.

Grounding takes no settings at all here: the provider decides how many searches to run and over what. A request that sets ai.HostedWeb.MaxUses, either domain list or a region is ai.ErrNoHosted rather than a search that quietly ignores what it was told.

A search and a structured Format cannot be asked for in the same call. This provider enforces a format by constraining what the model may emit, and that does not hold together with grounding, so a request that asks for both is ai.ErrFormatWithHosted before it leaves. Two calls do work: one that searches and answers in prose, one without Hosted that reshapes it into the schema.

Asking what this driver can do

Capabilities describes this driver for the decision taken before a call: whether to offer a feature at all, and whether it needs one request or two.

if ai.SupportsHosted(c, ai.Hosted{Kind: ai.HostedWebSearch}) { ... }

It is a hint and not a permission - support also depends on the model, the account and the region - so ai.ErrNoHosted and ai.ErrNoFormat remain the source of truth and a caller still handles them. What changes is that a refusal the provider only reports as a 400 now arrives as those same sentinels, wrapped around the original ai.APIError, so one errors.Is covers a limitation this driver knew in advance and one it learned over the wire.

Gemini keys tool results by function name rather than by call ID, so this package synthesizes a unique ai.ToolUse ID per call (the function name plus a counter, so repeated calls to one function stay distinct) and resolves it back to the name on the way in. It depends only on goloop/ai and the standard library.

Index

Examples

Constants

View Source
const (
	ModelGemini25Pro       = "gemini-2.5-pro"
	ModelGemini25Flash     = "gemini-2.5-flash"
	ModelGemini25FlashLite = "gemini-2.5-flash-lite"
	ModelGemini20Flash     = "gemini-2.0-flash"
	ModelTextEmbedding004  = "text-embedding-004"
)

Convenience model identifiers. Any model string is accepted; use Models to discover what the account can call.

View Source
const (
	ModelImagen4 = "imagen-4.0-generate-001"
	ModelImagen3 = "imagen-3.0-generate-002"
)

Imagen model identifiers. Any model string is accepted; a constant is just a documented name to reach for. Google versions these by date and retires old ones, so verify against the current catalog before pinning one for a release.

View Source
const DefaultBaseURL = "https://generativelanguage.googleapis.com/v1beta"

DefaultBaseURL is the Gemini API base URL, including the version segment.

Variables

View Source
var (
	// ErrNoImageRequest is returned by GenerateImage for a nil request.
	ErrNoImageRequest = errors.New("gemini: image request is nil")

	// ErrNoImagePrompt is returned by GenerateImage when the prompt is empty.
	// Imagen has nothing to draw from without one.
	ErrNoImagePrompt = errors.New("gemini: image request has no prompt")

	// ErrNoImageBytes is returned by ImageData.Bytes when there are no image
	// bytes to decode. Imagen answers with inline base64, so this only happens
	// on an empty result.
	ErrNoImageBytes = errors.New("gemini: no image bytes")
)

Errors reported for an image request or its result. The names match the other goloop image drivers, so application code reads the same way whichever provider is behind it.

Functions

This section is empty.

Types

type Blob

type Blob struct {
	MIMEType string `json:"mimeType"`
	Data     string `json:"data"`
}

Blob is inline binary data, such as an image, with its MIME type. Data is base64-encoded.

type Candidate

type Candidate struct {
	Content      Content `json:"content"`
	FinishReason string  `json:"finishReason,omitempty"`
	Index        int     `json:"index"`

	// GroundingMetadata is present when the answer was grounded in a search
	// the provider ran. It is where the sources live.
	GroundingMetadata *GroundingMetadata `json:"groundingMetadata,omitempty"`
}

Candidate is one generated response option.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a Gemini API client. It implements ai.Client and adds the provider's native endpoints.

func New

func New(apiKey string, opts ...Option) *Client

New returns a Client for the given API key. Shared options (WithBaseURL, WithHTTPClient, WithTimeout, WithMaxRetries, WithHeader) configure it.

Example
package main

import (
	"fmt"

	"github.com/goloop/gemini"
)

func main() {
	c := gemini.New("AIza...")
	_ = c // use c.Generate, c.Stream, c.GenerateContent, ...
	fmt.Println(gemini.ModelGemini25Flash)
}
Output:
gemini-2.5-flash

func (*Client) Capabilities added in v1.1.0

func (c *Client) Capabilities() ai.Capabilities

Capabilities describes what this driver can be asked for. It is a hint for the decision taken before a call - whether to offer a feature, and whether it needs one request or two - and never a substitute for handling ai.ErrNoHosted, ai.ErrNoFormat or ai.ErrFormatWithHosted, because support also depends on the model, the account and the region.

func (*Client) CountTokens

func (c *Client) CountTokens(
	ctx context.Context,
	model string,
	req *GenerateRequest,
) (int, error)

CountTokens reports how many tokens the given content would consume for a model, without generating a response.

func (*Client) Embed

func (c *Client) Embed(
	ctx context.Context,
	model string,
	texts ...string,
) ([][]float64, error)

Embed embeds one or more plain-text inputs and returns their vectors in order, using a single batchEmbedContents call.

func (*Client) EmbedContent

func (c *Client) EmbedContent(
	ctx context.Context,
	model string,
	req *EmbedRequest,
) (*Embedding, error)

EmbedContent embeds a single request with the given model.

func (*Client) Generate

func (c *Client) Generate(
	ctx context.Context,
	req *ai.Request,
) (*ai.Response, error)

Generate implements ai.Client. It maps the request onto generateContent and returns the first candidate as an ai.Response.

Example

ExampleClient_Generate builds a request. Sending it needs a real API key, so this example only shows the shape.

package main

import (
	"fmt"

	"github.com/goloop/ai"
	"github.com/goloop/gemini"
)

func main() {
	req := &ai.Request{
		Model: gemini.ModelGemini25Flash,
		Messages: []ai.Message{
			ai.UserText("Name the capital of France."),
		},
	}
	fmt.Println(req.Model, len(req.Messages))
}
Output:
gemini-2.5-flash 1

func (*Client) GenerateContent

func (c *Client) GenerateContent(
	ctx context.Context,
	model string,
	req *GenerateRequest,
) (*GenerateResponse, error)

GenerateContent sends a native generateContent request for the given model.

func (*Client) GenerateImage added in v1.2.0

func (c *Client) GenerateImage(
	ctx context.Context,
	req *ImageRequest,
) (*ImageResponse, error)

GenerateImage generates images from a text prompt with Imagen, through the predict endpoint. Read each image with ImageData.Bytes.

The request is fitted first: Size is mapped to the nearest aspect ratio Imagen supports, unless AspectRatio names one directly. A nil or promptless request is refused before the network.

func (*Client) GetModel

func (c *Client) GetModel(ctx context.Context, model string) (*Model, error)

GetModel retrieves one model by name, with or without the "models/" prefix.

func (*Client) Models

func (c *Client) Models(ctx context.Context) ([]Model, error)

Models lists the models available to the account.

func (*Client) Stream

func (c *Client) Stream(
	ctx context.Context,
	req *ai.Request,
) iter.Seq2[ai.Chunk, error]

Stream implements ai.Client. It maps the request onto streamGenerateContent and yields text deltas, completed tool calls and a final chunk carrying usage.

func (*Client) StreamGenerateContent

func (c *Client) StreamGenerateContent(
	ctx context.Context,
	model string,
	req *GenerateRequest,
) iter.Seq2[*GenerateResponse, error]

StreamGenerateContent sends a native streaming request and yields each response chunk as it arrives.

type Content

type Content struct {
	Role  string `json:"role,omitempty"`
	Parts []Part `json:"parts"`
}

Content is one turn of a conversation: a role ("user" or "model") and its parts. System text is carried separately in GenerateRequest.SystemInstruction and leaves Role empty.

type EmbedRequest

type EmbedRequest struct {
	Content              Content `json:"content"`
	TaskType             string  `json:"taskType,omitempty"`
	Title                string  `json:"title,omitempty"`
	OutputDimensionality int     `json:"outputDimensionality,omitempty"`
}

EmbedRequest is the native embedContent request body.

type Embedding

type Embedding struct {
	Values []float64 `json:"values"`
}

Embedding is a single embedding vector.

type FileData

type FileData struct {
	MIMEType string `json:"mimeType,omitempty"`
	FileURI  string `json:"fileUri"`
}

FileData references data by URI, such as an uploaded file or a supported remote resource.

type FunctionCall

type FunctionCall struct {
	Name string          `json:"name"`
	Args json.RawMessage `json:"args,omitempty"`
}

FunctionCall is a request from the model to call a declared function. Args is the JSON arguments object.

type FunctionCallingConfig

type FunctionCallingConfig struct {
	Mode string `json:"mode,omitempty"`
}

FunctionCallingConfig sets the tool-calling mode: "AUTO", "ANY" or "NONE".

type FunctionDeclaration

type FunctionDeclaration struct {
	Name        string          `json:"name"`
	Description string          `json:"description,omitempty"`
	Parameters  json.RawMessage `json:"parameters,omitempty"`
}

FunctionDeclaration describes a callable function. Parameters is a JSON Schema object describing its arguments.

type FunctionResponse

type FunctionResponse struct {
	Name     string          `json:"name"`
	Response json.RawMessage `json:"response"`
}

FunctionResponse carries a function's result back to the model. Response is a JSON object.

type GenerateRequest

type GenerateRequest struct {
	Contents          []Content         `json:"contents"`
	SystemInstruction *Content          `json:"systemInstruction,omitempty"`
	Tools             []ToolDecls       `json:"tools,omitempty"`
	ToolConfig        *ToolConfig       `json:"toolConfig,omitempty"`
	GenerationConfig  *GenerationConfig `json:"generationConfig,omitempty"`
}

GenerateRequest is the native generateContent request body.

type GenerateResponse

type GenerateResponse struct {
	Candidates     []Candidate     `json:"candidates"`
	UsageMetadata  *UsageMetadata  `json:"usageMetadata,omitempty"`
	ModelVersion   string          `json:"modelVersion,omitempty"`
	PromptFeedback *PromptFeedback `json:"promptFeedback,omitempty"`
}

GenerateResponse is the native generateContent (and stream chunk) response.

func (*GenerateResponse) Text

func (r *GenerateResponse) Text() string

Text returns the concatenation of the text parts of the first candidate.

type GenerationConfig

type GenerationConfig struct {
	Temperature      *float64 `json:"temperature,omitempty"`
	TopP             *float64 `json:"topP,omitempty"`
	MaxOutputTokens  int      `json:"maxOutputTokens,omitempty"`
	StopSequences    []string `json:"stopSequences,omitempty"`
	ResponseMIMEType string   `json:"responseMimeType,omitempty"`

	// ResponseSchema constrains the answer to a schema written in the
	// OpenAPI 3.0 subset the API defines. It is not JSON Schema, and the two
	// diverge as soon as a schema uses anything beyond type, properties and
	// required.
	ResponseSchema json.RawMessage `json:"responseSchema,omitempty"`

	// ResponseJSONSchema constrains the answer to a JSON Schema. It is what
	// ai.Format.Schema is, so that is where this driver puts it. The API
	// rejects a request that sets both this and ResponseSchema; either one
	// still needs ResponseMIMEType.
	ResponseJSONSchema json.RawMessage `json:"responseJsonSchema,omitempty"`
}

GenerationConfig tunes generation. Temperature and TopP are pointers so an explicit zero is distinct from unset.

type GoogleSearch added in v1.0.0

type GoogleSearch struct{}

GoogleSearch asks the provider to ground its answer in a search it runs itself. It has no fields: this provider decides how many searches to run and over what, and offers nothing to narrow them with.

type GroundingChunk added in v1.0.0

type GroundingChunk struct {
	Web *GroundingWeb `json:"web,omitempty"`
}

GroundingChunk is one source the search found.

type GroundingMetadata added in v1.0.0

type GroundingMetadata struct {
	WebSearchQueries  []string           `json:"webSearchQueries,omitempty"`
	GroundingChunks   []GroundingChunk   `json:"groundingChunks,omitempty"`
	GroundingSupports []GroundingSupport `json:"groundingSupports,omitempty"`
}

GroundingMetadata reports the search behind a grounded answer: what was searched for, what was found, and which stretch of the answer each finding supports.

type GroundingSupport added in v1.0.0

type GroundingSupport struct {
	Segment               *Segment `json:"segment,omitempty"`
	GroundingChunkIndices []int    `json:"groundingChunkIndices,omitempty"`
}

GroundingSupport ties a stretch of the answer to the sources behind it. Sources are named by their position in GroundingMetadata.GroundingChunks.

type GroundingWeb added in v1.0.0

type GroundingWeb struct {
	URI    string `json:"uri,omitempty"`
	Title  string `json:"title,omitempty"`
	Domain string `json:"domain,omitempty"`
}

GroundingWeb is a web page a search found.

type ImageData added in v1.2.0

type ImageData struct {
	B64JSON  string `json:"bytesBase64Encoded"`
	MIMEType string `json:"mimeType,omitempty"`
}

ImageData is one generated image. Imagen returns bytes inline, so Bytes decodes them directly.

func (ImageData) Bytes added in v1.2.0

func (d ImageData) Bytes() ([]byte, error)

Bytes returns the decoded image. Imagen delivers bytes inline, so this always has something to decode unless the result was empty (ErrNoImageBytes).

type ImageRequest added in v1.2.0

type ImageRequest struct {
	Model  string `json:"-"`
	Prompt string `json:"-"`

	// N is how many images to generate. Zero means one.
	N int `json:"-"`

	// AspectRatio is Imagen's native ratio, one of "1:1", "3:4", "4:3",
	// "9:16", "16:9". Empty leaves it to the provider.
	AspectRatio string `json:"-"`

	// Size is a "WxH" pixel string (for example "1536x1024"). Google does not
	// honor exact pixels, so it is mapped to the nearest supported aspect
	// ratio. AspectRatio, when set, takes precedence.
	Size string `json:"-"`
}

ImageRequest is an image generation request.

The field names match the other goloop image drivers where the meaning is the same (Model, Prompt, N). Imagen works in aspect ratios rather than exact pixels, so two size knobs are offered: Size takes a "WxH" string for cross-provider code and is mapped to the nearest aspect ratio, while AspectRatio sets Imagen's native knob directly. When both are set, AspectRatio wins.

type ImageResponse added in v1.2.0

type ImageResponse struct {
	Data []ImageData

	// Usage mirrors the field on the other drivers. Imagen's predict endpoint
	// does not report token usage, so this is always nil here; it exists so
	// accounting code reads the same across providers.
	Usage *ImageUsage
}

ImageResponse is an image generation response.

type ImageUsage added in v1.2.0

type ImageUsage struct {
	TotalTokens  int `json:"total_tokens"`
	InputTokens  int `json:"input_tokens"`
	OutputTokens int `json:"output_tokens"`
}

ImageUsage reports the tokens an image request consumed, in the shape the other goloop image drivers use. Imagen's predict endpoint reports none, so this is unused today; it keeps the type present for uniform accounting code.

type Model

type Model struct {
	Name                       string   `json:"name"`
	BaseModelID                string   `json:"baseModelId,omitempty"`
	Version                    string   `json:"version,omitempty"`
	DisplayName                string   `json:"displayName,omitempty"`
	Description                string   `json:"description,omitempty"`
	InputTokenLimit            int      `json:"inputTokenLimit,omitempty"`
	OutputTokenLimit           int      `json:"outputTokenLimit,omitempty"`
	SupportedGenerationMethods []string `json:"supportedGenerationMethods,omitempty"`
}

Model describes a Gemini model as reported by the API.

type Option

type Option func(*settings)

Option configures a Client in New.

func WithBaseURL

func WithBaseURL(u string) Option

WithBaseURL overrides the API base URL (proxies, gateways, mock servers, Gemini-compatible endpoints).

func WithHTTPClient

func WithHTTPClient(c *http.Client) Option

WithHTTPClient sets the HTTP client used for requests.

func WithHeader

func WithHeader(key, value string) Option

WithHeader adds a header sent with every request.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets how many times a request is retried on 429 and 5xx.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the per-request timeout when no custom HTTP client is set.

type Part

type Part struct {
	Text             string            `json:"text,omitempty"`
	InlineData       *Blob             `json:"inlineData,omitempty"`
	FileData         *FileData         `json:"fileData,omitempty"`
	FunctionCall     *FunctionCall     `json:"functionCall,omitempty"`
	FunctionResponse *FunctionResponse `json:"functionResponse,omitempty"`
}

Part is a single piece of a Content. Exactly one field is set: Text for plain text, InlineData for embedded bytes, FileData for a referenced file, FunctionCall for a model tool call, or FunctionResponse for a tool result.

type PromptFeedback added in v0.1.1

type PromptFeedback struct {
	BlockReason string `json:"blockReason,omitempty"`
}

PromptFeedback reports why a prompt was rejected. BlockReason is empty for a prompt that was not blocked (for example "SAFETY" or "PROHIBITED_CONTENT").

type Segment added in v1.0.0

type Segment struct {
	PartIndex  int    `json:"partIndex,omitempty"`
	StartIndex int    `json:"startIndex,omitempty"`
	EndIndex   int    `json:"endIndex,omitempty"`
	Text       string `json:"text,omitempty"`
}

Segment is a stretch of the generated answer. StartIndex and EndIndex are byte offsets, which is what ai.Citation wants, so unlike most providers this one can say which sentence a source backs rather than only listing sources.

type ToolConfig

type ToolConfig struct {
	FunctionCallingConfig *FunctionCallingConfig `json:"functionCallingConfig,omitempty"`
}

ToolConfig controls tool calling for a request.

type ToolDecls

type ToolDecls struct {
	FunctionDeclarations []FunctionDeclaration `json:"functionDeclarations,omitempty"`

	// GoogleSearch turns on grounding with Google Search, which the provider
	// runs itself. It takes no settings, which is why it is an empty object.
	GoogleSearch *GoogleSearch `json:"googleSearch,omitempty"`
}

ToolDecls is one entry of the tools list: either the caller's function declarations or one of the provider's own tools. A request that wants both sends two entries.

type UsageMetadata

type UsageMetadata struct {
	PromptTokenCount     int `json:"promptTokenCount"`
	CandidatesTokenCount int `json:"candidatesTokenCount"`
	TotalTokenCount      int `json:"totalTokenCount"`
}

UsageMetadata reports token counts for a request.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL