mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-13 02:00:10 +01:00
reset chat history
This commit is contained in:
parent
1bdc4b9975
commit
efc54be4ae
@ -3,7 +3,7 @@
|
||||
|
||||
<body>
|
||||
<h1>This is an example testing page for embedded AnythingLLM.</h1>
|
||||
<script data-embed-id="example-uuid" data-base-api-url='http://localhost:3001/api/embed'
|
||||
<script data-embed-id="example-uuid" data-base-api-url='http://localhost:3001/api/embed' data-open-on-load="on"
|
||||
src="/dist/embedded-anything-llm.umd.js">
|
||||
</script>
|
||||
</body>
|
||||
|
@ -12,7 +12,7 @@ export default function App() {
|
||||
const sessionId = useSessionId();
|
||||
|
||||
useEffect(() => {
|
||||
toggleOpenChat(embedSettings.openOnLoad === 'on')
|
||||
toggleOpenChat(embedSettings.openOnLoad === "on");
|
||||
}, [embedSettings.loaded]);
|
||||
|
||||
if (!embedSettings.loaded) return null;
|
||||
@ -25,10 +25,11 @@ export default function App() {
|
||||
width: isChatOpen ? 320 : "auto",
|
||||
height: isChatOpen ? "93vh" : "auto",
|
||||
}}
|
||||
className={`transition-all duration-300 ease-in-out ${isChatOpen
|
||||
? "max-w-md p-4 bg-white rounded-lg border shadow-lg w-72"
|
||||
: "w-16 h-16 rounded-full"
|
||||
}`}
|
||||
className={`transition-all duration-300 ease-in-out ${
|
||||
isChatOpen
|
||||
? "max-w-md p-4 bg-white rounded-lg border shadow-lg w-72"
|
||||
: "w-16 h-16 rounded-full"
|
||||
}`}
|
||||
>
|
||||
{isChatOpen && (
|
||||
<ChatWindow
|
||||
|
@ -46,11 +46,13 @@ export default function ChatHistory({ settings = {}, history = [] }) {
|
||||
|
||||
if (history.length === 0) {
|
||||
return (
|
||||
<div style={{ height: "85vh", paddingBottom: 100, paddingTop: 5 }}
|
||||
className="bg-gray-100 rounded-lg px-2 h-full mt-2 gap-y-2 overflow-y-scroll flex flex-col justify-start no-scroll">
|
||||
<div
|
||||
style={{ height: "85vh", paddingBottom: 100, paddingTop: 5 }}
|
||||
className="bg-gray-100 rounded-lg px-2 h-full mt-2 gap-y-2 overflow-y-scroll flex flex-col justify-start no-scroll"
|
||||
>
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<p className="text-slate-400 text-sm font-base py-4 text-center">
|
||||
{settings?.greeting ?? 'Send a chat to get started!'}
|
||||
{settings?.greeting ?? "Send a chat to get started!"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ChatHistory from "./ChatHistory";
|
||||
import PromptInput from "./PromptInput";
|
||||
import handleChat from "@/utils/chat";
|
||||
@ -12,6 +12,14 @@ export default function ChatContainer({
|
||||
const [message, setMessage] = useState("");
|
||||
const [loadingResponse, setLoadingResponse] = useState(false);
|
||||
const [chatHistory, setChatHistory] = useState(knownHistory);
|
||||
|
||||
// Resync history if the ref to known history changes
|
||||
// eg: cleared.
|
||||
useEffect(() => {
|
||||
if (knownHistory.length !== chatHistory.length)
|
||||
setChatHistory([...knownHistory]);
|
||||
}, [knownHistory]);
|
||||
|
||||
const handleMessageChange = (event) => {
|
||||
setMessage(event.target.value);
|
||||
};
|
||||
@ -68,7 +76,7 @@ export default function ChatContainer({
|
||||
}, [loadingResponse, chatHistory]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<ChatHistory settings={settings} history={chatHistory} />
|
||||
<PromptInput
|
||||
settings={settings}
|
||||
@ -78,6 +86,6 @@ export default function ChatContainer({
|
||||
inputDisabled={loadingResponse}
|
||||
buttonDisabled={loadingResponse}
|
||||
/>
|
||||
</>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -1,16 +1,66 @@
|
||||
import AnythingLLMLogo from "@/assets/anything-llm-dark.png";
|
||||
import { X } from "@phosphor-icons/react";
|
||||
import ChatService from "@/models/chatService";
|
||||
import { DotsThreeOutlineVertical, Lightning, X } from "@phosphor-icons/react";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function ChatWindowHeader({
|
||||
sessionId,
|
||||
settings = {},
|
||||
iconUrl = null,
|
||||
closeChat,
|
||||
setChatHistory,
|
||||
}) {
|
||||
const [showingOptions, setShowOptions] = useState(false);
|
||||
|
||||
const handleChatReset = async () => {
|
||||
await ChatService.resetEmbedChatSession(settings, sessionId);
|
||||
setChatHistory([]);
|
||||
setShowOptions(false);
|
||||
};
|
||||
|
||||
export default function ChatWindowHeader({ iconUrl = null, closeChat }) {
|
||||
return (
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex justify-between items-center relative">
|
||||
<img
|
||||
style={{ maxWidth: 100, maxHeight: 20 }}
|
||||
src={iconUrl ?? AnythingLLMLogo}
|
||||
alt={iconUrl ? "Brand" : "AnythingLLM Logo"}
|
||||
/>
|
||||
<button onClick={closeChat} className="text-xl font-bold">
|
||||
<X size={18} />
|
||||
<div className="flex gap-x-1 items-center">
|
||||
{settings.loaded && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowOptions(!showingOptions)}
|
||||
className="hover:bg-gray-100 rounded-sm text-slate-800"
|
||||
>
|
||||
<DotsThreeOutlineVertical
|
||||
size={18}
|
||||
weight={!showingOptions ? "regular" : "fill"}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={closeChat}
|
||||
className="hover:bg-gray-100 rounded-sm text-slate-800"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<OptionsMenu showing={showingOptions} resetChat={handleChatReset} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OptionsMenu({ showing, resetChat }) {
|
||||
if (!showing) return null;
|
||||
return (
|
||||
<div className="absolute bg-white flex flex-col gap-y-2 rounded-lg shadow-lg border border-gray-300 top-[3vh] right-[1vw] max-w-[150px]">
|
||||
<button
|
||||
onClick={resetChat}
|
||||
className="flex items-center gap-x-1 hover:bg-gray-100 text-sm text-gray-700 p-2 rounded-lg"
|
||||
>
|
||||
<Lightning size={14} />
|
||||
<p>Reset Chat</p>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
@ -4,20 +4,21 @@ import useChatHistory from "@/hooks/chat/useChatHistory";
|
||||
import ChatContainer from "./ChatContainer";
|
||||
|
||||
export default function ChatWindow({ closeChat, settings, sessionId }) {
|
||||
const { chatHistory, loading } = useChatHistory(settings, sessionId);
|
||||
const { chatHistory, setChatHistory, loading } = useChatHistory(
|
||||
settings,
|
||||
sessionId
|
||||
);
|
||||
|
||||
if (loading)
|
||||
return (
|
||||
<div>
|
||||
<p>loading...</p>
|
||||
</div>
|
||||
);
|
||||
if (loading) return null;
|
||||
setEventDelegatorForCodeSnippets();
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<ChatWindowHeader
|
||||
sessionId={sessionId}
|
||||
settings={settings}
|
||||
iconUrl={settings.brandImageUrl}
|
||||
closeChat={closeChat}
|
||||
setChatHistory={setChatHistory}
|
||||
/>
|
||||
<ChatContainer
|
||||
sessionId={sessionId}
|
||||
|
@ -5,8 +5,6 @@ export default function SessionId() {
|
||||
if (!sessionId) return null;
|
||||
|
||||
return (
|
||||
<div className="text-xs text-gray-300 w-full text-center">
|
||||
{sessionId}
|
||||
</div>
|
||||
<div className="text-xs text-gray-300 w-full text-center">{sessionId}</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
import ChatService from "@/models/chatService";
|
||||
import { useEffect, useState } from "react";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
const historyURL = ({ embedId, baseApiUrl }, sessionId) =>
|
||||
`${baseApiUrl}/${embedId}/${sessionId}`;
|
||||
|
||||
export default function useChatHistory(settings = null, sessionId = null) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
@ -12,24 +9,10 @@ export default function useChatHistory(settings = null, sessionId = null) {
|
||||
async function fetchChatHistory() {
|
||||
if (!sessionId || !settings) return;
|
||||
try {
|
||||
const formattedMessages = await fetch(historyURL(settings, sessionId))
|
||||
.then((res) => {
|
||||
if (res.ok) return res.json();
|
||||
throw new Error("Invalid response from server");
|
||||
})
|
||||
.then((res) => {
|
||||
return res.history.map((msg) => ({
|
||||
...msg,
|
||||
id: v4(),
|
||||
sender: msg.role === "user" ? "user" : "system",
|
||||
textResponse: msg.content,
|
||||
close: false,
|
||||
}));
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
return [];
|
||||
});
|
||||
const formattedMessages = await ChatService.embedSessionHistory(
|
||||
settings,
|
||||
sessionId
|
||||
);
|
||||
setMessages(formattedMessages);
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
|
@ -16,7 +16,7 @@ const DEFAULT_SETTINGS = {
|
||||
greeting: null, // empty chat window greeting.
|
||||
|
||||
// behaviors
|
||||
openOnLoad: 'off', // or "on"
|
||||
openOnLoad: "off", // or "on"
|
||||
};
|
||||
|
||||
export default function useGetScriptAttributes() {
|
||||
|
@ -2,6 +2,35 @@ import { fetchEventSource } from "@microsoft/fetch-event-source";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
const ChatService = {
|
||||
embedSessionHistory: async function (embedSettings, sessionId) {
|
||||
const { embedId, baseApiUrl } = embedSettings;
|
||||
return await fetch(`${baseApiUrl}/${embedId}/${sessionId}`)
|
||||
.then((res) => {
|
||||
if (res.ok) return res.json();
|
||||
throw new Error("Invalid response from server");
|
||||
})
|
||||
.then((res) => {
|
||||
return res.history.map((msg) => ({
|
||||
...msg,
|
||||
id: v4(),
|
||||
sender: msg.role === "user" ? "user" : "system",
|
||||
textResponse: msg.content,
|
||||
close: false,
|
||||
}));
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
return [];
|
||||
});
|
||||
},
|
||||
resetEmbedChatSession: async function (embedSettings, sessionId) {
|
||||
const { baseApiUrl, embedId } = embedSettings;
|
||||
return await fetch(`${baseApiUrl}/${embedId}/${sessionId}`, {
|
||||
method: "DELETE",
|
||||
})
|
||||
.then((res) => res.ok)
|
||||
.catch(() => false);
|
||||
},
|
||||
streamChat: async function (sessionId, embedSettings, message, handleChat) {
|
||||
const { baseApiUrl, embedId } = embedSettings;
|
||||
const overrides = {
|
||||
|
@ -10,7 +10,7 @@
|
||||
"node": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "cd server && yarn lint && cd ../frontend && yarn lint && cd ../collector && yarn lint",
|
||||
"lint": "cd server && yarn lint && cd ../frontend && yarn lint && cd ../embed && yarn lint && cd ../collector && yarn lint",
|
||||
"setup": "cd server && yarn && cd ../collector && yarn && cd ../frontend && yarn && cd .. && yarn setup:envs && yarn prisma:setup && echo \"Please run yarn dev:server, yarn dev:collector, and yarn dev:frontend in separate terminal tabs.\"",
|
||||
"setup:envs": "cp -n ./frontend/.env.example ./frontend/.env && cp -n ./server/.env.example ./server/.env.development && cp -n ./collector/.env.example ./collector/.env && cp -n ./docker/.env.example ./docker/.env && echo \"All ENV files copied!\n\"",
|
||||
"dev:server": "cd server && yarn dev",
|
||||
|
@ -79,6 +79,23 @@ function embeddedEndpoints(app) {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.delete(
|
||||
"/embed/:embedId/:sessionId",
|
||||
[validEmbedConfig],
|
||||
async (request, response) => {
|
||||
try {
|
||||
const { sessionId } = request.params;
|
||||
const embed = response.locals.embedConfig;
|
||||
|
||||
await EmbedChats.markHistoryInvalid(embed.id, sessionId);
|
||||
response.status(200).end();
|
||||
} catch (e) {
|
||||
console.log(e.message, e);
|
||||
response.sendStatus(500).end();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { embeddedEndpoints };
|
||||
|
@ -38,6 +38,7 @@ const EmbedChats = {
|
||||
where: {
|
||||
embed_id: embedId,
|
||||
session_id: sessionId,
|
||||
include: true,
|
||||
},
|
||||
...(limit !== null ? { take: limit } : {}),
|
||||
...(orderBy !== null ? { orderBy } : { orderBy: { id: "asc" } }),
|
||||
@ -49,45 +50,24 @@ const EmbedChats = {
|
||||
}
|
||||
},
|
||||
|
||||
// forWorkspace: async function (
|
||||
// workspaceId = null,
|
||||
// limit = null,
|
||||
// orderBy = null
|
||||
// ) {
|
||||
// if (!workspaceId) return [];
|
||||
// try {
|
||||
// const chats = await prisma.embed_chats.findMany({
|
||||
// where: {
|
||||
// workspaceId,
|
||||
// include: true,
|
||||
// },
|
||||
// ...(limit !== null ? { take: limit } : {}),
|
||||
// ...(orderBy !== null ? { orderBy } : { orderBy: { id: "asc" } }),
|
||||
// });
|
||||
// return chats;
|
||||
// } catch (error) {
|
||||
// console.error(error.message);
|
||||
// return [];
|
||||
// }
|
||||
// },
|
||||
markHistoryInvalid: async function (embedId = null, sessionId = null) {
|
||||
if (!embedId || !sessionId) return [];
|
||||
|
||||
// markHistoryInvalid: async function (workspaceId = null, user = null) {
|
||||
// if (!workspaceId) return;
|
||||
// try {
|
||||
// await prisma.embed_chats.updateMany({
|
||||
// where: {
|
||||
// workspaceId,
|
||||
// user_id: user?.id,
|
||||
// },
|
||||
// data: {
|
||||
// include: false,
|
||||
// },
|
||||
// });
|
||||
// return;
|
||||
// } catch (error) {
|
||||
// console.error(error.message);
|
||||
// }
|
||||
// },
|
||||
try {
|
||||
await prisma.embed_chats.updateMany({
|
||||
where: {
|
||||
embed_id: embedId,
|
||||
session_id: sessionId,
|
||||
},
|
||||
data: {
|
||||
include: false,
|
||||
},
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
},
|
||||
|
||||
get: async function (clause = {}, limit = null, orderBy = null) {
|
||||
try {
|
||||
|
@ -24,6 +24,7 @@ CREATE TABLE "embed_chats" (
|
||||
"prompt" TEXT NOT NULL,
|
||||
"response" TEXT NOT NULL,
|
||||
"session_id" TEXT NOT NULL,
|
||||
"include" BOOLEAN NOT NULL DEFAULT true,
|
||||
"connection_information" TEXT,
|
||||
"embed_id" INTEGER NOT NULL,
|
||||
"usersId" INTEGER,
|
@ -160,6 +160,7 @@ model embed_chats {
|
||||
prompt String
|
||||
response String
|
||||
session_id String
|
||||
include Boolean @default(true)
|
||||
connection_information String?
|
||||
embed_id Int
|
||||
usersId Int?
|
||||
|
Loading…
Reference in New Issue
Block a user