diff --git a/metrics/apikey.go b/metrics/apikey.go index f57e4fa..6dc6a12 100644 --- a/metrics/apikey.go +++ b/metrics/apikey.go @@ -39,6 +39,13 @@ var providerAPIKeyEnv = map[string][]string{ "openai-compat": {"OPENAI_COMPAT_API_KEY", "OPENAI_API_KEY"}, "anthropic": {"ANTHROPIC_API_KEY"}, "gemini": {"GOOGLE_API_KEY"}, + // "google" is the provider name agent manifests commonly use for + // Gemini models; the runner aliases it to "gemini" (see + // runner.normalizeProvider). Listing it here keeps credential + // detection consistent — without this entry an unknown provider is + // treated as "no key needed" and fallback selection picks it even + // when GOOGLE_API_KEY is absent. + "google": {"GOOGLE_API_KEY"}, } // KeyStatus mirrors provider token precedence used by the runner: an diff --git a/metrics/apikey_test.go b/metrics/apikey_test.go index 6c59e23..953dce2 100644 --- a/metrics/apikey_test.go +++ b/metrics/apikey_test.go @@ -13,6 +13,7 @@ func TestKeyStatusProviderEnvTable(t *testing.T) { {"openai-compat", "OPENAI_COMPAT_API_KEY"}, {"anthropic", "ANTHROPIC_API_KEY"}, {"gemini", "GOOGLE_API_KEY"}, + {"google", "GOOGLE_API_KEY"}, } for _, tc := range cases { t.Run(tc.provider, func(t *testing.T) { diff --git a/runner/model.go b/runner/model.go index 35c41f0..0d956d4 100644 --- a/runner/model.go +++ b/runner/model.go @@ -544,7 +544,14 @@ func buildGeminiLLM(ctx context.Context, opts *RunOptions, model string) (llms.M } func normalizeProvider(provider string) string { - return strings.ToLower(strings.TrimSpace(provider)) + p := strings.ToLower(strings.TrimSpace(provider)) + // Agent manifests commonly write "google" for Gemini models; the + // implemented provider is "gemini" (metrics/apikey.go carries the + // matching credential entry for both spellings). + if p == "google" { + return "gemini" + } + return p } func buildNativeOllamaLLM(opts *RunOptions, model string) llms.Model { diff --git a/runner/model_provider_test.go b/runner/model_provider_test.go new file mode 100644 index 0000000..d2dc66d --- /dev/null +++ b/runner/model_provider_test.go @@ -0,0 +1,25 @@ +package runner + +import "testing" + +// TestNormalizeProviderGoogleAlias pins the manifest-facing "google" spelling +// to the implemented "gemini" provider so buildLLM and telemetry treat them +// identically. Without the alias, fallback selection could pick a +// google-provider manifest entry and then fail at buildLLM with +// "provider not implemented: google". +func TestNormalizeProviderGoogleAlias(t *testing.T) { + t.Parallel() + cases := map[string]string{ + "google": "gemini", + " Google ": "gemini", + "gemini": "gemini", + "anthropic": "anthropic", + "": "", + " OpenAI ": "openai", + } + for in, want := range cases { + if got := normalizeProvider(in); got != want { + t.Errorf("normalizeProvider(%q) = %q, want %q", in, got, want) + } + } +}