ChatModel - gemini
A Google Gemini implementation for Eino that implements the ToolCallingChatModel interface. This enables seamless integration with Eino’s LLM capabilities for enhanced natural language processing and generation.
Features
- Implements
github.com/cloudwego/eino/components/model.Model - Easy integration with Eino’s model system
- Configurable model parameters
- Supports chat completion
- Supports streaming responses
- Supports custom response parsing
- Flexible model configuration
- Supports caching of generated responses
Installation
go get github.com/cloudwego/eino-ext/components/model/gemini@latest
Quick Start
Here’s a quick example of how to use the Gemini model:
package main
import (
"context"
"fmt"
"log"
"os"
"google.golang.org/genai"
"github.com/cloudwego/eino-ext/components/model/gemini"
"github.com/cloudwego/eino/schema"
)
func main() {
apiKey := os.Getenv("GEMINI_API_KEY")
ctx := context.Background()
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: apiKey,
})
if err != nil {
log.Fatalf("NewClient of gemini failed, err=%v", err)
}
cm, err := gemini.NewChatModel(ctx, &gemini.Config{
Client: client,
Model: "gemini-1.5-flash",
ThinkingConfig: &genai.ThinkingConfig{
IncludeThoughts: true,
ThinkingBudget: nil,
},
})
if err != nil {
log.Fatalf("NewChatModel of gemini failed, err=%v", err)
}
// If you are using a model that supports image understanding (e.g., gemini-1.5-flash-image-preview),
// you can provide both image and text input like this:
/*
image, err := os.ReadFile("./path/to/your/image.jpg")
if err != nil {
log.Fatalf("os.ReadFile failed, err=%v\n", err)
}
imageStr := base64.StdEncoding.EncodeToString(image)
resp, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
UserInputMultiContent: []schema.MessageInputPart{
{
Type: schema.ChatMessagePartTypeText,
Text: "What do you see in this image?",
},
{
Type: schema.ChatMessagePartTypeImageURL,
Image: &schema.MessageInputImage{
MessagePartCommon: schema.MessagePartCommon{
Base64Data: &imageStr,
MIMEType: "image/jpeg",
},
Detail: schema.ImageURLDetailAuto,
},
},
},
},
})
*/
resp, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
Content: "What is the capital of France?",
},
})
if err != nil {
log.Fatalf("Generate error: %v", err)
}
fmt.Printf("Assistant: %s\n", resp.Content)
if len(resp.ReasoningContent) > 0 {
fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
}
}
Configuration
You can configure the model using the gemini.Config struct:
type Config struct {
// Client is the Gemini API client instance
// Required for making API calls to Gemini
Client *genai.Client
// Model specifies which Gemini model to use
// Examples: "gemini-pro", "gemini-pro-vision", "gemini-1.5-flash"
Model string
// MaxTokens limits the maximum number of tokens in the response
// Optional. Example: maxTokens := 100
MaxTokens *int
// Temperature controls randomness in responses
// Range: [0.0, 1.0], where 0.0 is more focused and 1.0 is more creative
// Optional. Example: temperature := float32(0.7)
Temperature *float32
// TopP controls diversity via nucleus sampling
// Range: [0.0, 1.0], where 1.0 disables nucleus sampling
// Optional. Example: topP := float32(0.95)
TopP *float32
// TopK controls diversity by limiting the top K tokens to sample from
// Optional. Example: topK := int32(40)
TopK *int32
// ResponseSchema defines the structure for JSON responses
// Optional. Used when you want structured output in JSON format
ResponseSchema *openapi3.Schema
// EnableCodeExecution allows the model to execute code
// Warning: Be cautious with code execution in production
// Optional. Default: false
EnableCodeExecution bool
// SafetySettings configures content filtering for different harm categories
// Controls the model's filtering behavior for potentially harmful content
// Optional.
SafetySettings []*genai.SafetySetting
ThinkingConfig *genai.ThinkingConfig
// ResponseModalities specifies the modalities the model can return.
// Optional.
ResponseModalities []
MediaResolution genai.MediaResolution
// Cache controls prefix cache settings for the model.
// Optional. used to CreatePrefixCache for reused inputs.
Cache *CacheConfig
}
// CacheConfig controls prefix cache settings for the model.
type CacheConfig struct {
// TTL specifies how long cached resources remain valid (now + TTL).
TTL time.Duration `json:"ttl,omitempty"`
// ExpireTime sets the absolute expiration timestamp for cached resources.
ExpireTime time.Time `json:"expireTime,omitempty"`
}
Cache
This component supports two caching strategies to improve latency and reduce API calls:
- Explicit cache (prefix cache): Builds reusable context from system instructions, tools, and messages. Use
CreatePrefixCacheto create a cache, and pass its name in subsequent requests withgemini.WithCachedContentName(...). Configure TTL and absolute expiration withCacheConfig(TTL,ExpireTime). When using cached content, the request omits system instructions and tools, relying on the cached prefix. - Implicit cache: Managed by Gemini itself. The service may automatically reuse previous requests or responses. Expiration and reuse are controlled by Gemini and cannot be configured.
The example below shows how to create a prefix cache and reuse it in subsequent calls.
toolInfoList := []*schema.ToolInfo{
{
Name: "tool_a",
Desc: "desc",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{}),
},
}
cacheInfo, _ := cm.CreatePrefixCache(ctx, []*schema.Message{
{
Role: schema.System,
Content: `aaa`,
},
{
Role: schema.User,
Content: `bbb`,
},
}, model.WithTools(toolInfoList))
msg, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
Content: "give a very short summary about this transcript",
},
}, gemini.WithCachedContentName(cacheInfo.Name))
Examples
Text Generation
package main
import (
"context"
"fmt"
"log"
"os"
"google.golang.org/genai"
"github.com/cloudwego/eino-ext/components/model/gemini"
"github.com/cloudwego/eino/schema"
)
func main() {
apiKey := os.Getenv("GEMINI_API_KEY")
modelName := os.Getenv("GEMINI_MODEL")
ctx := context.Background()
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: apiKey,
})
if err != nil {
log.Fatalf("NewClient of gemini failed, err=%v", err)
}
cm, err := gemini.NewChatModel(ctx, &gemini.Config{
Client: client,
Model: modelName,
ThinkingConfig: &genai.ThinkingConfig{
IncludeThoughts: true,
ThinkingBudget: nil,
},
})
if err != nil {
log.Fatalf("NewChatModel of gemini failed, err=%v", err)
}
resp, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
Content: "What is the capital of France?",
},
})
if err != nil {
log.Fatalf("Generate error: %v", err)
}
fmt.Printf("Assistant: %s\n", resp.Content)
if len(resp.ReasoningContent) > 0 {
fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
}
}
Multimodal Support (Image Understanding)
package main
import (
"context"
"encoding/base64"
"fmt"
"log"
"os"
"google.golang.org/genai"
"github.com/cloudwego/eino-ext/components/model/gemini"
"github.com/cloudwego/eino/schema"
)
func main() {
apiKey := os.Getenv("GEMINI_API_KEY")
modelName := os.Getenv("GEMINI_MODEL")
ctx := context.Background()
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: apiKey,
})
if err != nil {
log.Fatalf("NewClient of gemini failed, err=%v", err)
}
cm, err := gemini.NewChatModel(ctx, &gemini.Config{
Client: client,
Model: modelName,
})
if err != nil {
log.Fatalf("NewChatModel of gemini failed, err=%v", err)
}
image, err := os.ReadFile("./examples/generate_with_image/test.jpg")
if err != nil {
log.Fatalf("os.ReadFile failed, err=%v\n", err)
}
imageStr := base64.StdEncoding.EncodeToString(image)
resp, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
UserInputMultiContent: []schema.MessageInputPart{
{
Type: schema.ChatMessagePartTypeText,
Text: "What do you see in this image?",
},
{
Type: schema.ChatMessagePartTypeImageURL,
Image: &schema.MessageInputImage{
MessagePartCommon: schema.MessagePartCommon{
Base64Data: &imageStr,
MIMEType: "image/jpeg",
},
Detail: schema.ImageURLDetailAuto,
},
},
},
},
})
if err != nil {
log.Fatalf("Generate error: %v", err)
}
fmt.Printf("Assistant: %s\n", resp.Content)
}
Text Generation with Prefix Cache
package main
import (
"context"
"encoding/base64"
"fmt"
"log"
"os"
"github.com/bytedance/sonic"
"github.com/cloudwego/eino/components/model"
"github.com/cloudwego/eino/components/tool/utils"
"github.com/cloudwego/eino/schema"
"google.golang.org/genai"
"github.com/cloudwego/eino-ext/components/model/gemini"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: os.Getenv("GEMINI_API_KEY"),
})
if err != nil {
log.Fatalf("genai.NewClient failed: %v", err)
}
cm, err := gemini.NewChatModel(ctx, &gemini.Config{
Model: os.Getenv("GEMINI_MODEL"),
Client: client,
})
if err != nil {
log.Fatalf("gemini.NewChatModel failed: %v", err)
}
type toolCallInput struct {
Answer int `json:"answer" jsonschema_description:"the answer of the question"`
}
answerTool, err := utils.InferTool("answer_to_user",
"answer to user",
func(ctx context.Context, in *toolCallInput) (string, error) {
return fmt.Sprintf("answer: %v", in.Answer), nil
})
if err != nil {
log.Fatalf("utils.InferTool failed: %v", err)
}
info, err := answerTool.Info(ctx)
if err != nil {
log.Fatalf("get tool info failed: %v", err)
}
// this file is from gemini cache usage example
fileData, err := os.ReadFile("./a11.test.txt")
if err != nil {
log.Fatalf("os.ReadFile failed: %v", err)
}
txtFileBase64 := base64.StdEncoding.EncodeToString(fileData)
cacheInfo, err := cm.CreatePrefixCache(ctx, []*schema.Message{
{
Role: schema.System,
Content: `You are an expert at analyzing transcripts.
answer the question with the tool "answer_to_user"
always include the start_time and end_time of the transcript in the output`,
},
{
Role: schema.User,
UserInputMultiContent: []schema.MessageInputPart{
{
Type: schema.ChatMessagePartTypeFileURL,
File: &schema.MessageInputFile{
MessagePartCommon: schema.MessagePartCommon{
Base64Data: &txtFileBase64,
MIMEType: "text/plain",
},
},
},
},
},
}, model.WithTools([]*schema.ToolInfo{info}), model.WithToolChoice(schema.ToolChoiceForced))
if err != nil {
log.Fatalf("CreatePrefixCache failed: %v", err)
}
data, _ := sonic.MarshalIndent(cacheInfo, "", " ")
log.Printf("prefix cache info:\n%v\n", string(data))
msg, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
Content: "give a very short summary about this transcript",
},
}, gemini.WithCachedContentName(cacheInfo.Name),
model.WithTools([]*schema.ToolInfo{info}),
model.WithToolChoice(schema.ToolChoiceForced))
if err != nil {
log.Fatalf("Generate failed: %v", err)
}
msgData, _ := sonic.MarshalIndent(msg, "", " ")
log.Printf("model output:\n%v\n", string(msgData))
}
Streaming Generation
package main
import (
"context"
"fmt"
"io"
"log"
"os"
"google.golang.org/genai"
"github.com/cloudwego/eino-ext/components/model/gemini"
"github.com/cloudwego/eino/schema"
)
func main() {
apiKey := os.Getenv("GEMINI_API_KEY")
modelName := os.Getenv("GEMINI_MODEL")
ctx := context.Background()
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: apiKey,
})
if err != nil {
log.Fatalf("NewClient of gemini failed, err=%v", err)
}
cm, err := gemini.NewChatModel(ctx, &gemini.Config{
Client: client,
Model: modelName,
ThinkingConfig: &genai.ThinkingConfig{
IncludeThoughts: true,
ThinkingBudget: nil,
},
})
if err != nil {
log.Fatalf("NewChatModel of gemini failed, err=%v", err)
}
stream, err := cm.Stream(ctx, []*schema.Message{
{
Role: schema.User,
Content: "Write a short poem about spring.",
},
})
if err != nil {
log.Fatalf("Stream error: %v", err)
}
fmt.Println("Assistant: ")
for {
resp, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("Stream receive error: %v", err)
}
fmt.Println("frame: ")
if len(resp.Content) > 0 {
fmt.Println("content: ", resp.Content)
}
if len(resp.ReasoningContent) > 0 {
fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
}
}
fmt.Println()
}
Tool Calling
package main
import (
"context"
"fmt"
"log"
"os"
"google.golang.org/genai"
"github.com/cloudwego/eino-ext/components/model/gemini"
"github.com/cloudwego/eino/schema"
)
func main() {
apiKey := os.Getenv("GEMINI_API_KEY")
modelName := os.Getenv("GEMINI_MODEL")
ctx := context.Background()
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: apiKey,
})
if err != nil {
log.Fatalf("NewClient of gemini failed, err=%v", err)
}
cm, err := gemini.NewChatModel(ctx, &gemini.Config{
Client: client,
Model: modelName,
ThinkingConfig: &genai.ThinkingConfig{
IncludeThoughts: true,
ThinkingBudget: nil,
},
})
if err != nil {
log.Fatalf("NewChatModel of gemini failed, err=%v", err)
}
err = cm.BindTools([]*schema.ToolInfo{
{
Name: "book_recommender",
Desc: "Recommends books based on user preferences and provides purchase links",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"genre": {
Type: "string",
Desc: "Preferred book genre",
Enum: []string{"fiction", "sci-fi", "mystery", "biography", "business"},
},
"max_pages": {
Type: "integer",
Desc: "Maximum page length (0 for no limit)",
},
"min_rating": {
Type: "number",
Desc: "Minimum user rating (0-5 scale)",
},
}),
},
})
if err != nil {
log.Fatalf("Bind tools error: %v", err)
}
resp, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
},
})
if err != nil {
log.Fatalf("Generate error: %v", err)
}
if len(resp.ToolCalls) > 0 {
fmt.Printf("Function called: \n")
if len(resp.ReasoningContent) > 0 {
fmt.Printf("ReasoningContent: %s\n", resp.ReasoningContent)
}
fmt.Println("Name: ", resp.ToolCalls[0].Function.Name)
fmt.Printf("Arguments: %s\n", resp.ToolCalls[0].Function.Arguments)
} else {
log.Printf("Function called without tool calls: %s\n", resp.Content)
}
resp, err = cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
Content: "Recommend business books with minimum 4.3 rating and max 350 pages",
},
resp,
{
Role: schema.Tool,
ToolCallID: resp.ToolCalls[0].ID,
Content: "{\"book name\":\"Microeconomics for Managers\"}",
},
})
if err != nil {
log.Fatalf("Generate error: %v", err)
}
fmt.Printf("Function call final result: %s\n", resp.Content)
}
Image Generation
package main
import (
"context"
"encoding/json"
"log"
"os"
"google.golang.org/genai"
"github.com/cloudwego/eino-ext/components/model/gemini"
"github.com/cloudwego/eino/schema"
)
func main() {
apiKey := os.Getenv("GEMINI_API_KEY")
modelName := os.Getenv("GEMINI_MODEL")
ctx := context.Background()
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: apiKey,
})
if err != nil {
log.Fatalf("NewClient of gemini failed, err=%v", err)
}
cm, err := gemini.NewChatModel(ctx, &gemini.Config{
Client: client,
Model: modelName,
ResponseModalities: []gemini.GeminiResponseModality{
gemini.GeminiResponseModalityText,
gemini.GeminiResponseModalityImage,
},
})
if err != nil {
log.Fatalf("NewChatModel of gemini failed, err=%v", err)
}
/*
The generated multimodal content is stored in the `AssistantGenMultiContent` field.
For this example, the resulting message will have a structure similar to this:
resp := &schema.Message{
Role: schema.Assistant,
AssistantGenMultiContent: []schema.MessageOutputPart{
{
Type: schema.ChatMessagePartTypeImageURL,
Image: &schema.MessageOutputImage{
MessagePartCommon: schema.MessagePartCommon{
Base64Data: &base64String, // The base64 encoded image data
MIMEType: "image/png",
},
},
},
},
}
*/
resp, err := cm.Generate(ctx, []*schema.Message{
{
Role: schema.User,
UserInputMultiContent: []schema.MessageInputPart{
{
Type: schema.ChatMessagePartTypeText,
Text: "Generate an image of a cat",
},
},
},
})
if err != nil {
log.Fatalf("Generate error: %v", err)
}
log.Printf("\ngenerate output: \n")
respBody, _ := json.MarshalIndent(resp, " ", " ")
log.Printf(" body: %s\n", string(respBody))
}
React Agent Mode Example
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/bytedance/sonic"
"github.com/cloudwego/eino/adk"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/components/tool/utils"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
"google.golang.org/genai"
"github.com/cloudwego/eino-ext/components/model/gemini"
)
func main() {
ctx := context.Background()
client, err := genai.NewClient(ctx, &genai.ClientConfig{
APIKey: os.Getenv("GEMINI_API_KEY"),
})
if err != nil {
log.Fatalf("genai.NewClient failed, err=%v", err)
}
cm, err := gemini.NewChatModel(ctx, &gemini.Config{
Model: os.Getenv("GEMINI_MODEL"),
Client: client,
})
if err != nil {
log.Fatalf("gemini.NewChatModel failed, err=%v", err)
}
type toolCallInput struct {
LastCount int `json:"last_count" jsonschema_description:"the last count"`
}
countsTool, err := utils.InferTool("count_tool_call",
"count the number of tool calls",
func(ctx context.Context, in *toolCallInput) (string, error) {
counts := in.LastCount + 1
return fmt.Sprintf("tool call counts: %v", counts), nil
})
if err != nil {
log.Fatalf("utils.InferTool failed, err=%v", err)
}
agent, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Name: "react_agent",
Description: "react_agent",
Instruction: `call count_tool_call 5 times, then say 'done'`,
Model: cm,
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{
countsTool,
},
},
},
})
if err != nil {
log.Fatalf("adk.NewChatModelAgent failed, err=%v", err)
}
iter := agent.Run(ctx, &adk.AgentInput{
Messages: []adk.Message{
{
Role: schema.User,
Content: "start to count",
},
},
})
idx := 0
for {
event, ok := iter.Next()
if !ok {
break
}
if event.Err != nil {
log.Fatalf("agent.Run failed, err=%v", event.Err)
}
msg, err_ := event.Output.MessageOutput.GetMessage()
if err_ != nil {
log.Fatalf("GetMessage failed, err=%v", err_)
}
idx++
msgData, _ := sonic.MarshalIndent(msg, "", " ")
log.Printf("\nmessage %v:\n%v\n", idx, string(msgData))
}
}
More Examples
Related Documentation
Eino: ChatModel Guideat/docs/eino/core_modules/components/chat_model_guideEino: ToolsNode & Tool Guideat/docs/eino/core_modules/components/tools_node_guideChatModel - ARKat/docs/eino/ecosystem_integration/chat_model/chat_model_arkChatModel - Ollamaat/docs/eino/ecosystem_integration/chat_model/chat_model_ollama
Last modified
December 16, 2025
: fix: improve readability of websocket and swagger docs (#1480) (f63ff55)