Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/editTelemetry/browser/aiContributionFeature.ts
13401 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js';
7
import { ResourceMap } from '../../../../base/common/map.js';
8
import { autorun } from '../../../../base/common/observable.js';
9
import { URI, UriComponents } from '../../../../base/common/uri.js';
10
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
11
import { AnnotatedDocument, IAnnotatedDocuments } from './helpers/annotatedDocuments.js';
12
import { createDocWithJustReason } from './helpers/documentWithAnnotatedEdits.js';
13
import { DocumentEditSourceTracker } from './telemetry/editTracker.js';
14
15
export type AiContributionLevel = 'chatAndAgent' | 'all';
16
17
interface TrackerEntry {
18
readonly trackerStore: DisposableStore;
19
readonly tracker: DocumentEditSourceTracker;
20
}
21
22
/**
23
* Tracks AI-generated edits across open documents using the edit telemetry pipeline.
24
*/
25
export class AiContributionFeature extends Disposable {
26
27
private readonly _trackers = new ResourceMap<TrackerEntry>();
28
private readonly _documentsByUri = new ResourceMap<AnnotatedDocument>();
29
30
constructor(
31
annotatedDocuments: IAnnotatedDocuments,
32
) {
33
super();
34
35
this._register(autorun(reader => {
36
const docs = annotatedDocuments.documents.read(reader);
37
const activeUris = new ResourceMap<boolean>();
38
39
for (const doc of docs) {
40
const uri = doc.document.uri;
41
activeUris.set(uri, true);
42
this._documentsByUri.set(uri, doc);
43
44
if (!this._trackers.has(uri)) {
45
this._trackers.set(uri, this._createTrackerEntry(doc));
46
}
47
}
48
49
for (const [uri, entry] of this._trackers) {
50
if (!activeUris.has(uri)) {
51
entry.trackerStore.dispose();
52
this._trackers.delete(uri);
53
this._documentsByUri.delete(uri);
54
}
55
}
56
}));
57
58
this._register(CommandsRegistry.registerCommand('_aiEdits.hasAiContributions', (_accessor, resources: UriComponents[], level: AiContributionLevel) => {
59
return this._hasAiContributions(resources, level);
60
}));
61
62
this._register(CommandsRegistry.registerCommand('_aiEdits.clearAiContributions', (_accessor, resources: UriComponents[]) => {
63
this._clearAiContributions(resources);
64
}));
65
66
this._register(CommandsRegistry.registerCommand('_aiEdits.clearAllAiContributions', () => {
67
this._clearAiContributions();
68
}));
69
}
70
71
override dispose(): void {
72
for (const [, entry] of this._trackers) {
73
entry.trackerStore.dispose();
74
}
75
super.dispose();
76
}
77
78
private _createTrackerEntry(doc: AnnotatedDocument): TrackerEntry {
79
const trackerStore = new DisposableStore();
80
const docWithJustReason = createDocWithJustReason(doc.documentWithAnnotations, trackerStore);
81
const tracker = trackerStore.add(new DocumentEditSourceTracker(docWithJustReason, undefined));
82
return { trackerStore, tracker };
83
}
84
85
private _hasAiContributions(resources: UriComponents[], level: AiContributionLevel): boolean {
86
for (const resource of resources) {
87
const entry = this._trackers.get(URI.revive(resource));
88
if (entry) {
89
for (const edit of entry.tracker.getTrackedRanges()) {
90
if (edit.source.category === 'ai' && (level === 'all' || edit.source.feature === 'chat')) {
91
return true;
92
}
93
}
94
}
95
}
96
return false;
97
}
98
99
private _clearAiContributions(resources?: UriComponents[]): void {
100
const uris = resources ? resources.map(r => URI.revive(r)) : [...this._trackers.keys()];
101
for (const uri of uris) {
102
const entry = this._trackers.get(uri);
103
if (entry) {
104
entry.trackerStore.dispose();
105
const doc = this._documentsByUri.get(uri);
106
if (doc) {
107
this._trackers.set(uri, this._createTrackerEntry(doc));
108
} else {
109
this._trackers.delete(uri);
110
this._documentsByUri.delete(uri);
111
}
112
}
113
}
114
}
115
}
116
117