Improve code mapper telemetry for quality signal (#225602)

This commit is contained in:
Martin Aeschlimann
2024-08-14 16:39:22 +02:00
committed by GitHub
parent f430a63a28
commit 89b2f90c3a
4 changed files with 32 additions and 12 deletions
@@ -85,6 +85,11 @@ abstract class ChatCodeBlockAction extends Action2 {
abstract runWithContext(accessor: ServicesAccessor, context: ICodeBlockActionContext): any;
}
interface IComputeEditsResult {
readonly edits: ResourceEdit[] | WorkspaceEdit;
readonly codeMapper?: string;
}
abstract class InsertCodeBlockAction extends ChatCodeBlockAction {
override async runWithContext(accessor: ServicesAccessor, context: ICodeBlockActionContext) {
@@ -140,35 +145,37 @@ abstract class InsertCodeBlockAction extends ChatCodeBlockAction {
}
const languageService = accessor.get(ILanguageService);
const chatService = accessor.get(IChatService);
const focusRange = notebookEditor.getFocus();
const next = Math.max(focusRange.end - 1, 0);
insertCell(languageService, notebookEditor, next, CellKind.Code, 'below', context.code, true);
this.notifyUserAction(accessor, context);
this.notifyUserAction(chatService, context);
}
protected async computeEdits(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext): Promise<ResourceEdit[] | WorkspaceEdit> {
protected async computeEdits(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext): Promise<IComputeEditsResult> {
const activeModel = codeEditor.getModel();
const range = codeEditor.getSelection() ?? new Range(activeModel.getLineCount(), 1, activeModel.getLineCount(), 1);
const text = reindent(codeBlockActionContext.code, activeModel, range.startLineNumber);
return [new ResourceTextEdit(activeModel.uri, { range, text })];
return { edits: [new ResourceTextEdit(activeModel.uri, { range, text })] };
}
private async handleTextEditor(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext) {
const bulkEditService = accessor.get(IBulkEditService);
const codeEditorService = accessor.get(ICodeEditorService);
const chatService = accessor.get(IChatService);
this.notifyUserAction(accessor, codeBlockActionContext);
const activeModel = codeEditor.getModel();
const mappedEdits = await this.computeEdits(accessor, codeEditor, codeBlockActionContext);
const result = await this.computeEdits(accessor, codeEditor, codeBlockActionContext);
this.notifyUserAction(chatService, codeBlockActionContext, result);
await bulkEditService.apply(mappedEdits);
await bulkEditService.apply(result.edits);
codeEditorService.listCodeEditors().find(editor => editor.getModel()?.uri.toString() === activeModel.uri.toString())?.focus();
}
private notifyUserAction(accessor: ServicesAccessor, context: ICodeBlockActionContext) {
private notifyUserAction(chatService: IChatService, context: ICodeBlockActionContext, result?: IComputeEditsResult) {
if (isResponseVM(context.element)) {
const chatService = accessor.get(IChatService);
chatService.notifyUserAction({
agentId: context.element.agent?.id,
command: context.element.slashCommand?.name,
@@ -179,6 +186,8 @@ abstract class InsertCodeBlockAction extends ChatCodeBlockAction {
kind: 'insert',
codeBlockIndex: context.codeBlockIndex,
totalCharacters: context.code.length,
userAction: this.desc.id,
codeMapper: result?.codeMapper,
}
});
}
@@ -371,7 +380,7 @@ export function registerChatCodeBlockActions() {
});
}
protected override async computeEdits(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext): Promise<ResourceEdit[] | WorkspaceEdit> {
protected override async computeEdits(accessor: ServicesAccessor, codeEditor: IActiveCodeEditor, codeBlockActionContext: ICodeBlockActionContext): Promise<IComputeEditsResult> {
const progressService = accessor.get(IProgressService);
const notificationService = accessor.get(INotificationService);
@@ -404,7 +413,7 @@ export function registerChatCodeBlockActions() {
const cancellationTokenSource = new CancellationTokenSource();
try {
const edits = await progressService.withProgress(
const edits = await progressService.withProgress<IComputeEditsResult | undefined>(
{ location: ProgressLocation.Notification, delay: 500, sticky: true, cancellable: true },
async progress => {
for (const provider of mappedEditsProviders) {
@@ -416,7 +425,7 @@ export function registerChatCodeBlockActions() {
cancellationTokenSource.token
);
if (mappedEdits) {
return mappedEdits;
return { edits: mappedEdits, codeMapper: provider.displayName };
}
}
return undefined;
@@ -503,7 +512,8 @@ export function registerChatCodeBlockActions() {
kind: 'insert',
codeBlockIndex: context.codeBlockIndex,
totalCharacters: context.code.length,
newFile: true
newFile: true,
userAction: this.desc.id,
}
});
}
@@ -236,6 +236,8 @@ export interface IChatInsertAction {
codeBlockIndex: number;
totalCharacters: number;
newFile?: boolean;
userAction?: string;
codeMapper?: string;
}
export interface IChatTerminalAction {
@@ -39,12 +39,16 @@ type ChatInsertEvent = {
newFile: boolean;
agentId: string;
command: string | undefined;
userAction: string | undefined;
codeMapper: string | undefined;
};
type ChatInsertClassification = {
newFile: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the code was inserted into a new untitled file.' };
agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that this insertion is for.' };
command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the slash command that this insertion is for.' };
userAction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Id of the UI command that was used to do the insert.' };
codeMapper: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The code mapper that wa used to compute the edit.' };
owner: 'roblourens';
comment: 'Provides insight into the usage of Chat features.';
};
@@ -124,6 +128,8 @@ export class ChatServiceTelemetry {
} else if (action.action.kind === 'insert') {
this.telemetryService.publicLog2<ChatInsertEvent, ChatInsertClassification>('interactiveSessionInsert', {
newFile: !!action.action.newFile,
userAction: action.action.userAction,
codeMapper: action.action.codeMapper,
agentId: action.agentId ?? '',
command: action.command,
});
@@ -280,6 +280,8 @@ declare module 'vscode' {
codeBlockIndex: number;
totalCharacters: number;
newFile?: boolean;
userAction?: string;
codeMapper?: string;
}
export interface ChatTerminalAction {