feat: Add export/import functions for chat history, promt and knowledge
This commit is contained in:
parent
11147fd951
commit
677aa6ef51
@ -41,12 +41,12 @@
|
||||
"confirm": "Are you sure you want to delete your chat history? This action cannot be undone."
|
||||
},
|
||||
"export": {
|
||||
"label": "Export Chat History, Settings, and Prompts",
|
||||
"label": "Export Chat History, Knowledge Base, and Prompts",
|
||||
"button": "Export Data",
|
||||
"success": "Export Success"
|
||||
},
|
||||
"import": {
|
||||
"label": "Import Chat History, Settings, and Prompts",
|
||||
"label": "Import Chat History, Knowledge Base, and Prompts",
|
||||
"button": "Import Data",
|
||||
"success": "Import Success",
|
||||
"error": "Import Error"
|
||||
|
@ -44,12 +44,12 @@
|
||||
"confirm": "チャット履歴を削除してもよろしいですか?この操作は元に戻せません。"
|
||||
},
|
||||
"export": {
|
||||
"label": "チャット履歴、設定、プロンプトをエクスポートする",
|
||||
"label": "チャット履歴、知識ベース、プロンプトをエクスポート",
|
||||
"button": "データをエクスポート",
|
||||
"success": "エクスポート成功"
|
||||
},
|
||||
"import": {
|
||||
"label": "チャット履歴、設定、プロンプトをインポートする",
|
||||
"label": "チャット履歴、知識ベース、プロンプトをインポート",
|
||||
"button": "データをインポート",
|
||||
"success": "インポート成功",
|
||||
"error": "インポートエラー"
|
||||
|
@ -44,16 +44,16 @@
|
||||
"confirm": "നിങ്ങളുടെ ചാറ്റ് ചരിത്രം ഇല്ലാതാക്കണമെന്ന് ഉറപ്പാണോ? ഈ പ്രവർത്തനം പിന്നീട് പിന്വലിക്കാനാവില്ല."
|
||||
},
|
||||
"export": {
|
||||
"label": "ചാറ്റ് ചരിത്രം, ക്രമീകരണങ്ങൾ, പ്രോംപ്റ്റുകൾ എക്സ്പോർട്ട് ചെയ്യുക",
|
||||
"label": "ചാറ്റ് ചരിത്രം, അറിവ് അടിസ്ഥാനം, പ്രോംപ്റ്റുകൾ എക്സ്പോർട്ട് ചെയ്യുക",
|
||||
"button": "ഡാറ്റ എക്സ്പോർട്ട് ചെയ്യുക",
|
||||
"success": "എക്സ്പോർട്ട് വിജയകരമായി"
|
||||
},
|
||||
"import": {
|
||||
"label": "ചാറ്റ് ചരിത്രം, ക്രമീകരണങ്ങൾ, പ്രോംപ്റ്റുകൾ ഇമ്പോർട്ട് ചെയ്യുക",
|
||||
},
|
||||
"import": {
|
||||
"label": "ചാറ്റ് ചരിത്രം, അറിവ് അടിസ്ഥാനം, പ്രോംപ്റ്റുകൾ ഇമ്പോർട്ട് ചെയ്യുക",
|
||||
"button": "ഡാറ്റ ഇമ്പോർട്ട് ചെയ്യുക",
|
||||
"success": "ഇമ്പോർട്ട് വിജയകരമായി",
|
||||
"error": "ഇമ്പോർട്ട് പരാജയപ്പെട്ടു"
|
||||
}
|
||||
"error": "ഇമ്പോർട്ട് പിശക്"
|
||||
}
|
||||
},
|
||||
"tts": {
|
||||
"heading": "ടെക്സ്റ്റ്-ടു-സ്പീച്ച് ക്രമീകരണങ്ങൾ",
|
||||
|
@ -41,13 +41,13 @@
|
||||
"confirm": "Вы уверены, что хотите удалить историю чата? Это действие нельзя отменить."
|
||||
},
|
||||
"export": {
|
||||
"label": "Экспорт истории чата, настроек и подсказок",
|
||||
"button": "Экспорт данных",
|
||||
"label": "Экспорт истории чата, базы знаний и подсказок",
|
||||
"button": "Экспортировать данные",
|
||||
"success": "Экспорт успешен"
|
||||
},
|
||||
"import": {
|
||||
"label": "Импорт истории чата, настроек и подсказок",
|
||||
"button": "Импорт данных",
|
||||
"label": "Импорт истории чата, базы знаний и подсказок",
|
||||
"button": "Импортировать данные",
|
||||
"success": "Импорт успешен",
|
||||
"error": "Ошибка импорта"
|
||||
}
|
||||
|
@ -44,12 +44,12 @@
|
||||
"confirm": "您确定要删除聊天记录吗?此操作无法撤销。"
|
||||
},
|
||||
"export": {
|
||||
"label": "导出聊天记录、设置和提示",
|
||||
"label": "导出聊天记录、知识库和提示",
|
||||
"button": "导出数据",
|
||||
"success": "导出成功"
|
||||
},
|
||||
"import": {
|
||||
"label": "导入聊天记录、设置和提示",
|
||||
"label": "导入聊天记录、知识库和提示",
|
||||
"button": "导入数据",
|
||||
"success": "导入成功",
|
||||
"error": "导入错误"
|
||||
|
8
src/components/Common/Beta.tsx
Normal file
8
src/components/Common/Beta.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import { Tag } from "antd"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
export const BetaTag = () => {
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
return <Tag color="yellow">{t("beta")}</Tag>
|
||||
}
|
@ -10,6 +10,7 @@ import { useTranslation } from "react-i18next"
|
||||
import { Link, useLocation } from "react-router-dom"
|
||||
import { OllamaIcon } from "../Icons/Ollama"
|
||||
import { Tag } from "antd"
|
||||
import { BetaTag } from "../Common/Beta"
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
return classes.filter(Boolean).join(" ")
|
||||
@ -81,7 +82,7 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
name={
|
||||
<div className="inline-flex items-center gap-2">
|
||||
{t("manageKnowledge.title")}
|
||||
<Tag color="yellow">{t("common:beta")}</Tag>
|
||||
<BetaTag />
|
||||
</div>
|
||||
}
|
||||
icon={BlocksIcon}
|
||||
|
@ -9,6 +9,11 @@ import { SearchModeSettings } from "./search-mode"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useI18n } from "@/hooks/useI18n"
|
||||
import { TTSModeSettings } from "./tts-mode"
|
||||
import {
|
||||
exportPageAssistData,
|
||||
importPageAssistData
|
||||
} from "@/libs/export-import"
|
||||
import { BetaTag } from "@/components/Common/Beta"
|
||||
|
||||
export const SettingOther = () => {
|
||||
const { clearChat, speechToTextLanguage, setSpeechToTextLanguage } =
|
||||
@ -90,7 +95,7 @@ export const SettingOther = () => {
|
||||
</button>
|
||||
</div>
|
||||
<SearchModeSettings />
|
||||
<TTSModeSettings />
|
||||
<TTSModeSettings />
|
||||
<div>
|
||||
<div className="mb-5">
|
||||
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
|
||||
@ -98,7 +103,7 @@ export const SettingOther = () => {
|
||||
</h2>
|
||||
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between">
|
||||
<div className="flex flex-row mb-3 justify-between">
|
||||
<span className="text-gray-500 dark:text-neutral-50 ">
|
||||
{t("generalSettings.system.deleteChatHistory.label")}
|
||||
</span>
|
||||
@ -122,6 +127,37 @@ export const SettingOther = () => {
|
||||
{t("generalSettings.system.deleteChatHistory.button")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-row mb-3 justify-between">
|
||||
<span className="text-gray-500 dark:text-neutral-50 ">
|
||||
<BetaTag /> {t("generalSettings.system.export.label")}
|
||||
</span>
|
||||
<button
|
||||
onClick={exportPageAssistData}
|
||||
className="bg-gray-800 dark:bg-white text-white dark:text-gray-900 px-4 py-2 rounded-md cursor-pointer">
|
||||
{t("generalSettings.system.export.button")}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-row mb-3 justify-between">
|
||||
<span className="text-gray-500 dark:text-neutral-50 ">
|
||||
<BetaTag /> {t("generalSettings.system.import.label")}
|
||||
</span>
|
||||
<label
|
||||
htmlFor="import"
|
||||
className="bg-gray-800 dark:bg-white text-white dark:text-gray-900 px-4 py-2 rounded-md cursor-pointer">
|
||||
{t("generalSettings.system.import.button")}
|
||||
</label>
|
||||
<input
|
||||
type="file"
|
||||
accept=".json"
|
||||
id="import"
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
if (e.target.files) {
|
||||
importPageAssistData(e.target.files[0])
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</dl>
|
||||
)
|
||||
|
@ -444,3 +444,15 @@ export const importChatHistory = async (
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +112,22 @@ export class PageAssistVectorDb {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
saveImportedData = async (data: VectorData[]): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const obj: Record<string, VectorData> = {}
|
||||
data.forEach((d) => {
|
||||
obj[d.id] = d
|
||||
})
|
||||
this.db.set(obj, () => {
|
||||
if (chrome.runtime.lastError) {
|
||||
reject(chrome.runtime.lastError)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const insertVector = async (
|
||||
@ -148,7 +164,5 @@ export const exportVectors = async () => {
|
||||
|
||||
export const importVectors = async (data: VectorData[]) => {
|
||||
const db = new PageAssistVectorDb()
|
||||
for (const d of data) {
|
||||
await db.insertVector(d.id, d.vectors)
|
||||
}
|
||||
return db.saveImportedData(data)
|
||||
}
|
||||
|
66
src/libs/export-import.ts
Normal file
66
src/libs/export-import.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import {
|
||||
exportChatHistory,
|
||||
exportPrompts,
|
||||
importChatHistory,
|
||||
importPrompts
|
||||
} from "@/db"
|
||||
import { exportKnowledge, importKnowledge } from "@/db/knowledge"
|
||||
import { exportVectors, importVectors } from "@/db/vector"
|
||||
import { message } from "antd"
|
||||
|
||||
export const exportPageAssistData = async () => {
|
||||
const knowledge = await exportKnowledge()
|
||||
const chat = await exportChatHistory()
|
||||
const vector = await exportVectors()
|
||||
const prompts = await exportPrompts()
|
||||
|
||||
const data = {
|
||||
knowledge,
|
||||
chat,
|
||||
vector,
|
||||
prompts
|
||||
}
|
||||
|
||||
const dataStr = JSON.stringify(data)
|
||||
|
||||
const blob = new Blob([dataStr], { type: "application/json" })
|
||||
const url = URL.createObjectURL(blob)
|
||||
|
||||
const a = document.createElement("a")
|
||||
a.href = url
|
||||
a.download = `page-assist-${new Date().toISOString()}.json`
|
||||
a.click()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
export const importPageAssistData = async (file: File) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = async () => {
|
||||
try {
|
||||
const data = JSON.parse(reader.result as string)
|
||||
|
||||
if (data?.knowledge) {
|
||||
await importKnowledge(data.knowledge)
|
||||
}
|
||||
|
||||
if (data?.chat) {
|
||||
await importChatHistory(data.chat)
|
||||
}
|
||||
|
||||
if (data?.vector) {
|
||||
await importVectors(data.vector)
|
||||
}
|
||||
|
||||
if (data?.prompts) {
|
||||
await importPrompts(data.prompts)
|
||||
}
|
||||
|
||||
message.success("Data imported successfully")
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
message.error("Failed to import data")
|
||||
}
|
||||
}
|
||||
|
||||
reader.readAsText(file)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user