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