diff --git a/Cargo.lock b/Cargo.lock index 829ed18bbb..b77ff20af3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,7 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", + "client", "collections", "ctor", "editor", @@ -127,6 +128,7 @@ dependencies = [ "theme", "tiktoken-rs 0.4.5", "util", + "uuid 1.4.1", "workspace", ] diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index d96e470d5c..8002b0d35d 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -9,6 +9,7 @@ path = "src/ai.rs" doctest = false [dependencies] +client = { path = "../client" } collections = { path = "../collections"} editor = { path = "../editor" } fs = { path = "../fs" } @@ -19,6 +20,7 @@ search = { path = "../search" } settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } +uuid = { version = "1.1.2", features = ["v4"] } workspace = { path = "../workspace" } anyhow.workspace = true diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs index 7d9b93b0a7..dfd9a523b4 100644 --- a/crates/ai/src/ai.rs +++ b/crates/ai/src/ai.rs @@ -61,6 +61,7 @@ struct SavedMessage { #[derive(Serialize, Deserialize)] struct SavedConversation { + id: Option, zed: String, version: String, text: String, diff --git a/crates/ai/src/assistant.rs b/crates/ai/src/assistant.rs index 7fa66f26fb..263382c03e 100644 --- a/crates/ai/src/assistant.rs +++ b/crates/ai/src/assistant.rs @@ -6,6 +6,7 @@ use crate::{ }; use anyhow::{anyhow, Result}; use chrono::{DateTime, Local}; +use client::{telemetry::AssistantKind, ClickhouseEvent, TelemetrySettings}; use collections::{hash_map, HashMap, HashSet, VecDeque}; use editor::{ display_map::{ @@ -48,6 +49,7 @@ use theme::{ AssistantStyle, }; use util::{paths::CONVERSATIONS_DIR, post_inc, ResultExt, TryFutureExt}; +use uuid::Uuid; use workspace::{ dock::{DockPosition, Panel}, searchable::Direction, @@ -296,6 +298,7 @@ impl AssistantPanel { self.include_conversation_in_next_inline_assist, self.inline_prompt_history.clone(), codegen.clone(), + self.workspace.clone(), cx, ); cx.focus_self(); @@ -724,6 +727,7 @@ impl AssistantPanel { self.api_key.clone(), self.languages.clone(), self.fs.clone(), + self.workspace.clone(), cx, ) }); @@ -1059,6 +1063,7 @@ impl AssistantPanel { } let fs = self.fs.clone(); + let workspace = self.workspace.clone(); let api_key = self.api_key.clone(); let languages = self.languages.clone(); cx.spawn(|this, mut cx| async move { @@ -1073,8 +1078,9 @@ impl AssistantPanel { if let Some(ix) = this.editor_index_for_path(&path, cx) { this.set_active_editor_index(Some(ix), cx); } else { - let editor = cx - .add_view(|cx| ConversationEditor::for_conversation(conversation, fs, cx)); + let editor = cx.add_view(|cx| { + ConversationEditor::for_conversation(conversation, fs, workspace, cx) + }); this.add_conversation(editor, cx); } })?; @@ -1348,6 +1354,7 @@ struct Summary { } struct Conversation { + id: Option, buffer: ModelHandle, message_anchors: Vec, messages_metadata: HashMap, @@ -1398,6 +1405,7 @@ impl Conversation { let model = settings.default_open_ai_model.clone(); let mut this = Self { + id: Some(Uuid::new_v4().to_string()), message_anchors: Default::default(), messages_metadata: Default::default(), next_message_id: Default::default(), @@ -1435,6 +1443,7 @@ impl Conversation { fn serialize(&self, cx: &AppContext) -> SavedConversation { SavedConversation { + id: self.id.clone(), zed: "conversation".into(), version: SavedConversation::VERSION.into(), text: self.buffer.read(cx).text(), @@ -1462,6 +1471,10 @@ impl Conversation { language_registry: Arc, cx: &mut ModelContext, ) -> Self { + let id = match saved_conversation.id { + Some(id) => Some(id), + None => Some(Uuid::new_v4().to_string()), + }; let model = saved_conversation.model; let markdown = language_registry.language_for_name("Markdown"); let mut message_anchors = Vec::new(); @@ -1491,6 +1504,7 @@ impl Conversation { }); let mut this = Self { + id, message_anchors, messages_metadata: saved_conversation.message_metadata, next_message_id, @@ -2108,6 +2122,7 @@ struct ScrollPosition { struct ConversationEditor { conversation: ModelHandle, fs: Arc, + workspace: WeakViewHandle, editor: ViewHandle, blocks: HashSet, scroll_position: Option, @@ -2119,15 +2134,17 @@ impl ConversationEditor { api_key: Rc>>, language_registry: Arc, fs: Arc, + workspace: WeakViewHandle, cx: &mut ViewContext, ) -> Self { let conversation = cx.add_model(|cx| Conversation::new(api_key, language_registry, cx)); - Self::for_conversation(conversation, fs, cx) + Self::for_conversation(conversation, fs, workspace, cx) } fn for_conversation( conversation: ModelHandle, fs: Arc, + workspace: WeakViewHandle, cx: &mut ViewContext, ) -> Self { let editor = cx.add_view(|cx| { @@ -2150,6 +2167,7 @@ impl ConversationEditor { blocks: Default::default(), scroll_position: None, fs, + workspace, _subscriptions, }; this.update_message_headers(cx); @@ -2157,6 +2175,13 @@ impl ConversationEditor { } fn assist(&mut self, _: &Assist, cx: &mut ViewContext) { + report_assistant_event( + self.workspace.clone(), + self.conversation.read(cx).id.clone(), + AssistantKind::Panel, + cx, + ); + let cursors = self.cursors(cx); let user_messages = self.conversation.update(cx, |conversation, cx| { @@ -2665,6 +2690,7 @@ enum InlineAssistantEvent { struct InlineAssistant { id: usize, prompt_editor: ViewHandle, + workspace: WeakViewHandle, confirmed: bool, has_focus: bool, include_conversation: bool, @@ -2780,6 +2806,7 @@ impl InlineAssistant { include_conversation: bool, prompt_history: VecDeque, codegen: ModelHandle, + workspace: WeakViewHandle, cx: &mut ViewContext, ) -> Self { let prompt_editor = cx.add_view(|cx| { @@ -2801,6 +2828,7 @@ impl InlineAssistant { Self { id, prompt_editor, + workspace, confirmed: false, has_focus: false, include_conversation, @@ -2859,6 +2887,8 @@ impl InlineAssistant { if self.confirmed { cx.emit(InlineAssistantEvent::Dismissed); } else { + report_assistant_event(self.workspace.clone(), None, AssistantKind::Inline, cx); + let prompt = self.prompt_editor.read(cx).text(cx); self.prompt_editor.update(cx, |editor, cx| { editor.set_read_only(true); @@ -3347,3 +3377,30 @@ mod tests { .collect() } } + +fn report_assistant_event( + workspace: WeakViewHandle, + conversation_id: Option, + assistant_kind: AssistantKind, + cx: &AppContext, +) { + let Some(workspace) = workspace.upgrade(cx) else { + return; + }; + + let client = workspace.read(cx).project().read(cx).client(); + let telemetry = client.telemetry(); + + let model = settings::get::(cx) + .default_open_ai_model + .clone(); + + let event = ClickhouseEvent::Assistant { + conversation_id, + kind: assistant_kind, + model: model.full_name(), + }; + let telemetry_settings = *settings::get::(cx); + + telemetry.report_clickhouse_event(event, telemetry_settings) +} diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index f8642dd7fa..1596e6d850 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -56,6 +56,13 @@ struct ClickhouseEventWrapper { event: ClickhouseEvent, } +#[derive(Serialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum AssistantKind { + Panel, + Inline, +} + #[derive(Serialize, Debug)] #[serde(tag = "type")] pub enum ClickhouseEvent { @@ -76,6 +83,11 @@ pub enum ClickhouseEvent { room_id: Option, channel_id: Option, }, + Assistant { + conversation_id: Option, + kind: AssistantKind, + model: &'static str, + }, } #[cfg(debug_assertions)] diff --git a/crates/theme/src/components.rs b/crates/theme/src/components.rs index 9011821b77..8c5bf21ef9 100644 --- a/crates/theme/src/components.rs +++ b/crates/theme/src/components.rs @@ -26,7 +26,6 @@ impl ComponentExt for C { } pub mod disclosure { - use gpui::{ elements::{Component, ContainerStyle, Empty, Flex, ParentElement, SafeStylable}, Action, Element,