chrome-devtools-frontend
Version:
Chrome DevTools UI
130 lines (109 loc) • 3.51 kB
text/typescript
// 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;
}
}