Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/browser/mainThreadChatDebug.ts
13397 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, MutableDisposable } from '../../../base/common/lifecycle.js';
7
import { URI } from '../../../base/common/uri.js';
8
import { VSBuffer } from '../../../base/common/buffer.js';
9
import { ChatDebugHookResult, ChatDebugLogLevel, IChatDebugEvent, IChatDebugResolvedEventContent, IChatDebugService } from '../../contrib/chat/common/chatDebugService.js';
10
import { IChatService } from '../../contrib/chat/common/chatService/chatService.js';
11
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
12
import { ExtHostChatDebugShape, ExtHostContext, IChatDebugEventDto, IChatDebugResolvedEventContentDto, MainContext, MainThreadChatDebugShape } from '../common/extHost.protocol.js';
13
import { Proxied } from '../../services/extensions/common/proxyIdentifier.js';
14
15
@extHostNamedCustomer(MainContext.MainThreadChatDebug)
16
export class MainThreadChatDebug extends Disposable implements MainThreadChatDebugShape {
17
private readonly _proxy: Proxied<ExtHostChatDebugShape>;
18
private readonly _providerDisposables = new Map<number, DisposableStore>();
19
private readonly _activeSessionResources = new Map<number, URI>();
20
private readonly _coreEventForwarder = this._register(new MutableDisposable());
21
22
constructor(
23
extHostContext: IExtHostContext,
24
@IChatDebugService private readonly _chatDebugService: IChatDebugService,
25
@IChatService private readonly _chatService: IChatService,
26
) {
27
super();
28
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostChatDebug);
29
}
30
31
$subscribeToCoreDebugEvents(): void {
32
this._coreEventForwarder.value = this._chatDebugService.onDidAddEvent(event => {
33
if (this._chatDebugService.isCoreEvent(event)) {
34
this._proxy.$onCoreDebugEvent(this._serializeEvent(event));
35
}
36
});
37
}
38
39
$unsubscribeFromCoreDebugEvents(): void {
40
this._coreEventForwarder.clear();
41
}
42
43
$registerChatDebugLogProvider(handle: number): void {
44
const disposables = new DisposableStore();
45
this._providerDisposables.set(handle, disposables);
46
47
disposables.add(this._chatDebugService.registerProvider({
48
provideChatDebugLog: async (sessionResource, token) => {
49
this._activeSessionResources.set(handle, sessionResource);
50
const dtos = await this._proxy.$provideChatDebugLog(handle, sessionResource, token);
51
return dtos?.map(dto => this._reviveEvent(dto, sessionResource));
52
},
53
resolveChatDebugLogEvent: async (eventId, token) => {
54
const dto = await this._proxy.$resolveChatDebugLogEvent(handle, eventId, token);
55
return dto ? this._reviveResolvedContent(dto) : undefined;
56
},
57
provideChatDebugLogExport: async (sessionResource, token) => {
58
// Gather core events and session title to pass to the extension.
59
const coreEventDtos = this._chatDebugService.getEvents(sessionResource)
60
.filter(e => this._chatDebugService.isCoreEvent(e))
61
.map(e => this._serializeEvent(e));
62
const sessionTitle = this._chatService.getSessionTitle(sessionResource);
63
const result = await this._proxy.$exportChatDebugLog(handle, sessionResource, coreEventDtos, sessionTitle, token);
64
return result?.buffer;
65
},
66
resolveChatDebugLogImport: async (data, token) => {
67
const result = await this._proxy.$importChatDebugLog(handle, VSBuffer.wrap(data), token);
68
if (!result) {
69
return undefined;
70
}
71
const uri = URI.revive(result.uri);
72
if (result.sessionTitle) {
73
this._chatDebugService.setImportedSessionTitle(uri, result.sessionTitle);
74
}
75
return uri;
76
}
77
}));
78
79
// Register a lazy fetcher so historical sessions are loaded from the
80
// extension only when the debug panel home page first needs them.
81
this._chatDebugService.registerAvailableSessionsFetcher(async (token) => {
82
const entries = await this._proxy.$getAvailableDebugSessionResources(handle, token);
83
return entries.map(e => ({ uri: URI.revive(e.uri), title: e.title }));
84
});
85
}
86
87
$unregisterChatDebugLogProvider(handle: number): void {
88
const disposables = this._providerDisposables.get(handle);
89
disposables?.dispose();
90
this._providerDisposables.delete(handle);
91
this._activeSessionResources.delete(handle);
92
}
93
94
$acceptChatDebugEvent(handle: number, dto: IChatDebugEventDto): void {
95
const sessionResource = (dto.sessionResource ? URI.revive(dto.sessionResource) : undefined)
96
?? this._activeSessionResources.get(handle)
97
?? this._chatDebugService.activeSessionResource;
98
if (!sessionResource) {
99
return;
100
}
101
const revived = this._reviveEvent(dto, sessionResource);
102
this._chatDebugService.addProviderEvent(revived);
103
}
104
105
private _serializeEvent(event: IChatDebugEvent): IChatDebugEventDto {
106
const base = {
107
id: event.id,
108
sessionResource: event.sessionResource,
109
created: event.created.getTime(),
110
parentEventId: event.parentEventId,
111
};
112
113
switch (event.kind) {
114
case 'toolCall':
115
return { ...base, kind: 'toolCall', toolName: event.toolName, toolCallId: event.toolCallId, input: event.input, output: event.output, result: event.result, durationInMillis: event.durationInMillis };
116
case 'modelTurn':
117
return { ...base, kind: 'modelTurn', model: event.model, requestName: event.requestName, inputTokens: event.inputTokens, outputTokens: event.outputTokens, cachedTokens: event.cachedTokens, totalTokens: event.totalTokens, durationInMillis: event.durationInMillis };
118
case 'generic':
119
return { ...base, kind: 'generic', name: event.name, details: event.details, level: event.level, category: event.category };
120
case 'subagentInvocation':
121
return { ...base, kind: 'subagentInvocation', agentName: event.agentName, description: event.description, status: event.status, durationInMillis: event.durationInMillis, toolCallCount: event.toolCallCount, modelTurnCount: event.modelTurnCount };
122
case 'userMessage':
123
return { ...base, kind: 'userMessage', message: event.message, sections: event.sections.map(s => ({ name: s.name, content: s.content })) };
124
case 'agentResponse':
125
return { ...base, kind: 'agentResponse', message: event.message, sections: event.sections.map(s => ({ name: s.name, content: s.content })) };
126
}
127
}
128
129
private _reviveEvent(dto: IChatDebugEventDto, sessionResource: URI): IChatDebugEvent {
130
const base = {
131
id: dto.id,
132
sessionResource,
133
created: new Date(dto.created),
134
parentEventId: dto.parentEventId,
135
};
136
137
switch (dto.kind) {
138
case 'toolCall':
139
return {
140
...base,
141
kind: 'toolCall',
142
toolName: dto.toolName,
143
toolCallId: dto.toolCallId,
144
input: dto.input,
145
output: dto.output,
146
result: dto.result,
147
durationInMillis: dto.durationInMillis,
148
};
149
case 'modelTurn':
150
return {
151
...base,
152
kind: 'modelTurn',
153
model: dto.model,
154
requestName: dto.requestName,
155
inputTokens: dto.inputTokens,
156
outputTokens: dto.outputTokens,
157
cachedTokens: dto.cachedTokens,
158
totalTokens: dto.totalTokens,
159
durationInMillis: dto.durationInMillis,
160
};
161
case 'generic':
162
return {
163
...base,
164
kind: 'generic',
165
name: dto.name,
166
details: dto.details,
167
level: dto.level as ChatDebugLogLevel,
168
category: dto.category,
169
};
170
case 'subagentInvocation':
171
return {
172
...base,
173
kind: 'subagentInvocation',
174
agentName: dto.agentName,
175
description: dto.description,
176
status: dto.status,
177
durationInMillis: dto.durationInMillis,
178
toolCallCount: dto.toolCallCount,
179
modelTurnCount: dto.modelTurnCount,
180
};
181
case 'userMessage':
182
return {
183
...base,
184
kind: 'userMessage',
185
message: dto.message,
186
sections: dto.sections,
187
};
188
case 'agentResponse':
189
return {
190
...base,
191
kind: 'agentResponse',
192
message: dto.message,
193
sections: dto.sections,
194
};
195
}
196
}
197
198
private _reviveResolvedContent(dto: IChatDebugResolvedEventContentDto): IChatDebugResolvedEventContent {
199
switch (dto.kind) {
200
case 'text':
201
return { kind: 'text', value: dto.value };
202
case 'message':
203
return {
204
kind: 'message',
205
type: dto.type,
206
message: dto.message,
207
sections: dto.sections,
208
};
209
case 'toolCall':
210
return {
211
kind: 'toolCall',
212
toolName: dto.toolName,
213
result: dto.result,
214
durationInMillis: dto.durationInMillis,
215
input: dto.input,
216
output: dto.output,
217
};
218
case 'modelTurn':
219
return {
220
kind: 'modelTurn',
221
requestName: dto.requestName,
222
model: dto.model,
223
status: dto.status,
224
durationInMillis: dto.durationInMillis,
225
inputTokens: dto.inputTokens,
226
outputTokens: dto.outputTokens,
227
cachedTokens: dto.cachedTokens,
228
totalTokens: dto.totalTokens,
229
errorMessage: dto.errorMessage,
230
sections: dto.sections,
231
};
232
case 'hook':
233
return {
234
kind: 'hook',
235
hookType: dto.hookType,
236
command: dto.command,
237
result: dto.result === 'success' ? ChatDebugHookResult.Success
238
: dto.result === 'error' ? ChatDebugHookResult.Error
239
: dto.result === 'nonBlockingError' ? ChatDebugHookResult.NonBlockingError
240
: undefined,
241
durationInMillis: dto.durationInMillis,
242
input: dto.input,
243
output: dto.output,
244
exitCode: dto.exitCode,
245
errorMessage: dto.errorMessage,
246
};
247
}
248
}
249
}
250
251