feat: Gemini OAuth provider (#8129)

Signed-off-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
Michael Neale
2026-03-27 08:08:51 +11:00
committed by GitHub
parent f8c491ee95
commit 38f11d84db
8 changed files with 1116 additions and 104 deletions
@@ -46,6 +46,7 @@ fn map_provider_name(provider: &str) -> &str {
"azure_openai" => "azure",
"aws_bedrock" => "amazon-bedrock",
"gcp_vertex_ai" => "google-vertex",
"gemini_oauth" => "google",
_ => provider,
}
}
-87
View File
@@ -1,87 +0,0 @@
use anyhow::Result;
use futures::future::BoxFuture;
use std::collections::HashMap;
use std::path::PathBuf;
use crate::acp::{
extension_configs_to_mcp_servers, AcpProvider, AcpProviderConfig, PermissionMapping,
ACP_CURRENT_MODEL,
};
use crate::config::search_path::SearchPaths;
use crate::config::{Config, GooseMode};
use crate::model::ModelConfig;
use crate::providers::base::{ProviderDef, ProviderMetadata};
const GEMINI_ACP_PROVIDER_NAME: &str = "gemini-acp";
const GEMINI_ACP_DOC_URL: &str = "https://github.com/google-gemini/gemini-cli";
pub struct GeminiAcpProvider;
impl ProviderDef for GeminiAcpProvider {
type Provider = AcpProvider;
fn metadata() -> ProviderMetadata {
ProviderMetadata::new(
GEMINI_ACP_PROVIDER_NAME,
"Gemini CLI (ACP)",
"Use goose with your Google Gemini subscription via the Gemini CLI.",
ACP_CURRENT_MODEL,
vec![],
GEMINI_ACP_DOC_URL,
vec![],
)
.with_setup_steps(vec![
"Install the Gemini CLI: `npm install -g @google/gemini-cli`",
"Run `gemini` once to authenticate with your Google account",
"Set in your goose config file (`~/.config/goose/config.yaml` on macOS/Linux):\n GOOSE_PROVIDER: gemini-acp\n GOOSE_MODEL: current",
"Restart goose for changes to take effect",
])
}
fn from_env(
model: ModelConfig,
extensions: Vec<crate::config::ExtensionConfig>,
) -> BoxFuture<'static, Result<AcpProvider>> {
Box::pin(async move {
let config = Config::global();
let command_name: String = config.get_gemini_cli_command().unwrap_or_default().into();
let resolved_command = SearchPaths::builder().with_npm().resolve(&command_name)?;
let goose_mode = config.get_goose_mode().unwrap_or(GooseMode::Auto);
let permission_mapping = PermissionMapping {
allow_option_id: Some("allow".to_string()),
reject_option_id: Some("reject".to_string()),
rejected_tool_status: sacp::schema::ToolCallStatus::Failed,
};
let mut args = vec!["--acp".to_string()];
if model.model_name != ACP_CURRENT_MODEL {
args.push("--model".to_string());
args.push(model.model_name.clone());
}
let mode_mapping = HashMap::from([
(GooseMode::Auto, "yolo".to_string()),
(GooseMode::Approve, "default".to_string()),
(GooseMode::SmartApprove, "auto_edit".to_string()),
(GooseMode::Chat, "plan".to_string()),
]);
let provider_config = AcpProviderConfig {
command: resolved_command,
args,
env: vec![],
env_remove: vec![],
work_dir: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")),
mcp_servers: extension_configs_to_mcp_servers(&extensions),
session_mode_id: Some(mode_mapping[&goose_mode].clone()),
mode_mapping,
permission_mapping,
notification_callback: None,
};
let metadata = Self::metadata();
AcpProvider::connect(metadata.name, model, goose_mode, provider_config).await
})
}
}
+1 -1
View File
@@ -161,7 +161,7 @@ impl ProviderDef for GeminiCliProvider {
ProviderMetadata::new(
GEMINI_CLI_PROVIDER_NAME,
"Gemini CLI",
"[Deprecated: use gemini-acp instead] Execute Gemini models via gemini CLI tool. Requires gemini CLI installed.",
"[Deprecated: use gemini_oauth instead] Execute Gemini models via gemini CLI tool. Requires gemini CLI installed.",
GEMINI_CLI_DEFAULT_MODEL,
GEMINI_CLI_KNOWN_MODELS.to_vec(),
GEMINI_CLI_DOC_URL,
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -111,7 +111,7 @@ impl ProviderDef for GoogleProvider {
fn metadata() -> ProviderMetadata {
ProviderMetadata::new(
GOOGLE_PROVIDER_NAME,
"Google Gemini",
"Google Gemini (API Key)",
"Gemini models from Google AI",
GOOGLE_DEFAULT_MODEL,
GOOGLE_KNOWN_MODELS.to_vec(),
+2 -2
View File
@@ -19,8 +19,8 @@ use super::{
cursor_agent::CursorAgentProvider,
databricks::DatabricksProvider,
gcpvertexai::GcpVertexAIProvider,
gemini_acp::GeminiAcpProvider,
gemini_cli::GeminiCliProvider,
gemini_oauth::GeminiOAuthProvider,
githubcopilot::GithubCopilotProvider,
google::GoogleProvider,
litellm::LiteLLMProvider,
@@ -57,7 +57,6 @@ async fn init_registry() -> RwLock<ProviderRegistry> {
registry.register::<LocalInferenceProvider>(false);
registry.register::<ChatGptCodexProvider>(true);
registry.register::<ClaudeAcpProvider>(false);
registry.register::<GeminiAcpProvider>(false);
registry.register::<ClaudeCodeProvider>(true);
registry.register::<CodexAcpProvider>(false);
registry.register::<CodexProvider>(true);
@@ -65,6 +64,7 @@ async fn init_registry() -> RwLock<ProviderRegistry> {
registry.register::<DatabricksProvider>(true);
registry.register::<GcpVertexAIProvider>(false);
registry.register::<GeminiCliProvider>(false);
registry.register::<GeminiOAuthProvider>(true);
registry.register::<GithubCopilotProvider>(false);
registry.register::<GoogleProvider>(true);
registry.register::<LiteLLMProvider>(false);
+1 -1
View File
@@ -21,8 +21,8 @@ pub mod errors;
pub mod formats;
mod gcpauth;
pub mod gcpvertexai;
pub mod gemini_acp;
pub mod gemini_cli;
pub mod gemini_oauth;
pub mod githubcopilot;
pub mod google;
mod init;
-12
View File
@@ -892,18 +892,6 @@ async fn test_codex_acp_provider() -> Result<()> {
.await
}
// Requires: npm install -g @google/gemini-cli
#[tokio::test]
async fn test_gemini_acp_provider() -> Result<()> {
// Don't run tests with ACP_CURRENT_MODEL, as gemini sets "auto-gemini-3" even when the user
// has no access to the Preview Release Channel, resulting in "Requested entity was not found."
// See https://github.com/google-gemini/gemini-cli/issues/22803
ProviderTestConfig::with_agentic_provider("gemini-acp", "auto-gemini-2.5", "gemini")
.model_switch_name("gemini-2.5-flash")
.run()
.await
}
#[ctor::dtor]
fn print_test_report() {
TEST_REPORT.print_summary();