Files
gnoma/internal/provider/google/provider.go
vikingowl d26b07c509 feat: add Google GenAI provider adapter
Streaming via goroutine+channel bridge (range-based iter.Seq2 → pull
iterator). Tool use with FunctionCall/FunctionResponse, tool name
sanitization, tool name map for FunctionResponse correlation.
Stop reason override (Google uses STOP for function calls).

Hardcoded model list (gemini-2.5-pro/flash, gemini-2.0-flash).
Wired into CLI with GOOGLE_API_KEY + GEMINI_API_KEY env support.

Live verified: text streaming + tool calling with gemini-2.5-flash.
Four providers now live: Mistral, Anthropic, OpenAI, Google.
2026-04-03 13:42:29 +02:00

95 lines
2.3 KiB
Go

package google
import (
"context"
"fmt"
"somegit.dev/Owlibou/gnoma/internal/provider"
"somegit.dev/Owlibou/gnoma/internal/stream"
"google.golang.org/genai"
)
const defaultModel = "gemini-2.5-flash"
// Provider implements provider.Provider for Google's Gemini API.
type Provider struct {
client *genai.Client
name string
model string
}
// New creates a Google GenAI provider from config.
func New(cfg provider.ProviderConfig) (provider.Provider, error) {
if cfg.APIKey == "" {
return nil, fmt.Errorf("google: api key required")
}
client, err := genai.NewClient(context.Background(), &genai.ClientConfig{
APIKey: cfg.APIKey,
Backend: genai.BackendGeminiAPI,
})
if err != nil {
return nil, fmt.Errorf("google: create client: %w", err)
}
model := cfg.Model
if model == "" {
model = defaultModel
}
return &Provider{
client: client,
name: "google",
model: model,
}, nil
}
// Stream initiates a streaming content generation request.
func (p *Provider) Stream(ctx context.Context, req provider.Request) (stream.Stream, error) {
model := req.Model
if model == "" {
model = p.model
}
contents := translateContents(req.Messages)
config := translateConfig(req)
iter := p.client.Models.GenerateContentStream(ctx, model, contents, config)
return newGoogleStream(ctx, iter, model), nil
}
// Name returns "google".
func (p *Provider) Name() string { return p.name }
// DefaultModel returns the configured default model.
func (p *Provider) DefaultModel() string { return p.model }
// Models returns known Google models with capabilities.
func (p *Provider) Models(_ context.Context) ([]provider.ModelInfo, error) {
return []provider.ModelInfo{
{
ID: "gemini-2.5-pro", Name: "Gemini 2.5 Pro", Provider: p.name,
Capabilities: provider.Capabilities{
ToolUse: true, JSONOutput: true, Thinking: true, Vision: true,
ContextWindow: 1048576, MaxOutput: 65536,
},
},
{
ID: "gemini-2.5-flash", Name: "Gemini 2.5 Flash", Provider: p.name,
Capabilities: provider.Capabilities{
ToolUse: true, JSONOutput: true, Thinking: true, Vision: true,
ContextWindow: 1048576, MaxOutput: 65536,
},
},
{
ID: "gemini-2.0-flash", Name: "Gemini 2.0 Flash", Provider: p.name,
Capabilities: provider.Capabilities{
ToolUse: true, JSONOutput: true, Vision: true,
ContextWindow: 1048576, MaxOutput: 8192,
},
},
}, nil
}