Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/customSessionTitleServiceImpl.ts
13405 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 { CancellationToken, ChatContext, ChatRequestTurn2 } from 'vscode';
7
import { IVSCodeExtensionContext } from '../../../../platform/extContext/common/extensionContext';
8
import { ILogService } from '../../../../platform/log/common/logService';
9
import { SequencerByKey } from '../../../../util/vs/base/common/async';
10
import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';
11
import { ChatTitleProvider } from '../../../prompt/node/title';
12
import { IChatSessionMetadataStore } from '../../common/chatSessionMetadataStore';
13
import { ICustomSessionTitleService } from '../common/customSessionTitleService';
14
15
const CUSTOM_SESSION_TITLE_MEMENTO_KEY = 'github.copilot.cli.customSessionTitles';
16
17
export class CustomSessionTitleService implements ICustomSessionTitleService {
18
declare readonly _serviceBrand: undefined;
19
private readonly _keyedSessionGenerator = new SequencerByKey<string>();
20
21
constructor(
22
@IVSCodeExtensionContext private readonly context: IVSCodeExtensionContext,
23
@IInstantiationService private readonly instantiationService: IInstantiationService,
24
@ILogService private readonly logService: ILogService,
25
@IChatSessionMetadataStore private readonly chatSessionMetadataStore: IChatSessionMetadataStore,
26
) { }
27
28
private _getCustomSessionTitles(): { [sessionId: string]: { title: string; updatedAt: number } | undefined } {
29
return this.context.globalState.get<{ [sessionId: string]: { title: string; updatedAt: number } | undefined }>(CUSTOM_SESSION_TITLE_MEMENTO_KEY, {});
30
}
31
32
public async getCustomSessionTitle(sessionId: string): Promise<string | undefined> {
33
// First check the metadata store (new storage location).
34
const metadataTitle = await this.chatSessionMetadataStore.getCustomTitle(sessionId);
35
if (metadataTitle) {
36
return metadataTitle;
37
}
38
39
// Fall back to global storage (legacy) and migrate if found.
40
const entries = this._getCustomSessionTitles();
41
const entry = entries[sessionId];
42
if (!entry) {
43
return undefined;
44
}
45
delete entries[sessionId];
46
47
// Migrate: store in metadata file and remove from global storage.
48
await Promise.all([
49
this.chatSessionMetadataStore.setCustomTitle(sessionId, entry.title),
50
this.context.globalState.update(CUSTOM_SESSION_TITLE_MEMENTO_KEY, Object.keys(entries).length > 0 ? entries : undefined)
51
]);
52
53
return entry.title;
54
}
55
56
public async setCustomSessionTitle(sessionId: string, title: string): Promise<void> {
57
await this.chatSessionMetadataStore.setCustomTitle(sessionId, title);
58
}
59
60
public async generateSessionTitle(sessionId: string, request: { prompt?: string; command?: string }, token: CancellationToken): Promise<string | undefined> {
61
return this._keyedSessionGenerator.queue(sessionId, () => this.generateSessionTitleImpl(sessionId, request, token));
62
}
63
64
private async generateSessionTitleImpl(sessionId: string, request: { prompt?: string; command?: string }, token: CancellationToken): Promise<string | undefined> {
65
if (!request.prompt && !request.command) {
66
return undefined;
67
}
68
try {
69
const titleProvider = this.instantiationService.createInstance(ChatTitleProvider);
70
// Construct a minimal ChatContext with the current request as a history entry so provideChatTitle can find it
71
const requestTurn = new ChatRequestTurn2(request.prompt ?? '', request.command, [], '', [], [], undefined, undefined, undefined);
72
const fakeContext: ChatContext = {
73
history: [requestTurn],
74
yieldRequested: false,
75
};
76
const title = await titleProvider.provideChatTitle(fakeContext, token);
77
if (title) {
78
return title;
79
}
80
} catch (error) {
81
this.logService.error('Failed to generate session title', error);
82
}
83
}
84
85
}
86
87