page-assist/src/db/index.ts
n4ze3m 7c805cfe22 feat: Include generation info in message history
Adds the `generationInfo` field to the message history output to provide more context about the message's origin. This will be helpful for debugging and understanding the provenance of messages.
2024-11-09 15:20:16 +05:30

519 lines
12 KiB
TypeScript

import {
type ChatHistory as ChatHistoryType,
type Message as MessageType
} from "~/store/option"
type HistoryInfo = {
id: string
title: string
is_rag: boolean
message_source?: "copilot" | "web-ui"
is_pinned?: boolean
createdAt: number
}
type WebSearch = {
search_engine: string
search_url: string
search_query: string
search_results: {
title: string
link: string
}[]
}
type Message = {
id: string
history_id: string
name: string
role: string
content: string
images?: string[]
sources?: string[]
search?: WebSearch
createdAt: number
messageType?: string
generationInfo?: any
}
type Webshare = {
id: string
title: string
url: string
api_url: string
share_id: string
createdAt: number
}
type Prompt = {
id: string
title: string
content: string
is_system: boolean
createdBy?: string
createdAt: number
}
type MessageHistory = Message[]
type ChatHistory = HistoryInfo[]
type Prompts = Prompt[]
export class PageAssitDatabase {
db: chrome.storage.StorageArea
constructor() {
this.db = chrome.storage.local
}
async getChatHistory(id: string): Promise<MessageHistory> {
return new Promise((resolve, reject) => {
this.db.get(id, (result) => {
resolve(result[id] || [])
})
})
}
async getChatHistories(): Promise<ChatHistory> {
return new Promise((resolve, reject) => {
this.db.get("chatHistories", (result) => {
resolve(result.chatHistories || [])
})
})
}
async getChatHistoryTitleById(id: string): Promise<string> {
const chatHistories = await this.getChatHistories()
const chatHistory = chatHistories.find((history) => history.id === id)
return chatHistory?.title || ""
}
async addChatHistory(history: HistoryInfo) {
const chatHistories = await this.getChatHistories()
const newChatHistories = [history, ...chatHistories]
this.db.set({ chatHistories: newChatHistories })
}
async addMessage(message: Message) {
const history_id = message.history_id
const chatHistory = await this.getChatHistory(history_id)
const newChatHistory = [message, ...chatHistory]
await this.db.set({ [history_id]: newChatHistory })
}
async removeChatHistory(id: string) {
const chatHistories = await this.getChatHistories()
const newChatHistories = chatHistories.filter(
(history) => history.id !== id
)
this.db.set({ chatHistories: newChatHistories })
}
async removeMessage(history_id: string, message_id: string) {
const chatHistory = await this.getChatHistory(history_id)
const newChatHistory = chatHistory.filter(
(message) => message.id !== message_id
)
this.db.set({ [history_id]: newChatHistory })
}
async clear() {
this.db.clear()
}
async deleteChatHistory(id: string) {
const chatHistories = await this.getChatHistories()
const newChatHistories = chatHistories.filter(
(history) => history.id !== id
)
this.db.set({ chatHistories: newChatHistories })
this.db.remove(id)
}
async deleteAllChatHistory() {
const chatHistories = await this.getChatHistories()
chatHistories.forEach((history) => {
this.db.remove(history.id)
})
this.db.set({ chatHistories: [] })
}
async deleteMessage(history_id: string) {
await this.db.remove(history_id)
}
async getAllPrompts(): Promise<Prompts> {
return new Promise((resolve, reject) => {
this.db.get("prompts", (result) => {
resolve(result.prompts || [])
})
})
}
async addPrompt(prompt: Prompt) {
const prompts = await this.getAllPrompts()
const newPrompts = [prompt, ...prompts]
this.db.set({ prompts: newPrompts })
}
async deletePrompt(id: string) {
const prompts = await this.getAllPrompts()
const newPrompts = prompts.filter((prompt) => prompt.id !== id)
this.db.set({ prompts: newPrompts })
}
async updatePrompt(
id: string,
title: string,
content: string,
is_system: boolean
) {
const prompts = await this.getAllPrompts()
const newPrompts = prompts.map((prompt) => {
if (prompt.id === id) {
prompt.title = title
prompt.content = content
prompt.is_system = is_system
}
return prompt
})
this.db.set({ prompts: newPrompts })
}
async getPromptById(id: string) {
const prompts = await this.getAllPrompts()
return prompts.find((prompt) => prompt.id === id)
}
async getWebshare(id: string) {
return new Promise((resolve, reject) => {
this.db.get(id, (result) => {
resolve(result[id] || [])
})
})
}
async getAllWebshares(): Promise<Webshare[]> {
return new Promise((resolve, reject) => {
this.db.get("webshares", (result) => {
resolve(result.webshares || [])
})
})
}
async addWebshare(webshare: Webshare) {
const webshares = await this.getAllWebshares()
const newWebshares = [webshare, ...webshares]
this.db.set({ webshares: newWebshares })
}
async deleteWebshare(id: string) {
const webshares = await this.getAllWebshares()
const newWebshares = webshares.filter((webshare) => webshare.id !== id)
this.db.set({ webshares: newWebshares })
}
async getUserID() {
return new Promise((resolve, reject) => {
this.db.get("user_id", (result) => {
resolve(result.user_id || "")
})
})
}
async setUserID(id: string) {
this.db.set({ user_id: id })
}
}
export const generateID = () => {
return "pa_xxxx-xxxx-xxx-xxxx".replace(/[x]/g, () => {
const r = Math.floor(Math.random() * 16)
return r.toString(16)
})
}
export const saveHistory = async (
title: string,
is_rag?: boolean,
message_source?: "copilot" | "web-ui"
) => {
const id = generateID()
const createdAt = Date.now()
const history = { id, title, createdAt, is_rag, message_source }
const db = new PageAssitDatabase()
await db.addChatHistory(history)
return history
}
export const saveMessage = async (
history_id: string,
name: string,
role: string,
content: string,
images: string[],
source?: any[],
time?: number,
message_type?: string,
generationInfo?: any
) => {
const id = generateID()
let createdAt = Date.now()
if (time) {
createdAt += time
}
const message = {
id,
history_id,
name,
role,
content,
images,
createdAt,
sources: source,
messageType: message_type,
generationInfo: generationInfo
}
const db = new PageAssitDatabase()
await db.addMessage(message)
return message
}
export const formatToChatHistory = (
messages: MessageHistory
): ChatHistoryType => {
messages.sort((a, b) => a.createdAt - b.createdAt)
return messages.map((message) => {
return {
content: message.content,
role: message.role as "user" | "assistant" | "system",
images: message.images
}
})
}
export const formatToMessage = (messages: MessageHistory): MessageType[] => {
messages.sort((a, b) => a.createdAt - b.createdAt)
return messages.map((message) => {
return {
isBot: message.role === "assistant",
message: message.content,
name: message.name,
sources: message?.sources || [],
images: message.images || [],
generationInfo: message?.generationInfo,
}
})
}
export const deleteByHistoryId = async (history_id: string) => {
const db = new PageAssitDatabase()
await db.deleteMessage(history_id)
await db.removeChatHistory(history_id)
return history_id
}
export const updateHistory = async (id: string, title: string) => {
const db = new PageAssitDatabase()
const chatHistories = await db.getChatHistories()
const newChatHistories = chatHistories.map((history) => {
if (history.id === id) {
history.title = title
}
return history
})
db.db.set({ chatHistories: newChatHistories })
}
export const pinHistory = async (id: string, is_pinned: boolean) => {
const db = new PageAssitDatabase()
const chatHistories = await db.getChatHistories()
const newChatHistories = chatHistories.map((history) => {
if (history.id === id) {
history.is_pinned = is_pinned
}
return history
})
db.db.set({ chatHistories: newChatHistories })
}
export const removeMessageUsingHistoryId = async (history_id: string) => {
const db = new PageAssitDatabase()
const chatHistory = await db.getChatHistory(history_id)
chatHistory.shift()
await db.db.set({ [history_id]: chatHistory })
}
export const getAllPrompts = async () => {
const db = new PageAssitDatabase()
return await db.getAllPrompts()
}
export const updateMessageByIndex = async (
history_id: string,
index: number,
message: string
) => {
const db = new PageAssitDatabase()
const chatHistory = (await db.getChatHistory(history_id)).reverse()
chatHistory[index].content = message
await db.db.set({ [history_id]: chatHistory.reverse() })
}
export const deleteChatForEdit = async (history_id: string, index: number) => {
const db = new PageAssitDatabase()
const chatHistory = (await db.getChatHistory(history_id)).reverse()
const previousHistory = chatHistory.slice(0, index + 1)
// console.log(previousHistory)
await db.db.set({ [history_id]: previousHistory.reverse() })
}
export const savePrompt = async ({
content,
title,
is_system = false
}: {
title: string
content: string
is_system: boolean
}) => {
const db = new PageAssitDatabase()
const id = generateID()
const createdAt = Date.now()
const prompt = { id, title, content, is_system, createdAt }
await db.addPrompt(prompt)
return prompt
}
export const deletePromptById = async (id: string) => {
const db = new PageAssitDatabase()
await db.deletePrompt(id)
return id
}
export const updatePrompt = async ({
content,
id,
title,
is_system
}: {
id: string
title: string
content: string
is_system: boolean
}) => {
const db = new PageAssitDatabase()
await db.updatePrompt(id, title, content, is_system)
return id
}
export const getPromptById = async (id: string) => {
if (!id || id.trim() === "") return null
const db = new PageAssitDatabase()
return await db.getPromptById(id)
}
export const getAllWebshares = async () => {
const db = new PageAssitDatabase()
return await db.getAllWebshares()
}
export const deleteWebshare = async (id: string) => {
const db = new PageAssitDatabase()
await db.deleteWebshare(id)
return id
}
export const saveWebshare = async ({
title,
url,
api_url,
share_id
}: {
title: string
url: string
api_url: string
share_id: string
}) => {
const db = new PageAssitDatabase()
const id = generateID()
const createdAt = Date.now()
const webshare = { id, title, url, share_id, createdAt, api_url }
await db.addWebshare(webshare)
return webshare
}
export const getUserId = async () => {
const db = new PageAssitDatabase()
const id = (await db.getUserID()) as string
if (!id || id?.trim() === "") {
const user_id = "user_xxxx-xxxx-xxx-xxxx-xxxx".replace(/[x]/g, () => {
const r = Math.floor(Math.random() * 16)
return r.toString(16)
})
db.setUserID(user_id)
return user_id
}
return id
}
export const exportChatHistory = async () => {
const db = new PageAssitDatabase()
const chatHistories = await db.getChatHistories()
const messages = await Promise.all(
chatHistories.map(async (history) => {
const messages = await db.getChatHistory(history.id)
return { history, messages }
})
)
return messages
}
export const importChatHistory = async (
data: {
history: HistoryInfo
messages: MessageHistory
}[]
) => {
const db = new PageAssitDatabase()
for (const { history, messages } of data) {
await db.addChatHistory(history)
for (const message of messages) {
await db.addMessage(message)
}
}
}
export const exportPrompts = async () => {
const db = new PageAssitDatabase()
return await db.getAllPrompts()
}
export const importPrompts = async (prompts: Prompts) => {
const db = new PageAssitDatabase()
for (const prompt of prompts) {
await db.addPrompt(prompt)
}
}
export const getRecentChatFromCopilot = async () => {
const db = new PageAssitDatabase()
const chatHistories = await db.getChatHistories()
if (chatHistories.length === 0) return null
const history = chatHistories.find(
(history) => history.message_source === "copilot"
)
if (!history) return null
const messages = await db.getChatHistory(history.id)
return { history, messages }
}
export const getTitleById = async (id: string) => {
const db = new PageAssitDatabase()
const title = await db.getChatHistoryTitleById(id)
return title
}