Skip to content

Commit

Permalink
export embedded chat history
Browse files Browse the repository at this point in the history
  • Loading branch information
shatfield4 committed Sep 20, 2024
1 parent 5a3d55d commit 7c4da6e
Show file tree
Hide file tree
Showing 15 changed files with 186 additions and 47 deletions.
1 change: 1 addition & 0 deletions frontend/src/locales/de/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ const TRANSLATIONS = {

"embed-chats": {
title: "Eingebettete Chats",
export: "Exportieren",
description:
"Dies sind alle aufgezeichneten Chats und Nachrichten von jeder Einbettung, die Sie veröffentlicht haben.",
table: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/en/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ const TRANSLATIONS = {

"embed-chats": {
title: "Embed Chats",
export: "Export",
description:
"These are all the recorded chats and messages from any embed that you have published.",
table: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/es/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ const TRANSLATIONS = {

"embed-chats": {
title: "Incrustar chats",
export: "Exportar",
description:
"Estos son todos los chats y mensajes grabados de cualquier incrustación que hayas publicado.",
table: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/fr/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ const TRANSLATIONS = {

"embed-chats": {
title: "Chats intégrés",
export: "Exporter",
description:
"Voici tous les chats et messages enregistrés de tout widget intégré que vous avez publié.",
table: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/he/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,7 @@ const TRANSLATIONS = {

"embed-chats": {
title: "הטמעת שיחות",
export: "ייצוא",
description: "אלה כל השיחות וההודעות שנרשמו מכל הטמעה שפרסמת.",
table: {
embed: "הטמעה",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/it/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ const TRANSLATIONS = {

"embed-chats": {
title: "Chat incorporate",
export: "Esporta",
description:
"Queste sono tutte le chat e i messaggi registrati da qualsiasi embedding che hai pubblicato.",
table: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/ko/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ const TRANSLATIONS = {

"embed-chats": {
title: "임베드 채팅",
export: "내보내기",
description: "게시한 임베드에서의 모든 채팅과 메시지의 기록입니다.",
table: {
embed: "임베드",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/pt_BR/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ const TRANSLATIONS = {

"embed-chats": {
title: "Incorporar Chats",
export: "Exportar",
description:
"Estes são todos os chats e mensagens registrados de qualquer incorporação que você publicou.",
table: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/ru/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ const TRANSLATIONS = {
},
"embed-chats": {
title: "Встраивание чатов",
export: "Экспорт",
description:
"Это все записанные чаты и сообщения от любого встраивания, которое вы опубликовали.",
table: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/locales/zh/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ const TRANSLATIONS = {
// Embeddable Chat History
"embed-chats": {
title: "嵌入的聊天历史纪录",
export: "导出",
description: "这些是您发布的任何嵌入的所有记录的聊天和消息。",
table: {
embed: "嵌入",
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/models/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -577,9 +577,10 @@ const System = {
return { success: false, error: e.message };
});
},
exportChats: async (type = "csv") => {
exportChats: async (type = "csv", chatType = "workspace") => {
const url = new URL(`${fullApiUrl()}/system/export-chats`);
url.searchParams.append("type", encodeURIComponent(type));
url.searchParams.append("chatType", encodeURIComponent(chatType));
return await fetch(url, {
method: "GET",
headers: baseHeaders(),
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/GeneralSettings/Chats/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function WorkspaceChats() {
const { t } = useTranslation();

const handleDumpChats = async (exportType) => {
const chats = await System.exportChats(exportType);
const chats = await System.exportChats(exportType, "workspace");
if (!!chats) {
const { name, mimeType, fileExtension, filenameFunc } =
exportOptions[exportType];
Expand Down
112 changes: 110 additions & 2 deletions frontend/src/pages/GeneralSettings/EmbedChats/index.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useState, useRef } from "react";
import Sidebar from "@/components/SettingsSidebar";
import { isMobile } from "react-device-detect";
import * as Skeleton from "react-loading-skeleton";
Expand All @@ -7,10 +7,86 @@ import useQuery from "@/hooks/useQuery";
import ChatRow from "./ChatRow";
import Embed from "@/models/embed";
import { useTranslation } from "react-i18next";
import { CaretDown, Download } from "@phosphor-icons/react";
import showToast from "@/utils/toast";
import { saveAs } from "file-saver";
import System from "@/models/system";

const exportOptions = {
csv: {
name: "CSV",
mimeType: "text/csv",
fileExtension: "csv",
filenameFunc: () => {
return `anythingllm-embed-chats-${new Date().toLocaleDateString()}`;
},
},
json: {
name: "JSON",
mimeType: "application/json",
fileExtension: "json",
filenameFunc: () => {
return `anythingllm-embed-chats-${new Date().toLocaleDateString()}`;
},
},
jsonl: {
name: "JSONL",
mimeType: "application/jsonl",
fileExtension: "jsonl",
filenameFunc: () => {
return `anythingllm-embed-chats-${new Date().toLocaleDateString()}-lines`;
},
},
jsonAlpaca: {
name: "JSON (Alpaca)",
mimeType: "application/json",
fileExtension: "json",
filenameFunc: () => {
return `anythingllm-embed-chats-${new Date().toLocaleDateString()}-alpaca`;
},
},
};

export default function EmbedChats() {
// TODO [FEAT]: Add export of embed chats
const [showMenu, setShowMenu] = useState(false);
const menuRef = useRef();
const openMenuButton = useRef();
const { t } = useTranslation();

const handleDumpChats = async (exportType) => {
const chats = await System.exportChats(exportType, "embed");
if (!!chats) {
const { name, mimeType, fileExtension, filenameFunc } =
exportOptions[exportType];
const blob = new Blob([chats], { type: mimeType });
saveAs(blob, `${filenameFunc()}.${fileExtension}`);
showToast(`Embed chats exported successfully as ${name}.`, "success");
} else {
showToast("Failed to export embed chats.", "error");
}
};

const toggleMenu = () => {
setShowMenu(!showMenu);
};

useEffect(() => {
function handleClickOutside(event) {
if (
menuRef.current &&
!menuRef.current.contains(event.target) &&
!openMenuButton.current.contains(event.target)
) {
setShowMenu(false);
}
}

document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);

return (
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
<Sidebar />
Expand All @@ -24,6 +100,38 @@ export default function EmbedChats() {
<p className="text-lg leading-6 font-bold text-white">
{t("embed-chats.title")}
</p>
<div className="relative">
<button
ref={openMenuButton}
onClick={toggleMenu}
className="flex items-center gap-x-2 px-4 py-1 rounded-lg bg-primary-button hover:text-white text-xs font-semibold hover:bg-secondary shadow-[0_4px_14px_rgba(0,0,0,0.25)] h-[34px] w-fit"
>
<Download size={18} weight="bold" />
{t("embed-chats.export")}
<CaretDown size={18} weight="bold" />
</button>
<div
ref={menuRef}
className={`${
showMenu ? "slide-down" : "slide-up hidden"
} z-20 w-fit rounded-lg absolute top-full right-0 bg-secondary mt-2 shadow-md`}
>
<div className="py-2">
{Object.entries(exportOptions).map(([key, data]) => (
<button
key={key}
onClick={() => {
handleDumpChats(key);
setShowMenu(false);
}}
className="w-full text-left px-4 py-2 text-white text-sm hover:bg-[#3D4147]"
>
{data.name}
</button>
))}
</div>
</div>
</div>
</div>
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
{t("embed-chats.description")}
Expand Down
11 changes: 4 additions & 7 deletions server/endpoints/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ const {
isMultiUserSetup,
} = require("../utils/middleware/multiUserProtected");
const { fetchPfp, determinePfpFilepath } = require("../utils/files/pfp");
const {
prepareWorkspaceChatsForExport,
exportChatsAsType,
} = require("../utils/helpers/chat/convertTo");
const { exportChatsAsType } = require("../utils/helpers/chat/convertTo");
const { EventLogs } = require("../models/eventLogs");
const { CollectorApi } = require("../utils/collectorApi");
const {
Expand Down Expand Up @@ -1009,13 +1006,13 @@ function systemEndpoints(app) {
[validatedRequest, flexUserRoleValid([ROLES.manager, ROLES.admin])],
async (request, response) => {
try {
const { type = "jsonl" } = request.query;
const chats = await prepareWorkspaceChatsForExport(type);
const { contentType, data } = await exportChatsAsType(chats, type);
const { type = "jsonl", chatType = "workspace" } = request.query;
const { contentType, data } = await exportChatsAsType(type, chatType);
await EventLogs.logEvent(
"exported_chats",
{
type,
chatType,
},
response.locals.user?.id
);
Expand Down
Loading

0 comments on commit 7c4da6e

Please sign in to comment.