Skip to content

Commit 01d4949

Browse files
authored
Continue chat session seamlessly when EH crashes. (microsoft#186807)
Need to sync the existing ChatModel to the new EH and reinitialize it for the new chat provider instance. Fix microsoft/vscode-copilot#155
1 parent 134e152 commit 01d4949

File tree

3 files changed

+29
-14
lines changed

3 files changed

+29
-14
lines changed

src/vs/workbench/contrib/chat/browser/chatEditorInput.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ export class ChatEditorInput extends EditorInput {
101101

102102
this.sessionId = this.model.sessionId;
103103
this.providerId = this.model.providerId;
104-
await this.model.waitForInitialization();
105104
this._register(this.model.onDidChange(() => this._onDidChangeLabel.fire()));
106105

107106
return this._register(new ChatEditorModel(this.model));

src/vs/workbench/contrib/chat/common/chatModel.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ export interface IChatModel {
195195
readonly requestInProgress: boolean;
196196
readonly inputPlaceholder?: string;
197197
getRequests(): IChatRequestModel[];
198-
waitForInitialization(): Promise<void>;
199198
toExport(): IExportableChatData;
200199
toJSON(): ISerializableChatData;
201200
}
@@ -385,6 +384,11 @@ export class ChatModel extends Disposable implements IChatModel {
385384
});
386385
}
387386

387+
startReinitialize(): void {
388+
this._session = undefined;
389+
this._isInitializedDeferred = new DeferredPromise<void>();
390+
}
391+
388392
initialize(session: IChat, welcomeMessage: ChatWelcomeMessageModel | undefined): void {
389393
if (this._session || this._isInitializedDeferred.isSettled) {
390394
throw new Error('ChatModel is already initialized');

src/vs/workbench/contrib/chat/common/chatServiceImpl.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -287,13 +287,8 @@ export class ChatService extends Disposable implements IChatService {
287287
private _startSession(providerId: string, someSessionHistory: ISerializableChatData | undefined, token: CancellationToken): ChatModel {
288288
const model = this.instantiationService.createInstance(ChatModel, providerId, someSessionHistory);
289289
this._sessionModels.set(model.sessionId, model);
290-
const modelInitPromise = this.initializeSession(model, someSessionHistory, token);
291-
modelInitPromise.then(resolvedModel => {
292-
if (!resolvedModel) {
293-
model.dispose();
294-
this._sessionModels.delete(model.sessionId);
295-
}
296-
}).catch(err => {
290+
const modelInitPromise = this.initializeSession(model, token);
291+
modelInitPromise.catch(err => {
297292
this.trace('startSession', `initializeSession failed: ${err}`);
298293
model.setInitializationError(err);
299294
model.dispose();
@@ -303,7 +298,22 @@ export class ChatService extends Disposable implements IChatService {
303298
return model;
304299
}
305300

306-
private async initializeSession(model: ChatModel, sessionHistory: ISerializableChatData | undefined, token: CancellationToken): Promise<ChatModel | undefined> {
301+
private reinitializeModel(model: ChatModel): void {
302+
model.startReinitialize();
303+
this.startSessionInit(model, CancellationToken.None);
304+
}
305+
306+
private startSessionInit(model: ChatModel, token: CancellationToken): void {
307+
const modelInitPromise = this.initializeSession(model, token);
308+
modelInitPromise.catch(err => {
309+
this.trace('startSession', `initializeSession failed: ${err}`);
310+
model.setInitializationError(err);
311+
model.dispose();
312+
this._sessionModels.delete(model.sessionId);
313+
});
314+
}
315+
316+
private async initializeSession(model: ChatModel, token: CancellationToken): Promise<void> {
307317
await this.extensionService.activateByEvent(`onInteractiveSession:${model.providerId}`);
308318

309319
const provider = this._providers.get(model.providerId);
@@ -319,18 +329,16 @@ export class ChatService extends Disposable implements IChatService {
319329
}
320330

321331
if (!session) {
322-
this.trace('startSession', 'Provider returned no session');
323-
return undefined;
332+
throw new Error('Provider returned no session');
324333
}
325334

326335
this.trace('startSession', `Provider returned session`);
327336

328-
const welcomeMessage = sessionHistory ? undefined : withNullAsUndefined(await provider.provideWelcomeMessage?.(token));
337+
const welcomeMessage = model.welcomeMessage ? undefined : withNullAsUndefined(await provider.provideWelcomeMessage?.(token));
329338
const welcomeModel = welcomeMessage && new ChatWelcomeMessageModel(
330339
welcomeMessage.map(item => typeof item === 'string' ? new MarkdownString(item) : item as IChatReplyFollowup[]), session.responderUsername, session.responderAvatarIconUri);
331340

332341
model.initialize(session, welcomeModel);
333-
return model;
334342
}
335343

336344
getSession(sessionId: string): IChatModel | undefined {
@@ -618,6 +626,10 @@ export class ChatService extends Disposable implements IChatService {
618626
this._providers.set(provider.id, provider);
619627
this._hasProvider.set(true);
620628

629+
Array.from(this._sessionModels.values())
630+
.filter(model => model.providerId === provider.id)
631+
.forEach(model => this.reinitializeModel(model));
632+
621633
return toDisposable(() => {
622634
this.trace('registerProvider', `Disposing chat provider`);
623635
this._providers.delete(provider.id);

0 commit comments

Comments
 (0)