Path: blob/main/src/vs/workbench/contrib/chat/browser/chatSessions/chatSessionTracker.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Disposable } from '../../../../../base/common/lifecycle.js';6import { Emitter } from '../../../../../base/common/event.js';7import { GroupModelChangeKind } from '../../../../common/editor.js';8import { IEditorGroup, IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js';9import { ChatEditorInput } from '../chatEditorInput.js';10import { EditorInput } from '../../../../common/editor/editorInput.js';11import { ChatSessionItemWithProvider, getChatSessionType, isChatSession } from './common.js';12import { ChatSessionStatus, IChatSessionItem, IChatSessionItemProvider } from '../../common/chatSessionsService.js';13import { ILocalChatSessionItem } from '../chatSessions.js';14import { IChatService } from '../../common/chatService.js';15import { Codicon } from '../../../../../base/common/codicons.js';16import { IChatModel } from '../../common/chatModel.js';1718export class ChatSessionTracker extends Disposable {19private readonly _onDidChangeEditors = this._register(new Emitter<{ sessionType: string; kind: GroupModelChangeKind }>());20readonly onDidChangeEditors = this._onDidChangeEditors.event;2122constructor(23@IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService,24@IChatService private readonly chatService: IChatService25) {26super();27this.setupEditorTracking();28}2930private setupEditorTracking(): void {31// Listen to all editor groups32this.editorGroupsService.groups.forEach(group => {33this.registerGroupListeners(group);34});3536// Listen for new groups37this._register(this.editorGroupsService.onDidAddGroup(group => {38this.registerGroupListeners(group);39}));40}4142private registerGroupListeners(group: IEditorGroup): void {43this._register(group.onDidModelChange(e => {44if (!isChatSession(e.editor)) {45return;46}4748const editor = e.editor as ChatEditorInput;49const sessionType = getChatSessionType(editor);5051// Emit targeted event for this session type52this._onDidChangeEditors.fire({ sessionType, kind: e.kind });53}));54}5556public getLocalEditorsForSessionType(sessionType: string): ChatEditorInput[] {57const localEditors: ChatEditorInput[] = [];5859this.editorGroupsService.groups.forEach(group => {60group.editors.forEach(editor => {61if (editor instanceof ChatEditorInput && getChatSessionType(editor) === sessionType) {62localEditors.push(editor);63}64});65});6667return localEditors;68}6970async getHybridSessionsForProvider(provider: IChatSessionItemProvider): Promise<IChatSessionItem[]> {71if (provider.chatSessionType === 'local') {72return []; // Local provider doesn't need hybrid sessions73}7475const localEditors = this.getLocalEditorsForSessionType(provider.chatSessionType);76const hybridSessions: (ILocalChatSessionItem & ChatSessionItemWithProvider)[] = [];7778localEditors.forEach((editor, index) => {79const group = this.findGroupForEditor(editor);80if (!group) {81return;82}83if (editor.options.ignoreInView) {84return;85}8687let status: ChatSessionStatus | undefined;88let timestamp: number | undefined;8990if (editor.sessionId) {91const model = this.chatService.getSession(editor.sessionId);92if (model) {93status = this.modelToStatus(model);94const requests = model.getRequests();95if (requests.length > 0) {96timestamp = requests[requests.length - 1].timestamp;97}98}99}100101const hybridSession: ILocalChatSessionItem & ChatSessionItemWithProvider = {102id: `${provider.chatSessionType}-local-${index}`,103label: editor.getName(),104iconPath: Codicon.chatSparkle,105editor,106group,107sessionType: 'editor',108status,109provider,110timing: {111startTime: timestamp ?? Date.now()112}113};114115hybridSessions.push(hybridSession);116});117118return hybridSessions;119}120121private findGroupForEditor(editor: EditorInput): IEditorGroup | undefined {122for (const group of this.editorGroupsService.groups) {123if (group.editors.includes(editor)) {124return group;125}126}127return undefined;128}129130private modelToStatus(model: IChatModel): ChatSessionStatus | undefined {131if (model.requestInProgress) {132return ChatSessionStatus.InProgress;133}134const requests = model.getRequests();135if (requests.length > 0) {136const lastRequest = requests[requests.length - 1];137if (lastRequest?.response) {138if (lastRequest.response.isCanceled || lastRequest.response.result?.errorDetails) {139return ChatSessionStatus.Failed;140} else if (lastRequest.response.isComplete) {141return ChatSessionStatus.Completed;142} else {143return ChatSessionStatus.InProgress;144}145}146}147return undefined;148}149}150151152