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