gemini.rs

  1use std::rc::Rc;
  2use std::{any::Any, path::Path};
  3
  4use crate::{AgentServer, AgentServerDelegate};
  5use acp_thread::AgentConnection;
  6use anyhow::{Context as _, Result};
  7use client::ProxySettings;
  8use collections::HashMap;
  9use gpui::{App, AppContext, SharedString, Task};
 10use language_models::provider::google::GoogleLanguageModelProvider;
 11use project::agent_server_store::GEMINI_NAME;
 12use settings::SettingsStore;
 13
 14#[derive(Clone)]
 15pub struct Gemini;
 16
 17impl AgentServer for Gemini {
 18    fn telemetry_id(&self) -> &'static str {
 19        "gemini-cli"
 20    }
 21
 22    fn name(&self) -> SharedString {
 23        "Gemini CLI".into()
 24    }
 25
 26    fn logo(&self) -> ui::IconName {
 27        ui::IconName::AiGemini
 28    }
 29
 30    fn connect(
 31        &self,
 32        root_dir: Option<&Path>,
 33        delegate: AgentServerDelegate,
 34        cx: &mut App,
 35    ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
 36        let name = self.name();
 37        let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string());
 38        let is_remote = delegate.project.read(cx).is_via_remote_server();
 39        let store = delegate.store.downgrade();
 40        let proxy_url = cx.read_global(|settings: &SettingsStore, _| {
 41            settings.get::<ProxySettings>(None).proxy.clone()
 42        });
 43
 44        cx.spawn(async move |cx| {
 45            let mut extra_env = HashMap::default();
 46            if let Some(api_key) = cx.update(GoogleLanguageModelProvider::api_key)?.await.ok() {
 47                extra_env.insert("GEMINI_API_KEY".into(), api_key.key);
 48            }
 49            let (mut command, root_dir, login) = store
 50                .update(cx, |store, cx| {
 51                    let agent = store
 52                        .get_external_agent(&GEMINI_NAME.into())
 53                        .context("Gemini CLI is not registered")?;
 54                    anyhow::Ok(agent.get_command(
 55                        root_dir.as_deref(),
 56                        extra_env,
 57                        delegate.status_tx,
 58                        delegate.new_version_available,
 59                        &mut cx.to_async(),
 60                    ))
 61                })??
 62                .await?;
 63
 64            // Add proxy flag if proxy settings are configured in Zed and not in the args
 65            if let Some(proxy_url_value) = &proxy_url
 66                && !command.args.iter().any(|arg| arg.contains("--proxy"))
 67            {
 68                command.args.push("--proxy".into());
 69                command.args.push(proxy_url_value.clone());
 70            }
 71
 72            let connection =
 73                crate::acp::connect(name, command, root_dir.as_ref(), is_remote, cx).await?;
 74            Ok((connection, login))
 75        })
 76    }
 77
 78    fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
 79        self
 80    }
 81}
 82
 83#[cfg(test)]
 84pub(crate) mod tests {
 85    use project::agent_server_store::AgentServerCommand;
 86
 87    use super::*;
 88    use std::path::Path;
 89
 90    crate::common_e2e_tests!(async |_, _, _| Gemini, allow_option_id = "proceed_once");
 91
 92    pub fn local_command() -> AgentServerCommand {
 93        let cli_path = Path::new(env!("CARGO_MANIFEST_DIR"))
 94            .join("../../../gemini-cli/packages/cli")
 95            .to_string_lossy()
 96            .to_string();
 97
 98        AgentServerCommand {
 99            path: "node".into(),
100            args: vec![cli_path],
101            env: None,
102        }
103    }
104}