Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor telemetry to use a more standardized format VSCODE-651 #891

Open
wants to merge 6 commits into
base: gagik/add-playground-buttons
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,6 @@ enum EXTENSION_COMMANDS {
SHOW_EXPORT_TO_LANGUAGE_RESULT = 'mdb.showExportToLanguageResult',
}

export type ExtensionCommand = EXTENSION_COMMANDS;

export default EXTENSION_COMMANDS;
2 changes: 1 addition & 1 deletion src/documentSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ export enum DocumentSource {
DOCUMENT_SOURCE_TREEVIEW = 'treeview',
DOCUMENT_SOURCE_PLAYGROUND = 'playground',
DOCUMENT_SOURCE_COLLECTIONVIEW = 'collectionview',
DOCUMENT_SOURCE_QUERY_WITH_COPILOT_CODELENS = 'query with copilot codelens',
DOCUMENT_SOURCE_CODELENS = 'codelens',
}
7 changes: 5 additions & 2 deletions src/editors/queryWithCopilotCodeLensProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ export class QueryWithCopilotCodeLensProvider
prompt: 'Describe the query you would like to generate',
placeHolder:
'e.g. Find the document in sample_mflix.users with the name of Kayden Washington',
messagePrefix: '/query',
command: 'query',
isNewChat: true,
source: DocumentSource.DOCUMENT_SOURCE_QUERY_WITH_COPILOT_CODELENS,
telemetry: {
source: DocumentSource.DOCUMENT_SOURCE_CODELENS,
sourceDetails: 'query with copilot code lens',
},
};

return [
Expand Down
13 changes: 6 additions & 7 deletions src/mdbExtensionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
} from './explorer';
import ExportToLanguageCodeLensProvider from './editors/exportToLanguageCodeLensProvider';
import { type ExportToLanguageResult } from './types/playgroundType';
import EXTENSION_COMMANDS from './commands';
import type { ExtensionCommand } from './commands';
import type FieldTreeItem from './explorer/fieldTreeItem';
import type IndexListTreeItem from './explorer/indexListTreeItem';
import { LanguageServerController } from './language';
Expand All @@ -40,10 +40,7 @@ import WebviewController from './views/webviewController';
import { createIdFactory, generateId } from './utils/objectIdHelper';
import { ConnectionStorage } from './storage/connectionStorage';
import type StreamProcessorTreeItem from './explorer/streamProcessorTreeItem';
import type {
ParticipantCommand,
RunParticipantCodeCommandArgs,
} from './participant/participant';
import type { RunParticipantCodeCommandArgs } from './participant/participant';
import ParticipantController from './participant/participant';
import type { OpenSchemaCommandArgs } from './participant/prompts/schema';
import { QueryWithCopilotCodeLensProvider } from './editors/queryWithCopilotCodeLensProvider';
Expand All @@ -52,6 +49,8 @@ import type {
SendMessageToParticipantFromInputOptions,
} from './participant/participantTypes';
import { COPILOT_CHAT_EXTENSION_ID } from './participant/constants';
import type { ParticipantCommand } from './participant/participantTypes';
import EXTENSION_COMMANDS from './commands';

// This class is the top-level controller for our extension.
// Commands which the extensions handles are defined in the function `activate`.
Expand Down Expand Up @@ -385,7 +384,7 @@ export default class MDBExtensionController implements vscode.Disposable {
};

