UNPKG

chrome-devtools-frontend

Version:
130 lines (109 loc) 3.51 kB
// Copyright 2024 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Common from '../../core/common/common.js'; import {type ResponseData, ResponseType} from './agents/AiAgent.js'; export const enum ConversationType { STYLING = 'freestyler', FILE = 'drjones-file', NETWORK = 'drjones-network-request', PERFORMANCE = 'drjones-performance', PERFORMANCE_INSIGHT = 'performance-insight', } export interface SerializedConversation { id: string; type: ConversationType; history: ResponseData[]; } export class Conversation { static fromSerialized(serialized: SerializedConversation): Conversation { return new Conversation(serialized.type, serialized.history, serialized.id, true); } readonly id: string; readonly history: ResponseData[]; readonly type: ConversationType; readonly isReadOnly: boolean; constructor(type: ConversationType, data: ResponseData[] = [], id: string = crypto.randomUUID(), isReadOnly = true) { this.type = type; this.history = data; this.id = id; this.isReadOnly = isReadOnly; } get title(): string|undefined { return this.history .filter(response => { return response.type === ResponseType.USER_QUERY; }) .at(0) ?.query; } get isEmpty(): boolean { return this.history.length === 0; } addHistoryItem(item: ResponseData): void { this.history.push(item); void AiHistoryStorage.instance().upsertHistoryEntry(this.serialize()); } serialize(): SerializedConversation { return { id: this.id, history: this.history, type: this.type, }; } } let instance: AiHistoryStorage|null = null; export class AiHistoryStorage { #historySetting: Common.Settings.Setting<SerializedConversation[]>; #mutex = new Common.Mutex.Mutex(); constructor() { // This should not throw as we should be creating the setting in the `-meta.ts` file this.#historySetting = Common.Settings.Settings.instance().createSetting('ai-assistance-history-entries', []); } clearForTest(): void { this.#historySetting.set([]); } async upsertHistoryEntry(agentEntry: SerializedConversation): Promise<void> { const release = await this.#mutex.acquire(); try { const history = structuredClone(await this.#historySetting.forceGet()); const historyEntryIndex = history.findIndex(entry => entry.id === agentEntry.id); if (historyEntryIndex !== -1) { history[historyEntryIndex] = agentEntry; } else { history.push(agentEntry); } this.#historySetting.set(history); } finally { release(); } } async deleteHistoryEntry(id: string): Promise<void> { const release = await this.#mutex.acquire(); try { const history = structuredClone(await this.#historySetting.forceGet()); this.#historySetting.set( history.filter(entry => entry.id !== id), ); } finally { release(); } } async deleteAll(): Promise<void> { const release = await this.#mutex.acquire(); try { this.#historySetting.set([]); } finally { release(); } } getHistory(): SerializedConversation[] { return structuredClone(this.#historySetting.get()); } static instance(forceNew = false): AiHistoryStorage { if (!instance || forceNew) { instance = new AiHistoryStorage(); } return instance; } }