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        let default_mode = self.default_mode(cx);
 44
 45        cx.spawn(async move |cx| {
 46            let mut extra_env = HashMap::default();
 47            extra_env.insert("SURFACE".to_owned(), "zed".to_owned());
 48            if let Some(api_key) = cx
 49                .update(GoogleLanguageModelProvider::api_key_for_gemini_cli)?
 50                .await
 51                .ok()
 52            {
 53                extra_env.insert("GEMINI_API_KEY".into(), api_key);
 54            }
 55            let (mut command, root_dir, login) = store
 56                .update(cx, |store, cx| {
 57                    let agent = store
 58                        .get_external_agent(&GEMINI_NAME.into())
 59                        .context("Gemini CLI is not registered")?;
 60                    anyhow::Ok(agent.get_command(
 61                        root_dir.as_deref(),
 62                        extra_env,
 63                        delegate.status_tx,
 64                        delegate.new_version_available,
 65                        &mut cx.to_async(),
 66                    ))
 67                })??
 68                .await?;
 69
 70            // Add proxy flag if proxy settings are configured in Zed and not in the args
 71            if let Some(proxy_url_value) = &proxy_url
 72                && !command.args.iter().any(|arg| arg.contains("--proxy"))
 73            {
 74                command.args.push("--proxy".into());
 75                command.args.push(proxy_url_value.clone());
 76            }
 77
 78            let connection = crate::acp::connect(
 79                name,
 80                command,
 81                root_dir.as_ref(),
 82                default_mode,
 83                is_remote,
 84                cx,
 85            )
 86            .await?;
 87            Ok((connection, login))
 88        })
 89    }
 90
 91    fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
 92        self
 93    }
 94}
 95
 96#[cfg(test)]
 97pub(crate) mod tests {
 98    use project::agent_server_store::AgentServerCommand;
 99
100    use super::*;
101    use std::path::Path;
102
103    crate::common_e2e_tests!(async |_, _, _| Gemini, allow_option_id = "proceed_once");
104
105    pub fn local_command() -> AgentServerCommand {
106        let cli_path = Path::new(env!("CARGO_MANIFEST_DIR"))
107            .join("../../../gemini-cli/packages/cli")
108            .to_string_lossy()
109            .to_string();
110
111        AgentServerCommand {
112            path: "node".into(),
113            args: vec![cli_path],
114            env: None,
115        }
116    }
117}