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

Save input value as IChatTransferData for restoring after transfer #188543

Merged
merged 2 commits into from
Jul 25, 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
9 changes: 8 additions & 1 deletion src/vs/workbench/api/browser/mainThreadChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape {
}

$transferChatSession(sessionId: number, toWorkspace: UriComponents): void {
this._chatService.transferChatSession(sessionId, URI.revive(toWorkspace));
const sessionIdStr = this._chatService.getSessionId(sessionId);
if (!sessionIdStr) {
throw new Error(`Failed to transfer session. Unknown session provider ID: ${sessionId}`);
}

const widget = this._chatWidgetService.getWidgetBySessionId(sessionIdStr);
const inputValue = widget?.inputEditor.getValue() ?? '';
this._chatService.transferChatSession({ sessionId: sessionIdStr, inputValue: inputValue }, URI.revive(toWorkspace));
}

async $registerChatProvider(handle: number, id: string): Promise<void> {
Expand Down
2 changes: 2 additions & 0 deletions src/vs/workbench/contrib/chat/browser/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export interface IChatWidgetService {
revealViewForProvider(providerId: string): Promise<IChatWidget | undefined>;

getWidgetByInputUri(uri: URI): IChatWidget | undefined;

getWidgetBySessionId(sessionId: string): IChatWidget | undefined;
}


Expand Down
13 changes: 10 additions & 3 deletions src/vs/workbench/contrib/chat/browser/chatViewPane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ export class ChatViewPane extends ViewPane implements IChatViewPane {
private updateModel(model?: IChatModel | undefined): void {
this.modelDisposables.clear();

model = model ?? (this.chatService.transferredSessionId
? this.chatService.getOrRestoreSession(this.chatService.transferredSessionId)
model = model ?? (this.chatService.transferredSessionData?.sessionId
? this.chatService.getOrRestoreSession(this.chatService.transferredSessionData.sessionId)
: this.chatService.startSession(this.chatViewOptions.providerId, CancellationToken.None));
if (!model) {
throw new Error('Could not start chat session');
Expand Down Expand Up @@ -102,7 +102,14 @@ export class ChatViewPane extends ViewPane implements IChatViewPane {
}));
this._widget.render(parent);

const sessionId = this.chatService.transferredSessionId ?? this.viewState.sessionId;
let sessionId: string | undefined;
if (this.chatService.transferredSessionData) {
sessionId = this.chatService.transferredSessionData.sessionId;
this.viewState.inputValue = this.chatService.transferredSessionData.inputValue;
} else {
sessionId = this.viewState.sessionId;
}

const initialModel = sessionId ? this.chatService.getOrRestoreSession(sessionId) : undefined;
this.updateModel(initialModel);
} catch (e) {
Expand Down
4 changes: 4 additions & 0 deletions src/vs/workbench/contrib/chat/browser/chatWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,10 @@ export class ChatWidgetService implements IChatWidgetService {
return this._widgets.find(w => isEqual(w.inputUri, uri));
}

getWidgetBySessionId(sessionId: string): ChatWidget | undefined {
return this._widgets.find(w => w.viewModel?.sessionId === sessionId);
}

async revealViewForProvider(providerId: string): Promise<ChatWidget | undefined> {
const viewId = this.chatContributionService.getViewIdForProvider(providerId);
const view = await this.viewsService.openView<ChatViewPane>(viewId);
Expand Down
10 changes: 8 additions & 2 deletions src/vs/workbench/contrib/chat/common/chatService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,18 +189,24 @@ export interface IChatProviderInfo {
displayName: string;
}

export interface IChatTransferredSessionData {
sessionId: string;
inputValue: string;
}

export const IChatService = createDecorator<IChatService>('IChatService');

export interface IChatService {
_serviceBrand: undefined;
transferredSessionId: string | undefined;
transferredSessionData: IChatTransferredSessionData | undefined;

onDidSubmitSlashCommand: Event<{ slashCommand: string; sessionId: string }>;
registerProvider(provider: IChatProvider): IDisposable;
registerSlashCommandProvider(provider: ISlashCommandProvider): IDisposable;
getProviderInfos(): IChatProviderInfo[];
startSession(providerId: string, token: CancellationToken): ChatModel | undefined;
getSession(sessionId: string): IChatModel | undefined;
getSessionId(sessionProviderId: number): string | undefined;
getOrRestoreSession(sessionId: string): IChatModel | undefined;
loadSessionFromContent(data: ISerializableChatData): IChatModel | undefined;

Expand All @@ -221,5 +227,5 @@ export interface IChatService {
onDidPerformUserAction: Event<IChatUserActionEvent>;
notifyUserAction(event: IChatUserActionEvent): void;

transferChatSession(sessionProviderId: number, toWorkspace: URI): void;
transferChatSession(transferredSessionData: IChatTransferredSessionData, toWorkspace: URI): void;
}
40 changes: 24 additions & 16 deletions src/vs/workbench/contrib/chat/common/chatServiceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys';
import { ChatModel, ChatWelcomeMessageModel, IChatModel, ISerializableChatData, ISerializableChatsData } from 'vs/workbench/contrib/chat/common/chatModel';
import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatProgress, IChatProvider, IChatProviderInfo, IChatReplyFollowup, IChatService, IChatUserActionEvent, ISlashCommand, ISlashCommandProvider, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatProgress, IChatProvider, IChatProviderInfo, IChatReplyFollowup, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, ISlashCommandProvider, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';

const serializedChatKey = 'interactive.sessions';
Expand All @@ -32,6 +32,7 @@ interface IChatTransfer {
toWorkspace: UriComponents;
timestampInMilliseconds: number;
chat: ISerializableChatData;
inputValue: string;
}
const SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS = 1000 * 60;

Expand Down Expand Up @@ -127,9 +128,9 @@ export class ChatService extends Disposable implements IChatService {
private readonly _persistedSessions: ISerializableChatsData;
private readonly _hasProvider: IContextKey<boolean>;

private _transferred: ISerializableChatData | undefined;
public get transferredSessionId(): string | undefined {
return this._transferred?.sessionId;
private _transferredSessionData: IChatTransferredSessionData | undefined;
public get transferredSessionData(): IChatTransferredSessionData | undefined {
return this._transferredSessionData;
}

private readonly _onDidPerformUserAction = this._register(new Emitter<IChatUserActionEvent>());
Expand Down Expand Up @@ -162,10 +163,12 @@ export class ChatService extends Disposable implements IChatService {
this._persistedSessions = {};
}

this._transferred = this.getTransferredSession();
if (this._transferred) {
this.trace('constructor', `Transferred session ${this._transferred.sessionId}`);
this._persistedSessions[this._transferred.sessionId] = this._transferred;
const transferredData = this.getTransferredSessionData();
const transferredChat = transferredData?.chat;
if (transferredChat) {
this.trace('constructor', `Transferred session ${transferredChat.sessionId}`);
this._persistedSessions[transferredChat.sessionId] = transferredChat;
this._transferredSessionData = { sessionId: transferredChat.sessionId, inputValue: transferredData.inputValue };
}

this._register(storageService.onWillSaveState(() => this.saveState()));
Expand Down Expand Up @@ -252,7 +255,7 @@ export class ChatService extends Disposable implements IChatService {
}
}

private getTransferredSession(): ISerializableChatData | undefined {
private getTransferredSessionData(): IChatTransfer | undefined {
const data: IChatTransfer[] = this.storageService.getObject(globalChatKey, StorageScope.PROFILE, []);
const workspaceUri = this.workspaceContextService.getWorkspace().folders[0]?.uri;
if (!workspaceUri) {
Expand All @@ -266,7 +269,7 @@ export class ChatService extends Disposable implements IChatService {
// Keep data that isn't for the current workspace and that hasn't expired yet
const filtered = data.filter(item => URI.revive(item.toWorkspace).toString() !== thisWorkspace && (currentTime - item.timestampInMilliseconds < SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS));
this.storageService.store(globalChatKey, JSON.stringify(filtered), StorageScope.PROFILE, StorageTarget.MACHINE);
return transferred?.chat;
return transferred;
}

getHistory(): IChatDetail[] {
Expand Down Expand Up @@ -355,6 +358,10 @@ export class ChatService extends Disposable implements IChatService {
return this._sessionModels.get(sessionId);
}

getSessionId(sessionProviderId: number): string | undefined {
return Iterable.find(this._sessionModels.values(), model => model.session?.id === sessionProviderId)?.sessionId;
}

getOrRestoreSession(sessionId: string): ChatModel | undefined {
const model = this._sessionModels.get(sessionId);
if (model) {
Expand All @@ -366,8 +373,8 @@ export class ChatService extends Disposable implements IChatService {
return undefined;
}

if (sessionId === this.transferredSessionId) {
this._transferred = undefined;
if (sessionId === this.transferredSessionData?.sessionId) {
this._transferredSessionData = undefined;
}

return this._startSession(sessionData.providerId, sessionData, CancellationToken.None);
Expand Down Expand Up @@ -671,17 +678,18 @@ export class ChatService extends Disposable implements IChatService {
});
}

transferChatSession(sessionProviderId: number, toWorkspace: URI): void {
const model = Iterable.find(this._sessionModels.values(), model => model.session?.id === sessionProviderId);
transferChatSession(transferredSessionData: IChatTransferredSessionData, toWorkspace: URI): void {
const model = Iterable.find(this._sessionModels.values(), model => model.sessionId === transferredSessionData.sessionId);
if (!model) {
throw new Error(`Failed to transfer session. Unknown session provider ID: ${sessionProviderId}`);
throw new Error(`Failed to transfer session. Unknown session ID: ${transferredSessionData.sessionId}`);
}

const existingRaw: IChatTransfer[] = this.storageService.getObject(globalChatKey, StorageScope.PROFILE, []);
existingRaw.push({
chat: model.toJSON(),
timestampInMilliseconds: Date.now(),
toWorkspace: toWorkspace
toWorkspace: toWorkspace,
inputValue: transferredSessionData.inputValue,
});

this.storageService.store(globalChatKey, JSON.stringify(existingRaw), StorageScope.PROFILE, StorageTarget.MACHINE);
Expand Down