registerParticipantCommand = (
command: string,
command: ExtensionCommand,
commandHandler: (...args: any[]) => Promise<boolean>
): void => {
const commandHandlerWithTelemetry = (args: any[]): Promise<boolean> => {
Expand All @@ -403,7 +402,7 @@ export default class MDBExtensionController implements vscode.Disposable {
};

registerCommand = (
command: string,
command: ExtensionCommand,
commandHandler: (...args: any[]) => Promise<boolean>
): void => {
const commandHandlerWithTelemetry = (args: any[]): Promise<boolean> => {
Expand Down
11 changes: 1 addition & 10 deletions src/participant/constants.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
import type * as vscode from 'vscode';
import { ChatMetadataStore } from './chatMetadata';
import type { ParticipantResponseType } from './participantTypes';

export const CHAT_PARTICIPANT_ID = 'mongodb.participant';
export const CHAT_PARTICIPANT_MODEL = 'gpt-4o';
export const COPILOT_EXTENSION_ID = 'GitHub.copilot';
export const COPILOT_CHAT_EXTENSION_ID = 'GitHub.copilot-chat';

export type ParticipantResponseType =
| 'query'
| 'schema'
| 'docs'
| 'generic'
| 'emptyRequest'
| 'cancelledRequest'
| 'askToConnect'
| 'askForNamespace';

export const codeBlockIdentifier = {
start: '```javascript',
end: '```',
Expand Down
83 changes: 58 additions & 25 deletions src/participant/participant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,13 @@ import { PromptHistory } from './prompts/promptHistory';
import type {
SendMessageToParticipantOptions,
SendMessageToParticipantFromInputOptions,
ParticipantCommand,
ParticipantCommandType,
} from './participantTypes';
import { DEFAULT_EXPORT_TO_LANGUAGE_DRIVER_SYNTAX } from '../editors/exportToLanguageCodeLensProvider';
import { EXPORT_TO_LANGUAGE_ALIASES } from '../editors/playgroundSelectionCodeActionProvider';
import { CollectionTreeItem, DatabaseTreeItem } from '../explorer';
import { DocumentSource } from '../documentSource';

const log = createLogger('participant');

Expand All @@ -73,8 +76,6 @@ export type RunParticipantCodeCommandArgs = {
runnableContent: string;
};

export type ParticipantCommand = '/query' | '/schema' | '/docs';

const MAX_MARKDOWN_LIST_LENGTH = 10;

export default class ParticipantController {
Expand Down Expand Up @@ -179,6 +180,8 @@ export default class ParticipantController {
message,
isNewChat = false,
isPartialQuery = false,
telemetry,
command,
...otherOptions
} = options;

Expand All @@ -188,10 +191,28 @@ export default class ParticipantController {
'workbench.action.chat.clearHistory'
);
}
const commandPrefix = command ? `/${command} ` : '';
const query = `@MongoDB ${commandPrefix}${message}`;

if (telemetry) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an example of it might be nice to structure tracking in other functions as some hot-pluggable thing.

if (isNewChat) {
this._telemetryService.trackParticipantChatOpenedFromAction({
...telemetry,
command,
});
}
if (!isPartialQuery) {
this._telemetryService.trackParticipantPromptSubmittedFromAction({
...telemetry,
command: command ?? 'generic',
input_length: query.length,
});
}
}

return await vscode.commands.executeCommand('workbench.action.chat.open', {
...otherOptions,
query: `@MongoDB ${message}`,
query,
isPartialQuery,
});
}
Expand All @@ -200,27 +221,34 @@ export default class ParticipantController {
options: SendMessageToParticipantFromInputOptions
): Promise<unknown> {
const {
messagePrefix = '',
isNewChat = false,
isPartialQuery = false,
source,
isNewChat,
isPartialQuery,
telemetry,
command,
...inputBoxOptions
} = options;

this._telemetryService.trackCopilotParticipantSubmittedFromInputBox({
source,
});

const message = await vscode.window.showInputBox({
...inputBoxOptions,
});

if (telemetry) {
this._telemetryService.trackParticipantInputBoxSubmitted({
...telemetry,
input_length: message?.length,
dismissed: message === undefined,
command,
});
}

if (message === undefined || message.trim() === '') {
return Promise.resolve();
}

return this.sendMessageToParticipant({
message: `${messagePrefix ? `${messagePrefix} ` : ''}${message}`,
message,
telemetry,
command,
isNewChat,
isPartialQuery,
});
Expand All @@ -235,13 +263,21 @@ export default class ParticipantController {
await this.sendMessageToParticipant({
message: `I want to ask questions about the \`${databaseName}\` database.`,
isNewChat: true,
telemetry: {
source: DocumentSource.DOCUMENT_SOURCE_TREEVIEW,
sourceDetails: 'copilot button on database tree item',
},
});
} else if (treeItem instanceof CollectionTreeItem) {
const { databaseName, collectionName } = treeItem;

await this.sendMessageToParticipant({
message: `I want to ask questions about the \`${databaseName}\` database's \`${collectionName}\` collection.`,
isNewChat: true,
telemetry: {
source: DocumentSource.DOCUMENT_SOURCE_TREEVIEW,
sourceDetails: 'copilot button on collection tree item',
},
});
} else {
throw new Error('Unsupported tree item type');
Expand Down Expand Up @@ -276,7 +312,7 @@ export default class ParticipantController {
})
),
});
this._telemetryService.trackCopilotParticipantPrompt(modelInput.stats);
this._telemetryService.trackParticipantPrompt(modelInput.stats);

const modelResponse = await model.sendRequest(
modelInput.messages,
Expand Down Expand Up @@ -456,7 +492,7 @@ export default class ParticipantController {
stream,
});

this._telemetryService.trackCopilotParticipantResponse({
this._telemetryService.trackParticipantResponse({
command: 'generic',
has_cta: false,
found_namespace: false,
Expand Down Expand Up @@ -1423,7 +1459,7 @@ export default class ParticipantController {
],
});

this._telemetryService.trackCopilotParticipantResponse({
this._telemetryService.trackParticipantResponse({
command: 'schema',
has_cta: true,
found_namespace: true,
Expand Down Expand Up @@ -1534,7 +1570,7 @@ export default class ParticipantController {
token,
});

this._telemetryService.trackCopilotParticipantResponse({
this._telemetryService.trackParticipantResponse({
command: 'query',
has_cta: false,
found_namespace: true,
Expand Down Expand Up @@ -1640,7 +1676,7 @@ export default class ParticipantController {

this._streamGenericDocsLink(stream);

this._telemetryService.trackCopilotParticipantResponse({
this._telemetryService.trackParticipantResponse({
command: 'docs/copilot',
has_cta: true,
found_namespace: false,
Expand Down Expand Up @@ -1720,7 +1756,7 @@ export default class ParticipantController {
}
}

this._telemetryService.trackCopilotParticipantResponse({
this._telemetryService.trackParticipantResponse({
command: 'docs/chatbot',
has_cta: !!docsResult.responseReferences,
found_namespace: false,
Expand Down Expand Up @@ -1838,10 +1874,7 @@ export default class ParticipantController {
return true;
} catch (error) {
const message = formatError(error).message;
this._telemetryService.trackCopilotParticipantError(
error,
'exportToPlayground'
);
this._telemetryService.trackParticipantError(error, 'exportToPlayground');
void vscode.window.showErrorMessage(
`An error occurred exporting to a playground: ${message}`
);
Expand Down Expand Up @@ -1948,9 +1981,9 @@ Please see our [FAQ](https://www.mongodb.com/docs/generative-ai-faq/) for more i
return await this.handleGenericRequest(...args);
}
} catch (error) {
this._telemetryService.trackCopilotParticipantError(
this._telemetryService.trackParticipantError(
error,
request.command || 'generic'
(request.command as ParticipantCommandType) || 'generic'
);
// Re-throw other errors so they show up in the UI.
throw error;
Expand Down Expand Up @@ -1999,7 +2032,7 @@ Please see our [FAQ](https://www.mongodb.com/docs/generative-ai-faq/) for more i
'unhelpfulReason' in feedback
? (feedback.unhelpfulReason as string)
: undefined;
this._telemetryService.trackCopilotParticipantFeedback({
this._telemetryService.trackParticipantFeedback({
feedback: chatResultFeedbackKindToTelemetryValue(feedback.kind),
reason: unhelpfulReason,
response_type: (feedback.result as ChatResult)?.metadata.intent,
Expand Down
33 changes: 29 additions & 4 deletions src/participant/participantTypes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
import type * as vscode from 'vscode';
import type { DocumentSource } from '../documentSource';

export type ParticipantCommandType = 'query' | 'schema' | 'docs';
export type ParticipantCommand = `/${ParticipantCommandType}`;

export type ParticipantRequestType = ParticipantCommandType | 'generic';

export type ParticipantResponseType =
| 'query'
| 'schema'
| 'docs'
| 'docs/chatbot'
| 'docs/copilot'
| 'exportToPlayground'
| 'generic'
| 'emptyRequest'
| 'cancelledRequest'
| 'askToConnect'
| 'askForNamespace';

type TelemetryMetadata = {
source: DocumentSource;
sourceDetails?: string;
};

/** Based on options from Copilot's chat open command IChatViewOpenOptions */
export type SendMessageToParticipantOptions = {
message: string;
command?: ParticipantCommandType;
isNewChat?: boolean;
isPartialQuery?: boolean;
telemetry?: TelemetryMetadata;
/**
* Any previous chat requests and responses that should be shown in the chat view.
* Note that currently these requests do not end up included in vscode's context.history.
Expand All @@ -16,8 +41,8 @@ export type SendMessageToParticipantOptions = {
}[];
};

export type SendMessageToParticipantFromInputOptions = {
messagePrefix?: string;
source?: DocumentSource;
} & Omit<SendMessageToParticipantOptions, 'message'> &
export type SendMessageToParticipantFromInputOptions = Pick<
SendMessageToParticipantOptions,
'isNewChat' | 'isPartialQuery' | 'command' | 'telemetry'
> &
vscode.InputBoxOptions;
3 changes: 2 additions & 1 deletion src/participant/prompts/promptBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
ParticipantPromptProperties,
} from '../../telemetry/telemetryService';
import { PromptHistory } from './promptHistory';
import type { ParticipantCommandType } from '../participantTypes';

export interface PromptArgsBase {
request: {
Expand Down Expand Up @@ -175,7 +176,7 @@ export abstract class PromptBase<TArgs extends PromptArgsBase> {
),
user_input_length: request.prompt.length,
has_sample_documents: hasSampleDocs,
command: request.command || 'generic',
command: (request.command as ParticipantCommandType) || 'generic',
history_size: context?.history.length || 0,
internal_purpose: this.internalPurposeForTelemetry,
};
Expand Down
3 changes: 2 additions & 1 deletion src/participant/prompts/promptHistory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { ParticipantErrorTypes } from '../participantErrorTypes';
import type { ChatResult, ParticipantResponseType } from '../constants';
import type { ChatResult } from '../constants';
import type { ParticipantResponseType } from '../participantTypes';

export class PromptHistory {
private static _handleChatResponseTurn({
Expand Down
Loading
Loading