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

debt - move telemetry and recordings data into session object so that controller itself has less state #181117

Merged
merged 1 commit into from Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -23,6 +23,7 @@ import { IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor';
import { ILogService } from 'vs/platform/log/common/log';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { Range } from 'vs/editor/common/core/range';
import { fromNow } from 'vs/base/common/date';


export class StartSessionAction extends EditorAction2 {
Expand Down Expand Up @@ -476,24 +477,20 @@ export class CopyRecordings extends AbstractInteractiveEditorAction {
const clipboardService = accessor.get(IClipboardService);
const quickPickService = accessor.get(IQuickInputService);

const picks: (IQuickPickItem & { rec: Recording })[] = ctrl.recordings().map(rec => {
const recordings = ctrl.recordings().filter(r => r.exchanges.length > 0);
if (recordings.length === 0) {
return;
}

const picks: (IQuickPickItem & { rec: Recording })[] = recordings.map(rec => {
return {
rec,
label: localize('label', "{0} messages, started {1}", rec.exchanges.length, rec.when.toLocaleTimeString()),
tooltip: rec.exchanges.map(ex => ex.req.prompt).join('\n'),
label: localize('label', "'{0}' and {1} follow ups ({2})", rec.exchanges[0].prompt, rec.exchanges.length - 1, fromNow(rec.when, true)),
tooltip: rec.exchanges.map(ex => ex.prompt).join('\n'),
};
});

if (picks.length === 0) {
return;
}

let pick: typeof picks[number] | undefined;
if (picks.length === 1) {
pick = picks[0];
} else {
pick = await quickPickService.pick(picks, { canPickMany: false });
}
const pick = await quickPickService.pick(picks, { canPickMany: false });
if (pick) {
clipboardService.writeText(JSON.stringify(pick.rec, undefined, 2));
}
Expand Down
Expand Up @@ -11,7 +11,6 @@ import { isCancellationError } from 'vs/base/common/errors';
import { Event } from 'vs/base/common/event';
import { Iterable } from 'vs/base/common/iterator';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { LRUCache } from 'vs/base/common/map';
import { isEqual } from 'vs/base/common/resources';
import { StopWatch } from 'vs/base/common/stopwatch';
import { URI } from 'vs/base/common/uri';
Expand Down Expand Up @@ -49,32 +48,17 @@ import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/se
import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon';


type Exchange = { req: IInteractiveEditorRequest; res: IInteractiveEditorResponse };
export type Recording = { when: Date; session: IInteractiveEditorSession; value: string; exchanges: Exchange[] };

class SessionRecorder {

private readonly _data = new LRUCache<IInteractiveEditorSession, Recording>(3);

add(session: IInteractiveEditorSession, model: ITextModel) {
this._data.set(session, { when: new Date(), session, value: model.getValue(), exchanges: [] });
}

addExchange(session: IInteractiveEditorSession, req: IInteractiveEditorRequest, res: IInteractiveEditorResponse) {
this._data.get(session)?.exchanges.push({ req, res });
}

getAll(): Recording[] {
return [...this._data.values()];
}
}
export type Recording = {
when: Date;
session: IInteractiveEditorSession;
exchanges: { prompt: string; res: IInteractiveEditorResponse }[];
};

type TelemetryData = {
extension: string;
rounds: string;
undos: string;
edits: boolean;
terminalEdits: boolean;
startTime: string;
endTime: string;
editMode: string;
Expand All @@ -87,7 +71,6 @@ type TelemetryDataClassification = {
rounds: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Number of request that were made' };
undos: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Requests that have been undone' };
edits: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Did edits happen while the session was active' };
terminalEdits: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Did edits terminal the session' };
startTime: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'When the session started' };
endTime: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'When the session ended' };
editMode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What edit mode was choosen: live, livePreview, preview' };
Expand Down Expand Up @@ -227,8 +210,8 @@ export class EditResponse {
class Session {

private readonly _exchange: SessionExchange[] = [];

readonly teldata: TelemetryData;
private readonly _startTime = new Date();
private readonly _teldata: Partial<TelemetryData>;

constructor(
readonly editMode: EditMode,
Expand All @@ -237,12 +220,10 @@ class Session {
readonly provider: IInteractiveEditorSessionProvider,
readonly session: IInteractiveEditorSession,
) {
this.teldata = {
this._teldata = {
extension: provider.debugName,
startTime: new Date().toISOString(),
endTime: new Date().toISOString(),
startTime: this._startTime.toISOString(),
edits: false,
terminalEdits: false,
rounds: '',
undos: '',
editMode
Expand All @@ -251,12 +232,31 @@ class Session {

addExchange(exchange: SessionExchange): void {
const newLen = this._exchange.push(exchange);
this.teldata.rounds += `${newLen}|`;
this._teldata.rounds += `${newLen}|`;
}

get lastExchange(): SessionExchange | undefined {
return this._exchange[this._exchange.length - 1];
}

recordExternalEditOccurred() {
this._teldata.edits = true;
}

asTelemetryData(): TelemetryData {
return <TelemetryData>{
...this._teldata,
endTime: new Date().toISOString(),
};
}

asRecording(): Recording {
return {
session: this.session,
when: this._startTime,
exchanges: this._exchange.map(e => ({ prompt: e.prompt, res: e.response.raw }))
};
}
}

export interface InteractiveEditorRunOptions {
Expand Down Expand Up @@ -289,7 +289,6 @@ export class InteractiveEditorController implements IEditorContribution {
private _historyOffset: number = -1;

private readonly _store = new DisposableStore();
private readonly _recorder = new SessionRecorder();
private readonly _zone: InteractiveEditorZoneWidget;
private readonly _ctxHasActiveRequest: IContextKey<boolean>;
private readonly _ctxInlineDiff: IContextKey<boolean>;
Expand All @@ -302,13 +301,11 @@ export class InteractiveEditorController implements IEditorContribution {
private _inlineDiffEnabled: boolean = false;

private _currentSession?: Session;
private _recordings: Recording[] = [];

private _ctsSession: CancellationTokenSource = new CancellationTokenSource();
private _ctsRequest?: CancellationTokenSource;

private _requestPrompt: string | undefined;
private _messageReply: string | undefined;

constructor(
private readonly _editor: ICodeEditor,
@IInstantiationService private readonly _instaService: IInstantiationService,
Expand Down Expand Up @@ -347,8 +344,10 @@ export class InteractiveEditorController implements IEditorContribution {
}

viewInChat() {
if (this._messageReply && this._requestPrompt) {
this._instaService.invokeFunction(showMessageResponse, this._requestPrompt, this._messageReply);
if (this._currentSession?.lastExchange?.response instanceof MarkdownResponse) {

this._instaService.invokeFunction(showMessageResponse, this._currentSession.lastExchange.prompt, this._currentSession.lastExchange.response.raw.message.value);

}
}

Expand Down Expand Up @@ -380,7 +379,6 @@ export class InteractiveEditorController implements IEditorContribution {
this._logService.trace('[IE] NO session', provider.debugName);
return;
}
this._recorder.add(session, textModel);
this._logService.trace('[IE] NEW session', provider.debugName);

const store = new DisposableStore();
Expand Down Expand Up @@ -449,7 +447,7 @@ export class InteractiveEditorController implements IEditorContribution {
inlineDiffDecorations.clear();

// note when "other" edits happen
this._currentSession!.teldata.edits = true;
this._currentSession?.recordExternalEditOccurred();

// CANCEL if the document has changed outside the current range
const wholeRange = wholeRangeDecoration.getRange(0);
Expand Down Expand Up @@ -569,13 +567,10 @@ export class InteractiveEditorController implements IEditorContribution {
}


this._recorder.addExchange(session, request, reply);
this._zone.widget.updateToolbar(true);

if (reply.type === 'message') {
this._logService.info('[IE] received a MESSAGE, continuing outside editor', provider.debugName);
this._messageReply = reply.message.value;
this._requestPrompt = request.prompt;
const renderedMarkdown = renderMarkdown(reply.message, { inline: true });
this._zone.widget.updateStatus('');
this._zone.widget.updateMarkdownMessage(renderedMarkdown.element);
Expand Down Expand Up @@ -689,11 +684,15 @@ export class InteractiveEditorController implements IEditorContribution {


this._logService.trace('[IE] session DONE', provider.debugName);
this._currentSession.teldata.endTime = new Date().toISOString();
this._telemetryService.publicLog2<TelemetryData, TelemetryDataClassification>('interactiveEditor/session', this._currentSession.teldata);
this._telemetryService.publicLog2<TelemetryData, TelemetryDataClassification>('interactiveEditor/session', this._currentSession.asTelemetryData());

// done, cleanup
// keep recording
const newLen = this._recordings.unshift(this._currentSession.asRecording());
if (newLen > 5) {
this._recordings.pop();
}

// done, cleanup
diffZone.hide();
diffZone.dispose();

Expand Down Expand Up @@ -787,8 +786,8 @@ export class InteractiveEditorController implements IEditorContribution {
this._historyOffset = pos;
}

recordings() {
return this._recorder.getAll();
recordings(): Recording[] {
return this._recordings;
}

undoLast(): string | void {
Expand Down