Detailed changes
@@ -15,9 +15,9 @@ use std::io::Write;
use std::{env, mem, path::PathBuf, sync::Arc, time::Duration};
use sysinfo::{CpuRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
use telemetry_events::{
- ActionEvent, AppEvent, AssistantEvent, AssistantKind, CallEvent, CopilotEvent, CpuEvent,
- EditEvent, EditorEvent, Event, EventRequestBody, EventWrapper, ExtensionEvent, MemoryEvent,
- SettingEvent,
+ ActionEvent, AppEvent, AssistantEvent, AssistantKind, CallEvent, CpuEvent, EditEvent,
+ EditorEvent, Event, EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent,
+ MemoryEvent, SettingEvent,
};
use tempfile::NamedTempFile;
#[cfg(not(debug_assertions))]
@@ -241,14 +241,14 @@ impl Telemetry {
self.report_event(event)
}
- pub fn report_copilot_event(
+ pub fn report_inline_completion_event(
self: &Arc<Self>,
- suggestion_id: Option<String>,
+ provider: String,
suggestion_accepted: bool,
file_extension: Option<String>,
) {
- let event = Event::Copilot(CopilotEvent {
- suggestion_id,
+ let event = Event::InlineCompletion(InlineCompletionEvent {
+ provider,
suggestion_accepted,
file_extension,
});
@@ -15,8 +15,9 @@ use serde::{Serialize, Serializer};
use sha2::{Digest, Sha256};
use std::sync::{Arc, OnceLock};
use telemetry_events::{
- ActionEvent, AppEvent, AssistantEvent, CallEvent, CopilotEvent, CpuEvent, EditEvent,
- EditorEvent, Event, EventRequestBody, EventWrapper, ExtensionEvent, MemoryEvent, SettingEvent,
+ ActionEvent, AppEvent, AssistantEvent, CallEvent, CpuEvent, EditEvent, EditorEvent, Event,
+ EventRequestBody, EventWrapper, ExtensionEvent, InlineCompletionEvent, MemoryEvent,
+ SettingEvent,
};
use uuid::Uuid;
@@ -424,13 +425,19 @@ pub async fn post_events(
first_event_at,
country_code.clone(),
)),
- Event::Copilot(event) => to_upload.copilot_events.push(CopilotEventRow::from_event(
- event.clone(),
- &wrapper,
- &request_body,
- first_event_at,
- country_code.clone(),
- )),
+ // Needed for clients sending old copilot_event types
+ Event::Copilot(_) => {}
+ Event::InlineCompletion(event) => {
+ to_upload
+ .inline_completion_events
+ .push(InlineCompletionEventRow::from_event(
+ event.clone(),
+ &wrapper,
+ &request_body,
+ first_event_at,
+ country_code.clone(),
+ ))
+ }
Event::Call(event) => to_upload.call_events.push(CallEventRow::from_event(
event.clone(),
&wrapper,
@@ -512,7 +519,7 @@ pub async fn post_events(
#[derive(Default)]
struct ToUpload {
editor_events: Vec<EditorEventRow>,
- copilot_events: Vec<CopilotEventRow>,
+ inline_completion_events: Vec<InlineCompletionEventRow>,
assistant_events: Vec<AssistantEventRow>,
call_events: Vec<CallEventRow>,
cpu_events: Vec<CpuEventRow>,
@@ -531,14 +538,14 @@ impl ToUpload {
.await
.with_context(|| format!("failed to upload to table '{EDITOR_EVENTS_TABLE}'"))?;
- const COPILOT_EVENTS_TABLE: &str = "copilot_events";
+ const INLINE_COMPLETION_EVENTS_TABLE: &str = "inline_completion_events";
Self::upload_to_table(
- COPILOT_EVENTS_TABLE,
- &self.copilot_events,
+ INLINE_COMPLETION_EVENTS_TABLE,
+ &self.inline_completion_events,
clickhouse_client,
)
.await
- .with_context(|| format!("failed to upload to table '{COPILOT_EVENTS_TABLE}'"))?;
+ .with_context(|| format!("failed to upload to table '{INLINE_COMPLETION_EVENTS_TABLE}'"))?;
const ASSISTANT_EVENTS_TABLE: &str = "assistant_events";
Self::upload_to_table(
@@ -708,9 +715,9 @@ impl EditorEventRow {
}
#[derive(Serialize, Debug, clickhouse::Row)]
-pub struct CopilotEventRow {
+pub struct InlineCompletionEventRow {
pub installation_id: String,
- pub suggestion_id: String,
+ pub provider: String,
pub suggestion_accepted: bool,
pub app_version: String,
pub file_extension: String,
@@ -730,9 +737,9 @@ pub struct CopilotEventRow {
pub patch: Option<i32>,
}
-impl CopilotEventRow {
+impl InlineCompletionEventRow {
fn from_event(
- event: CopilotEvent,
+ event: InlineCompletionEvent,
wrapper: &EventWrapper,
body: &EventRequestBody,
first_event_at: chrono::DateTime<chrono::Utc>,
@@ -759,7 +766,7 @@ impl CopilotEventRow {
country_code: country_code.unwrap_or("XX".to_string()),
region_code: "".to_string(),
city: "".to_string(),
- suggestion_id: event.suggestion_id.unwrap_or_default(),
+ provider: event.provider,
suggestion_accepted: event.suggestion_accepted,
}
}
@@ -22,6 +22,7 @@ pub struct CopilotCompletionProvider {
pending_cycling_refresh: Task<Result<()>>,
copilot: Model<Copilot>,
telemetry: Option<Arc<Telemetry>>,
+ should_allow_event_to_send: bool,
}
impl CopilotCompletionProvider {
@@ -36,6 +37,7 @@ impl CopilotCompletionProvider {
pending_cycling_refresh: Task::ready(Ok(())),
copilot,
telemetry: None,
+ should_allow_event_to_send: false,
}
}
@@ -59,6 +61,10 @@ impl CopilotCompletionProvider {
}
impl InlineCompletionProvider for CopilotCompletionProvider {
+ fn name() -> &'static str {
+ "copilot"
+ }
+
fn is_enabled(
&self,
buffer: &Model<Buffer>,
@@ -99,6 +105,7 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
this.update(&mut cx, |this, cx| {
if !completions.is_empty() {
+ this.should_allow_event_to_send = true;
this.cycled = false;
this.pending_cycling_refresh = Task::ready(Ok(()));
this.completions.clear();
@@ -187,13 +194,17 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
.update(cx, |copilot, cx| copilot.accept_completion(completion, cx))
.detach_and_log_err(cx);
if let Some(telemetry) = self.telemetry.as_ref() {
- telemetry.report_copilot_event(
- Some(completion.uuid.clone()),
- true,
- self.file_extension.clone(),
- );
+ if self.should_allow_event_to_send {
+ telemetry.report_inline_completion_event(
+ Self::name().to_string(),
+ true,
+ self.file_extension.clone(),
+ );
+ }
}
}
+
+ self.should_allow_event_to_send = false;
}
fn discard(&mut self, cx: &mut ModelContext<Self>) {
@@ -210,9 +221,18 @@ impl InlineCompletionProvider for CopilotCompletionProvider {
copilot.discard_completions(&self.completions, cx)
})
.detach_and_log_err(cx);
+
if let Some(telemetry) = self.telemetry.as_ref() {
- telemetry.report_copilot_event(None, false, self.file_extension.clone());
+ if self.should_allow_event_to_send {
+ telemetry.report_inline_completion_event(
+ Self::name().to_string(),
+ false,
+ self.file_extension.clone(),
+ );
+ }
}
+
+ self.should_allow_event_to_send = false;
}
fn active_completion_text<'a>(
@@ -3,6 +3,7 @@ use gpui::{AppContext, Model, ModelContext};
use language::Buffer;
pub trait InlineCompletionProvider: 'static + Sized {
+ fn name() -> &'static str;
fn is_enabled(
&self,
buffer: &Model<Buffer>,
@@ -129,9 +129,8 @@ impl LogStore {
let copilot_subscription = Copilot::global(cx).map(|copilot| {
let copilot = &copilot;
- cx.subscribe(
- copilot,
- |this, copilot, copilot_event, cx| match copilot_event {
+ cx.subscribe(copilot, |this, copilot, inline_completion_event, cx| {
+ match inline_completion_event {
copilot::Event::CopilotLanguageServerStarted => {
if let Some(server) = copilot.read(cx).language_server() {
let server_id = server.server_id();
@@ -159,8 +158,8 @@ impl LogStore {
);
}
}
- },
- )
+ }
+ })
});
let this = Self {
@@ -1,30 +1,46 @@
use crate::{Supermaven, SupermavenCompletionStateId};
use anyhow::Result;
+use client::telemetry::Telemetry;
use editor::{Direction, InlineCompletionProvider};
use futures::StreamExt as _;
-use gpui::{AppContext, Model, ModelContext, Task};
+use gpui::{AppContext, EntityId, Model, ModelContext, Task};
use language::{language_settings::all_language_settings, Anchor, Buffer};
-use std::time::Duration;
+use std::{path::Path, sync::Arc, time::Duration};
pub const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(75);
pub struct SupermavenCompletionProvider {
supermaven: Model<Supermaven>,
+ buffer_id: Option<EntityId>,
completion_id: Option<SupermavenCompletionStateId>,
+ file_extension: Option<String>,
pending_refresh: Task<Result<()>>,
+ telemetry: Option<Arc<Telemetry>>,
}
impl SupermavenCompletionProvider {
pub fn new(supermaven: Model<Supermaven>) -> Self {
Self {
supermaven,
+ buffer_id: None,
completion_id: None,
+ file_extension: None,
pending_refresh: Task::ready(Ok(())),
+ telemetry: None,
}
}
+
+ pub fn with_telemetry(mut self, telemetry: Arc<Telemetry>) -> Self {
+ self.telemetry = Some(telemetry);
+ self
+ }
}
impl InlineCompletionProvider for SupermavenCompletionProvider {
+ fn name() -> &'static str {
+ "supermaven"
+ }
+
fn is_enabled(&self, buffer: &Model<Buffer>, cursor_position: Anchor, cx: &AppContext) -> bool {
if !self.supermaven.read(cx).is_enabled() {
return false;
@@ -58,6 +74,15 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
while let Some(()) = completion.updates.next().await {
this.update(&mut cx, |this, cx| {
this.completion_id = Some(completion.id);
+ this.buffer_id = Some(buffer_handle.entity_id());
+ this.file_extension = buffer_handle.read(cx).file().and_then(|file| {
+ Some(
+ Path::new(file.file_name(cx))
+ .extension()?
+ .to_str()?
+ .to_string(),
+ )
+ });
cx.notify();
})?;
}
@@ -75,11 +100,30 @@ impl InlineCompletionProvider for SupermavenCompletionProvider {
}
fn accept(&mut self, _cx: &mut ModelContext<Self>) {
+ if let Some(telemetry) = self.telemetry.as_ref() {
+ if let Some(_) = self.completion_id {
+ telemetry.report_inline_completion_event(
+ Self::name().to_string(),
+ true,
+ self.file_extension.clone(),
+ );
+ }
+ }
self.pending_refresh = Task::ready(Ok(()));
self.completion_id = None;
}
fn discard(&mut self, _cx: &mut ModelContext<Self>) {
+ if let Some(telemetry) = self.telemetry.as_ref() {
+ if let Some(_) = self.completion_id {
+ telemetry.report_inline_completion_event(
+ Self::name().to_string(),
+ false,
+ self.file_extension.clone(),
+ );
+ }
+ }
+
self.pending_refresh = Task::ready(Ok(()));
self.completion_id = None;
}
@@ -53,7 +53,8 @@ impl Display for AssistantKind {
#[serde(tag = "type")]
pub enum Event {
Editor(EditorEvent),
- Copilot(CopilotEvent),
+ Copilot(CopilotEvent), // Needed for clients sending old copilot_event types
+ InlineCompletion(InlineCompletionEvent),
Call(CallEvent),
Assistant(AssistantEvent),
Cpu(CpuEvent),
@@ -74,6 +75,7 @@ pub struct EditorEvent {
pub copilot_enabled_for_language: bool,
}
+// Needed for clients sending old copilot_event types
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CopilotEvent {
pub suggestion_id: Option<String>,
@@ -81,6 +83,13 @@ pub struct CopilotEvent {
pub file_extension: Option<String>,
}
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct InlineCompletionEvent {
+ pub provider: String,
+ pub suggestion_accepted: bool,
+ pub file_extension: Option<String>,
+}
+
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CallEvent {
pub operation: String,
@@ -118,7 +118,9 @@ fn assign_inline_completion_provider(
}
language::language_settings::InlineCompletionProvider::Supermaven => {
if let Some(supermaven) = Supermaven::global(cx) {
- let provider = cx.new_model(|_| SupermavenCompletionProvider::new(supermaven));
+ let provider = cx.new_model(|_| {
+ SupermavenCompletionProvider::new(supermaven).with_telemetry(telemetry.clone())
+ });
editor.set_inline_completion_provider(Some(provider), cx);
}
}
@@ -97,7 +97,6 @@ The following data is sent:
- `copilot_enabled_for_language`: A boolean that indicates whether the user has copilot enabled for the language of the file that was opened or saved
- `milliseconds_since_first_event`: Duration of time between this event's timestamp and the timestamp of the first event in the current batch
- `copilot`
- - `suggestion_id`: The ID of the suggestion
- `suggestion_accepted`: A boolean that indicates whether the suggestion was accepted or not
- `file_extension`: The file extension of the file that was opened or saved
- `milliseconds_since_first_event`: Same as above