backend: add gemini backend (Google AI Studio / OpenAI-compat)

This commit is contained in:
Levi Neely 2026-05-21 17:04:52 +00:00
parent 47f8eed119
commit 2ef2dd4290
2 changed files with 41 additions and 3 deletions

29
pkg/backend/gemini.go Normal file
View File

@ -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")
}

View File

@ -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: