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