diff --git a/pkg/backend/gemini.go b/pkg/backend/gemini.go new file mode 100644 index 0000000..b6ba724 --- /dev/null +++ b/pkg/backend/gemini.go @@ -0,0 +1,29 @@ +package backend + +import "os" + +const ( + geminiBaseURL = "https://generativelanguage.googleapis.com/v1beta/openai/" + geminiDefaultModel = "gemini-2.5-flash-preview-05-20" +) + +// NewGemini constructs an OpenAI-compatible backend pointed at Google AI Studio. +// apiKey is the Google AI Studio key (OLLIE_GEMINI_KEY). +// If model is empty the default model is used. +func NewGemini(apiKey string) (*OpenAIBackend, error) { + b, err := NewOpenAI("gemini", geminiBaseURL, apiKey) + if err != nil { + return nil, err + } + b.model = geminiDefaultModel + return b, nil +} + +// geminiKey returns the API key from OLLIE_GEMINI_KEY, falling back to +// OLLIE_OPENAI_KEY so existing env files that set the generic key still work. +func geminiKey() string { + if k := os.Getenv("OLLIE_GEMINI_KEY"); k != "" { + return k + } + return os.Getenv("OLLIE_OPENAI_KEY") +} diff --git a/pkg/backend/new.go b/pkg/backend/new.go index d2fb7ab..937a705 100644 --- a/pkg/backend/new.go +++ b/pkg/backend/new.go @@ -45,11 +45,12 @@ func loadEnvFile(path string) { // so the file acts as a default; variables already set in the environment take // precedence. // -// OLLIE_BACKEND ollama | openai | openrouter | anthropic | copilot | kiro (default: ollama) +// OLLIE_BACKEND ollama | openai | openrouter | anthropic | copilot | kiro | gemini // OLLIE_OLLAMA_URL base URL for ollama (default: http://localhost:11434) // OLLIE_OPENAI_URL base URL for openai-compatible backends // OLLIE_OPENAI_KEY API key (required for openai/openrouter) // OLLIE_ANTHROPIC_KEY API key (required for anthropic) +// OLLIE_GEMINI_KEY Google AI Studio API key (required for gemini; falls back to OLLIE_OPENAI_KEY) // OLLIE_COPILOT_TOKEN bearer token (required for copilot) // OLLIE_KIRO_TOKEN bearer token or sqlite:// URL for kiro/codewhisperer // (default: sqlite path auto-detected from Kiro CLI data dir) @@ -104,14 +105,20 @@ func newBackend(which string) (Backend, error) { return NewCopilot(token) case "kiro", "codewhisperer": return NewCodeWhisperer(os.Getenv("OLLIE_KIRO_TOKEN")) + case "gemini": + key := geminiKey() + if key == "" { + return nil, fmt.Errorf("OLLIE_GEMINI_KEY is required for gemini backend") + } + return NewGemini(key) default: - return nil, fmt.Errorf("unknown OLLIE_BACKEND %q (supported: ollama, openai, openrouter, anthropic, copilot, kiro)", which) + return nil, fmt.Errorf("unknown OLLIE_BACKEND %q (supported: ollama, openai, openrouter, anthropic, copilot, kiro, gemini)", which) } } // Backends returns the list of supported backend names. func Backends() []string { - return []string{"ollama", "openai", "openrouter", "anthropic", "copilot", "kiro"} + return []string{"ollama", "openai", "openrouter", "anthropic", "copilot", "kiro", "gemini"} } // openAIName derives a short backend label from the OLLIE_BACKEND value and @@ -129,6 +136,8 @@ func openAIName(which, url string) string { return "mistral" case strings.Contains(url, "anthropic"): return "anthropic" + case strings.Contains(url, "generativelanguage") || strings.Contains(url, "google"): + return "gemini" case strings.Contains(url, "localhost") || strings.Contains(url, "127.0.0.1"): return "local" default: