anymous telemetry based on redhat (#212)

* initial telemetry

* fixed segment bugs

* Move telemetry impl to submodule, add more actions

* Replace privacy policy link, minor refactoring

---------

Co-authored-by: Carl-Robert Linnupuu <carlrobertoh@gmail.com>
This commit is contained in:
keith siilats 2023-09-27 11:44:01 -04:00 committed by GitHub
parent 05c7560ec9
commit 8f9980fbf1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 5348 additions and 32 deletions

View file

@ -1,9 +1,7 @@
package ee.carlrobert.codegpt;
import com.intellij.openapi.util.IconLoader;
import java.util.Objects;
import javax.swing.Icon;
import javax.swing.ImageIcon;
public final class Icons {
@ -13,9 +11,4 @@ public final class Icons {
public static final Icon OpenAIIcon = IconLoader.getIcon("/icons/openai.svg", Icons.class);
public static final Icon AzureIcon = IconLoader.getIcon("/icons/azure.svg", Icons.class);
public static final Icon YouIcon = IconLoader.getIcon("/icons/you.svg", Icons.class);
public static final ImageIcon DefaultImageIcon = getImageIcon("/icons/chatgpt.png");
private static ImageIcon getImageIcon(String path) {
return new ImageIcon(Objects.requireNonNull(Icons.class.getResource(path)));
}
}

View file

@ -0,0 +1,16 @@
package ee.carlrobert.codegpt;
import ee.carlrobert.codegpt.telemetry.core.service.TelemetryMessageBuilder;
import ee.carlrobert.codegpt.telemetry.core.util.Lazy;
public class TelemetryService {
private static final TelemetryService INSTANCE = new TelemetryService();
private final Lazy<TelemetryMessageBuilder> builder = new Lazy<>(() -> new TelemetryMessageBuilder(TelemetryService.class.getClassLoader()));
public static TelemetryMessageBuilder instance() {
return INSTANCE.builder.get();
}
}

View file

@ -1,12 +1,13 @@
package ee.carlrobert.codegpt.actions.toolwindow;
import static ee.carlrobert.codegpt.Icons.DefaultImageIcon;
import static ee.carlrobert.codegpt.Icons.DefaultIcon;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil;
import ee.carlrobert.codegpt.completions.TelemetryAction;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.toolwindow.chat.standard.StandardChatToolWindowContentManager;
import org.jetbrains.annotations.NotNull;
@ -32,12 +33,21 @@ public class DeleteAllConversationsAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent event) {
int answer = Messages.showYesNoDialog("Are you sure you want to delete all conversations?", "Clear History", DefaultImageIcon);
int answer = Messages.showYesNoDialog(
"Are you sure you want to delete all conversations?",
"Clear History",
DefaultIcon);
if (answer == Messages.YES) {
var project = event.getProject();
if (project != null) {
ConversationService.getInstance().clearAll();
StandardChatToolWindowContentManager.getInstance(project).resetActiveTab();
try {
ConversationService.getInstance().clearAll();
StandardChatToolWindowContentManager.getInstance(project).resetActiveTab();
} finally {
TelemetryAction.createActionMessage(TelemetryAction.IDE_ACTION)
.property("action", "DELETE_ALL_CONVERSATIONS")
.send();
}
}
this.onRefresh.run();
}

View file

@ -5,6 +5,7 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;
import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil;
import ee.carlrobert.codegpt.completions.TelemetryAction;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.ConversationsState;
import ee.carlrobert.codegpt.util.OverlayUtils;
@ -30,6 +31,10 @@ public class DeleteConversationAction extends AnAction {
if (OverlayUtils.showDeleteConversationDialog() == Messages.YES) {
var project = event.getProject();
if (project != null) {
TelemetryAction.createActionMessage(TelemetryAction.IDE_ACTION)
.property("action", "DELETE_CONVERSATION")
.send();
ConversationService.getInstance().deleteSelectedConversation();
onDelete.run();
}

View file

@ -9,6 +9,7 @@ import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.testFramework.LightVirtualFile;
import ee.carlrobert.codegpt.TelemetryService;
import ee.carlrobert.codegpt.actions.editor.EditorActionsUtil;
import ee.carlrobert.codegpt.conversations.ConversationsState;
import java.time.format.DateTimeFormatter;

View file

@ -1,5 +1,6 @@
package ee.carlrobert.codegpt.completions;
import ee.carlrobert.codegpt.TelemetryService;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.message.Message;
import ee.carlrobert.codegpt.settings.state.AzureSettingsState;
@ -122,16 +123,21 @@ public class CompletionRequestHandler {
}
protected Void doInBackground() {
var settings = SettingsState.getInstance();
try {
eventSource = startCall(
conversation,
message,
isRetry,
SettingsState.getInstance().isUseYouService() ? getYouCompletionEventListener() : getCompletionEventListener());
settings.isUseYouService() ?
getYouCompletionEventListener() :
getCompletionEventListener());
} catch (TotalUsageExceededException e) {
if (tokensExceededListener != null) {
tokensExceededListener.run();
}
} finally {
sendInfo(settings);
}
return null;
}
@ -162,8 +168,12 @@ public class CompletionRequestHandler {
@Override
public void onError(ErrorDetails error, Throwable ex) {
if (errorListener != null) {
errorListener.accept(error, ex);
try {
if (errorListener != null) {
errorListener.accept(error, ex);
}
} finally {
sendError(error, ex);
}
}
};
@ -186,8 +196,12 @@ public class CompletionRequestHandler {
@Override
public void onError(ErrorDetails error, Throwable ex) {
if (errorListener != null) {
errorListener.accept(error, ex);
try {
if (errorListener != null) {
errorListener.accept(error, ex);
}
} finally {
sendError(error, ex);
}
}
@ -199,5 +213,27 @@ public class CompletionRequestHandler {
}
};
}
private void sendInfo(SettingsState settings) {
var service = "openai";
if (settings.isUseAzureService()) {
service = "azure";
}
if (settings.isUseYouService()) {
service = "you";
}
TelemetryAction.createActionMessage(TelemetryAction.COMPLETION)
.property("conversationId", conversation.getId().toString())
.property("model", conversation.getModel())
.property("service", service)
.send();
}
private void sendError(ErrorDetails error, Throwable ex) {
TelemetryAction.createActionMessage(TelemetryAction.COMPLETION)
.property("conversationId", conversation.getId().toString())
.error(new RuntimeException("Received an error during completion.\n" + error, ex))
.send();
}
}
}

View file

@ -0,0 +1,24 @@
package ee.carlrobert.codegpt.completions;
import ee.carlrobert.codegpt.TelemetryService;
import ee.carlrobert.codegpt.telemetry.core.service.TelemetryMessageBuilder.ActionMessage;
public enum TelemetryAction {
COMPLETION("CodeGPT-Completion"),
IDE_ACTION("CodeGPT-Action");
private final String code;
TelemetryAction(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static ActionMessage createActionMessage(TelemetryAction action) {
return TelemetryService.instance().action(action.getCode());
}
}

View file

@ -11,8 +11,10 @@ import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.ui.JBUI;
import ee.carlrobert.codegpt.TelemetryService;
import ee.carlrobert.codegpt.completions.CompletionRequestHandler;
import ee.carlrobert.codegpt.completions.SerpResult;
import ee.carlrobert.codegpt.completions.TelemetryAction;
import ee.carlrobert.codegpt.conversations.Conversation;
import ee.carlrobert.codegpt.conversations.ConversationService;
import ee.carlrobert.codegpt.conversations.message.Message;
@ -111,7 +113,11 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
}
var messageWrapper = createNewMessageWrapper(message.getId());
messageWrapper.add(new UserMessagePanel(project, message, message.getUserMessage() != null, this));
messageWrapper.add(new UserMessagePanel(
project,
message,
message.getUserMessage() != null,
this));
var responsePanel = new ResponsePanel()
.withReloadAction(() -> reloadMessage(message, conversation))
.withDeleteAction(() -> deleteMessage(message.getId(), messageWrapper, conversation))
@ -134,7 +140,11 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
return OpenAICredentialsManager.getInstance().isApiKeySet();
}
private void call(Conversation conversation, Message message, ResponsePanel responsePanel, boolean isRetry) {
private void call(
Conversation conversation,
Message message,
ResponsePanel responsePanel,
boolean isRetry) {
ChatMessageResponseBody responseContainer = (ChatMessageResponseBody) responsePanel.getContent();
if (!isCredentialSet()) {
@ -167,13 +177,22 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
if (containsResults) {
message.setSerpResults(serpResults.stream()
.map(result -> new SerpResult(result.getUrl(), result.getName(), result.getSnippet(), result.getSnippetSource()))
.map(result -> new SerpResult(
result.getUrl(),
result.getName(),
result.getSnippet(),
result.getSnippetSource()))
.collect(toList()));
}
});
requestHandler.addTokensExceededListener(() -> SwingUtilities.invokeLater(() -> {
var answer = OverlayUtils.showTokenLimitExceededDialog();
if (answer == OK) {
TelemetryService.instance().action(TelemetryAction.IDE_ACTION.getCode())
.property("action", "DISCARD_TOKEN_LIMIT")
.property("model", conversation.getModel())
.send();
conversationService.discardTokenLimits(conversation);
requestHandler.call(conversation, message, true);
} else {
@ -185,9 +204,14 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
responseContainer.displayError(error.getMessage());
stopStreaming(responseContainer);
});
requestHandler.addSerpResultsListener(serpResults -> serpResultsMapping.put(message.getId(), serpResults.stream()
.map(result -> new SerpResult(result.getUrl(), result.getName(), result.getSnippet(), result.getSnippetSource()))
.collect(toList())));
requestHandler.addSerpResultsListener(
serpResults -> serpResultsMapping.put(message.getId(), serpResults.stream()
.map(result -> new SerpResult(
result.getUrl(),
result.getName(),
result.getSnippet(),
result.getSnippetSource()))
.collect(toList())));
userPromptTextArea.setRequestHandler(requestHandler);
userPromptTextArea.setSubmitEnabled(false);
requestHandler.call(conversation, message, isRetry);
@ -196,7 +220,8 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
protected void reloadMessage(Message message, Conversation conversation) {
ResponsePanel responsePanel = null;
try {
responsePanel = (ResponsePanel) Arrays.stream(visibleMessagePanels.get(message.getId()).getComponents())
responsePanel = (ResponsePanel) Arrays.stream(
visibleMessagePanels.get(message.getId()).getComponents())
.filter(component -> component instanceof ResponsePanel)
.findFirst().orElseThrow();
((ChatMessageResponseBody) responsePanel.getContent()).clear();
@ -259,7 +284,8 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
var selectionModel = editor.getSelectionModel();
var selectedText = selectionModel.getSelectedText();
if (selectedText != null && !selectedText.isEmpty()) {
var fileExtension = FileUtils.getFileExtension(((EditorImpl) editor).getVirtualFile().getName());
var fileExtension = FileUtils.getFileExtension(
((EditorImpl) editor).getVirtualFile().getName());
message = new Message(text + format("\n```%s\n%s\n```", fileExtension, selectedText));
message.setUserMessage(text);
selectionModel.removeSelection();
@ -293,7 +319,6 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridy = 1;
var model = getModel();
var modelIconWrapper = JBUI.Panels.simplePanel(
new ModelIconLabel(getClientCode(), model)).withBorder(JBUI.Borders.empty(0, 0, 8, 4));

View file

@ -2,7 +2,7 @@ package ee.carlrobert.codegpt.util;
import static com.intellij.openapi.ui.Messages.CANCEL;
import static com.intellij.openapi.ui.Messages.OK;
import static ee.carlrobert.codegpt.Icons.DefaultImageIcon;
import static ee.carlrobert.codegpt.Icons.DefaultIcon;
import com.intellij.execution.ExecutionBundle;
import com.intellij.notification.Notification;
@ -56,7 +56,7 @@ public class OverlayUtils {
return Messages.showYesNoDialog(
CodeGPTBundle.get("dialog.deleteConversation.description"),
CodeGPTBundle.get("dialog.deleteConversation.title"),
DefaultImageIcon);
DefaultIcon);
}
public static int showTokenLimitExceededDialog() {
@ -65,7 +65,7 @@ public class OverlayUtils {
CodeGPTBundle.get("dialog.tokenLimitExceeded.description"))
.yesText(CodeGPTBundle.get("dialog.tokenLimitExceeded.continue"))
.noText(CodeGPTBundle.get("dialog.tokenLimitExceeded.cancel"))
.icon(DefaultImageIcon)
.icon(DefaultIcon)
.doNotAsk(new DoNotAskOption.Adapter() {
@Override
public void rememberChoice(boolean isSelected, int exitCode) {